git 撤销操作思考
正如我们所熟知的:
git撤销操作有两种,git revert
和 git 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 revert是用一次逆向的commit“中和”之前的提交,因此日后合并老的branch时,导致这部分改变不会再次出现
但是git reset是之间把某些commit在某个branch上删除,因而和老的branch再次merge时,这些被回滚的commit应该还会被引入。
2、git reset 操作的风险
假如我们现在的分支状态是这样的,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;
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。
转载自:https://juejin.cn/post/7052681379057762312