git撤销

在使用git时我们通常会因为一些特殊的原因或者错误的操作造成需要撤销过去的当前或者过去工作区的某个提交,本篇文章将介绍git提供的四种方法:checkout, revert, reset, clean 解决此问题。

1. checkout

checkout提供了一种安全的机制,用于将当前工作区切换成过去保存的工作区。它包含三个功能:

  • 1.checkout branch:
    切换当前分支到指定分支上,这是我们最常用的,用于分支间的切换。另外-b参数可用于创建以当前分支作为基础创建一个新的分支。

    1
    git checkout -b branch
  • 2.checkout commit
    将当前的工作区状态切换成过去某次commit时的工作区状态。该功能主要用于查看过去某次提交的内容。它背后的逻辑是将HEAD指针指向了过去的COMMIT,并不会对当前commit造成任何影响,如下图所示:
    checkout
    举个例子,假设我们的项目状态如下:

    1
    2
    3
    4
    5
    6
    7
    > git log --oneline

    b7119f2 Continue doing crazy things
    872fa7e Try something crazy
    a1e8fb5 Make some important changes to hello.py
    435b61d Create hello.py
    9773e52 Initial import

我们想看看435b61d提交时的工作区数据内容可以使用:

1
git checkout 435b61d

恢复到435b61d提交时的状态后,我们可以查看当时文件,以及做一些测试等工作。当完成我们的检查后,我们用下面命令,恢复当前工作区为最新的状态(这里我们假设处于master分支)。

1
git checkout master

  • 3.checkout commit file
    当我们只想查看过去某次提交的一个文件,可以使用上面的命令。此时git将会把当前file更新为过去某次提交的内容,并将其放入staged区中。如果我们想恢复该文件,可以使用下面命令,这时将更新file为最新HEAD的文件。
    1
    git checkout HEAD file

2. revert

revert用于撤销过去某次提交。为了避免丢失commit历史信息,git将试图把revert提交的内容在当前提交undo。过程如图:
revert commit
例如,我们当前的git log如下:

1
2
3
4
> git log --oneline

a447eda add b
a3197ff add a

此时我们想放弃a3197ff提交,删除a文件。 我们使用:

1
> git revert a3197ff

此时将产生一次新的提交,它的操作是删除a文件。
但通常情况下,如果是修改文件,此时将会产生部分冲突,此时的处理和merge一样,处理完冲突后提交即可。
如果revert 某次commit发现提交的内容没有达到预期,此时可以使用:

1
> git revert HEAD

恢复到此前HEAD状态。如果有冲突无法进行。可以使用后续将介绍到的reset:

1
git reset --hard HEAD~

3. reset

如果说revert提供了一种安全的方式撤销某次提交,那么reset则提供了一种危险的方式来撤销提交。
同checkout类似,它提供了多种方式:

  • 1.git reset file
    用于将file从staged区移出
  • 2.git reset
    将撤销staged区中所有的修改移出,但修改的内容将保留。
    例如下面这个例子,git add a操作原先将a文件添加到了staged工作区,但是git reset将把a文件从staged工作区移出。此时a文件内容仍然保留。

    1
    2
    3
    > echo "haha" > a
    > git add a
    > git reset
  • 3.git reset –hard
    –hard将把处于staged工作区的所有修改恢复到HEAD的状态。同样是上面那个例子,执行git reset –hard之后,a文件将被删除(因为a文件在HEAD中并不存在),并且无法像revert一样把文件找回,所以执行此命令需格外小心。

  • 4.git reset commit
    它的过程如图:
    reset-commit
    它试图将HEAD指到过去某次提交,并将此次提交之后的提交都删除。
    例如,我们当前的git log如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    > ls
    a b c
    > git log --oneline
    c932fdb add c
    a447eda add b
    a3197ff add a
    > git reset a3197ff
    > git log --oneline
    a3197ff add a
    > ls
    a b c

执行reset命令之后,HEAD将指向a3197ff,b,c的提交记录都将被删除。但是b,c文件仍然保留。如果使用–hard参数,b,c文件都将随之消失。git reset –hard通常用于撤销提交。

1
2
3
4
> git reset --hard a3197ff
HEAD is now at a3197ff a
> ls
a

4. clean

git clean将删除工作区中所有untracked file。同linux rm命令一样,被清理的file将不能恢复,执行此命令需慎重。
此命令通常和git reset命令结合使用,它起到的效果和git reset –hard一样。

总结

文章分别介绍了checkout, revert, reset以及clean 4种方式撤销对当前工作区的修改。其中reset和clean较为危险,一旦执行数据将可能无法找回。做此操作时需要谨慎。

参考文章

git tutorial undo changes: https://www.atlassian.com/git/tutorials/undoing-changes