究竟是什么导致了git中的合并冲突?

时间:2016-04-20 05:36:52

标签: git github gitlab git-merge merge-conflict-resolution

1)有一个'主人'分支包含

的文件

1

2

3

4

5

2)A从[' master'并编辑如

1

2

100

3

4

5

3)B从主人那里拿出一个分支'并编辑如

1

2

3

4

200

5

4)现在推送变成了主人。然后B也试图推动。

B会发生什么?任何合并冲突或没有合并冲突?原因是什么?

4 个答案:

答案 0 :(得分:4)

你的问题意味着事情并非如此。

具体而言,git push仅推送现有提交。它没有合并任何东西,事实上,它甚至从未尝试来合并任何东西。

在您的问题中,涉及三个实体(人员和存储库)。 A人(让她称她为Alice),B人(让他们称他为Bob)和C人(让他们称他为Central-Server,他实际上只是一台机器,不是一个人,虽然这并不重要。)

Alice和Bob都是从Central-Server获取某个存储库的副本(克隆)开始的。他们得到完全相同的克隆(这就是为什么他们被称为&#34;克隆&#34;):爱丽丝的克隆匹配鲍勃的克隆匹配了什么中央服务器。他们通过运行git clone <url>来执行此操作,其中<url>指向中央服务器(github或其他任何内容),并且他们的git将名称origin下的网址保存起来(我们&#39;我很快会再次看到这个名字。)

让我们绘制所有三个实体现在拥有的git提交图(的一部分):

... - C7 - C8   <-- master

现在,Alice和Bob都进行了更改,但是他们进行了不同的更改。爱丽丝承诺改变:

... - C7 - C8 - A   <-- master

然后Alice运行git push origin将她的工作推回到中央服务器。中央服务器会查看她的请求,该请求会在A处向链的末尾添加提交C8,并使master指向A&# 34 ;.此操作将新的提交添加到链中,因此是允许的,因此中央服务器回复&#34;确定&#34;对爱丽丝来说,她已经完成了。 Central-Server的存储库现在看起来也和Alice一样,因为它们在旧提交A之后都有新的提交C8,而master指向提交A(并提交A指向旧的C8)。

同时Bob已做出改变并添加了一个新提交,他的提交图现在看起来像这样:

... - C7 - C8 - B   <-- master

Bob不知道Alice已经提交了A,也没有成功将它推送到Central-Server。他转到git push origin,但这次Central-Server收到一条请求,说明&#34;将提交B添加到C8链的末尾,然后将master指向{ {1}}&#34 ;. 如果中央服务器执行此操作,效果将为:

B

也就是说,提交 A / ... - C7 - C8 - B <-- master 将保持浮动,没有指向它的任何内容。 (分支A将指向master,而B将指向B,没有任何指向C8。)这通常是一种糟糕的事态并且git拒绝它,所以Central-Server告诉Bob:

A

请注意,没有合并也没有变基。

现在 Bob 的工作是进行合并(或rebase)。他应该这样做:

  1. 从拥有它的人那里获取更新。谁拥有它?爱丽丝拥有它,但Central-Server也是如此。他刚刚要求推送到Central-Server,后者告诉Bob&#34; no&#34;,所以他也可以从Central-Server获取它。

  2. 进行合并(或变基),解决任何冲突。

  3. 重试推送。

  4. 如果Bob选择&#34;合并&#34;并且做了合适的合适工作,这是他的新提交图:

    rejected (non-fast-forward)
    

    请注意新的合并提交 ... - C7 - C8 - A - M <-- master \ / B 。现在,Bob可以重新尝试推送到Central-Server,后者目前的链以M结尾。这次中央服务器将看到A指向master的请求,并且M指向M,此请求将被允许(现在是& #34;快进&#34;。)

    当然,如果Alice(或Dave或Emily或Frank)通过添加A之前的新提交并将其发送回Central-Server来击败Bob,那么Bob将不得不合并(或改组)再次,再试一次。

    Bob如何管理这一切?

    Bob的选择是合并还是变基。无论哪种方式,Bob都必须解决任何合并冲突 - 无论他使用哪种方法,他都会得到相同的合并冲突。并且,在任何一种情况下,他都应该开始运行:

    A

    (或仅git fetch origin ,会自动使用git fetch

    在重组或合并之前,让我们先看看Bob的提交图

    origin

    请注意,Bob的 A <-- origin/master / ... - C7 - C8 \ B <-- master 指向提交master,Bob有另外一件事 - 这个B - 指向Alice的提交{{1} }。这就是origin/master所做的:它从Central-Server中带来了最新版本(或者如果Bob直接从Alice获取,将其从她身上带过来,因为她有相同的提交),然后让一些标签指向承诺。标签以A开头,因为这是我们允许git fetch使用的名称:它只是将origin/...粘贴在另一个名称前面git clone,在此())以便我们区分它们。

    如果Bob选择 rebase 而不是合并,他将把他的git 复制他的提交origin/发送到新的提交master:< / p>

    B

    Bob的原始B'会怎样?答案是:它被废弃了。它保留在存储库中一段时间​​(默认为30天),以防Bob需要它,保存在Bob的 reflogs 中,但除非你(或Bob)明确要求git查看,你没有看到这些提交,所以他们似乎已经了。

    如果Bob选择合并,他会得到这个:

                    A       <-- origin/master
                  /   \
    ... - C7 - C8       B'  <-- master
                  \
                    B
    

    这是我们上面提到的相同的图,我们刚刚将B节点抬起来,以便我们可以指向它的箭头(标记为{{1} })。

    在任何一种情况下,Bob现在都可以尝试推送,因为他的新提交 - A <-- origin/master / \ ... - C7 - C8 M <-- master \ / B A - 指回提交origin/master,因此他只询问Central-服务器添加新提交而不是忘记或放弃提交B'

    合并本身怎么样?

    Git将尝试通过比较Alice所做的更改(向M添加一行)与Bob所做的更改(添加一行A)来帮助Bob。如果git决定这些更改相互冲突,它将保持两个更改。如果它确定这两个更改会影响文件的相同部分,它将为Bob提供合并冲突,标记文件的更改区域,并让Bob决定如何将它们结合起来。

    鲍勃可以使用他喜欢的任何东西来实现综合结果。 Bob应该确保结果是正确的,然后Bob应该告诉他的git A文件的最终版本和100提交更改。 1 200,那么这将进行合并提交。如果是git add,则会生成新副本git commit

    1 如果Bob选择了git merge,他可以使用git rebase,它将为他提交。 Bob先安排B'然后执行git rebase是安全的(但是在git 1.5左右的时候,人们必须手动执行提交部分,然后再继续变基)。

    关于git rebase --continue

    的最后说明

    我鼓励新的git用户从git commit开始,然后自己做git rebase --continuegit pull。许多文档都告诉你从git fetch开始,但我认为这是一个错误。 git merge命令是便利:它运行git rebase然后运行git pullgit pull,但这有几个缺陷。没有一个是非常严重的,但这些对新用户不利:

    1. 在之前选择合并vs rebase ,您甚至可以查看更改。

    2. 默认是合并,这通常是新用户的错误答案,他们通常可能应该重新定价。您可以更改默认设置,但新用户不知道提前做到这一点。您可以添加git fetch告诉它进行rebase,但您可以忘记包含此标记。

    3. 它所做的合并是"foxtrot merge":它让父提交的方向错误。

    4. 与手动合并相比,这些参数令人困惑:git merge vs git rebase。 (当然,如果你想避免foxtrot合并,你也不能使用后者,但你可能应该反驳。)

    5. 如果您提供的参数太多(--rebase),则会产生章鱼合并,这不是新用户应该考虑的事情。 : - )

    6. 它曾经有过许多破坏工作的bug案例。我相信它们都是固定的,但使用git pull origin <branch>后跟单独的git merge origin/<branch>git pull origin master develop一直避免这些错误。

    7. 最后,这里发生的魔法太多了。它应该是方便的(对于那些使用git的老手来说,它很方便)但它只是模糊不清。

答案 1 :(得分:3)

最简单的答案是:如果两个开发人员在同一个文件(和同一个分支)中更改了同一行代码,git将假定存在冲突。

承诺和推动的人是“胜利者”。第二个必须处理冲突。他不能推动,直到他从原点撤下所有变化并解决冲突。

答案 2 :(得分:0)

当B尝试推送时,会出现错误返回,因为A已被推送更改为master。 那你应该这样做,

git fetch
git rebase origin/master

执行rebase步骤时,不会发生冲突,因为A更改了文件的第3行,B更改了文件的第5行。 那么你可以做到,

git commit -m ""
git push

答案 3 :(得分:0)

当git不知道如何处理代码的相同部分中的两个更改(未按顺序执行)时,会发生合并冲突。在您的情况下,已更改的行是不同的,因此git将能够合并提交而不会发生冲突。但是,一旦推送了A中的提交,就不能在没有在A之上重新设置B的情况下推送B中的提交。

在这种情况下你不能推B:

----M-----A
     \----B

如果你去B并输入并使用:

git rebase A

您的代码在本地存储库中将如下所示:

----M-----A-----B

你也可以推送提交B