git 回退之reset与revert

1,313 阅读5分钟

前言

        我们在使用git作为代码的分布式版本控制工具时,稍有不慎可能会遇到合并错代码分支或者提交错误代码分支的情况,而我们也有reset和revert两种方式帮我们弥补错误,而reset和revert都有着不同的使用场景。下面就以我的视角来分享一下reset、revert的具体原理与使用场景,还有在我们之前的一次合错master分支后使用git push -f 强推方式的不错的解决方案。

代码回退

       git 中的resetrevert都可以对当前的代码进行回退,两种的原理不一样,reset相当于时光倒流到指定的commit id,而revert则是建立合并一个新的提交回退对应的commit提交。现在我们在github上创建一个仓库,然后在master分支新建test.java文件,分多次顺序提交以“第一次提交Test”、“第二次提交 22222”、“第二次提交 33333”。文件内容及提交历史如下:分别在第二次和第三次追加 System.out.println(22222);System.out.println(33333);

图片名称
图片名称

reset

        下面我们手动测试及验证一下reset的原理,直接在master分支对这几个提交的commit_id进行reset a0596c92b4be3c99ae16985bf239dd95ac783d0e git reset --hard a0596c92b4be3c99ae16985bf239dd95ac783d0e,执行一下后,可以看到git log当前本地仓库master分支的head已经指向了“a0596c92b4be3c99ae16985bf239dd95ac783d0e” commit id,完成了时光倒流,但由于当前远程仓库的代码分支比较靠前,因此如果我们使用git push orign master 会提示错误。于是我们只能使用git push -f这样的危险操作,将本次回退的master内容推向至远程仓库,完成本次回退操作的本地与远程同步。

输入图片说明
屏幕截图.png

        通过上述reset的操作与结果,我们可以看到reset的回退更偏向于对于自己本地操作的一个回退,而如果想同步至远程,则必须使用git push -f这样危险的强推操作,而在强推的过程中还有可能会覆盖掉别的伙伴的代码,因此相对危险。

revert

        接下来对revert进行验证,我们将基于之前git仓库的master分支创新的一个分支C_test,即下图所示,同样是3次commit。

输入图片说明
屏幕截图.png
        接着我们执行revert a0596c92b4be3c99ae16985bf239dd95ac783d0e的操作,git revert -n a0596c92b4be3c99ae16985bf239dd95ac783d0e,然后处理完相应的冲突后可以看到下图,多出了一次提交,即 f9786a7a7312cfa515。也就是说,我们在revert反做这个commit时,会新建一次新的提交。
输入图片说明
屏幕截图.png
        git reset和git revert执行后如下图所示:
输入图片说明
屏幕截图.png

分支上的使用

        上述我们都是在同一个分支进行的git reset和git revert,但是我们在merge 分支时,同样会产生一个新的commit id,如果我们对这个新的commit id进行回退会怎么样?git reset在分支的merge commit上没有什么区别,而git revert则需要更多的注意,下面就操作一下:

初始化分支

        首先我们在master分支创建了Test.java并且提交,接着在这个节点,新建分支A_Test,然后创建A.txt提交到A_Test。如下图所示:

图片名称

将A_Test分支合并至master

        接着就进行我们平时操作中的普通操作,将A_Test分支合并至master。如下图所示,合并后产生一个merge commit id,并且此时master已经比A_Test提前一个节点。

图片名称

想在master分支回退此次合并

        然后我们发现分支合错了,不该合并A_Test分支,于是执行了git revert,结构又如下图所示,master执行回退操作后比A_Test领先了两步。

图片名称

将master代码改动合回A_Test

        master作为主分支,我们在最后,总要将代码合回到正在开发的A_Test分支,因此在A_Test分支执行 merge master的操作,我们就会发现,这个时候A_Test做的代码变动全都被回退了,因为这个时候分支的情况是下图,我们的A_Test在合回master代码的时候,把master在之前做的对于A_Test分支的回退操作也一并合并了回来,因此我们A_Test分支的改动便全都消失了。

图片名称

        可以看到我们其实在对于分支的合并产生的 commit id进行回退操作时一定要谨慎,reset尚可,一旦执行revert后,再从主分支master向这个A_Tes分支进行合并时,会进行同样的逆向反做操作,把这个A_Tes分支的所有提交全部回退,因此对于这种情况的revert一定要谨慎,因为会导致整个分支直接废掉,所以我们可以使用上面的reset + git push -f这样的‘危险操作’来回退这次的merge commit。

结语

        本文对于git 回退的reset、revert进行了commit id维度上的操作和原理图解释,并且说明了在分支merge情况下如果使用git revert会造成的严重后果,即:

  1. 非merge commit id,最好使用Git revert回退,因为可以产生有效的日志及操作记录;
  2. 对于分支间的merge commit id的回退,可以使用git reset + git push -f在本地回退后强推的操作,使对应分支不会作废。