我观察到,当在不同分支中的同一文件中进行更改时,然后尝试重新设置基础并将这些分支一个接一个地合并到主线中时,它有时会通过或有时会失败。结果不一致。例如 情况1): - 我在主线有一个文件- test.txt
a
b
c
d
我从主线创建了两个分支b1和b2 在分支b1中,我编辑并提交了这些更改-test.txt
a
b1
c
在分支b2中-> test.txt
a
b
c1
d
然后,我在b1上执行了“ git rebase mainline”,在主线上做了“ git merge b1” 到目前为止很好。现在,当我在分支b2上执行“ git rebase mainline”时,它失败并要求我首先解决冲突。
情况(2):- 我在主线中有文件test2.txt。
MEMBER xe-1/1/1 REMPORT xe-1/1/1
MEMBER xe-2/2/2 REMPORT xe-2/2/2
MEMBER xe-3/3/3 REMPORT xe-3/3/3
MEMBER xe-4/4/4 REMPORT xe-4/4/4
MEMBER xe-11/11/11 REMPORT xe-11/11/11
MEMBER xe-21/21/21 REMPORT xe-21/21/21
MEMBER xe-31/31/31 REMPORT xe-31/31/31
MEMBER xe-41/41/41 REMPORT xe-41/41/41
我已经从主线创建了分支c1和c2。在c1中,我将test2.txt编辑并提交为:-
MEMBER xe-1/1/1 REMPORT xe-1/1/1:1
MEMBER xe-2/2/2 REMPORT xe-2/2/2:1
MEMBER xe-11/11/11 REMPORT xe-11/11/11
MEMBER xe-21/21/21 REMPORT xe-21/21/21
MEMBER xe-31/31/31 REMPORT xe-31/31/31
MEMBER xe-41/41/41 REMPORT xe-41/41/41
在分支c2中,我将test2.txt编辑并提交为:-
MEMBER xe-1/1/1 REMPORT xe-1/1/1
MEMBER xe-2/2/2 REMPORT xe-2/2/2
MEMBER xe-3/3/3 REMPORT xe-3/3/3
MEMBER xe-4/4/4 REMPORT xe-4/4/4
MEMBER xe-11/11/11 REMPORT xe-11/11/11:1
MEMBER xe-21/21/21 REMPORT xe-21/21/21:1
MEMBER xe-31/31/31 REMPORT xe-31/31/31:1
MEMBER xe-41/41/41 REMPORT xe-41/41/41:1
然后,我在c1上执行了“ git rebase mainline”,在主线上做了“ git merge c1”。 到目前为止很好。现在,当我在c2上执行“ git rebase mainline”时,它会通过。 “ git merge c2”之后主线上的最终内容如下:-
MEMBER xe-1/1/1 REMPORT xe-1/1/1:1
MEMBER xe-2/2/2 REMPORT xe-2/2/2:1
MEMBER xe-11/11/11 REMPORT xe-11/11/11:1
MEMBER xe-21/21/21 REMPORT xe-21/21/21:1
MEMBER xe-31/31/31 REMPORT xe-31/31/31:1
MEMBER xe-41/41/41 REMPORT xe-41/41/41:1
这符合我的期望,但是我试图理解为什么Case(2)通过时Case(1)失败。这里的“ git rebase”算法是什么?
答案 0 :(得分:1)
要正确了解此处发生的情况,您需要了解几件事:
git merge
实现了很多事情,包括要合并的步骤,然后执行产生 merge commit 的提交,即将合并视为一个形容词。 -pick操作永远不会产生合并提交,但会利用合并即动词过程。)考虑原始设计时,合并操作最有意义。假设有两个人修改了某些内容,为简单起见,假设某物在这种情况下只是一个文件。我们称两个人为A(爱丽丝)和B(鲍勃)。
它们以该文件的通用基本版本开头。爱丽丝对文件进行了一些更改,鲍勃对文件进行了一些更改。最终,有人-爱丽丝(Alice),鲍勃(Bob)甚至是第三人C(卡罗尔(Carol))必须结合他们的更改。
在Git中,为了合并这些更改,我们让Git找出它们都是从哪个文件开始的,然后比较该基本版本为两个最新版本。基本版本是进入基本提交的文件的版本:
o--o--A <-- Alice
/
...--o--o--*
\
o--o--B <-- Bob
Git可以简单地运行两个git diff
命令:
git diff --find-renames <hash-of-*> <hash-of-A> > /tmp/alice
git diff --find-renames <hash-of-*> <hash-of-B> > /tmp/bob
Git然后可以提取提交*
的内容,应用Alice的更改,应用Bob的更改,并将最终结果用作合并结果。请注意,此操作可以处理 all 对 all 文件(一次一个文件)的更改。
如果Alice和Bob触摸任何一个文件中的 same 行,则当然存在合并冲突。这意味着我们必须定义行“相同”的含义,但这往往很清楚。如果您进行了简单的合并,这就是您在“案例1”中看到的内容:爱丽丝更改了:
a
b
c
d
收件人:
a
b1
d
因此她删除了b
和c
两行,并在b1
之前a
之后添加了一行d
。
鲍勃将相同的原始输入更改为:
a
b
c1
d
也就是说,鲍勃保留 b
,删除了c
,并在保留的c1
和保留的{{1}之间添加了b
}。这些变化是重叠的,因此它们显然是冲突的。
对于d
来说,上面的内容很好,也很不错,该git merge
进行动词合并,然后进行形容词合并提交(或者在这种情况下,由于合并冲突而停止,并且让您完成工作)。但是摘樱桃呢?
要了解变基及其选择,请再次开始,绘制提交图。我们有一系列提交,名称mainline
指向这些提交:
...--A--B--C <-- mainline (HEAD)
然后您创建一个新分支b1
,指向提交C
:
...--A--B--C <-- mainline, b1 (HEAD)
然后更改文件并提交新快照:
...--A--B--C <-- mainline
\
D <-- b1 (HEAD)
您还创建了指向提交b2
的分支C
,并更改了一个文件并提交了新的快照:
E <-- b2 (HEAD)
/
...--A--B--C <-- mainline
\
D <-- b1
(在这些图形中,名称HEAD
始终附加到当前分支。)
然后,我在b1上执行了“ git rebase mainline”,在主线上做了“ git merge b1”
也就是说,您运行了git checkout b1 && git rebase mainline
。这根本不执行任何操作,因为对D
的提交b1
已经在正确的位置。然后您做了git checkout mainline && git merge b1
。这会执行快进,实际上根本不是合并:它实际上等同于在移动名称D
时检出commit mainline
。结果是此图:
E <-- b2
/
...--A--B--C
\
D <-- b1, mainline (HEAD)
到目前为止很好。现在,当我在分支b2上执行“ git rebase mainline”时,它失败并要求我首先解决冲突。
如果您git checkout b2 && git rebase mainline
,Git会发现提交E
之后没有提交D
。而是在提交C
之后出现。因此,Git现在必须复制提交E
到新的提交。此复制为git cherry-pick
。
Cherry-pick通常被描述为“将提交与父提交进行比较以获取补丁,然后将该补丁应用于当前提交”。对于简单的情况,这已经足够接近了-实际上,git cherry-pick
一次完成了操作。但是,对于更复杂的情况,git cherry-pick
的操作是运行 merging 操作,但是将合并基础设置为要选择的提交的父级。
在这种情况下,E
的父级是C
,因此这是“挑选”的“合并基数”。 Git现在运行两个git diff
:一个将合并基础C
与D
比较,另一个将合并基础C
与E
比较。现在非常清楚,这就是我们合并爱丽丝和鲍勃的变更时的情况。这些更改重叠,因此Git引发了合并冲突。
解决合并冲突的过程与git merge
相同:您在Git的索引中拥有每个冲突文件的所有三个版本,另外Git最好自行进行合并,并存储在工作树。此工作树版本具有合并冲突标记。您必须以任何您喜欢的方式解决冲突,并更新索引,以使其具有文件的最终版本。例如,您可以只将工作树文件编辑为形状,然后使用git add
将工作树版本复制到索引中。
然后,您运行git rebase --continue
以完成变基正在执行的选择,并让变基继续选择更多的提交(如果有)。在这种情况下,没有任何内容,因此rebase通过移动分支名称以指向复制的提交来完成其工作:
E [abandoned]
/
...--A--B--C
\
D <-- b1, mainline
\
E' <-- b2 (HEAD)
您现在拥有所有工具来了解第二个基准。查看哪些提交被复制。注意哪个提交充当合并基础。运行两个git diff
命令,以将合并基础提交与其他两个提交进行比较。两个diff
输出中的更改是否冲突?如果不是,将这两个更改都应用于文件的基于合并的版本会得到什么结果?那和Git得到的一样吗?