第8章 Git检出

在上一章我们学习了重置命令(git reset)。重置命令的一个用途就是修改引用(如master)的游标指向。实际上在执行重置命令的时候没有使用任何参数对所要重置的分支名(如master)进行设置,这是因为重置命令实际上所针对的是头指针HEAD。之所以重置命令没有改变头指针HEAD的内容,是因为HEAD指向了一个引用refs/heads/master,所以重置命令体现为分支“游标”的变更,HEAD本身一直指向的是refs/heads/master,并没有在重置时改变。

如果HEAD的内容不能改变而一直都指向master分支,那么Git如此精妙的分支设计岂不是浪费?如果HEAD要改变该如何改变呢?本章将学习检出命令(git checkout),该命令的实质就是修改HEAD本身的指向,该命令不会影响分支“游标”(如master)。

8.1 HEAD的重置即检出

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


$cat.git/HEAD

ref:refs/heads/master


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


$git branch-v

*master 4902dc3 does master follow this new commit?


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


$git checkout 4902dc3^

Note:checking out '4902dc3^'.

You are in 'detached HEAD' state.You can look around,make experimental

changes and commit them,and you can discard any commits you make in this

state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create,you may

do so(now or later)by using-b with the checkout command again.Example:

git checkout-b new_branch_name

HEAD is now at e695606…which version checked in?


出现了大段的输出!翻译一下,Git肯定又在提醒我们了。


$git checkout 4902dc3^

注意:正检出 '4902dc3^'.

您现在处于‘分离头指针’状态。您可以检查、测试和提交,而不影响任何分支。

通过执行另外的一个checkout检出指令会丢弃在此状态下的修改和提交。

如果想保留在此状态下的修改和提交,使用-b参数调用checkout检出指令以

创建新的跟踪分支。如:

git checkout-b new_branch_name

头指针现在指向e695606…提交说明为:which version checked in?


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


$cat.git/HEAD

e695606fc5e31b2ff9038a48a3d363f4c21a3d86


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

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


$git reflog-1

e695606 HEAD@{0}:checkout:moving from master to 4902dc3^


注意上面的reflog是HEAD头指针的变迁记录,而非master分支。

查看一下HEAD和master对应的提交ID,会发现现在它们指向的不一样。


$git rev-parse HEAD master

e695606fc5e31b2ff9038a48a3d363f4c21a3d86

4902dc375672fbf52a226e0354100b75d4fe31e3


前一个是HEAD头指针的指向,后一个是master分支的指向。而且还可以看到执行git checkout命令与执行git reset命令不同,分支(master)的指向并没有改变,仍旧指向原有的提交ID。

现在版本库的HEAD是指向e695606提交的。再做一次提交,HEAD会如何变化呢?具体操作过程如下。

(1)先做一次修改:创建一个新文件detached-commit.txt,添加到暂存区中。


$touch detached-commit.txt

$git add detached-commit.txt


(2)看一下状态,会发现其中有“当前不处于任何分支”的字样,显然这是因为HEAD处于“分离头指针”模式。


$git status

Not currently on any branch.

Changes to be committed:

(use "git reset HEAD<file>…"to unstage)

#

new file:detached-commit.txt

#


(3)执行提交。在提交输出中也会出现[detached HEAD……]的标识,这也是对用户的警示。


$git commit-m "commit in detached HEAD mode."

[detached HEAD acc2f69]commit in detached HEAD mode.

0 files changed,0 insertions(+),0 deletions(-)

create mode 100644 detached-commit.txt


(4)此时头指针指向了新的提交。


$cat.git/HEAD

acc2f69cf6f0ae346732382c819080df75bb2191


(5)再查看一下日志会发现新的提交是建立在之前的提交基础上的。


$git log—graph—pretty=oneline

*acc2f69cf6f0ae346732382c819080df75bb2191 commit in detached HEAD mode.

*e695606fc5e31b2ff9038a48a3d363f4c21a3d86 which version checked in?

*a0c641e92b10d8bcca1ed1bf84ca80340fdefee6 who does commit?

*9e8a761ff9dd343a1380032884f488a2422c495a initialized.


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

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


$git checkout master

Previous HEAD position was acc2f69…commit in detached HEAD mode.

Switched to branch 'master'


因为HEAD头指针重新指向了分支,而不是处于“断头模式”(分离头指针模式)。


$cat.git/HEAD

ref:refs/heads/master


切换之后,之前本地建立的新文件detached-commit.txt不见了。


$ls

new-commit.txt welcome.txt


切换之后,刚才的提交日志也不见了。


$git log—graph—pretty=oneline

*4902dc375672fbf52a226e0354100b75d4fe31e3 does master follow this new commit?

*e695606fc5e31b2ff9038a48a3d363f4c21a3d86 which version checked in?

*a0c641e92b10d8bcca1ed1bf84ca80340fdefee6 who does commit?

*9e8a761ff9dd343a1380032884f488a2422c495a initialized.


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


$git show acc2f69

commit acc2f69cf6f0ae346732382c819080df75bb2191

Author:Jiang Xin<jiangxin@ossxp.com>

Date:Sun Dec 5 15:43:24 2010+0800

commit in detached HEAD mode.

diff—git a/detached-commit.txt b/detached-commit.txt

new file mode 100644

index 0000000..e69de29


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