繁体   English   中英

在 Git cherry-pick 或 rebase 合并冲突中,BASE(又名“祖先”)、LOCAL 和 REMOTE 是如何确定的?

[英]In a Git cherry-pick or rebase merge conflict, how are BASE (aka "the ancestor"), LOCAL, and REMOTE determined?

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

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

当一个Git cherry-pick产生merge conflict时,没有共同的祖先,严格来说,那么这些东西是怎么确定的呢? 关于变基也可以问同样的问题。

樱桃挑选

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

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

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

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

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

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

变基

对于背景上下文,rebase 大约是迭代的 cherry-picking。 特别是,在 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.

在此过程中应用的标签是正常的 cherry-pick 规则的扩展:

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

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

即使您在启动 rebase 时位于分支主题上,在进行 rebaseLOCAL 也永远不会引用主题分支上的提交 相反,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?

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

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

  • 如果 <commit C> 没有修改文件的给定给定区域,那么您的分支中该区域的版本将占上风。 (也就是说,“补丁”不需要更改的区域不会被打补丁。)
  • 如果 <commit C> 修改了文件的给定区域并且您的分支单独保留该区域,则该区域的版本来自 <commit x> 将占上风。 (也就是说,“补丁”要求更改的区域会被打补丁。)
  • 如果 <commit C> 修改了文件的给定区域,但您的分支也修改了该区域,那么您会遇到合并冲突。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM