需求分支首先通过MR合并到了master分支,部署后发现有问题,于是回滚代码,操作了MR处的revert,后面修复了问题,使用原功能分支提新的MR时,发现变更处是空的,本文来解决此类无法合并情形的问题。

首先说结论:对原MR的revert再次revert,后续即可使用原功能分支合并到主干分支。

分析过程以及原因详见下文。

背景

20240311晚,有个需求上线后发现有问题,于是回滚了部署资源,同时需要回滚代码。

回滚代码使用的是gitlab MR处的revert:

上线范围包含了两个功能分支:

  • a
  • b

其中a分支的内容需要回滚,b分支原先合并到了a分支。需要将b分支的内容重新合到master,进行部署上线。

使用b分支提MRmaster时,发现不显示一行变更,即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会生成一个新的标识mergecommit

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的目的。