git 恢复合并:确定哪个 parent 是哪个(-m 1 vs -m 2)

git revert a merge: determine which parent is which (-m 1 vs -m 2)

我正在尝试还原合并,但我不知道是使用 git revert -m 1 <merge commit's sha> 还是 git revert -m 2 <merge commit's sha>。我如何找出哪个 parent 是 -m 1,哪个 parent 是 -m 2

嗯,super-short 的答案是总是 -m 1。 :-) 但这值得一些解释:

  • parent是有序的,git loggit show等命令显示顺序:

    commit c13c783c9d3d7d3eff937b7bf3642d2a7fe32644
    Merge: 3f7ebc6ec 39ee4c6c2
    

    所以这里 3f7ebc6ec 是 parent #1 而 39ee4c6c2 是 parent #2.

  • 后缀 ^ 操作采用这些相同的值:

    $ git rev-parse c13c783c9d3d7d3eff937b7bf3642d2a7fe32644^1
    3f7ebc6ece46f1c23480d094688b8b5f24eb345c
    

    (当然 ...^2 会是另一个)。

  • 绘制图表的程序,包括 git log --graph,将向您展示这些是如何连接的。

  • 但最重要的是,任何合并的 第一个 parent 是您进行合并时的最新提交.

特别是,这意味着如果您在分支 main 和 运行 git merge sidebranch 上,您现在(如果一切顺利)或最终(如果您必须手动解决合并)作为其 first parent,main 分支的前一个提示。它的第二个 parent 因此是 sidebranch.

的尖端

那么,假设我们从以下内容开始:

...--o--*--A--B------C   <-- main
         \
          D--E--F--G--H   <-- sidebranch

当我们运行git merge。公共基础提交是 *,Git 通过做一个新的合并提交 M,本质上:

  1. git diff * C(我们改变了什么?)
  2. git diff * H(他们改变了什么?)

然后组合这两组更改并将它们应用到 *,得到这个最终结果:

...--o--*--A--B------C--M   <-- main
         \             /
          D--E--F--G--H   <-- sidebranch

现在,如果 A-B-C 中更改的所有内容完全独立于 D-E-F-G-H 中更改的所有内容,那么精确 how Git 进行了还原,只要它在放弃 D-E-F-G-H 更改的同时保留 A-B-C 更改即可。

但是如果 BF 大部分相同,即 BF 都修复了一个错误怎么办?在这种情况下,我们不想撤消BF的共享更改,Git复制了一份。 这是 -m 1 部分的用武之地。

git revert 去撤消某些更改时,它 运行 它自己的 git diffgit diff it 运行s 将您要还原的提交与其 parent 进行比较。对于任何普通的 non-merge 提交,这很容易:比较 BA,或 ED,或其他,看看发生了什么,然后返回出来。但是,对于合并提交,与 parent 进行比较并不明显(除了它有点像 :-) )。这里的第一个parent是C,我们看看运行:

会得到什么
git diff C M

CM 之间的变化是我们通过 添加 D-E-F-G-H 到我们 A-B-C中已经有,如果我们比较M*。换句话说:

  • 如果BF重叠100%,C-vs-M的变化是D-E-G-H:一切除了重叠。所以我们最终只恢复了那些。

  • 如果FB更多的变化,C-vs-[的变化=52=] 是 D-E-(some-of-F)-G-H:我们最终恢复了这些更改,但不是 B.

  • 中的更改
  • 如果FB更少的变化,C-vs-[的变化=52=] 又是 D-E-G-H,我们最终只还原了那些。

由于第一个 parent 是 C,我们想要取消 D-E-F-G-H 更改(不包括我们已经通过 A-B-C 进行的任何更改),我们想要 -m 1 在此还原中。