Git 如何重新设置已覆盖历史记录的分支的基础?

Git 如何重新设置已覆盖历史记录的分支的基础?,git,version-control,rebase,Git,Version Control,Rebase,假设我的分支1带有提交A 1:A 从1开始,我创建另一个分支,2 2:A 在2上,我添加了一个提交 2:A->B 然后在1上,我做了一些更改,并git提交--amend,覆盖A 1:C 在2上,我想在1上进行这些新的更改,并且只想将我的更改从B应用到C。然而,据我所知,git将看到分支2有新的提交A和B,并尝试在C之上应用这两个提交。因此,当我完成重定基址时,2将看起来像 2:C->A->B 当我真正想要的是C->B 处理这种情况的正确方法是什么?到目前为止,我处理这个问题的方法是创建1的一个新

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

1:A

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

2:A

2
上,我添加了一个提交

2:A->B

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

1:C

2
上,我想在
1
上进行这些新的更改,并且只想将我的更改从
B
应用到
C
。然而,据我所知,git将看到分支
2
有新的提交
A
B
,并尝试在
C
之上应用这两个提交。因此,当我完成重定基址时,
2
将看起来像

2:C->A->B

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


处理这种情况的正确方法是什么?到目前为止,我处理这个问题的方法是创建
1
的一个新分支,然后cherry从
2
中选择commit
B
。但这似乎有点老套,而且当
2
有不止一个新提交时,这样做也不是那么简单。

让我们更常规地安排一下,看看发生了什么

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

假设我有一个带有提交A的分支1

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

在2上,我添加了一个commit

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

这就是事情不稳定的地方

  C [1]
 /
Z - A
     \
      B [2]
Git从不“覆盖”提交。不可能。提交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,所以您真正想要的是a之后2的所有内容

结果就是你想要的

    B1 [2]
   /
  C [1]
 /
Z - A
     \
      B
(A和B将不可见,它们最终将被垃圾收集。)


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

这就是为什么最好不要重新设置或修改“稳定”分支的基础,通常是
master
。相反,只需使用普通提交或功能分支进行更大的更改。然后应用更新分支的正常过程

  C [1]
 /
Z - A
     \
      B [2]
    A1 - B1 [2]
   /
  C [1]
 /
Z - A
     \
      B
git rebase --onto 1 A..2
    B1 [2]
   /
  C [1]
 /
Z - A
     \
      B