如果你使用git完成了commit之后又后悔了,git reset
命令会给你一次吃后悔药的机会,本篇博客将会演示如何使用git reset
命令,以及这个命令到底做了些什么。
#关于HEAD
我们之前的操作中,很多地方会用到HEAD,有过编程经验的人一般都知道,HEAD是头指针,在链表中通过移动HEAD来访问其它元素。在git中,我们也是通过移动HEAD来访问每一次提交的。
在git中,HEAD的信息保存在.git/HEAD文件中。在我的git仓库中,这个文件的内容是
ref: refs/heads/master
意思就是,HEAD是一个引用,指向refs/heads/master
这个地址。打开refs/heads/master
文件,看看是什么东西:
723687a41685667a01dbd6254eb148d19501c3f1
可以看出来,里面是一个哈希值id。这个id其实是我最新的一个提交的id。
到现在,我们就清楚git是如何通过HEAD来访问到每一次提交了:HEAD指向一个分支(这里是指向master分支),分支指向一个commit,而commit的parent又指向它的上一次提交,上一次提交的parent又指向上上一次提交,一直可以访问到第一次提交。
每一次提交,又关联一个文件树,所以,git通过HEAD还能访问到每一次提交时的所有文件的内容。
下面是一张比较完整的git对象库的图
#git reset的原理
通过对上面内容的理解,其实不就是通过HEAD来操作链表嘛!HEAD指向master,master指向一个最新的提交,回到本篇博客开头的那个问题,如果提交错了,如何吃后悔药,其实很简单,让master指向最新提交的上一次提交就可以了。
git reset --soft HEAD^
^
符号代表parent。这个命令的意思是,让当前分支(这里是master)指向HEAD的上一次提交。关于--soft
参数,我们稍后再讨论。
此时,运行git log
命令,会发现最新的一次提交不见了。
#git reset的用法
git reset有两种用法,分别是
git reset [commit] --<file>
git reset [--soft | --mixed | --hard | --merge | --keep] [commit]
两种用法中都可以提供一个commit,默认是HEAD所指向的提交id。
第一种用法在git 暂存区这篇博客中提到过,就是用版本库中的某次提交时的文件树中的某个文件来覆盖暂存区的某个文件,从而撤销git add
命令对暂存区的修改。
第二种用法就是我们今天讨论的,它会重置分支的引用,从而使当前分支指向某个提交id。
##git reset的参数
git reset
命令的第二种用法可以添加不一样的参数,下面介绍一下常用的参数。
--soft
参数仅仅重置分支的引用,不会修改暂存区和工作区。吃后悔药仅仅是为了修改注释的话,一般都使用这个参数--mixed
参数是默认值,它除了重置分支的引用以外,还会重置暂存区--hard
参数最干脆,因为它除了重置分支的引用以外,还会重置暂存区和工作区。
#如何挽救错误的重置?
通过git reset
命令重置了分支的引用之后,通过git log
已经无法查询出重置之前最新的提交id了,如果reset之后又后悔了,那该怎么办呢?git总是有办法的。
打开.git/logs/HEAD
文件,会发现这里面记录着每一次移动HEAD时的历史记录,假设里面的内容是这样子的
0000000000000000000000000000000000000000 1a29bde9519195f14e98270c29d125e9d18b8d87 zdk <zdk@menhoo.com> 1497192021 +0800 commit (initial): 新增了a.txt和b.txt文件
1a29bde9519195f14e98270c29d125e9d18b8d87 723687a41685667a01dbd6254eb148d19501c3f1 zdk <zdk@menhoo.com> 1497796049 +0800 commit: add c.txt
723687a41685667a01dbd6254eb148d19501c3f1 1a29bde9519195f14e98270c29d125e9d18b8d87 zdk <zdk@menhoo.com> 1497796077 +0800 reset: moving to HEAD^
可以看出来,每移动一次HEAD的指向就会产生一条记录,并且HEAD是从第一个ID指向了第二个ID。首先,HEAD从一个空指针(40个0)指向了1a29bde9519195f14e98270c29d125e9d18b8d87
,然后又从1a29bde9519195f14e98270c29d125e9d18b8d87
指向了723687a41685667a01dbd6254eb148d19501c3f1
,然后,又从723687a41685667a01dbd6254eb148d19501c3f1
指向了1a29bde9519195f14e98270c29d125e9d18b8d87
。
有了这些操作记录中的提交id,我们就可以继续通过git reset [commit]
来让HEAD指向你希望的那次提交了。
git为我们提供了git reflog
命令来查看HEAD的移动历史记录,只是,git reflog
命令返回的历史记录与.git/logs/HEAD
文件中的历史记录的顺序正好相反。