如何撤销使用了strategy=ours的合并?

11

我正在处理一个仓库,几周前执行了一次合并,我们刚刚发现它使用了--strategy=ours标志(本应使用--strategy-option=ours标志),因此没有应用任何更改到HEAD。但是,我们需要应用这些更改。Git已经将分支识别为已合并,并且在分支历史记录中存在提交。

这种合并无法使用git revert -m ...进行撤销。

反转和/或重新应用合并以更改文件的适当方法是什么?

master  A - B - E - F - G ---> L - M - N
             \     /
topic         C - D

在这种情况下,合并提交(F)将是罪魁祸首。


你是否可以这样说,你不想重写历史,只是在分支的最新提交上产生一个合并文件的新提交? - Lily Ballard
实际上,重写历史记录是一个不错的选择。在这种情况下,只要更改实际应用即可,这并不重要。需要注意的是:我们正在尝试纠正合并到“主”分支的错误,主分支已经分支了数十次,因此历史记录必须能够在合并到其他分支时传播。 - Highway of Life
3个回答

9
我发现了解决这个问题的方法。其中的关键在于Linus写的有关撤销错误合并的信中:如何撤销错误合并。 在我们的情况下,git merge --strategy=ours topic 是不应该出现的。尽管它是一个错误的合并,但它不能被撤销,而且已经很久以前就已经推送了,所以与撤销合并提交的效果相同,而无法撤销撤销提交。

解决方案是检出主题分支,从第一个提交运行 rebase --no-ff,然后将该分支合并回主分支。

git checkout topic
git rebase -i --no-ff <C>
git checkout master
git merge topic

这产生了以下效果:
fixed–topic   C'–––D'––––––––––––––––––––-
             /                            \
master  A–––B–––E–––F–––G –––> L–––M–––N–––F2
             \     /
topic         C–––D

要深入理解这个问题,请阅读信件最后部分,使用--no-ff的rebase选项重新创建分支。您可以通过如何撤销错误合并来了解更多信息。

4

@Highway of Life提供了很好的答案。但有一个问题是变基(commit rebase)后的提交现在看起来像是新的不同提交,这在许多情况下可能是不可取的。我想第二次合并相同的提交,但是git-merge会阻止这样做。然而,我一遍又一遍地发现,总是可以让git按照我们的意愿去做。

  1. Follow Highway of Life's instructions but don't merge straight onto master:

    git checkout -b fixed-topic <D>
    git rebase -i --no-ff <B>
    git checkout -b re-merged master
    git merge fixed-topic
    

    This results in:

    re-merged               ––––––––––––––––––––------R
                           /                         /
    fixed–topic      C'–––D'                        /
                    /                              /
    master     A–––B–––E–––F–––G –––> L–––M–––N–––F2
                    \     /
    topic            C–––D
    
  2. Now the tree is set up exactly how you want—all the files show exactly the contents you desire. The one problem is that your merge commit's parents are rebased copies of your original commits, instead of actually being the original commits themselves. It's easy to fix this with git-reparent:

    git reparent -p master -p <D>
    

    This ends up with:

    master     A–––B–––E–––F–––G –––> L–––M–––N–––F2
                    \     /                        \
    topic            C–––D                          \
                          \                          \
    re-merged              ---------------------------R
    

    An important thing to note is that the contents of R haven't changed since the last step—only the parents.

  3. Fast-forward master to the new merge commit:

    git checkout master
    git merge re-merged
    
  4. Finally you can clean up with

    git branch -d fixed-topic re-master
    

完成了!现在你的历史记录看起来像这样,一个分支被合并了两次:

master  A–––B–––E–––F–––G –––> L–––M–––N–––F2---R
             \     /                           /
topic         C–––D----------------------------

我不知道reparent选项是第三方函数。看起来是一个非常好的解决方案,但我正在寻找更简单的方法。 - Jp_

0
一个合并策略说:“无论你合并什么,都要保留这里的内容”,本质上是无法撤销的。在新分支中检出合并的第一个父提交,然后再次进行合并。将剩余的提交通过 rebase --onto 到原始合并之上。
更新:
在您的情况下,如果您不关心过去,可以将 G-N 变基到 E 上,然后只需将 D 合并到这个新主分支上即可。由于 F 是一个 --ours 合并,所以变基将是微不足道的。

顺便提一下,我已经编辑了我的问题,使其稍微清晰了一些。-- 自此合并以来,主分支已经进行了69次提交。我担心在这种情况下重写历史记录可能会对团队成员的存储库造成潜在的灾难性故障,并且将他们的合并重新基础合并回我的存储库可能会变得有趣。也许如果没有更好的选择,走补丁路线可能是最安全的方式? - Highway of Life
在最终结果上,rebase 和 patch 是相同的。然而,rebase 更加自动化。记得打开 rerere,以防你需要重新开始,而又不想再次进行相同的冲突解决。 - Adam Dymitruk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接