在Git樱桃挑选或rebase合并冲突中,BASE(又名“祖先”),LOCAL和REMOTE如何确定?

时间:2012-04-07 20:26:50

标签: git cherry-pick git-cherry-pick

在正常的Git合并冲突中,三向合并的三个版本的文件大致如下:

  • LOCAL:我分支机构的版本
  • REMOTE:来自其他分支的版本
  • BASE:来自两个分支的共同祖先的版本(特别是我的分支的HEAD的共同祖先和另一个分支的HEAD)

当一个Git樱桃挑选产生合并冲突时,没有共同的祖先,正确地说,那么这些事情是如何确定的?关于rebase也是如此。

1 个答案:

答案 0 :(得分:36)

<强>樱桃挑选

除非我误导自己,否则如果你做“git cherry-pick&lt; commit C&gt;”,那么你得到:

  • LOCAL:你要合并的提交(即你的分支的HEAD)
  • REMOTE:您正在挑选的提交(即&lt; commit C&gt;)
  • BASE:您正在挑选的提交的父级(即C ^,即C的父级)

如果不能立即清楚为什么BASE应该是C ^,请参阅下面的“为什么”部分。

与此同时,让我们举一个例子,看看BASE 可以,但在挑选时,通常不会是共同的祖先。假设提交图看起来像这样

E <-- master
|
D 
| C <-- foo_feature(*)
|/
B
|
A

你在分支foo_feature(因此是星号)。如果您执行“git cherry-pick&lt; commit D&gt;”,则该樱桃选择的BASE将提交B,这是C和D的共同祖先。(C将是LOCAL,D将是REMOTE。)但是,如果您改为“git cherry-pick&lt; commit E&gt;”,那么BASE将提交D.(C将是LOCAL,E将是REMOTE。)

<强>底垫

对于背景上下文,rebase大致是迭代的挑选。特别是,在master之上重新定位主题(即“git checkout topic; git rebase master”)意味着大约:

git checkout master # switch to master's HEAD commit
git checkout -b topic_rebased # create new branch rooted there
for each commit C in master..topic # for each topic commit not already in master...
    git cherry-pick C # bring it over to the new branch
finally, forget what "topic" used to mean and now defined "topic" as the HEAD of topic_rebased.

在此过程中应用的标签是正常樱桃挑选规则的扩展:

  • LOCAL:你正在挑选的提交
    • 这是新的topic_rebased分支的HEAD
    • 仅对于第一次提交,这将与主
    • 的HEAD相同
  • REMOTE:您正在挑选的提交(即&lt; commit C&gt;)
  • BASE:您正在挑选的提交的父级(C ^,即C的父级)

如果您想避免混淆,这意味着需要牢记LOCAL与REMOTE:

  

即使您在启动rebase时处于分支主题 LOCAL也从未引用主题分支上的提交 当一个rebase正在进行中时。相反,LOCAL总是引用正在创建的 new 分支的提交(topic_rebased)。

(如果一个人没有牢记这一点,那么在一次令人讨厌的合并期间,人们可能会开始问自己,“等等,为什么说这些是本地的变化?我发誓他们是对主人,而不是我的分支。“)

更具体地说,这是一个例子:

假设我们有提交图

D <-- foo_feature(*)
|
| C <-- master
B |
|/
|
A

我们目前在分支foo_feature上(用“*”表示)。如果我们运行“git rebase master”,则rebase将分两步进行:

首先,B的变化将在C之上重播。在此期间,C是LOCAL,B是REMOTE,A是BASE。请注意,A是B和C的真正共同祖先。在第一步之后,您有一个大致相同的图形:

   B' <-- foo_feature
D  |
|  |
|  C <-- master
B /
|/
|
A

(在现实生活中,B和D可能已经在树上被修剪掉了,但是我将它们留在这里,以便更容易发现任何潜在的共同祖先。)

其次,D的变化将在B'之上重播。在此期间,B'是LOCAL,D是REMOTE,B是BASE。请注意,B不是任何事物的相关共同祖先。 (例如,它不是当前LOCAL和REMOTE,B'和D的共同祖先。它不是原始分支头的共同祖先,C和D)。在这一步之后,你有一个大致相同的分支:

   D' <-- foo_feature
   |
   B'
D  |
|  |
|  C <-- master
B /
|/
|
A

为了完整性,请在图表中删除rebase B和D末尾的注释,产生:

D' <-- foo_feature
|
B'
|
C <-- master
|
A

为什么BASE按原样定义?

如上所述,对于cherry-pick和rebase,BASE都是提交C的父(C ^)。在一般情况下,C ^不是共同的祖先,为什么叫它BASE? (在正常的合并中,BASE 的共同祖先。而git在合并方面的成功部分归功于它能够找到一个好的共同祖先。)

基本上,人们通过普通的three-way merge算法来实现“补丁”功能。特别是你会得到这些“不完整”的属性:

  • 如果&lt; commit C&gt;如果不修改文件的给定给定区域,则以分支中该区域的版本为准。 (这是“补丁”不会要求更改的区域不会被修补。)
  • 如果&lt; commit C&gt;修改文件的给定区域,您的分支单独留下该区域,然后从&lt; commit x&gt;中删除该区域的版本。将占上风。 (也就是说,“补丁”要求更改的区域会被修补。)
  • 如果&lt; commit C&gt;修改文件的给定区域,但您的分支也修改了该区域,然后就会出现合并冲突。