我是Git
的新用户并试图了解git merge
命令的工作原理。所以我尝试了一个简单的git项目,例如:
我有两个分支:master
和b
。您可以在这里看到两个git log(1,2,4
是在分支中创建的示例文件):
* cf3456b (HEAD, b) add and modify 4 in b
* 68b9086 edit 1 in branch b
| * 81e6490 (master) remove 1 from branch b1
| * e0a6844 modify 2 in branch b1
| * 06bad1d add2 in branch b1
|/
* c667d3b add 1 in branch master
因此
master
只有一个文件:2
b
只有两个文件:1
,4
现在我试着这样做:
git merge master
我看到了这条消息:
CONFLICT(修改/删除):1在master中删除并在HEAD中修改。 版本HEAD为1留在树中。自动合并失败;解决冲突 然后提交结果。
对我来说这很奇怪,我之前想过:
merge
就像checkout
尝试将内容从merge in
分支的最新提交复制到merge into
分支的最新提交但与checkout
相反,{{ 1}}尝试不更改merge
中的任何文件。最后,如果merge into
和merge in
分支具有不同状态的相同文件,则会发生冲突。merge into
只保存git
个文件而不是snapshot
。那么git如何知道最新的分支提交中的变化? (在消息中,您可以看到differences
哪些是差异)现在,如果我的想法是正确的,那么文件1 deleted in master and modified in HEAD
不会发生冲突。所以我的想法不正确,但1
如何运作呢?
答案 0 :(得分:4)
重点#2,你是正确的,git保存快照。但是,给定任意两个快照a
和b
,我们可以通过比较b
与a
来获得变更集(差异) - 并且git会根据需要执行此操作。< / p>
至于合并本身,首先,我们必须注意git保留提交图(也称为&#34; DAG&#34;或&#34;提交DAG&#34; ,因为图是 D 竖立的 A 循环 G raph或DAG)。这是你的git log --graph
输出显示的内容,尽管在这种特殊情况下我们有一个简单的图形形式,它只是一棵树(直到合并被提交)。
对于任何树,以及许多DAG, 1 在树中给出两个节点,我们可以找到唯一的 L owest C ommon < strong> A ncestor节点,或 LCA 。这里的两个节点是两个分支的提示 - 提交cf3456b
(当前分支的尖端)和81e6490
(master
的尖端) - 以及此特定情况下的LCA是第一个提交,c667d3b
。在这种简单的情况下,LCA很容易在视觉上找到:你只需查看提交图,找到两个分支连接的位置(从那里返回到根,所有提交都在两者 branches)。
此LCA节点是合并基础。 Git首先找到当前提交的合并基础和你给它的参数。 (对于&#34;章鱼合并&#34;,你指导git将多个提交合并到当前分支上,这个工作有点复杂,但我们可以忽略这些。)
接下来,给定现有的合并库和两个不同的tip提交,git必须计算两个变更集:一个从merge-base到当前提交,一个从merge-base到参数commit。请注意,git会在整个过程中预先执行此操作,之后可以继续执行合并操作。
现在,对于每个有变化的文件,git必须组合这些变化。对于大多数简单修改,该方法很简单:如果仅在一个分支中修改文件1
,则按原样进行修改。如果它在两个分支中被修改,则尝试组合修改,只需要一个副本,其中两个分支进行相同的更改。当然,如果两个分支对单个文件的同一区域进行了不同的更改,则会发生冲突。
对于文件创建或删除情况,或者有重命名时,事情变得有点棘手。如果文件在一个分支中删除而在另一个分支中未触及,git可以通过删除文件来解决此问题(&#34;保持删除&#34;如果它已在HEAD
中删除,则将其删除如果它在其他提交中被删除)。如果文件在一个分支中重命名,并在另一个分支中进行修改,则git也可以组合这些更改(执行或保留重命名,同时还导入或保留其他更改)。但是,对于其他任何事情,git只是宣布冲突,抛出其隐喻之手,并让你解决冲突。
在这种情况下,文件1
确实在当前分支中被修改,并且在master
中确实被删除了。 Git不确定是否要删除文件(按照合并基础指向master
diff),或保留更改(按照合并基础指向HEAD
diff),因此它会离开你有文件和冲突。
如果你在两个分支中都创建了一个新文件5
,那么git会再次给你一个冲突(可能,如果文件的两个分支中的新内容相同 - 我没有测试了这个案例)。
1 对于复杂的DAG,可能有多个最低共同祖先候选者。对于git&#34;递归&#34;合并,git通过合并每个LCA候选人来形成一个新的&#34;虚拟基地&#34;来处理这个问题。此虚拟基础成为与两个提交进行比较的合并基础。如果只有两个LCA候选者,则通过粗略等效于:
获得虚拟合并基础git checkout -b temp candidate_1
git merge candidate_2
git commit -a -m ""
在此内部合并期间发生的任何合并冲突&#34;被忽略:冲突的合并基础与其冲突一起使用。这可能会产生一些看似时髦的冲突,尤其是当&#34;外部&#34;的相同区域发生冲突时。合并:见this SO question。
如果有两个以上的LCA候选者,merge-recursive
取前两个并合并它们,然后合并第三个,然后合并第四个,依此类推,迭代。即使它可以合并成对来将数字减半,然后合并合并对再将其切成两半,依此类推。也就是说,给定N个LCA候选者,可以进行ceil(log2(N))合并,而不是N-1合并,但是git并不是很少超过2。
答案 1 :(得分:0)
冲突讯息:
CONFLICT(删除/修改):1在master中删除并在HEAD中修改
表示在您正在合并的主分支中删除了1,但在HEAD中进行了修改(在您实际使用的分支中)。
所以你必须决定是否
remove file using "git rm 1"
或
accept version from HEAD with "git add 1"