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

Chr*_*ris 27 git cherry-pick git-cherry-pick

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

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

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

Chr*_*ris 38

摘樱桃

除非我误导自己,否则如果你做"git cherry-pick <commit C>",那么你得到:

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

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

与此同时,让我们举一个例子,看看BASE 可以但往往不会成为樱桃选择中的共同祖先.假设提交图看起来像这样

E <-- master
|
D 
| C <-- foo_feature(*)
|/
B
|
A
Run Code Online (Sandbox Code Playgroud)

你在分支foo_feature(因此是星号).如果你做"git cherry-pick <commit D>",那么那个樱桃选择的BASE将提交B,它是C和D的共同祖先.(C将是LOCAL,D将是REMOTE.)但是,如果你改为"git cherry-pick <commit E>,那么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.
Run Code Online (Sandbox Code Playgroud)

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

  • LOCAL:你正在挑选的提交
    • 这是新的基于topic_rebased的分支的HEAD
    • 对于第一次提交,这将与master的HEAD相同
  • REMOTE:你正在挑选的提交(即<commit C>)
  • BASE:您正在挑选的提交的父级(C ^,即C的父级)

如果你想避免混淆,这意味着需要记住LOCAL与REMOTE的关系:

即使您在启动rebase时处于分支主题,LOCAL也不会在 rebase正在进行时引用主题分支上的提交.相反,LOCAL始终引用正在创建的分支上的提交(topic_rebased).

(如果没有记住这一点,那么在一次令人讨厌的合并期间,人们可能会开始问自己,"等等,为什么说这些是局部变化?我发誓他们是对主人做出的改变,而不是在我的分支上.")

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

假设我们已提交图表

D <-- foo_feature(*)
|
| C <-- master
B |
|/
|
A
Run Code Online (Sandbox Code Playgroud)

我们目前在分支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
Run Code Online (Sandbox Code Playgroud)

(在现实生活中,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
Run Code Online (Sandbox Code Playgroud)

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

D' <-- foo_feature
|
B'
|
C <-- master
|
A
Run Code Online (Sandbox Code Playgroud)

为什么BASE定义为它?

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

实质上,人们通过普通的三向合并算法来实现"补丁"功能.特别是你得到这些"不完整"的属性:

  • 如果<commit C>不修改文件的给定给定区域,则以该分支的版本为准.(这是"补丁"不会要求更改的区域不会被修补.)
  • 如果<commit C>修改文件的给定区域并且您的分支单独留下该区域,那么<commit x>中该区域的版本将占优势.(也就是说,"补丁"要求更改的区域会被修补.)
  • 如果<commit C>修改了文件的给定区域,但您的分支也修改了该区域,则会出现合并冲突.