Git合并会影响工作目录和暂存区域

时间:2016-03-12 06:15:01

标签: git version-control merge

我是Git的新用户并试图了解git merge命令的工作原理。所以我尝试了一个简单的git项目,例如:

我有两个分支:masterb。您可以在这里看到两个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只有两个文件:14

现在我试着这样做:

git merge master

我看到了这条消息:

  

CONFLICT(修改/删除):1在master中删除并在HEAD中修改。   版本HEAD为1留在树中。自动合并失败;解决冲突   然后提交结果。

对我来说这很奇怪,我之前想过:

  1. merge就像checkout尝试将内容从merge in分支的最新提交复制到merge into分支的最新提交但与checkout相反,{{ 1}}尝试不更改merge中的任何文件。最后,如果merge intomerge in分支具有不同状态的相同文件,则会发生冲突。
  2. 我知道merge into只保存git个文件而不是snapshot。那么git如何知道最新的分支提交中的变化? (在消息中,您可以看到differences哪些是差异)
  3. 现在,如果我的想法是正确的,那么文件1 deleted in master and modified in HEAD不会发生冲突。所以我的想法不正确,但1如何运作呢?

2 个答案:

答案 0 :(得分:4)

重点#2,你是正确的,git保存快照。但是,给定任意两个快照ab,我们可以通过比较ba来获得变更集(差异) - 并且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(当前分支的尖端)和81e6490master的尖端) - 以及此特定情况下的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"