@lemonguge
2017-09-13T14:49:21.000000Z
字数 10110
阅读 386
Git
分布式版本控制系统(Distributed Version Control System,简称 DVCS)
Git就属于分布式版本控制系统,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。
Git 有三种状态,你的文件可能处于其中之一:已提交(committed)、已修改(modified)和已暂存(staged)。已提交表示数据已经安全的保存在本地数据库中。已修改表示修改了文件,但还没保存到数据库中。已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
由此引入 Git 项目的三个工作区域的概念:Git仓库、工作目录以及暂存区域。

工作目录是对项目的某个版本独立提取出来的内容。这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。
暂存区域是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。有时候也被称作“索引”,不过一般说法还是叫暂存区域。
Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,拷贝的就是这里的数据。
基本的 Git 工作流程如下:
如果 Git 目录中保存着的特定版本文件,就属于已提交状态。如果作了修改并已放入暂存区域,就属于已暂存状态。如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。
Git 配置文件共有 3 个,仓库级别配置文件、全局级别配置文件、系统级别配置文件
--local 仓库级别:位于当前仓库下,目录路径 .git/,文件名为 config,这个配置中的设置只对当前所在仓库有效,--global 全局级别:在用户目录下,~/.gitconfig 文件--system 系统级别:在 Git 的安装目录下,目录路径 etc/,文件名为 gitconfiggit config 命令中的 --list 选项的简写是 -l,可以加入配置级别选项查看指定级别的配置,git config --local -l 查看仓库级别的配置。
$ git config --global user.name "HomJie"$ git config --global user.email lemonguge@example.com
如果使用了 --global 选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, Git 都会使用那些信息。当你想针对特定项目使用不同的用户名称与邮件地址时,可以在那个项目目录下运行 --local 选项的命令来配置。
git config --list 命令来列出所有 Git 当时能找到的配置(三个级别的配置汇总)。git config <key> 来检查 Git 的某一项配置。
$ git config user.nameHomJie
如果想删除某个配置,需要知道该配置在哪个级别,使用指定配置级别的 --unset 选项。例如, git config --global --unset user.name 删除用户名称配置。
如果你打算使用 Git 来对现有的项目进行管理,你只需要进入该项目目录并输入:
$ git init
该命令将创建一个名为 .git 的子目录,这个子目录含有你初始化的 Git 仓库中所有的必须文件,这些文件是 Git 仓库的骨干。但是,在这个时候,我们仅仅是做了一个初始化的操作,你的项目里的文件还没有被跟踪。
如果你想获得一份已经存在了的 Git 仓库的拷贝,比如说,你想为某个开源项目贡献自己的一份力,这时就要用到 git clone 命令。当你执行 git clone 命令的时候,默认配置下远程 Git 仓库中的每一个文件的每一个版本都将被拉取下来。事实上,如果你的服务器的磁盘坏掉了,你通常可以使用任何一个克隆下来的用户端来重建服务器上的仓库。
克隆仓库的命令格式是 git clone [url] 。 比如,要克隆 Git 的可链接库 libgit2,可以用下面的命令:
$ git clone https://github.com/libgit2/libgit2
这会在当前目录下创建一个名为 “libgit2” 的目录,并在这个目录下初始化一个 .git 文件夹,从远程仓库拉取下所有数据放入 .git 文件夹,然后从中读取最新版本的文件的拷贝。 如果你进入到这个新建的 libgit2 文件夹,你会发现所有的项目文件已经在里面了,准备就绪等待后续的开发和使用。
如果你想在克隆远程仓库的时候,自定义本地仓库的名字,你可以使用如下命令:
$ git clone https://github.com/libgit2/libgit2 mylibgit
这将执行与上一个命令相同的操作,不过在本地创建的仓库名字变为 mylibgit。
工作目录下的每一个文件都不外乎这两种状态:已跟踪或未跟踪
初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态。编辑过某些文件之后,由于自上次提交后你对它们做了修改,Git 将它们标记为已修改文件。我们逐步将这些修改过的文件放入暂存区,然后提交所有暂存了的修改,如此反复。所以使用 Git 时文件的生命周期如下:

一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表
通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。在这种情况下,我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件模式。来看一个实际的例子:
$ cat .gitignore*.[oa]*~
第一行告诉 Git 忽略所有以 .o 或 .a 结尾的文件。一般这类对象文件和存档文件都是编译过程中出现的。第二行告诉 Git 忽略所有以波浪符(~)结尾的文件,许多文本编辑软件(比如 Emacs)都用这样的文件名保存副本。
文件 .gitignore 的格式规范如下:
# 开头的行都会被 Git 忽略。/)开头防止递归。/)结尾指定目录。!)取反。所谓的 glob 模式是指 shell 所使用的简化了的正则表达式,常用的符号匹配如下:
*)匹配零个或多个任意字符;[abc] 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);?)只匹配一个任意字符;[0-9] 表示匹配所有 0 到 9 的数字,范围内的都可以匹配;*)表示匹配任意中间目录,比如 a/**/z 可以匹配 a/z、a/b/z 或 a/b/c/z等。再看一个 .gitignore 文件的例子:
# 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/ directorybuild/# ignore doc/notes.txt, but not doc/server/arch.txtdoc/*.txt# ignore all .pdf files in the doc/ directorydoc/**/*.pdf
要查看哪些文件处于什么状态,可以用 git status 命令。如果在克隆仓库后立即使用此命令,会看到类似这样的输出:
$ git statusOn branch masternothing to commit, working directory clean
On branch master 表示当前分支名是 “master”,这是默认的分支名。nothing to commit, working directory clean 表示现在的工作目录相当干净,所有已跟踪文件在上次提交后都未被更改过,当前目录下没有出现任何处于未跟踪状态的新文件。在工作目录中创建一个新的文件 README 文件,如果之前并不存在这个文件,使用 git status 命令,你将看到一个新的未跟踪文件:
$ git statusOn branch masterUntracked files:(use "git add <file>..." to include in what will be committed)READMEnothing added to commit but untracked files present (use "git add" to track)
在状态报告中可以看到新建的 README 文件出现在 Untracked files 下面。
使用命令 git add 开始跟踪一个文件。 所以,要跟踪 README 文件,运行:
$ git add README
此时再运行 git status 命令,会看到 README 文件已被跟踪,并处于暂存状态:
$ git statusOn branch masterChanges to be committed:(use "git reset HEAD <file>..." to unstage)new file: README
只要在 Changes to be committed 这行下面的,就说明是已暂存状态。
现在的暂存区域已经准备妥当可以提交了。在此之前,请一定要确认还有什么修改过的或新建的文件还没有 git add 过,否则提交的时候不会记录这些还没暂存起来的变化。这些修改过的文件只保留在本地磁盘。 所以,每次准备提交前,先用 git status 看下,是不是都已暂存起来了,然后再运行提交命令 git commit:
$ git commit
这种方式会启动文本编辑器以便输入本次提交的说明,使用 git config --global core.editor 命令设定你喜欢的编辑软件。
另外,你也可以在 commit 命令后添加 -m 选项,将提交信息与命令放在同一行,如下所示:
$ git commit -m "README commit"[master 901fc25] README commit1 file changed, 1 insertion(+)create mode 100644 README
现在你已经创建了第一个提交!可以看到,提交后它会告诉你,当前是在哪个分支(master)提交的,本次提交的完整 SHA-1 校验和是什么(463dc4f),以及在本次提交中,有多少文件修订过,多少行添加和删改过。
对刚提交的 README 文件进行修改,然后运行 git status 命令,会看到下面内容:
$ git statusOn branch masterChanges not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified: READMEno changes added to commit (use "git add" and/or "git commit -a")
文件 README 出现在 Changes not staged for commit 这行下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。要暂存这次更新,需要运行 git add 命令。
git add 多功能命令:
1. 开始跟踪新文件
2. 把修改后的已跟踪的文件放到暂存区
3. 合并时把有冲突的文件标记为已解决状态
现在让我们运行 git add 将"README"放到暂存区,然后再看看 git status 的输出:
$ git add README$ git statusOn branch masterChanges to be committed:(use "git reset HEAD <file>..." to unstage)modified: README
假设此时,你想要在 README 重新编辑保存后,再运行 git status 看看:
$ git statusOn branch masterChanges to be committed:(use "git reset HEAD <file>..." to unstage)modified: READMEChanges not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified: README
可以看到 README 文件同时出现在暂存区和非暂存区。实际上 Git 只不过暂存了你运行 git add 命令时的版本,如果你现在提交,README 的版本是你最后一次运行 git add 命令时的那个版本,而不是你运行 git commit 时,在工作目录中的当前版本。所以,运行了 git add 之后又作了修订的文件,需要重新运行 git add 把最新版本重新暂存起来:
$ git add README$ git statusOn branch masterChanges to be committed:(use "git reset HEAD <file>..." to unstage)modified: README
git status 命令的输出十分详细,但其用语有些繁琐。如果你使用 git status -s 命令或 git status --short 命令,你将得到一种更为紧凑的格式输出。运行 git status -s ,状态报告输出如下:
$ git status -sM README
标记含义:
?? 标记:新添加的未跟踪文件A 标记:新添加到暂存区中的文件M 标记:修改过的文件,具体含义看在左还是右,如下MM 标记:右边的 M 表示该文件被修改了但是还没放入暂存区,出现在靠左边的 M 表示该文件被修改了并放入了暂存区。某 git status -s 输出如下:
$ git status -sM index.htmlMM RakefileA lib/git.rbM lib/simplegit.rb?? LICENSE.txt
M 标记在右边,该文件在工作区被修改了但是还没有将修改后的文件放入暂存区如果 git status 命令的输出对于你来说过于模糊,你想知道具体修改了什么地方,可以用 git diff 命令。
git diffgit diff --staged在刚刚的命令中,已经将 README 文件进行了暂存,使用命令:
$ git diff$ git diff --cacheddiff --git a/README b/READMEindex 557db03..4254c06 100644--- a/README+++ b/README@@ -1 +1,2 @@-Hello World+Hello Git+UPDATE
可以看到使用 git diff 没有任何输出,而使用 git diff --cached 能够看到改动差异,当提交 README 文件后,再进行编辑,保存不暂存,使用 git diff 命令:
$ git statusOn branch masterChanges not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified: READMEno changes added to commit (use "git add" and/or "git commit -a")$ git diffdiff --git a/README b/READMEindex 4254c06..646ffe9 100644--- a/README+++ b/README@@ -1,2 +1,3 @@Hello Git-UPDATE+delete+Befora staged
当然也可以使用 git diff --staged 来代替 git diff --cached,--staged 和 --cached 是同义词。
尽管使用暂存区域的方式可以精心准备要提交的细节,但有时候这么做略显繁琐。 Git 提供了一个跳过使用暂存区域的方式,只要在提交的时候,给 git commit 加上 -a 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤:
$ git statusOn branch masterChanges not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified: READMEno changes added to commit (use "git add" and/or "git commit -a")$ git commit -a -m "skip git add"[master 59215ad] skip git add1 file changed, 2 insertions(+), 1 deletion(-)$ git statusOn branch masternothing to commit, working directory clean
要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交
可以用 git rm 命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了。
如果只是简单地从工作目录中手工删除文件,运行 git status 时就会在 “Changes not staged for commit” 部分(也就是 未暂存清单)看到:
$ rm README$ git statusOn branch masterChanges 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: READMEno changes added to commit (use "git add" and/or "git commit -a")
使用 git add 或者 git rm 命令来记录此次移除文件的操作:
$ git rm PROJECTS.mdrm 'PROJECTS.md'$ git statusOn branch masterChanges to be committed:(use "git reset HEAD <file>..." to unstage)deleted: PROJECTS.md
下一次提交时,该文件就不再纳入版本管理了。
如果想删除修改之后还没有暂存起来的文件,或者已经放到暂存区域但未提交的文件,则必须要用强制删除选项 -f。这是一种安全特性,用于防止误删还没有添加到快照的数据,这样的数据不能被 Git 恢复。
$ git reset HEAD READMEUnstaged changes after reset:D README$ git statusOn branch masterChanges 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: READMEno changes added to commit (use "git add" and/or "git commit -a")$ git checkout -- README$ lsREADME$ git statusOn branch masternothing to commit, working directory clean
执行上面命令取消暂存和撤销删除,对 README 文件进行编辑后保存,执行如下 git rm 命令:
$ git statusOn branch masterChanges not staged for commit:(use "git add <file>..." to update what will be committed)(use "git checkout -- <file>..." to discard changes in working directory)modified: READMEno changes added to commit (use "git add" and/or "git commit -a")$ git rm READMEerror: the following file has local modifications:README(use --cached to keep the file, or -f to force removal)
可以看到对修改之后还没有暂存起来的文件进行删除失败了,如果对已暂存的将要添加到下次提交里的文件进行删除:
$ git add README$ git statusOn branch masterChanges to be committed:(use "git reset HEAD <file>..." to unstage)modified: README$ git rm READMEerror: the following file has local modifications:README(use --cached to keep the file, or -f to force removal)
同样对已暂存的将要添加到下次提交里的文件删除失败了。
另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。换句话说,你想让文件保留在磁盘,但是并不想让 Git 继续跟踪。
$ git commit -m "between git rm"[master e9014cb] between git rm1 file changed, 1 insertion(+)$ git statusOn branch masternothing to commit, working directory clean$ git rm --cached READMErm 'README'$ git statusOn branch masterChanges to be committed:(use "git reset HEAD <file>..." to unstage)deleted: READMEUntracked files:(use "git add <file>..." to include in what will be committed)README
git rm 命令后面可以列出文件或者目录的名字,也可以使用 glob 模式。 比方说:
$ git rm log/\*.log
注意到星号 * 之前的反斜杠 \, 因为 Git 有它自己的文件模式扩展匹配方式,所以我们不用 shell 来帮忙展开。 此命令删除 log/ 目录下扩展名为 .log 的所有文件。 类似的比如:
$ git rm \*~
该命令为删除以 ~ 结尾的所有文件。
使用 git mv 命令移动文件或者重命名文件
$ git reset HEAD README$ git statusOn branch masternothing to commit, working directory clean$ git mv README RD$ git statusOn branch masterChanges to be committed:(use "git reset HEAD <file>..." to unstage)renamed: README -> RD