git 基础
本章涵盖了在使用Git完成各种工作中将要使用的各种基本命令。在学习完本章后,你应该能配置并初始化一个仓库(repository)、开始或停止跟踪(track)文件、暂存(stage)或提交更改(commit)。同时本章也演示如何配置Git来忽略指定的文件和文件模式、如何迅速而简单地撤销错误操作、如何浏览你的项目的历史版本以及不同提交间的差异、如何向你的远程仓库推送以及如何从你的远程仓库拉取文件
一、获取git仓库
1. 在现有目录中初始化仓库
如果打算将一个本地文件夹使用git来进行管理,只需在该文件夹目录下执行
git init
该命令将会创建一个名为 .git 的子目录,这个子目录含有你初始化的Git仓库中所有的必须文件,这些文件是Git仓库的骨干。但是这一步,我们只是做了一个初始化的操作,项目里的文件并没有被跟踪。
如果当前这个文件夹里有其他文件的话,通过下列命令来跟踪这些文件并提交
git add *.c git add LICENSE git commit -m 'initial project version'
现在,你已经得到了一个实际维护(或跟踪)着若干个文件的Git仓库
2. 克隆现有的仓库
如果你想获得一份已经存在了的Git仓库的拷贝,需要使用 git clone 命令。Git 克隆的是该Git仓库服务器上的几乎所有数据,而不是仅仅复制完成你的工作所需要文件,这是Git区别于其他版本控制系统的一个重要特性。
克隆仓库的命令格式是git clone [url]。比如,要克隆Git的可链接库 libgit2,可使用如下命令:
git clone https://github.com/libgit2/libgit2.git
这会在当前目录下创建一个名为 “libgit2” 的目录,并在这个目录下初始化一个 .git 文件夹,从远程仓库拉取下所有数据放入 .git 文件夹,然后从中读取最新版本的文件的拷贝。如果你想克隆远程仓库的时候,自定义本地仓库的名字,可以使用如下命令,在本地创建的仓库名字将变为 mylibgit
git clone https://github.com/libgit2/libgit2.git mylibgit
git 支持多种传输协议,上面使用的是 https, 也可以使用git协议或者ssh传输协议,比如 user@server:path/to/repo.git
二、记录每次更新到仓库
工作目录下的每一个文件不外乎两种状态:已跟踪或未跟踪。已跟踪是指那些被纳入了版本控制的文件,再上一次快照中有他们的记录,工作一段时间后,他们的状态可能处于未修改,已修改或者已放入暂存区。工作目录中除已跟踪文件以外的其他文件都属于未跟踪文件,他们既不存在于上次快照的记录中,也没有放入暂存区。
编辑过某些文件之后,由于自上次提交后你对他们做了修改,Git将他们标记为已修改文件。我们将这些修改过的文件放入暂存区,然后提交所有暂存了的修改,如此反复。
1. 检查当前文件状态:
要查看哪些文件处于什么状态,可以使用 git status 命令。
# 这说明你现在的工作目录相当干净。换句话说,所有已跟踪文件在上次提交后都未被更改过。 此外,上面的信息还表明,当前目录下没有出现任何处于未跟踪状态的新文件,否则 Git 会在这里列出来。 最后,该命令还显示 了当前所在分支,并告诉你这个分支同远程服务器上对应的分支没有偏离。 $ git status On branch master nothing to commit, working directory clean # 现在可以看到有一个新建的README文件出现在Untracked files下面。未跟踪的文件意味着Git在之前 的快照(提交)中没有这些文件;Git 不会自动将之纳入跟踪范围,除非你明明白白地告诉它“我需要跟踪该文 件”。 $ git status On branch master Untracked files: (use "git add <file>..." to include in what will be committed) README nothing added to commit but untracked files present (use "git add" to track)
git status 命令的输出十分详细,如果使用 git status -s 或者 git status --short,将得到一种更为紧凑的格式输出。
# 新添加的未跟踪文件前面有 ?? 标记 # 新添加到暂存区中的文件前面有 A 标记 # 修改过的文件前面有 M 标记。出现在右边的 M 表示该文件被修改了但是还没放入暂存区;出现在靠左 边的 M 表示该文件被修改了并放入了暂存区。两个M表示在工作区被修改并提交到暂存区后又在工作区中被修改了。 $ git status -s M README MM Rakefile A lib/git.rb M lib/simplegit.rb ?? LICENSE.txt
2. 跟踪新文件、暂存已修改文件
使用命令 git add 开始跟踪一个文件,或者暂存已修改文件
$ git add README
此时再查看文件状态,会发现README文件已经被跟踪,并处于暂存状态
$ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: README
3. 忽略文件
有些文件无需纳入Git的管理,也不希望他们总出现在未跟踪文件列表。这种情况下,我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件模式。
# 第一行告诉 Git 忽略所有以 .o 或 .a 结尾的文件。一般这类对象文件和存档文件都是编译过程中出现的。 # 第二 行告诉 Git 忽略所有以波浪符(~)结尾的文件 # 此外,你可能还需要忽略 log,tmp 或者 pid 目录,以及自动生成的文档等等。 $ cat .gitignore *.[oa] *~
要养成一开始就设置好 .gitignore 文件的习惯,以免将来误提交这类无用的文件
文件 .gitignore 的格式规范如下:
• 所有空行或者以 # 开头的行都会被 Git 忽略。
• 可以使用标准的 glob 模式匹配。
• 匹配模式可以以(/)开头防止递归。
• 匹配模式可以以(/)结尾指定目录。
• 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。
# no .a files *.a # but do track lib.a, even though you're ignoring .a files above !lib.a # only ignore the TODO file in the current directory, not subdir/TODO /TODO # ignore all files in the build/ directory build/ # ignore doc/notes.txt, but not doc/server/arch.txt doc/*.txt # ignore all .pdf files in the doc/ directory doc/**/*.pdf
4. 查看已暂存和未暂存的修改
git diff 将通过文件补丁的格式显示具体哪些行发生了改变
要查看尚未暂存的文件更新了哪些部分,不加参数直接输入 git diff
若要查看已暂存的将要添加到下次提交里的内容,可以用 git diff --cached 命令 或者 git diff --staged
5. 提交更新
每次提交前,先用git status看下,是不是都已暂存起来了,然后再运行提交命令 git commit,这种方式会启动文本编辑器以便输入本次提交的说明。
另外,也可以在commit命令后添加 -m 选项,将提交信息与命令放在同一行。
$ git commit -m "Story 182: Fix benchmarks for speed"
6. 跳过使用暂存区域
Git提供了一个跳过使用暂存区域的方式,只要在提交的时候,给 git commit 加上 -a选项,Git就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过git add步骤。
7. 移除文件
要从Git中移除某个文件,就必须要从已跟踪文件清单中移除(确切的说,是从暂存区域移除),然后提交。
可以使用 git rm 命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了。
如果只是简单地从工作目录中手工删除文件,运行 git status 时就会在 “Changes not stated for commit” 部分。
$ rm PROJECTS.md $ git status On branch master Your branch is up-to-date with 'origin/master'. Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) deleted: PROJECTS.md no changes added to commit (use "git add" and/or "git commit -a")
然后再运行 git rm 记录此次移除文件的操作,下一次提交时,该文件就不再纳入版本管理了。
$ git rm PROJECTS.md rm 'PROJECTS.md' $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) deleted: PROJECTS.md
如果删除之前已经修改过并且已经放到暂存区域的文件,则必须要用强制删除选项 -f 。 这是一种安全特性,用于防止误删还没有添加到快照的数据,这样的数据不能被Git恢复。
另外一种情况:我们想让文件保留在本地,但是并不想让Git继续跟踪。当你忘记添加 .gitignore文件,不小心把日志文件或者编译生成文件添加到暂存区时,这一做法尤其有用,为达这一目的,使用 --cached 选项。 git rm 命令后面可以列出文件或者目录的名字。
git rm --cached README
# 此命令删除log/目录下扩展名为.log的所有文件
git rm log/*.log
8. 移动文件
要在Git中对文件改名,可以这么做:
$ git mv file_from file_to
它相当于以下三条命令:
$ mv file_from file_to
$ git rm file_from
$ git add file_to
9. 查看提交历史
$ git log commit ca82a6dff817ec66f44342007202690a93763949 Author: Scott Chacon <schacon@gee-mail.com> Date: Mon Mar 17 21:52:11 2008 -0700 changed the version number commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Author: Scott Chacon <schacon@gee-mail.com> Date: Sat Mar 15 16:40:33 2008 -0700 removed unnecessary test commit a11bef06a3f659402fe7563abf99ad00de2209e6 Author: Scott Chacon <schacon@gee-mail.com> Date: Sat Mar 15 10:31:28 2008 -0700 first commit
git log 不带任何参数的话,会按提交时间列出所有更新,最近的更新排在上面。
选项:
-p: 用来显示每次提交的内容差异, 也可以加上 -2 来仅显示最近两次提交。
--stat:显示每次提交的简略统计信息
--pretty:指定使用不同于默认格式的方式展示提交历史。这个选项有一些内建的子选项供你使用。比如用 oneline 将每个提交放在一行显示,查看的提交书很大时非常有用。另外,还有short、full 和 fuller 可以用。format 可以定制要显示的记录格式
--graph:当 online 或者 format 与 --graph 结合使用时尤其有用。这个选项形象地展示了你的分支、合并历史
-S:可以列出那些添加或移除了某些字符串的提交。
-- {path}: 如果只关心某些文件或者目录的提交历史,可以在gitlog选项的最后指定他们的路径。因为是放在最后位置的选项, 所以我们用两个短划线(--)隔开之前的选项和后面限定的路径名
$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 changed the version number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test
a11bef06a3f659402fe7563abf99ad00de2209e6 first commit
$ git log --pretty=format:"%h - %an, %ar : %s" ca82a6d - Scott Chacon, 6 years ago : changed the version number 085bb3b - Scott Chacon, 6 years ago : removed unnecessary test a11bef0 - Scott Chacon, 6 years ago : first commit
$ git log --pretty=format:"%h %s" --graph * 2d3acf9 ignore errors from SIGCHLD on trap * 5e3ee11 Merge branch 'master' of git://github.com/dustin/grit | | * 420eac9 Added a method for getting the current branch. * | 30e367c timeout code and tests * | 5a09431 add timeout protection to grit * | e1193f8 support for heads with slashes in them |/ * d6016bc require time for xmlschema * 11d191e Merge branch 'defunkt' into local
# 找出添加或移除了某一个特定函数的饮用的提交
$ git log -Sfunction_name
三、撤销操作
在任何一个阶段,你都有可能想撤销某些操作。但是有些撤销操作是不可逆的,这是在使用Git过程中,会因为操作失误而导致之前工作丢失的少有的几个地方之一。
有时我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。此时可以运行带有 --amend 选项的提交命令尝试重新提交。这个命令会将暂存取中的文件提交,如果自上次提交以来你还未做任何修改(例如,在上次提交之后马上执行了此命令),那么快照会保持不变,而你所修改的只是提交信息。
# 下面的操作最终只会有一个提交,第二次提交将会代替第一次提交的结果 $ git commit -m 'initial commit' $ git add forgotten_file $ git commit --amend
1. 取消暂存的文件
如果你修改了两个文件并且想要将它们作为两次独立的修改提交,但却意外的输入了 git add * 暂存了他们两个,如何取消一个暂存呢?
git status 命令提示你,在 “changes to be committed” 文字下方,提示使用 git reset HEAD <file> ...来取消暂存。
如: $ git reset HEAD CONTRIBUTING.md
虽然在使用时加上 --hard 选项可以令 git reset 成为一个危险的命令,可能导致工作目录中当前进度丢失,但是不加选项地使用 git reset 并不危险——他指挥修改暂存区域。
2. 撤销对文件的修改
如果你并不想保留对某个文件所做的修改,该如何方便的撤销修改——将它还原成上次提交时的样子?
git status 也提示了应该如何做, 在“Changes not stages for commit”下方,提示 use "git chechout -- <file> ..." to discard changes in working directory
如:$ git checkout -- CONTRIBUTING.md
git checkout -- <file> 是一个危险的命令,你对那个文件做的任何修改都会消失—你只是拷贝了另一个文件来覆盖它。
除非你确实清楚不想要那个文件了,否则不要使用这个命令。
四、远程仓库的使用
为了能在任意 Git 项目上协作,你需要知道如何管理自己的远程仓库。远程仓库是指托管在因特网或其他网络中的你的项目的版本库。
你可以有好几个远程仓库,有些仓库对你只读,有些可以读写。与他人协作涉及管理远程仓库以及根据需要推送或拉取数据。管理远程仓库包括了解如何添加远程仓库、移除无效的远程仓库、管理不同的远程分支并定义它们是否被跟踪的等等。
1. 查看远程仓库
如果想查看你已经配置的远程仓库服务器,可以运行git remote 命令,它会列出你指定的每一个远程服务器的简写。
你也可以指定选项 -v ,会显示需要读写远程仓库使用的Git保存的简写与其对应的URL
如果想查看某一个远程仓库的更多信息,可以使用 git remote show [remote-name] 命令
$ git remote
origin
$ git remote -v origin https://github.com/schacon/ticgit (fetch) origin https://github.com/schacon/ticgit (push)
$ git remote show origin * remote origin
Fetch URL: https://github.com/schacon/ticgit Push URL: https://github.com/schacon/ticgit HEAD branch: master Remote branches:
master tracked
dev-branch tracked Local branch configured for 'git pull':
master merges with remote master Local ref configured for 'git push':
master pushes to master (up to date)
2. 添加远程仓库
运行 git remote add <shortname> <url> 添加一个新的远程 Git 仓库,同时指定一个你可以轻松引用的简写
$ git remote origin $ git remote add pb https://github.com/paulboone/ticgit $ git remote -v origin https://github.com/schacon/ticgit (fetch) origin https://github.com/schacon/ticgit (push) pb https://github.com/paulboone/ticgit (fetch) pb https://github.com/paulboone/ticgit (push) # 现在你可以在命令行中使用 pb 来代替整个 url
3. 从远程仓库抓取与拉取
从远程仓库获取数据,可以执行:
$ git fetch [remote-name]
如果你使用clone命令克隆了一个仓库,命令会自动将其添加为远程仓库并默认以“origin”为简写。
git fetch命令会将数据拉取到你的本地仓库,它并不会自动合并或修改你当前的工作。当准备好时你必须手动将其合并入你的工作。
4. 推送到远程仓库
当你想分享你的项目时,必须将其推送到上游。只有当你有所克隆服务器的写入权限,并且之前没有人推送过时,这条命令才能生效。
如果有其他人先推送到上游,你再推送,你的推送就会被拒绝。你必须先将它们的工作拉取下来并将其合并进你的工作后才能推送。
git push [remote-name] [branch-name]
$ git push origin master
5. 远程仓库的移除与重命名
如果想重命名引用的名字可以运行 git remote rename 去修改一个远程仓库的简写名。
这样同样也会修改你的远程分支名字,那些过去引用pb/master 的现在会引用 paul/master
如果因为某些原因想要移除一个远程仓库,可以使用 git remote rm
$ git remote rename pb paul
$ git remote
origin
paul
$ git remote rm paul $ git remote origin