我可以看到三个选项:回滚合并、变基或过滤。
回滚
"正确"的解决方案是回滚合并,正如其他人所主张的那样。这意味着你不会"重写历史",这通常被认为是一件好事,尤其是因为这意味着其他人在处理这段代码时不需要处理历史更改。
然而,你不能只使用
git revert
,因为它不知道要保留哪个分支的历史记录。解决方案很简单,只需向Git提供额外的信息即可。
git revert -m 2 <sha-of-B>
-m 2
表示你想保留第二个父提交,即包含提交C的父提交;如果要选择另一种方案,请切换到-m 1
。
需要注意的是:如果你想将其他分支合并到主分支中,你会遇到问题,因为Git认为该分支已经在主分支中了。有几种解决方法,但最简单的(我认为)是将还原操作放在一个从主分支分出来的分支上,并将其合并到主分支和其他分支中。那个分支可能看起来像从哪个版本的主分支分出来的,此时你可以撤销还原操作,当你将来进行合并时,一切都会很好。
变基
变基可能是最简单的选项:git rebase <sha-of-B> master --onto <sha-of-C>
。
这将把从B到master
的所有提交移动到C上。问题在于它将“线性化”合并历史记录。图像中的合并历史非常简单,但如果你想保留历史记录或者在更复杂的存储库上执行此操作,则可能会出现问题。
这也有一个缺陷,即它是“重写历史”,使用此存储库的其他人将发现他们正在处理与已还原的不同的代码分支,因为从 C 开始的每个更改都将具有不同的 sha1 哈希。
过滤
使用 git filter-branch 将允许您在图像中精确获取所需内容,即保留合并历史记录,但这是最复杂的选项,仍涉及重写历史。
实际上,您想要使用 git filter-branch 来过滤提交以保留除了那个瑕疵合并之外的所有内容。这样做非常复杂,我不会尝试编写说明; 您需要结合 --commit-filter 删除有问题的合并和 --tree-filter 来撤消合并的更改。
过滤 redux
好吧,这是第四种方法,我包括它是为了完整性。您可以只检出 C,然后逐个手动挑选和合并您想要在主分支中拥有的每个更改。
进一步阅读
git status
说了什么? - Filip Bartuzi