玩转 git reset
玩转 git reset
keywords: git reset soft mixed hard
前言
首先对网上一些对git reset
用法的谬解进行辟谣,有些人会说git reset --hard
是一种不可逆的操作,但是这种说法其实是错误的,因为--hard
仅仅只是移动了指向HEAD
,INDEX
和Working directory
。
也就是说,如果仅仅只是调用了git reset --hard
没有执行其他操作,还是可以被还原的,具体方法和原理,请继续阅读。
起因
我之前在多个分支上滥用rebase interactive导致最后这些分支合并到主分支之后,有很多重复的commit。
因为我用了rebase的squash功能,这些commit的message一样但是hash不一样,所以合并之后出现了很多重复的commit。
为了解决这些重复,我尝试了reset功能(虽然最后还是靠rebase,解铃还须系铃人啊,用的rebase -i中的drop功能)。
介绍
HEAD
,INDEX
和Working directory
以上三个词汇的意思来自于git book中的Reset Demystified
(见文末reference)。
在这里我对它们的意思给出了我自己的理解,用我自己的思维来解释他们。
我简单的将所有的git commits认为是一个链表,一个可以产生分岔的链表,用不同的指针来指定不同分岔链表的表头,这些指针被成为分支。(以上全是我的个人理解,助于我理解git行为的,不代表git源码是如此组织的)
在链表论的基础上就很容易的理解HEAD
,INDEX
和Working directory
。
HEAD
: 它是指向当前链表的头的指针。
INDEX
: 它是指向即将被提交的commit的指针(还没被提交的commit哦)。
Working directory
: 它是当前工作文件夹中的内容。
NOTE: 每个branch中的这三个指针都是独立的,但是被删的commit是会影响到其他的branch的。
如果Working directory
和INDEX
之间内容不同,文件就是红色的。
$ git status
On branch master
Changes not staged for commit:
(use "git add..." to update what will be committed)
(use "git restore..." to discard changes in working directory)
modified: file.txtno changes added to commit (use "git add" and/or "git commit -a")
如果INDEX
和HEAD
之间不同,那么文件就是绿色的。
$ git status
On branch master
Changes to be committed:
(use "git restore --staged..." to unstage)
modified: file.txt
NOTE: HEAD
不会和Working directory
比较
用法
git reset --soft
这个命令很简单,就是移动HEAD
的指针,能往前移也能往后移。
如果当前是working tree clean的状态,那么输入
git reset --soft HEAD~
HEAD
就会往前移一格(一次commit),此时INDEX
和HEAD
的内容就会出现差异,文件差异就是绿色的。
此时,Working directory
和INDEX
的内容没有变化,只是HEAD
的指针前移了。
在这种情况下,如果使用git commit
那么就能在当前HEAD
的基础上提交,相当于重新修改了commit。
如果将HEAD
前移多格(git reset --soft hash_num),再使用git commit,那么就可以将这些commits全部压缩成一个commit,
这种操作有点像rebase interactive中的squash,见我前一篇博客,git squash commits。
NOTE: 如果你用了git reset --soft命令后悔,想将HEAD变回最开始的样子,就输入git reset --soft hash_num,这里hash_num
就是你想移回的commit的hash值
git reset --mixed
这个命令就是同时修改INDEX
和HEAD
把他们移动到指定的commit中。
如果当前是working tree clean的状态,那么输入
git reset --mixed HEAD~
INDEX
和HEAD
就会往前移动一格,此时Working directory
和INDEX
之间内容不同,文件就是红色的。
如果在当前情况下再输入git reset --soft就会发现,绿色和红色,同时出现了,哈哈。如果想要撤回就输入git reset --mixed hash_num
。
在这个命令下有个特殊的用法,可以单独还原某一个提交中的某一个文件
git reset --mixed hash_num file_nmae
git reset --hard
这个命令就是同时修改Working directory
,INDEX
和HEAD
把他们移动到指定的commit中。
此时这三者都是都被改变了,代表它们三者完全相同,那么输入git status之后可以看到working tree clean。
想要还原就输入
git reset --hard hash_num
hash_num 就是想要还原到的那个commit的hash值。