reset 和 checkout

在最开始遇到的命令里面,resetcheckout最让我困惑。因为据说这两个命令可能会造成不可逆转的后果,但很多教程语语焉不详,自己却用git reset --hard这种看上去很危险的命令却并没有提--hard代表了什么。这让人很不爽。

三棵树

实际上这两个命令通过控制 HEAD,暂存区,工作区这三棵树来影响提交历史。 1

reset

现在假设一个仓库里面只有一个文件file.txt,经历过三次提交之后,提交历史如下图所示:

2

然后跟着reset看看它都做了什么。

reset 的流程

第一步:移动 HEAD(--soft)

reset做的第一件事儿是移动 HEAD 的指向:

3

无论你调用了何种形式的带有一个提交的 reset,它首先都会尝试这样做。使用reset --soft,它将仅仅停在那儿。

现在看一眼上图,理解一下发生的事情:它本质上是撤销了上一次git commit命令。当你在运行git commit时,Git 会创建一个新的提交,并移动 HEAD 所指向的分支来使其指向该提交。当你将它reset回 HEAD~(HEAD 的父结点)时,其实就是把该分支移动回原来的位置,而不会改变暂存区和工作目录。现在你可以更新暂存区并再次运行git commit来完成git commit --amend所要做的事情了。

第二步:更新暂存区(-mixed)

接下来,reset会用 HEAD 指向的当前快照的内容来更新暂存区。 4

如果指定--mixed选项,reset将会在这里停止。这也是默认行为,所以如果没有指定任何选项,这就是命令停止的地方。

可以看到这个命令依然会撤销上一次提交,还会取消暂存所有的东西。于是我回滚到了git addgit commit命令执行之前的状态。

第三步:更新工作区(--hard)

reset第三件要做的事儿就是让工作区也重置。如果用--hard 选项,那么会继续走到这一步。

5

现在回想一下刚刚发生的事儿。reset做的事儿就是把提交、暂存、工作区保存这三件事儿全部重置了一遍,三个选项就是这三个操作的逆操作。

以路径/文件做参数的重置

在暂存完之后,git status总是会给出一个提示信息:

use "git reset HEAD <file>..." to unstage

说是git reset <file>可以把一个文件取消暂存。以前我不知道这是为什么,现在我知道了这个缩略形式的命令其实就是git reset HEAD --mixed <file>。第一步,HEAD 指向不用变;第二步,取消暂存。

6

修改提交历史

有时候,碰到一些特殊情况,需要停下当前分支的活儿,去别的分支工作,那么当前工作区和暂存区的内容需要暂时保存起来。一般来说这种情况是用 stash 来完成。不过先提交,把提交信息写成"WIP",等切回来之后再用reset重置也是可以的。

假设你有一个项目,第一次提交中有一个文件,第二次提交增加了一个新的文件并修改了第一个文件,第三次提交再次修改了第一个文件。 由于第二次提交是一个未完成的工作,因此你想要压缩它。

7

那么可以运行git reset --soft HEAD~2来将 HEAD 分支移动到一个旧一点儿的提交上:

8

然后只需再次运行git commit:

9

现在你就把中间那个不必要的提交给去掉了。

checkout

reset一样,checkout也操纵三棵树。不过它有一点不同,这取决于我是否传给它一个文件路径。

不带路径

运行git chekcout <branch>和运行git reset --hard [branch]非常类似,它们都会更新所有三棵树,使其重置成 branch 所指向的提交的样子。不过有两点重要的区别。

首先不同于reset --hardcheckout会通过检查确保不会将工作区和暂存区的修改丢掉。它会在工作目录中先试着简单合并一下,这样所有还未修改过的文件都会被更新。而reset --hard则会不做检查就全面地替换所有东西。

简单来说,当检出一个分支时,它会修改 HEAD 指向新的分支引用,将索引 填充为该次提交的快照,然后将索引的内容复制到工作目录中。至于工作区不干净的情况,可以参考未提交的情况下切换分支

带路径

当我们在工作区修改一个已经追踪了的文件,运行git status的时候,会给出这样的提示:

use "git checkout -- <file>..." to discard changes in working directory

意思是说把工作区的文件丢弃,而且这个动作比git reset --hard更危险,因为工作区的成果尚未提交,在 reflog 里面都没有记录,一旦丢弃了,几乎就找不到了。

最后

git reset 的介绍页面给出了一张有意思的表格,其中不止三个阶段,还给出了另外两个选项--merge--keep

results matching ""

    No results matching ""