需求分支首先通过MR合并到了master
分支,部署后发现有问题,于是回滚代码,操作了MR
处的revert
,后面修复了问题,使用原功能分支提新的MR
时,发现变更处是空的,本文来解决此类无法合并情形的问题。
首先说结论:对原MR的revert再次revert,后续即可使用原功能分支合并到主干分支。
分析过程以及原因详见下文。
背景
20240311晚,有个需求上线后发现有问题,于是回滚了部署资源,同时需要回滚代码。
回滚代码使用的是gitlab MR处的revert:
上线范围包含了两个功能分支:
a
b
其中a
分支的内容需要回滚,b
分支原先合并到了a
分支。需要将b
分支的内容重新合到master
,进行部署上线。
使用b
分支提MR
到master
时,发现不显示一行变更,即MR
无法正常操作。
revert
操作后,会提交一个新的commit
,用来表示前面的mr
被回退。
该团队该项目git协作使用的是功能分支完备后
merge
到主干分支的方式。
分析
操作关键步骤
我们对上面的问题进行简化,在本地环境开启新的分支进行模拟:
test-mr-revert
模拟功能分支;master-bak-20240312
模拟主干分支;
1. 开发功能
在 test-mr-revert
上提交一些commits
。
2. 使用MR合并 test-mr-revert 到 master-bak-20240312
MR
会生成一个新的标识merge
的commit
。
3. 进入刚刚的MR,点revert
revert
后创建了一个分支,其中新增了一个commit
,回退了之前的MR
,之后会提交一个新的MR
,将revert
对应commit
合并到master-bak
。
4. 再次合并 test-mr-revert 到 master-bak-20240312
此时复现了我们上线时的情形:
找根因
Google
搜索「git revert merge and remerge」,找到了StackOverflow
上的一个问答:
https://stackoverflow.com/questions/1078146/re-doing-a-reverted-merge-in-git
08、09年的时候就有人提出了这种情况的疑问。
对应官方 git-scm
回退一个有问题的merge
文档:https://mirrors.edge.kernel.org/pub/software/scm/git/docs/howto/revert-a-faulty-merge.txt
我贴一下官方文档中的关键解释:
小结一下这种情况的原因:
revert
操作后,会提交一个新的commit
,用来表示前面的mr
被回退。- 产生的影响就是,生成了新的
revert commit
,保留了原来功能分支上的commit
。由于这个revert
记录了操作了某些commit
的回退,所以用原始功能分支上的commit
操作merge
,会没有变更。
因此问题根因就是,针对MR的revert
,会将功能分支的所有commit
标记为回退,因此再往主干分支合并时会不生效。
针对整个过程画了个图,帮助读者更好理解:
解决方案
官方文档给出了解法:对前面的revert,进行一次revert操作。这种解法有一个前提是,功能分支问题已修复。
针对我们11号上线的情况,由于我们其实是把两个功能分支合并到了一起,所以提了一个统一的MR
,针对MR
操作revert
后,两个功能分支上的commit其实都会被标记为回退。因此针对此类情况,之后可以不合并两个分支,多个分支可以多次提交MR
,这样针对MR
回退时,多个分支间不受影响。
另外在对commit
的管理足够规范前提下,如果commit
提交足够原子性,并且数量可控,在对一个融合的MR
,可以考虑使用cherry-pick
,这样可以完成我们一个MR中仅保留部分commit的
目的。