TL;DR(2018 年 5 月添加)
整个事情从根本上来说至少有点令人困惑,因为 Git 让它的内部运作直接向你展示。
请注意,我们在这里关注的情况会在您运行时发生:
git checkout somebranch; git rebase origin/their-branch
或类似的。rebase 暂时停止以强制您解决合并冲突,之后您应该git add
解决冲突并运行git rebase --continue
. (如果您使用一些带有 的合并工具git mergetool
,或花哨的 GUI 界面,该界面可能会以其他方式为您完成部分或全部操作,但在下面,它正在git add
解析已解析的文件并正在运行git rebase --continue
。)
一开始,HEAD
提交是他们的分支,所以如果你使用git checkout --ours
or git checkout --theirs
,--ours
表示他们的——最后一次提交origin/their-branch
——而--theirs
表示你的,你要变基的第一个提交。这是日常生活中常见的 Git 混淆(请参阅What is the exact meaning of "ours" and "theirs" in git?),并不是导致原始问题的原因。
然而,后来,HEAD
提交实际上是一种混合。这是在最新提交上复制一些提交的结果。您现在在自己部分构建的新系列提交和您自己的原始提交之间遇到冲突。这种冲突的根源通常是“他们”所做的事情(在 的过程中发生了变化origin/their-branch
)。你仍然需要解决这个冲突。当您这样做时,您可能会在以后的提交中看到相同的冲突再次出现。
再次,HEAD
or local
or--ours
是rebase 通过组合您的更改及其更改构建的提交,而另一个提交 ( remote
or>>>>>>>
或--theirs
) 是您自己的提交,rebase 正在尝试复制 atop HEAD
。
更长
合并时(包括变基,这是内部重复“合并”的一种特殊情况),涉及两个“头”(两个特定的分支提示)。让我们称它们为your-branch
and origin/their-branch
:
G - H -------- <-- HEAD=your-branch
/ \
... - E - F M <-- desired merge commit [requires manual merge]
\ /
I - J - K - L <-- origin/their-branch
这一点通常(并且不足为奇)令人困惑,尽管当这样标记时,它已经足够清楚了。
更糟糕的是,git 在合并期间使用--ours
and--theirs
来引用两个头部提交,其中“我们的”是您在H
运行时(提交)的那个git merge
,而“他们的”是他们的(提交L
)。但是当你做一个变基时,两个头是相反的,所以“我们的”是你要变基的头——即他们更新的代码——而“他们的”是你当前变基的提交,即,您自己的代码。
这是因为 rebase 实际上使用了一系列的樱桃挑选操作。您从几乎相同的图片开始:
G - H <-- HEAD=your-branch
/
... - E - F
\
I - J - K - L <-- origin/their-branch
git在这里需要做的是复制G
commits的效果H
,即git cherry-pick
commit G
,然后用commit再做一次H
。但要做到这一点,git 必须先在内部切换到提交L
(使用“分离的 HEAD”模式):
G - H <-- your-branch
/
... - E - F
\
I - J - K - L <-- HEAD, origin/their-branch
现在它可以通过比较提交的树F
和G
(查看您更改的内容),然后比较F
vs L
(查看您的某些工作是否已经在L
)来启动 rebase 操作,并进行任何尚未在的更改L
并添加它。这是内部的“合并”操作。
G - H <-- your-branch
/
... - E - F G' <-- HEAD
\ /
I - J - K - L <-- origin/their-branch
如果合并不顺利,HEAD
仍然留在提交L
(因为提交G'
还不存在)。因此,是的,HEAD
是他们开发部门的负责人——至少现在是。
但是,一旦 的副本G
存在,HEAD
移动到G'
并且 git 尝试以H
相同的方式从 复制更改(diff G
vs H
,然后 diff F
vs G'
,并合并结果):
G - H <-- your-branch
/
... - E - F G' - H' <-- HEAD
\ /
I - J - K - L <-- origin/their-branch
同样,如果合并失败并需要帮助,您将只剩下HEAD
指向G'
而不是H'
asH'
尚不存在。
一旦合并全部成功并提交G'
并且H'
确实your-branch
存在,git从 commit中删除标签H
,并使其指向 commit H'
:
G - H
/
... - E - F G' - H' <-- HEAD=your-branch
\ /
I - J - K - L <-- origin/their-branch
你现在被重新定位并且HEAD
再次是你所期望的。但是在变基期间,HEAD
要么是他们的分支提示(提交L
),要么是复制并附加到他们的分支提示之外的新提交之一;并且意味着在while--ours
结束时增长的分支意味着从(或更高版本)复制的提交。L
--theirs
G
H
(这基本上是 git 暴露了它如何做它所做的事情的原始机制,这在 git 中发生了很多。)