Git 什么时候会成功合并与冲突



如果我有一个新的干净项目(主),我添加一个包含:

硕士:测试.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而索引中的所有未更改文件),并将它们存储到新的提交中,在那里它们将永远冻结,以便将来的任何时候都可以返回到新的提交。

我们称之为新的提交II的父母将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 masteron 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,依此类推。

    共享提交是提交IHGF等,但最好的共享提交是最后一个,或提交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中的快照。

如果一切顺利,组合更改(将快照从IL拍摄的更改,以及从IJ的更改)现在位于每个文件的索引和工作树副本中,并且git merge提交结果,就像通过git commit一样,但进行一次同时记住LJ的提交:

K--L
/    
...--F--G--H--I      M   <-- master (HEAD)
 ___/
J   <-- MyBranch

合并使用的提交I不是直接记录的,而是隐含的:LJ提交的合并基础是I的,并且永远是永远的,因为任何提交中的每一点信息都被永久冻结。 事实是L的父母是K,而K的父母是I的,例如:这些事实无法改变。M的父母现在LJ,这些都无法改变。 任何提交的任何部分都不能更改:最多,我们可以进行新的和改进的(和不同的)提交,并停止使用旧的,但我们不能更改旧的。

当事情进展顺利时,您会遇到合并冲突。 例如,如果从IJ的差异说将文件test.txt的第 10 行从ABC更改为XYZ,而从IL的差异说将文件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 行......删除了另一个分支上的文件。合并:轰!

如果一个分支上有一组更改,而另一个分支上没有更改,则不会发生冲突。

最新更新