在错误合并后重建 git 分支(尊重合并提交)

Rebuilding git branch after a faulty merge (respecting merge commits)

我有一个典型的情况:我将 dev 分支合并到 master,然后意识到我犯了一个错误并恢复了合并。然后在 dev 中添加了一个修复并想再次合并它。当然不能直接合并,因为在git看来,已经合并了

经典方案

还原还原提交,应用您的修复。继续前进。

我想要的

我想从头开始重建 dev 分支,然后再次合并。根据书籍,使用 git rebase --force-rebase 应该很容易完成。但是分支历史并没有那么简单,它包含了很多合并,包括来自 master 的合并。我要尊重这些。

让我们举个例子:

                                     master|
                                           v
-*------------X-----Y---------*-----*----M-W
  \          /       \         \   /    /
   \        /         \         -*-    /
    \      /  -D-      G--H--         /
     \    /  /   \    /      \       /
      A--B--C-----E--F--------I-----J
                                    ^
                                    |dev

错误的合并是M。它的还原是 W。我假设 devB 的提交是可以的,因为 B 被合并到 master 作为 X。我想用完全相同的拓扑重建从 B 开始的分支。请看下面的树。新提交标有素数。您可以看到提交 C..J 是如何重新设置为 C'..J' 的。相应地,dev 变基为 dev'。其他提交没有改变。

                                dev'|
                                    v
            C'----E'-F'-------I'----J'
           / \   /    \      /        
          |   -D'      G'-H'-           
          |           /                    |master
          |          /                     v
-*--------+---X-----Y---------*-----*----M-W
  \       |  /       \         \   /    /
   \      | /         \         -*-    /
    \     |/  -D-      G--H--         /
     \    /  /   \    /      \       /
      A--B--C-----E--F--------I-----J
                                    ^
                                    |dev


然后我将应用我的修复 K 并将这个新分支合并到 master:

                                dev'|
                                    v
            C'----E'-F'-------I'----J'--K
           / \   /    \      /           \
          |   -D'      G'-H'-             \
          |           /                    \ |master
          |          /                      \v
-*--------+---X-----Y---------*-----*----M-W-M2
  \       |  /       \         \   /    /
   \      | /         \         -*-    /
    \     |/  -D-      G--H--         /
     \    /  /   \    /      \       /
      A--B--C-----E--F--------I-----J
                                    ^
                                    |dev

好吧,好像 rebase 有一个选项,叫做 --rebase-merges。但它没有做我想要的。它变基所有提交,dev 的祖先和 B 的后代。其中包括提交 XY。哪些已经在 master 上了,我为什么要它们? (实际上,它还带来了一些更古老历史的其他提交,我还不明白为什么,但这不是重点)。

相反,我想带来提交,它们是 dev 但不是 master 的祖先(考虑到错误合并之前的 master当然)。对我来说听起来真的很容易。但是我不知道该怎么做,除非手动构建提交的这种差异并以某种方式手动 cherry-picking 它们,小心地保存 parents.

那么,如何重建我的分支,包含仅在我的分支上完成的工作并尊重合并提交?

PS我们不能简单地将master重置为M^,那太容易了。

基于https://github.com/git/git/blob/master/Documentation/howto/revert-a-faulty-merge.txt,我会

git checkout dev
$EDITOR files
git commit files -m 'Fix'
git checkout master
git revert W
git merge dev

当您 re-merge.

时,revert-revert 是保持更改所必需的

好的,看来我找到了解决办法:

  1. git rebase --interactive --rebase-merges B dev
  2. 改写 C

这产生了我想要的历史。提示归功于 @mstrap

我不太清楚为什么这会按照我想要的方式工作。