如果我有一个新的干净项目(主),我添加一个包含:
硕士:测试.txt
"123"
现在我正在创建一个名为"MyBranch"的新分支,然后我更改文件的内容:
我的分支 : 测试.txt
"193"
现在我想合并Mybranch --> Master
,
在这种情况下,我不知道是成功的合并还是冲突。 我见过有冲突和成功合并的情况,但仍然没有找到黄金法则。
问题
git 什么时候引发冲突与成功合并?
git 在同一代码段的不同分支之间进行两次编辑时引发冲突。
在您的情况下,由于您只在Mybranch
中进行了一次编辑,因此合并时没有冲突。
如果在 Master 上有一个新的提交,我将文本更改为"987"并在您尝试合并之前提交该代码,那么我们将发生冲突。
有用的资源:https://www.atlassian.com/git/tutorials/using-branches/git-merge
正如 eftshift0 所说,虽然不是用这些相同的话,但这里的诀窍是合并不是两个而是三个输入。
您将流程描述为:
- 在
master
上创建新提交 - 创建新的分支
MyBranch
- 在
MyBranch
上创建新提交 -
跑:
git checkout master git merge MyBranch
但这并不完全是发展趋向的方式。 它通常更像是:创建三个分支,开始处理它们,再创建六个分支,处理其中一些,删除一些分支,创建其他分支,在几个分支上工作一段时间,然后最终检查一个分支并在另一个分支上运行git merge
分支。 这种更加混乱的工作流程会导致合并过程的输入更加复杂,正如我上面提到的,合并过程实际上由三个提交组成。
在这三个提交中,您只能选择两个。 第三个,Git 会自动为你挑选。
让我们花点时间回顾一下使用绘图创建新提交的过程。 你:
git checkout somebranch
<edit various files>
git add <some edited files>
git commit
checkout
步骤使用分支somebranch
当前提示的提交中的所有文件填充索引(建议的下一次提交)和工作树
...--F--G--H <-- somebranch
(其中每个字母代表实际的提交哈希 ID)。 最新提交H
的父级是提交G
;G
的父级是F
的,依此类推,在分支"上"(包含在)的提交行中
向下。编辑文件时,将更改工作树副本。 然后,您必须git add
这些更改才能将这些更改复制回索引 -git commit
使用索引(建议的提交,文件处于特殊的 Git 化冻干状态)而不是工作树(正常日常形式的文件,可由常规非 Git 程序使用)。 最后一步,git commit
,打包所有文件的索引副本(不仅是您更改的文件,还包括由于提交H
而索引中的所有未更改文件),并将它们存储到新的提交中,在那里它们将永远冻结,以便将来的任何时候都可以返回到新的提交。
我们称之为新的提交I
。I
的父母将H
:
...--F--G--H <-- somebranch
I
git commit
的最后一步是更新名称somebranch
,使其指向提交I
而不是提交H
:
...--F--G--H--I <-- somebranch
(我们不再需要绘图中的扭结)。
因此,对于您的特定进程,您选择的分支是master
,并且您添加了一个新的提交:
...--F--G--H--I <-- master
然后,您创建了一个新的分支名称。 默认情况下,此新名称还指向提交I
。 提交到I
现在位于(包含在)两个分支中:
...--F--G--H--I <-- master, MyBranch
现在,我们的图纸还需要一个项目。 Git 如何知道在这里使用哪个分支名称? 答案是 Git 将特殊名称HEAD
,在所有大写字母中,都像这样,附加到其中一个分支名称上。 那是你"在"的分支,在git status
的描述中,当它说on branch master
或on branch MyBranch
时。 假设您创建并进入MyBranch
以便我们拥有:
...--F--G--H--I <-- master, MyBranch (HEAD)
现在,您创建另一个新提交。 这得到了一个丑陋的哈希ID,但我们只称它为J
。 和以前一样,Git 将从索引中的任何内容进行新的提交,因此您可以编辑文件,git add
它们以将它们复制回索引,git commit
进行J
:
...--F--G--H--I <-- master
J <-- MyBranch (HEAD)
(请注意,HEAD
仍附加到更新的分支。
现在可以运行git checkout master; git merge Mybranch
—但这种合并不是很有趣。 太微不足道了。 这里永远不会有任何合并冲突。 事实上,默认情况下,git merge
会注意到不需要合并,而是进行快进。 您必须运行git merge --no-ff
以强制它进行真正的合并(这会很顺利)。
与其现在进行合并,不如首先,让我们在master
上进行新的提交。 实际上,只是为了让合并得到字母M
,让我们在master
上进行两次提交,这样我们就有了:
K--L <-- master (HEAD)
/
...--F--G--H--I
J <-- MyBranch
现在当我们运行git merge MyBranch
时,Git 必须做一些实际的工作。
第一部分在于查找三个输入提交:
这三个提交中的一个是你现在正在做的提交 - 提交
L
,或master
,或HEAD
(这两个名称加上原始哈希ID选择该特定提交)。 该特定提交的文件将位于您的索引和工作树中,索引或工作树中没有更改(git merge
检查是否是这种情况,并且通常不会让您使用"脏"索引或工作树启动合并过程)。这三个是你命名的提交之一:在
git merge MyBranch
中,你已将提交命名为J
。最后一个提交 - 你无法选择的提交 - 是 Git 所说的合并基础,它是两个分支分歧的共同起点。 由于我们绘制这些提交的方式,很容易看出哪个提交是:它是提交
I
。当我们从提交
L
开始并向后工作时(如 Git 所做的那样),我们枚举提交L
,然后是K
,然后是I
,然后是H
,依此类推。 同时,当我们从提交J
开始并向后工作时,我们枚举提交J
,然后是I
,然后是H
,依此类推。共享提交是提交
I
、H
、G
、F
等,但最好的共享提交是最后一个,或提交I
。 所以提交I
是合并基础。
现在git merge
已经找到了它的三个输入,它的主要工作开始了:
将提交
I
(或者更确切地说,其中的快照)与提交L
的快照进行比较。 无论这里有什么不同,我们都master
更改了以下内容:git diff --find-renames <hash-of-I> <hash-of-L>
将提交
I
与提交J
进行比较。 无论这里有什么不同,他们(好吧,"我们")在MyBranch
上更改了以下内容:git diff --find-renames <hash-of-I> <hash-of-J>
结合这两个比较的结果。
将合并的更改应用于提交
I
中的快照。
如果一切顺利,组合更改(将快照从I
L
拍摄的更改,以及从I
到J
的更改)现在位于每个文件的索引和工作树副本中,并且git merge
提交结果,就像通过git commit
一样,但进行一次同时记住L
和J
的提交:
K--L
/
...--F--G--H--I M <-- master (HEAD)
___/
J <-- MyBranch
合并使用的提交I
不是直接记录的,而是隐含的:L
和J
提交的合并基础是I
的,并且永远是永远的,因为任何提交中的每一点信息都被永久冻结。 事实是L
的父母是K
,而K
的父母是I
的,例如:这些事实无法改变。M
的父母现在L
J
,这些都无法改变。 任何提交的任何部分都不能更改:最多,我们可以进行新的和改进的(和不同的)提交,并停止使用旧的,但我们不能更改旧的。
当事情进展不顺利时,您会遇到合并冲突。 例如,如果从I
到J
的差异说将文件test.txt
的第 10 行从ABC
更改为XYZ
,而从I
到L
的差异说将文件test.txt
的第 10 行从ABC
更改为AEIOU
,则这些更改冲突。 Git 将:
- 将所有三个输入文件保留在索引中(在特殊的高编号暂存槽中,我们不会在这里讨论),并且 在
- 工作树中留下文件的混乱、合并冲突版本,您可以在其中查看和处理它。
合并不会结束,但也不会完成。 存在未完成合并的事实现在记录在两个位置:
- 在索引中,因为占用了非零暂存槽;和
- 在包含提交
J
哈希 ID 的文件中,以便最终合并的"提交"步骤知道要包含哪个哈希 ID 作为另一个父级。 (第一个父L
哈希 ID 一如既往地通过HEAD
提供。
您的工作变为:为此文件生成正确的组合,并将其写入暂存槽零处的索引中,以宣布此文件已解析。git add
命令将负责第二部分 - 写入索引 - 但您必须自己或在合并工具的帮助下提出正确的内容。
解决所有冲突后,运行:
git merge --continue
或:
git commit
在这种情况下,两者都做同样的事情:git merge --continue
确保有一个正在进行的合并可以完成,如果是这样,实际上只是运行git commit
(无论如何,目前 - Git-for-Windows 人员一直将一个命令粘在另一个命令中,而不是运行另一个命令,因为 Windows 显然需要几个小时⏱......好的,十分之一秒?...将一个命令链接到另一个命令,而不是 Linux 上的微秒)。
一般来说,当有两个具有特定共同祖先的分支时,每个分支对给定数量的行都有一组不同的变化,就会发生冲突。
所以。。。。如果你有一个共同的祖先 A,那么分支 B 和 C,从 A 到 B 你从文件中删除了 5 行......在 A 和 C 之间,您修改了这五行中的任何一条,您都会发生冲突。有很多种类的冲突。单向修改了 5 行,以另一种方式修改了相同的 5 行。.你合并,轰!还有其他类型的冲突,例如:在一个文件上编辑 5 行......删除了另一个分支上的文件。合并:轰!
如果一个分支上有一组更改,而另一个分支上没有更改,则不会发生冲突。