【问题标题】:Git merge conflicts during PRsPR 期间的 Git 合并冲突
【发布时间】:2020-11-27 17:51:28
【问题描述】:

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

而且,接下来,主题分支将在 development 分支之外创建。完成后,为 topic --> development 创建 PR,为 development --> master 创建另一个 PR。但是,我不断遇到第二个 PR development --> master 的合并冲突,例如 Azure DevOps 上的 Added in BothEdited in Both 等。

目前,只有 squash 合并用于完成 PR 到 developmentmaster

我想知道的是,我看到的这些合并冲突是否是由于存储库的原始设置或 squash 合并而发生的。 developmentmaster 的 PR 的 diff 也包含很多我没想到的变化。

我无法理解它。

【问题讨论】:

    标签: git azure-devops pull-request


    【解决方案1】:

    在 knittl 现有的answer 的基础上,您可以在自己舒适的家中制定一个场景,以演示所谓的“壁球合并”是多么容易导致冲突。

    我们首先在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'
    

    我们返回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'
    

    然后我们返回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”

    所以,要形成“squash merge”提交,我们只需将“a”更改为“b”。

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

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

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

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

    但你不能两者都做;这是冲突!

    所以你看,重用之前被 squash-merged 的​​分支之所以麻烦,是因为 merge base 没有移动(历史上没有任何东西涉及任何合并);因此,每个连续的 squash 合并都可能与同一分支的早期“squash 合并”发生冲突。

    【讨论】:

    • 如果我们进行常规合并,那么下一次合并的“开始”将是常规合并提交点,我想?
    • @KZee 完全正确!这一切都不同了。即便如此,重复将同一个分支合并到同一个其他分支可能有点棘手,但重复将同一个分支压缩合并到同一个其他分支只是保证有点棘手,因为分支历史一直在积累。
    【解决方案2】:

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

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

    我看到了 2 个选项:

    • 不要对重新集成分支使用“squash”并使用常规合并
    • 在每次合并后从 master 重新创建 development

    【讨论】:

    • “使用“squash”你并没有进行真正的合并”我希望人们能清楚地了解这个事实。这是一个可怕的名字。壁球合并不是任何类型的合并!这是一个糟糕的主意,而这个用例就是一个明显的原因。
    • 谢谢。我想,通过常规合并,您是指基本合并(无快进)吗?这里有四种可用的 Azure DevOps 合并类型:prnt.sc/vres0r
    • @KZee 是的,我说的是“非前向合并”,它将创建一个具有 2 个父级的新合并提交。如果您计划多次合并一个分支,则需要这些合并提交。
    【解决方案3】:

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

    1. 合并(无快进)
    2. 壁球提交
    3. 变基和快进
    4. 半线性合并

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

    仅在完成 ​​PR 时使用 Merge 的第一个选项 source 分支是 developmentmaster(在 Git Flow 中也是 releasehotfix)。

    Azure DevOps 允许您将合并类型锁定为作为 目标 的分支,但不幸的是,当分支是 时,我认为您不能这样做>.

    【讨论】:

    • 你是对的。我们可以设置可以限制合并类型的分支策略(分支是destination)。我认为足以防止我/人们在 master 分支上进行不必要的提交。
    猜你喜欢
    • 2012-02-19
    • 2017-07-24
    • 1970-01-01
    • 2013-10-22
    • 1970-01-01
    • 2019-07-05
    • 1970-01-01
    • 2015-10-15
    • 2014-10-07
    相关资源
    最近更新 更多