如何重新定位到已覆盖历史记录的分支?

How to rebase onto branch that has overwritten history?

假设我有分支 1,提交 A

1: A

从 1,我创建了另一个分支,2

2: A

2,我添加了一个提交。

2: A->B

然后在 1 上,我做了一些更改并 git commit --amend,覆盖了 A

1: C

2,我想要在 1 上进行这些新更改,并且只想将我在 B 上所做的更改应用到 C 上。但是,据我了解,git 会看到分支 2 有新的提交 AB,并尝试在 [=24] 之上应用这两个提交=].所以当我完成变基后,2 看起来像

2: C->A->B

当我真正想要的是C->B

处理这种情况的正确方法是什么?到目前为止,我处理这个问题的方法是从 1 创建一个新分支,然后从 2 中挑选提交 B。但这似乎有点 hacky,而且当 2 有不止一个新提交时做起来并不是那么微不足道。

让我们用更传统的方式来展示它,看看发生了什么。

如果 A 是第一个提交,事情就变得复杂了,所以我将添加一个祖先 Z。

Say I have branch 1 with commit A.

Z - A [1]

From 1, I create another branch, 2.

Z - A [1][2]

On 2, I add a commit.

Z - A [1]
     \
      B [2]

Then on 1, I make some changes and git commit --amend, overwriting A.

这就是事情变得不稳定的地方。

  C [1]
 /
Z - A
     \
      B [2]

Git 从不 "overwrites" 提交。它不能。提交 ID 是提交内容及其所有祖先的校验和。 commit --amend(以及 rebase)所做的是创建新的提交并假装它一直都是那样。

但是旧的提交仍然存在。如果有任何东西以它为祖先,就像这里的分支 2,它将是可见的。

On 2, I want these new changes on 1 and want to just apply my changes from B onto C. However, from what I understand, git will see that branch 2 has new commits A and B, and try to apply both of these commits on top of C. So when I'm done rebasing, 2 will look like 2: C->A->B when what I really want is C->B.

这里的问题是因为 commit --amend 分支 1 和分支 2 不再有 A 作为共同祖先。现在是 Z。如果您尝试将 2 变基到 1,它会将 A 和 B 都放在 C 之上。

    A1 - B1 [2]
   /
  C [1]
 /
Z - A
     \
      B

虽然这可能有效,但也可能导致混乱的冲突。你必须使用更精确的 rebase 命令来让 Git 知道你真正想要什么。由于C实际上是A的重做,所以您真正想要的是2 after A.

上的所有内容
git rebase --onto 1 A..2

结果就是你想要的。

    B1 [2]
   /
  C [1]
 /
Z - A
     \
      B

(A 和 B 将不可见,它们最终将被垃圾回收。)


如果您认为这很复杂且充满危险,那是!

这就是为什么最好不要变基或修改你的"stable"分支,通常是master。相反,只需使用普通提交或功能分支进行更大的更改。然后应用更新分支的正常过程。