Git 在 PR 期间合并冲突

Git merge conflicts during PRs

我做了一些不寻常的设置,首先设置了存储库,而不是用项目文件填充 master 分支,而是创建并填充了 transfer 分支。然后,文件通过 PR 拉入 development,然后通过另一个 PR 再次拉入 master

并且,今后,主题分支将在 development 分支之外创建。完成后,为 topic --> development 创建了 PR,为 development --> master 创建了另一个 PR。但是,我一直在为第二个 PR development --> master 合并冲突,例如 Added in BothEdited in Both Azure DevOps 等。

目前,仅使用压缩合并将 PR 完成为 developmentmaster

我想知道的是,我看到的那些合并冲突是由于存储库的原始设置还是挤压合并而发生的。 developmentmaster 的 PR diff 也包含了很多我没有预料到的变化。

我无法理解它。

发生冲突是因为您正在使用“挤压”合并并不断重新整合一个分支。使用“squash”你并没有进行真正的合并(换句话说:历史没有连接)。相反,您正在 master 分支上创建一个新提交。

稍后,当您尝试将 development 合并到 master 时,所有先前的开发更改将再次合并(但它们已经存在于 master 中)。这就是您看到冲突的原因。

我看到 2 个选项:

  • 完全不要对重新集成分支使用“挤压”,而使用常规合并
  • 在每次合并后从 master 重新创建 development

基于 knittl 的现有 ,这里有一个场景,您可以在舒适的家中进行演示,以演示所谓的“挤压合并”是多么容易导致冲突。

我们首先在 master 上创建文件并提交它:

$ git init
$ echo "a" >> a.txt
$ git add .
$ git commit -m'start'

现在我们创建 develop 并修改该文件:

$ git branch develop
$ git checkout develop
$ echo "b" > a.txt
$ git add .
$ git commit -m'changed a to b'

我们 return 到 master 并进行“挤压合并”,它似乎工作正常:

$ git checkout master
$ git merge --squash develop
$ git commit -m'a squash commit from develop'

到目前为止一切顺利。现在我们犯了一个可怕的错误:我们再次 。我们切换到 develop 并进一步修改该文件:

$ git checkout develop
$ echo "c" > a.txt
$ git add .
$ git commit -m'changed b to c'

然后我们 return 到 master 并再次进行“挤压合并”:

$ git checkout master
$ git merge --squash develop

啊啊啊我们发生了冲突。游戏结束。


发生了什么事?嗯,问题是“挤压合并”不是合并。这是一种自我造成的补丁。它构建索引(暂存区)的配置,然后您提交它。在我上面的制定中,我们将其作为一个单独的步骤提交;用GitHub,它是为你承诺的。但重点是,该提交虽然包含真正合并产生的 更改 ,但 不是 合并提交:它只是正常提交,就好像您自己完成了这些更改一样,直接在 master.

上工作

嗯,进行实际合并产生的更改是什么意思?要回答这个问题,您必须知道合并是如何工作的:

  1. 合并从计算合并分支分叉的公共点开始:合并基础.

  2. 然后计算两个差异:从合并基到第一个分支的末尾,以及从合并基到第二个分支的末尾。

  3. 最后,它在合并基础上执行两个差异

好吧,你自己试试吧,使用思想实验。

对于我上面场景中的第一个“挤压合并”,合并基础是“start”,其中 a.txt 是“a”。所以:

  • master 上,它仍然是“a”,因此没有要执行的差异。

  • develop 上是“b”,所以差异是 change-“a”-to-“b”.

因此,为了形成“挤压合并”提交,我们只需将“a”更改为“b”。

很好,让我们继续我的场景中的第二个“挤压合并”。事情是这样的:合并基础仍然是“开始”,其中 a.txtstill “一种”。所以:

  • master 上,差异是 change-"a"-to-"b".

    (请注意,master 不“知道”这是由于任何类型的合并而发生的;它认为此更改是由直接在 master 上工作的人独立执行的。)

  • develop 上,差异是 change-"a"-to-"c".

但你不能两者兼顾;那是冲突!

所以你看,重用一个之前被压扁合并的分支之所以麻烦是因为合并基础没有移动(历史上没有任何涉及任何合并);因此,每次连续的挤压合并都可能与同一分支的早期“挤压合并”发生冲突。

现有的答案很好地解释了为什么会发生这种情况。我想简单地量化一个简单的经验法则来确定哪些合并类型可以用于哪些分支。 Azure DevOps 在完成 PR 时有以下选项:

  1. 合并(无快进)
  2. 压缩提交
  3. 变基并快进
  4. 半线性合并

根据您的喜好,在完成从 topicdevelopment 的 PR 时,可以使用这些选项中的任何一个。但是 developmentmaster 中的所有提交都是神圣的,不应更改。请注意,选项 2-4 都可能重写源分支上的提交。为此,您应该:

Only use the first option of Merge when completing a PR when the source branch is development or master (and also release or hotfix in Git Flow).

Azure DevOps 允许您将合并类型锁定到分支中作为 目标 ,但不幸的是,我认为当分支是 来源.