如何重写到覆盖历史的分支?

时间:2018-03-12 19:01:41

标签: git version-control rebase

假设我的分支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,并尝试在C之上应用这两个提交。所以,当我完成变基,2看起来像

2: C->A->B

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

处理这种情况的正确方法是什么?到目前为止,我一直在处理这个问题的方法是从1创建一个新分支,然后从B挑选提交2。但是这似乎有些苛刻,而且当2不仅仅有一个新提交时,这并不是那么简单。

1 个答案:

答案 0 :(得分:2)

让我们更常规地说明这一点,看看会发生什么。

如果A是第一次提交事情变得复杂,那么我将添加一个祖先Z.

  

假设我的分支1有提交A。

Z - A [1]
  

从1开始,我创建另一个分支,2。

Z - A [1][2]
  

在2上,我添加一个提交。

Z - A [1]
     \
      B [2]
  

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

这里的事情变得不稳定。

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

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

但旧的承诺仍然存在。如果有任何东西作为祖先,就像这里的分支2一样,它将是可见的。

  

在2上,我想在1上进行这些新的更改,并希望将我的更改从B应用到C.但是,根据我的理解,git将看到分支2有新的提交A和B,并尝试同时应用这两个这些提交在C之上。所以当我完成变基时,2将看起来像2:C-> A-> B当我真正想要的是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,你真正想要的是之后的所有内容。

git rebase --onto 1 A..2

结果将是你想要的。

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

(A和B不会被看见,最终会被垃圾收集。)

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

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