你的图形绘制非常漂亮,但它有些误导人。
让我们从一个问题开始:蓝色提交位于哪个分支?
这是一个诡计问题。它们不在一个分支上,而是在多个分支上,复数形式。如果你说它们在分支A
上,那是对的,但它们也在master
上,大部分还同时在分支B
上。
在您的图中,每个提交没有标识符。这使得它们很难谈论:我可以说“从左边数第二个灰色提交”或“最右边的蓝色提交”,但这有点难以掌握,所以让我将图形重新绘制为文本,并在每个提交中使用单个大写字母。我在此处也不会使用箭头,因为在文本中太难了。
A--B--C--D--E--F--G--H--I--J--K--L <-- master
\ \ /
M--N--O--P--Q---R---S <-- branch-A
\ \
T--U--V--W--X--Y--Å--Ø--Z <-- branch-B
也就是说,
master
分支的
tip commit 是提交记录
L
。
branch-A
分支的
tip commit 是提交记录
S
。
branch-B
分支的
tip commit 是提交记录
Z
。
在 Git 中,分支名(比如
master
)总是指向一个单独的提交记录。你可以选择任何一个提交记录作为你的目标,被你这样选中的提交记录就成为了该分支的
tip commit 。 从该
tip commit 开始往前(在这些图示中就是向左)沿着由提交到提交的箭头,我们会先到达
K
,再从
K
到
J
。但是
J
是一个合并提交,它有两个父提交记录,而不是一个。因此,从
J
我们同时到达了
S
和
I
。从这两个节点,我们分别继续到达
H
和
R
,然后到达
G
和
Q
,
F
和
P
,最后到达
E
。虽然有两种到达
E
的方式,但我们仍然只访问了一次。从这里,我们可以继续前往
O
和
D
,
N
和
C
,
M
和
B
,最后到达
A
。因此,这些提交记录的列表就是属于
master
分支的提交记录集合。
(注意,我们不能从
Q
到达
W
,虽然我们可以从
W
到达
Q
:所有箭头都是单向指向向后的。)
branch-A
上的提交记录集合从
S
开始向后(在这里是向右)延伸,经过
R
、
Q
、
P
、
E
、
O
、
D
、
N
等等。所有这些提交记录也都属于
master
分支。
branch-B
上的提交从Z
开始,穿过Ø
,Å
,Y
,X
和W
,然后捡起P
和V
,接着是E
,O
和U
等。因此,在branch-B
上有四个提交不在任何其他分支上。
1技术上,Git内部的所有箭头都指向后面。所以你画箭头时其实是反向的。这种情况发生是因为提交是完全只读的:父提交无法事先知道它的子提交的哈希值,但子提交在创建时确实知道它的父提交的哈希值。因此,箭头必须指向后面。
(为了向前移动,您要告诉Git您想要到达哪里,然后它会从那里向后移动,以确保可以从某个其他起始点到达那里。)
git merge
如何工作,大大简化
当您运行以下命令:
git checkout master
git merge branch-B
Git必须找到提交记录L和Z的merge base提交。为了做到这一点,像大多数Git操作一样,它向后工作,从这些分支尖端提交开始,找到最佳共享提交:一个可从两个分支尖端到达的提交,并且在两个分支上,但是“最接近尖端”。 在这种情况下,虽然这可能不是立即显而易见的,但那就是提交Q。 从L开始,通过K回到J,然后转到S,然后转到R,最后转到Q。 同时,从Z开始,回到W再上升到Q。提交P,E,O等都在两个分支上,但提交Q更好,因为它是最后一个这样的提交:它是所有这些提交的后代。
现在,Git将在内部运行两个git diff命令。 这将比较合并基础-提交Q与两个分支尖端:
git diff --find-renames <hash-of-Q> <hash-of-L> # what we changed
git diff --find-renames <hash-of-Q> <hash-of-Z> # what they changed
在这两个不同的清单中,如果某个文件在
两个分支末端看起来是新创建的,则该文件将出现一个add/add冲突。该文件不在
Q
中,但它在
L
和 Z
中。
对于所有三个提交中的文件,Git将尝试组合在两个diff集中显示的任何更改。对于这些更改不重叠(并且也不“接触边缘”)的地方,Git可以将它们组合在一起。如果它们重叠但这些重叠完全相同-覆盖
Q
中的相同原始行,并进行
相同更改-Git可以通过只取一份更改的副本来组合它们。所有其他重叠都会导致合并冲突。
你的工作现在是解决所有的冲突,任何你喜欢的方式。例如,在add/add整个文件冲突中,如果两个文件匹配,只需选择其中一个文件的内容。如果不匹配,以某种方式组合这两个文件。完成后,使用
git add
将每个有冲突的文件的最终内容写入索引。这标记了索引冲突为“已解决”,现在你可以运行
git merge --continue
或
git commit
来完成合并。
2
2git merge --continue
检查你是否在完成一个合并。如果没有,它会出错。如果是的话,它只运行git commit
-因此两者之间没有实际区别,除非你实际上没有完成一个有冲突的合并。