不应该将整个`develop`合并到`master`的回滚操作,而应该仅回滚一个特定分支。在您的情况下,应回滚`featureB`到`develop`的合并。
如果您的回滚无法撤消,则最简单的解决方案是将其还原,然后仅回滚`featureB`到`develop`的合并。否则,建议取消初始回滚并按照以下说明操作。
根据描述,让我们画出发生的情况:
master a------------------M3
\ /
dev b-------M1-----M2
|\ / /
featureA | c---d /
\ /
featureB e--------f
这里的M3
是您要撤销的合并提交:
git checkout master
git revert -m 1 <M3-commit-hash>
如果您按照所述的完全操作,使用上述命令将回滚整个
dev
到
master
合并提交
M3
,这将产生还原提交并将其放置在时间轴上
M3
之后:
master a------------------M3---rvM3
\ /
dev b-------M1-----M2
|\ / /
featureA | c---d /
\ /
featureB e--------f
这里的rvM3
是整个dev
分支合并的还原提交。
然后您要执行以下操作:
git checkout master
git merge featureA
现在让我们想象一下预期结果的画面:
master a------------------M3---rvM3---(M4)
\ / /
dev b-------M1-----M2 /
|\ / / /
featureA | c---d----- / -----------
\ /
featureB e--------f
如果仔细观察上一个视图,我们可以发现
featureA
分支的
c
和
d
提交已经在
M3
合并后成为
master
分支的一部分,并且它们可以通过父链接从
M3
访问。
你可能期望从
featureA
创建带有提交的
M4
,但实际上不会发生这种情况,因为对于Git来说,从提交的角度来看,
featureA
分支中没有新内容需要添加到
master
中,因为之前已经合并过了。Git只关心提交本身,而不关心更改本身以及它们在未来提交中发生了什么。
考虑到这一点,我们可以得出一个简单的解决方案——你真正需要的不是撤销整个
M3
合并,而是仅撤销
M2
,并将具有
M1
索引的分支作为还原目标进行合并。
git checkout master
git revert -m 1 <M2-commit-hash>
这将完全实现您想要的,最终您将得到类似于这样的结果:
master a------------------M3---rvM2
\ /
dev b-------M1-----M2
|\ / /
featureA | c---d /
\ /
featureB e--------f
rvM2
存储了来自 M2
的更改,抛弃了来自 e
和 f
提交的更改。
通常情况下,每次想要还原某些内容时,尽可能具体是最好的。例如,如果我们想要还原一个提交,我们需要仅对其执行还原,而不是保留它的整个分支;如果我们想要还原一个特定的分支,则不应该还原多个分支的组合合并,就像您最初在 M3
中所做的那样。