Git合并对工作目录和暂存区的影响

3

我是Git的新手,正在尝试了解git merge命令的工作原理。因此,我尝试了一个简单的git项目作为例子:

我有两个分支:masterb。你可以在这里查看两者的git日志(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

我看到了这个消息:

冲突(修改/删除):1在主分支中已被删除,但在HEAD中已被修改。版本HEAD的1留在树中。自动合并失败;解决冲突,然后提交结果。

对我来说这很奇怪,我之前认为:

  1. mergecheckout 一样尝试从 merge in 分支的最新提交中复制内容到 merge into 分支的最新提交中,但与 checkout 相反,merge 尝试不更改 merge into 中的任何文件。最后,如果 merge inmerge into 分支都有相同的文件但状态不同,则会发生冲突。
  2. 我知道 git 只保存文件的快照而不是差异。那么 git 如何知道分支的最新提交中的更改呢?(在消息中您可以看到 1 deleted in master and modified in HEAD 这些差异)

现在,如果我的想法是正确的,那么文件 1 不应该出现冲突。所以我的想法是不正确的,但是 merge 究竟是如何工作的呢?


潜在的重复(虽然它们没有详细说明):https://dev59.com/jmUp5IYBdhLWcg3w5KvV - torek
2个回答

4

关于第二点,您是正确的,git保存快照。然而,给定任何两个快照ab,我们可以通过比较ba来得到一个变更集(差异)- git会根据需要执行此操作。

至于合并本身,首先,我们必须注意git保留提交图(也称为“DAG”或“提交DAG”,因为该图是有向无环图或DAG)。这就是您的git log --graph输出所显示的,尽管在这种特殊情况下,我们具有如此简单的图形形式,以至于它只是一棵树(直到合并被提交为止)。

对于任何树结构以及许多DAGs,1给定树中的两个节点,我们可以找到唯一的LCA(最低公共祖先)节点。这里的两个节点是两个分支的末端 - 提交cf3456b(当前分支的末端)和81e6490master的末端),而在这种特殊情况下,LCA是第一个提交c667d3b。在像这样的简单情况下,LCA很容易在视觉上发现:您只需查看提交图以查找两个分支加入的位置(从那里开始,一直回到根,都在两个分支上)。

此LCA节点是合并基础。 Git首先找到当前提交和您给出的参数的合并基础。(对于“章鱼合并”,其中您指示git将多个提交合并到当前分支,该工作有点更复杂,但我们可以在这里不予考虑。)

接下来,鉴于现有的合并基础和两个不同的末端提交,git必须计算两个变更集:一个从合并基础到当前提交,另一个从合并基础到参数提交。请注意,git在整个过程中仅执行一次此操作,之后即可继续合并操作。

现在,对于每个存在更改的文件,git必须组合更改。 对于大多数简单的修改,方法很简单:如果文件1只在一个分支中进行了修改,请将修改按原样采取。 如果它在两个分支中都被修改,请尝试组合修改,在两个分支都进行相同更改的地方只使用一个副本。当然,如果两个分支对单个文件的相同区域进行了不同的更改,则会发生冲突。

对于文件创建或删除的情况,或者存在重命名的情况,这些情况就有点棘手了。如果在一个分支中删除了一个文件而在另一个分支中没有改动,则 git 可以通过删除文件来解决这个冲突(如果它已经在 HEAD 中被删除,就“保持删除”,如果它在另一个提交中被删除,就将其删除)。如果在一个分支中对文件进行了重命名并在另一个分支中进行了修改,则 git 也可以组合这些更改(同时进行或保留重命名,并且导入或保留其他更改)。然而,对于其他的绝大多数情况,git 只会声明冲突,放弃并让你解决冲突。
在这种情况下,文件 1 确实在当前分支中被修改了,在 master 中也确实被删除了。Git 不确定是要删除该文件(根据与 merge-base 到 master 的 diff 指示),还是要保留更改(根据与 merge-base 到 HEAD 的 diff 指示),因此它让你处理这个文件和冲突。
如果你在两个分支中都创建了一个新文件 5,git 会再次给你一个冲突(除非在两个分支中新文件的内容相同——我没有测试过这种情况)。
1对于复杂的 DAG,可能有多个最低公共祖先候选者。对于 git 的“递归合并”,git 通过合并每个 LCA 候选者来形成一个新的“虚拟基础”。这个虚拟基础成为两个提交进行比较的 merge-base。如果只有两个 LCA 候选者,则可以通过大致相当于以下操作来获得虚拟合并基础:
git checkout -b temp candidate_1
git merge candidate_2
git commit -a -m ""

在这个“内部合并”过程中发生的任何合并冲突都会被忽略:带有冲突的合并基础将被使用。这可能会导致一些看起来很奇怪的冲突,尤其是当在“外部”合并的同一区域发生冲突时:请参见此 SO 问题
如果存在两个以上的LCA候选项,则merge-recursive将取前两个进行合并,然后合并第三个,然后合并第四个,依此类推。尽管它可以合并成对减少数量,然后再合并合并对以再次减少数量,以此类推,但它仍然执行上述操作。也就是说,对于给定的N个LCA候选项,可以进行ceil(log2(N))次合并,而不是N-1次合并,但是git很少使用N超过2。

我对 git merge 有另一个问题,似乎会打破你的预测。请查看此链接:http://stackoverflow.com/questions/35969245/why-merge-does-not-cause-conflict - hasanghaforian

0

冲突信息:

CONFLICT(删除/修改):1在主分支中被删除,但在HEAD中被修改

这意味着你正在合并的主分支中删除了1,但在HEAD(你现在所在的分支)中进行了修改。

因此,你需要决定:

remove file using "git rm 1"

或者

accept version from HEAD with "git add 1"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接