第7章 Git重置

上一章讲解了版本库中对象的存储方式,以及分支master的实现。即master分支在版本库的引用目录(.git/refs)中体现为一个引用文件.git/refs/heads/master,其内容就是分支中最新提交的提交ID。


$cat.git/refs/heads/master

e695606fc5e31b2ff9038a48a3d363f4c21a3d86


上一章还通过对提交本身数据结构的分析看到,提交可以通过对父提交的关联实现对提交历史的追溯。注意,下面的git log命令中使用了—oneline参数,类似于—pretty=oneline,但是可以显示更短小的提交ID。参数—oneline在Git 1.6.3及以后的版本中才有,老版本的Git可以使用参数—pretty=oneline—abbrev-commit替代。


$git log—graph—oneline

*e695606 which version checked in?

*a0c641e who does commit?

*9e8a761 initialized.


那么,是不是有新的提交发生的时候,master分支对应的引用文件中的内容就会改变呢?master分支对应的引用文件中的内容可以人为地改变吗?本章就来探讨如何用git reset命令改变分支引用文件的内容,即实现分支的重置。

7.1 分支游标master探秘

先来看看当有新的提交发生的时候,文件.git/refs/heads/master的内容如何改变。首先在工作区创建一个新文件,姑且叫作new-commit.txt,然后提交到版本库中。


$touch new-commit.txt

$git add new-commit.txt

$git commit-m "does master follow this new commit?"

[master 4902dc3]does master follow this new commit?

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

create mode 100644 new-commit.txt


此时工作目录下会有两个文件,其中文件new-commit.txt是新增的。


$ls

new-commit.txt welcome.txt


来看看master分支指向的提交ID是否改变了。

可以看出在版本库引用空间(.git/refs/目录)下的master文件内容的确改变了,指向了新的提交。


$cat.git/refs/heads/master

4902dc375672fbf52a226e0354100b75d4fe31e3


再用git log查看一下提交日志,可以看到刚刚完成的提交。


$git log—graph—oneline

*4902dc3 does master follow this new commit?

*e695606 which version checked in?

*a0c641e who does commit?

*9e8a761 initialized.


引用refs/heads/master就好像是一个游标,在有新的提交发生的时候指向了新的提交。可是如果只可上、不可下,就不能称为“游标”。Git提供了git reset命令,可以将“游标”指向任意一个存在的提交ID。下面的示例就尝试人为地更改游标。(注意下面的命令中使用了—hard参数,会破坏工作区未提交的改动,慎用。)


$git reset—hard HEAD^

HEAD is now at e695606 which version checked in?


还记得上一章介绍的HEAD^代表了HEAD的父提交吗?这条命令就相当于将master重置到上一个老的提交上。我们来看一下master文件的内容是否更改了。


$cat.git/refs/heads/master

e695606fc5e31b2ff9038a48a3d363f4c21a3d86


果然,master分支的引用文件的指向更改为前一次提交的ID了,而且通过下面的命令可以看出新添加的文件new-commit.txt也丢失了。


$ls

welcome.txt


重置命令不仅可以重置到前一次提交,而且还可以直接使用提交ID重置到任何一次提交。

(1)通过git log查询到最早的提交ID。


$git log—graph—oneline

*e695606 which version checked in?

*a0c641e who does commit?

*9e8a761 initialized.


(2)然后重置到最早的一次提交。


$git reset—hard 9e8a761

HEAD is now at 9e8a761 initialized.


(3)重置后会发现welcome.txt也回退到原始版本库,曾经的修改都丢失了。


$cat welcome.txt

Hello.


使用重置命令很危险,会彻底地丢弃历史。那么,还能够通过浏览提交历史的办法找到丢弃的提交ID,再使用重置命令恢复历史吗?不可能!因为重置让提交历史也改变了,提交日志如下:


$git log

commit 9e8a761ff9dd343a1380032884f488a2422c495a

Author:Jiang Xin<jiangxin@ossxp.com>

Date:Sun Nov 28 12:48:26 2010+0800

initialized.