还原使用 'ours' 策略解决的合并提交

Revert merge commit resolved with 'ours' strategy

我的一位同事使用“--strategy=ours”将我们的开发分支合并回功能分支,或者使用一系列有效导致放弃开发更改的操作。

我试图找到一种方法来恢复合并提交以恢复更改,但我无法找到一个简单的方法:

我在网上搜索并找到了一些关于此类或类似情况的文章,但无法找到彻底解决此问题的正确方法。最后,我们在错误合并之前创建了一个新分支,并从头开始。如何以干净的方式解决这种情况?

编辑:

为了更好地解释:

--A--B--C--D--E--F--G--H--I
         \           \
          D'--E'--F'--M--G'

M 是错误的合并,它丢弃了从 DG 的所有更改。 使用第一个选项 (-m 1),什么也不会发生。使用第二个选项 (-m 2),还原似乎删除了 D'-F' 中发生的更改,同时添加了已删除的更改。

抱歉,忘记补充说 git reset 不是一个选项,因为合并已经被推送到远程仓库并且 git push --force 是不允许的。

如果你想恢复到特定的提交,你只需要 运行 git reset <sha1>。无论其后的提交类型如何,这都应该有效。 运行 git log 找到合并前的最新提交,然后重置为它。

... I searched online and found some articles about situation like these or similar but couldn't figure out the right way to cleanly resolve this mess. In the end we made a new branch from right before the faulty merge and started from scratch. How could this situation be resolved in a clean way?

实际上 正确/干净的方式——虽然你不需要一个新的 branch 本身,因为分支名称是对 Git.

没那么重要

[given this graph]

--A--B--C--D--E--F--G--H--I   <-- br1
         \           \
          D'--E'--F'--M--G'   <-- br2

[where] M is the faulty merge, that discarded all changes from D to G ...

所有 revert 选项通过将 M 与其两个 parent 之一进行比较来工作。由于 M 是 运行 和 --ours,它的树匹配 FF'(以第一个 parent 为准):

我不太清楚为什么你用 prime-marks 标记了一些提交(D'-E'-F' 等)但是如果 M 应该 一个真正的合并,而不是 --ours,你需要 re-run 合并。您可以按照自己喜欢的任何方式执行此操作,但最简单的方法是检查其中一个 parent,这里分别是 GF',然后是 运行 git merge 与其另一个 parent 的哈希 ID。一般来说,我建议保留两者的 first- 和 second-parent-ness 属性,所以我会这样做:

git checkout <hash-of-G>
git merge <hash-of-F'>

像往常一样完成合并,如果Git本身没有提交,提交:

                      _________
                     /         \
--A--B--C--D--E--F--G--H--I     \   <-- br1
         \           \          M2   <-- HEAD
          D'--E'--F'--M--G'     /   <-- br2
                   \___________/

然后,根据你想看到的,要么cherry-pick G',要么强行重置br2指向结果:

                      _________
                     /         \
--A--B--C--D--E--F--G--H--I     \   <-- br1
         \           \          M2--G"   <-- br2
          D'--E'--F'--M--G'     /
                   \___________/

(但这是一个 non-fast-forward 案例,需要 br2 的所有其他用户进行调整),或者您可以在这里放置一个临时名称(标签或分支),git checkout br2, git merge临时名称,然后删除临时名称:

                      _________
                     /         \
--A--B--C--D--E--F--G--H--I     \   <-- br1
         \           \          M2--M3   <-- br2
          D'--E'--F'--M--G'-----/---/
                   \___________/

请注意,生成合并 M3 时的合并基础是 F'G,因此 git merge 默认情况下会对这两者进行递归合并用作合并基础;然后它将 M2G' 与该虚拟合并基础合并以生成 M3。您可以使用git merge -s resolve 随机选择F'G 之一作为合并基础,如果在创建虚拟合并基础时存在合并冲突,这样可能会不那么混乱。如果你正确地制作了 M2——对于 "correctly" 的某些定义——无论如何——-s resolve 的结果将是相同的。

最后,Git 不关心如何 您到达最终提交图和这些提交中的快照。 Git 真正关心的是保留提交(保留图形)。 Branch names 仅用于查找提示提交:图中的入口点。各种单独的命令,例如 git diffgit cherry-pick,比较特定的提交,也许从 图中找到它们 ,并从那里做一些有趣的事情,所以你可能希望特定的提交有特定的内容,所以让这些内容尽可能有趣。

与此同时,git merge 本身只是查看图表以找到合并基础,然后进行 two 差异(从合并基础到每个提示提交)以查找更改,然后合并更改。当然也有一些例外:-s ours 使新的合并提交与真正的合并具有相同的图形链接,但完全忽略了两个分支提示之一,因此不必费心寻找合并基础,它只是re-uses 来自 then-current 提交的树。并且,如上文所述,-s recursive 找到 all 合并基础。通常只有一个,所以这没什么特别的,但是有很多方法可以获得多个合并基础,为此 -s recursive 只是合并合并基础以产生一个新的(临时)提交以用作合并基础. (完成 merge-as-a-verb 过程后,它会释放临时提交以供 Git 的常规垃圾收集过程回收。)