36

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

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

当一个 Git cherry-pick 产生合并冲突时,没有共同的祖先,正确地说,那么这些东西是如何确定的呢?关于变基也可以问同样的问题。

4

1 回答 1

45

樱桃采摘

除非我误导了自己,否则如果你执行“git cherry-pick <commit C>”,那么你会得到:

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

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

同时,让我们举个例子,看看 BASE可以但通常不会是樱桃采摘过程中的共同祖先。假设提交图看起来像这样

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

你在分支 foo_feature (因此是星号)。如果您执行“git cherry-pick <commit D>”,那么该cherry-pick 的BASE 将是commit B,它是C 和D 的共同祖先。(C 将是LOCAL,D 将是REMOTE。)但是,如果您改为执行“git cherry-pick <commit E>,则 BASE 将是提交 D。(C 将是本地的,E 将是远程的。)

变基

对于背景上下文,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.

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

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

如果您想避免混淆,这意味着要记住有关 LOCAL 与 REMOTE 的一些事情:

即使您在启动 rebase 时处于分支主题上,当rebase 正在进行时,LOCAL 也不会引用主题分支上的提交。 相反,LOCAL 总是指正在创建的新分支上的提交(topic_rebased)。

(如果一个人没有记住这一点,那么在一个讨厌的合并过程中,一个人可能会开始问自己,“等等,为什么说这些是本地更改?我发誓它们是在 master 上进行的更改,而不是在我的分支上。”)

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

假设我们有提交图

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

为了完整起见,请注意在变基的末尾 B 和 D 从图中删除,产生:

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

为什么 BASE 是这样定义的?

如上所述,对于樱桃选择和变基,BASE 是被拉入的提交 C 的父 (C^)。在一般情况下,C^ 不是共同祖先,所以为什么称它为 BASE ? (在正常的合并中,BASE一个共同的祖先。git 在合并方面的成功部分是由于它能够找到一个好的共同祖先。)

本质上,这是通过普通的三向合并算法实现“补丁”功能的一种方式。特别是你会得到这些“不完整”的属性:

  • 如果 <commit C> 不修改文件的给定区域,则以您分支中该区域的版本为准。(也就是说,“补丁”不需要更改的区域不会得到补丁。)
  • 如果 <commit C> 修改了文件的给定区域,而您的分支不理会该区域,则 <commit x> 中该区域的版本将占上风。(也就是说,“补丁”要求更改的区域会被打补丁。)
  • 如果 <commit C> 修改了文件的给定区域,但您的分支也修改了该区域,那么您会遇到合并冲突。
于 2012-04-07T20:27:10.197 回答