Git学习之~检出

HEAD的重置即检出

HEAD可以理解为“头指针”,是当前工作区的“基础版本”,当执行提交时,HEAD指向的提交作为新提交的父提交。看看当前HEAD的指向。

1
$ cat .git/HEAD

图片1

可以看出HEAD指向了分支master,此时执行git branch会看到当前处于master分支。

1
$ git branch -v

图片2

现在使用git checkout命令检出该ID的父提交,看看会怎么样。

1
$ git checkout  23995a2

图片3

翻译一下上面的输出结果:

1
2
3
4
5
6
7
8
9
10
$ git checkout  23995a2
注意:正在检出 '23995a2'.

您现在处于'分离头指针'状态,您可以检查、测试和提交,而不影响任何分支。
通过执行另外的一个 checkout 检出指令会丢弃在此状态下的修改和提交。

如果想保留在此状态下的修改和提交,使用 -b 参数调用 checkout 检出指令以创建新的跟踪分支。如:
git checkout -b new_branch_name

头指针现在指向 23995a2... 提交说明为:does master follow this new commit?

什么叫做“分离头指针”状态?查看一下此时的HEAD的内容就明白了。

1
2
$ cat .git/HEAD
23995a2d61d0a2667ba0db8aa4a17b127c1977d1

图片4
原来“分离头指针”状态指的就是HEAD头指针指向了一个具体的提交ID,而不是一个引用(分支)。

查看最新提交的reflog也可以看到当针对提交执行git checkout命令时,HEAD头指针就被更改了:由指向master分支变成了指向一个提交ID。

1
2
$ git reflog -l
23995a2 (HEAD, master) HEAD@{0}: checkout: moving from master to 23995a2

图片5

注意上面的reflog是HEAD头指针的变迁记录,而非master分支。
查看一下HEAD和master对应的提交ID,会发现现在它们指向的不一样。

1
$ git rev-parse HEAD master

前一个是HEAD头指针的指向,后一个是master分支的指向。而且还可以看到执行git checkout命令与执行git reset命令不同,分支(master)的指向并没有改变,仍旧指向原有的提交ID。
现在版本库的HEAD是指向23995a提交的,再做一次提交,HEAD会如何变化?

  • 先做一次修改:创建一个新文件detached-commit.txt,添加到暂存区中。
1
2
$ touch detached-commit.txt 
$ git add detached-commit.txt
  • 看一下状态,会发现其中有“当前不处于任何分支”的字样,显然这是因为HEAD处于“分离头指针”模式。
1
$ git status

图片6

  • 执行提交。在提交输出中也会出现[detached HEAD...]的标识,这也是对用户的警示。
1
$ git commit -m "commit in detached HEAD mode"

图片7

  • 此时头指针指向了新的提交。
1
2
$ cat .git/HEAD
1fe77f7b9993cd79b050826df1c73715cc1d1e45

图片8

  • 再查看一下日志会发现新的提交是建立在之前的提交基础上的。
1
$ git log --graph --oneline

图片9

记下新的提交ID(1fe77f7b9993cd79b050826df1c73715cc1d1e45),然后以master分支名作为参数执行git checkout命令,会切换到master分支上。

  • 切换到master分支上,再没有之前大段的文字警告。
1
$ git checkout master

图片10

  • 因为HEAD头指针重新指向了分支,而不是处于“分离头指针模式”。
1
$ cat .git/HEAD

图片11

  • 切换之后,之前本地建立的detached-commit.txt文件不见了。
1
2
$ ls
new-commit.txt welcome.txt

图片12

  • 切换之后,刚才的提交日志也不见了。
1
$ git log --graph --oneline

图片13

刚才的提交还存在于版本库的对象库中吗?看看刚才记下的提交ID。

1
$ git show 1fe77

图片14
可以看出这个提交现在仍在版本库中。由于这个提交没有被任何分支跟踪到,因此不能保证这个提交会永久存在。实际上当reflog中含有该提交的日志过期后,这个提交随时都会从版本库中彻底清除。

挽救分离头指针

在“分离头指针”模式下进行的测试提交除了使用ID(1fe77)访问之外,不能通过master分支或其他引用访问到。如果这个提交是master分支所需的,那么该如何处理?
如果使用git reset,的确可以将master分支重置到该测试提交的1fe77,但这就会丢掉master分支原先的提交23995a2。使用合并操作git merge可以实现两者的兼顾。
下面的操作会将提交1fe77合并到master分支中来,具体操作如下:

  • 确认当前处于master分支。
1
$ git branch -v

图片15

  • 执行合并操作,将1fe77提交合并到当前分支。
1
$ git merge 1fe77

图片16

  • 工作区中多了一个detached-commit.txt文件。
    图片17

深入了解git checkout命令

git checkout是Git常用的命令之一,同时也是很危险的命令,因为这条命令会重写工作区。检出命令的用法如下:

1
2
3
用法一: git checkout [-q] [<commit>] [--] <paths>...
用法二: git checkout [<branch>]
用法三: git checkout [-m] [[-b|--orphan] <new_branch>] [<start_point>]

用法一和用法二的区别在于,用法一在命令中包含路径。为了避免路径和引用(或者提交ID)同名而发生冲突,可以在前用两个连续的短线作为分隔。

第一种用法的是可选项,如果省略相当于从暂存区(index)进行检出。这和重置命令大不相同:重置的默认值是HEAD,而检出的默认值是暂存区。
因此重置一般用于重置暂存区(除非使用–hard参数,否则不重置工作区),而检出命令主要是覆盖工作区(如果不省略,也会替换暂存区中相应的文件)。

用法一(包含了路径的用法)不会改变HEAD头指针,主要是用于指定版本的文件覆盖工作区中对应的文件。如果省略,则会用暂存区的文件覆盖工作区的文件,否则用指定提交中的文件覆盖暂存区和工作区中对应的文件。

用法二(不使用路径的用法)则会改变HEAD头指针。之所以后面的参数写作,是因为只有HEAD切换一个分支才可以对提交进行跟踪,否则仍然会进入“分离头指针”状态。在“分离头指针”状态下的提交不能被引用关联到,从而可能丢失。
用法二的最主要作用就是切换到分支。如果省略则相当于对工作区进行状态检查。

用法三主要是创建和切换到新分支(),新的分支从指定的提交开始创建。新分支和我们熟悉的master分支没有什么实质的不同,都是在refs/heads命名空间下引用。

本文标题:Git学习之~检出

文章作者:王洪博

发布时间:2018年05月24日 - 21:05

最后更新:2019年09月12日 - 10:09

原始链接:http://whb1990.github.io/posts/b27802af.html

▄︻┻═┳一如果你喜欢这篇文章,请点击下方"打赏"按钮请我喝杯 ☕
0%