likes
comments
collection
share

git 撤销操作思考

作者站长头像
站长
· 阅读数 7

正如我们所熟知的: git撤销操作有两种,git revertgit reset

关于二者的使用方法:

$ git revert HEAD 在当前提交后面,新增一次提交,抵消掉上一次提交导致的所有变化。它不会改变过去的历史,所以是首选方式,没有任何丢失代码的风险。

git revert 命令只能抵消上一个提交,如果想抵消多个提交,必须在命令行依次指定这些提交。比如,抵消前两个提交,要这样写。$ git revert [倒数第一个提交] [倒数第二个提交]

$ git reset [last good SHA]丢弃掉某个提交之后的所有提交。它的原理是,让最新提交的指针回到以前某个时点,该时点之后的提交都从历史中消失。

默认情况下,git reset不改变工作区的文件(但会改变暂存区),$ git reset --hard [last good SHA] --hard 参数可以让工作区里面的文件也回到以前的状态。执行git reset命令之后,如果想找回那些丢弃掉的提交,可以使用git reflog命令,具体做法参考这里。不过,这种做法有时效性,时间长了可能找不回来。

一些场景:

1、回退后再继续merge以前的老版本

git 撤销操作思考

因为git revert是用一次逆向的commit“中和”之前的提交,因此日后合并老的branch时,导致这部分改变不会再次出现

但是git reset是之间把某些commit在某个branch上删除,因而和老的branch再次merge时,这些被回滚的commit应该还会被引入。

2、git reset 操作的风险

git 撤销操作思考 假如我们现在的分支状态是这样的,feature1,common_feature 两个功能分支,common_feature 的c1, feature2开发到c5时合并到 common_feature ,此后 common_feature 上又继续迭代新功能;这时候在feature2发现之前的c4提交有问题,需要将代码回退到c2版本;

如果执行git reset c2_HEAD,将其合并到 common_feature 分支时却被提示报错。这是因为 feature2 分支回退了提交后,在 git 的 workflow 里,feature2 分支是落后于 common_feature 分支的,而合并向 common_feature 分支,又需要和 common_feature 分支保持最新的同步,需要将 common_feature 分支的数据合并到 feature2 分支上,而合并后,原来被 reset 的代码又回来了。

这个时候另一个可选项是在 common_feature 分支上执行 reset,使用 --hard 选项完全抛弃这些旧代码,reset 后再强制推到远端。

common_feature > git reset --hard c2_HEAD
common_feature > git push --force origin common_feature 

!! 会发现在 merged feature2 之后的提交c6以及c6的提交历史都不见了,因此多人协作开发时,在公共开放分支上使用git reset 要格外慎重,一般不推荐使用。

那git reset 适合什么场景呢?

已在本地进行了多次 git commit 操作但还没推送到 origin,现在想撤销到其中某次 commit ⚠️:注意是想要撤销本地还未 push 的 commit git reset [--hard|soft|mixed|merge|keep] [commit|HEAD]

那上面这种场景怎么做呢?

3、git revert 撤销代码

针对上面场景,可以使用下面的代码安全撤销c4、c5两次提交

feature2 > git revert c2_HEAD
common_feature > git merge feature2

但revert之后 后面修复了问题,将修复的版本 fix_verson 合并回 common_feature 时,可能会发现有代码丢失的情况,这是因为 git revert 是新增了一次“反向提交”,之前的提交 c4 c5 都还在,再合并 fix_verson 时,fix_verson 上的一些改动可能和 c4 、c5 上的改动一样,git不会合并这些重复的提交,正确的做法是 revert revert 后再合并 fix_verson;

git 撤销操作思考

4、git revert 和 git rebase 搭配操作

git revert commit_id 能产生一个 与 commit_id 相反的提交,上面有提到想抵消多个提交,必须在命令行依次指定这些提交,如果这时候我们需要回退的commit 比较杂乱,中途还有几次从其他分支的 merge 操作,这时候可能需要revert 很多次,且要严格保证顺序,中间有一次revert的顺序错了,就可能导致最终的结果不对;

先使用 rebase 把多个提交合并成一个提交,再使用 revert 产生一次反提交;

feature2 >git rebase common_feature
common_feature > git merge feature2
common_feature > git revert ...
...

关于rebase的使用和注意事项,具体可以查阅这里

综上:

  • revert 适合需要回退的历史提交不多,且无合并冲突的情景。
  • git reset --hard + git push --force 会向 公共分支 强推代码,且 git log 里不会出现被回退代码的痕迹。
  • 如果需要revert的commit多且杂乱,可以先使用 rebase 将分支清理成一条干净的直线,再revert。