2012/08/13

git使用之如何撤销某个操作

本文聊聊在使用git的过程中,如何撤销一个操作,这个操作可能是git add,git commit,或是git merge,或是其他的。接下来分情况讨论:

情景一 文件被修改,还没有git add。
这时候文件还没有被添加到object database中,可以使用index中的版本来覆盖工作区。
git checkout -- [filename]

情景二 文件被修改,且已经git add。
此时修改过的文件已经作为一个新的object添加到object databse中,可以使用当前HEAD中的版本来覆盖index。
这时候文件还没有被添加到object database中,可以使用index中的版本来覆盖工作区。
git reset HEAD -- [filename]
如果还想重置工作区,可以继续:
这时候文件还没有被添加到object database中,可以使用index中的版本来覆盖工作区。
git checkout -- [filename]

情景三 已经提交commit,需要撤销commit。
这种情况又分为多种子情景,详细讨论,首先给出一个模拟的提交历史,从左到右一次提交,master执行提交H。
A---B---C---D---E---F---G---H
                            |
                          master
子情形1. 要撤销最近1个commit,要撤销H。
可以使用2个命令:
git reset --hard G

git revert H
二者区别在于git reset不会产生新的提交,而是将分支指向指定的commit,生成的提交历史为:
A---B---C---D---E---F---G---H
                        |  
                      master
而git revert会产生一个新的提交,生成的提交历史为:
A---B---C---D---E---F---G---H---I
                                |  
                              master

子情形2. 要撤销的提交不是最新的提交,即在错误提交之后又有若干个提交。
比如我们想撤销E,F两个提交,此时git reset已经无法胜任,因为我们在E,F后又有多个正常提交。这时可以使用git revert,但是要操作两次。这种情况下,推荐使用git rebase:
git rebase --onto D G^ H

情形四 执行merge操作,但是遇到conflict,想要撤销merge操作。
此时并为生成新的提交,当前分支头并未移动,因此可以简单的:
git reset --hard HEAD

情形五 执行merge操作,且已生成新的提交,要撤销merge操作。
这是简单的git revert 已经不能胜任。因为merge生成的提交有2个父提交,git不知道该使用哪个作为主线。这时需要添加一个额外的参数:
git revert -m [parent-number] [refs] 
-m指定要采用哪条主线(编号从1开始)。

如果还有其他情形本文未考虑到,请留言写出,大家一起讨论。

没有评论:

发表评论