【问题标题】:Forcing git merge not working as I thought it should强制 git merge 不能像我认为的那样工作
【发布时间】:2017-10-12 17:31:39
【问题描述】:

我的提交树看起来像

---A---B---C   master
    \
     D-----E     dev

我想将 E 合并到 master 上,以便领先的 master 提交看起来 完全像 E

---A---B--C--F   master
    \       /
     D-----E     dev

所以 F 应该和 E 完全一样。

我所做的是:

> git checkout master
> git merge -X theirs E

生成 F 时没有错误消息,但 E 和 F 之间存在一些差异。它们并不完全相同,就像我需要的那样。

我知道我可以手动合并和修复差异。但是,1)我确实需要 E 来完全覆盖 C 和 2)我不能把它搞砸,所以一些自动的方法来做这件事比手工做更可取。

我做错了什么?我需要做些什么不同的事情?

更新:

> git merge -X theirs dev

也无济于事。结果一样。

【问题讨论】:

    标签: git merge git-merge


    【解决方案1】:

    TL;DR:你需要 -s theirs 不存在

    不过,有几种替代品。查看此问题的其他答案,在 StackOverflow 中搜索“git merge strategy theirs”,和/或阅读Is there a "theirs" version of "git merge -s ours"? 仔细(因为最初的询问者实际上是在寻找-X theirs,我将在下面描述)。或者使用以下相当神奇的 sh/bash 脚本代码:

    git merge --ff-only $(git commit-tree -p HEAD -p dev dev^{tree} -F /tmp/commitmsg)
    

    将您想要的提交消息放入文件/tmp/commitmsg 之后。 要非常确定这真的是你想要做的,因为它几乎从来都不是。

    说明

    -X(扩展参数)ourstheirs 并不意味着使用我们的版本使用他们的版本。相反,他们的意思是,当发生冲突时,使用我们的更改或使用他们的更改

    这是什么意思

    在您的示例中,master 的提示是提交Cdev 的提示是提交E。所以如果当前分支是master,那么当前commitC。此时无论是传递名称dev,还是将提交E 的哈希ID 传递给git merge,都无关紧要:两者都选择提交E 作为要合并的提交。

    无论如何,提交A 是合并基础,因为同时从CE 回溯到它们相遇的第一个点,到达提交A

    因此,git merge 将:

    • diff commit A 与 commit C:这是您当前分支中发生的变化,即ours
    • diff commit A 与 commit E:这是 theirs 中发生的变化
    • 尝试合并这两个差异。

    假设在所有三个提交中,其中一个文件名为animals.txt。在提交 A 中,我们发现了一些关于猫的行和一些关于大象的行。还有更多的台词(关于土豚、蝙蝠、狗、雀、沙鼠、仓鼠、鬣蜥、长耳兔等等——字母表中的每一个字母都有一些东西!)但我们现在将专注于猫和大象......以及共享的鼩鼱。

    在提交CA 相比,有一个关于猫的新的或不同的声明。所以从AC 的差异显示了关于猫(C)的这种变化。鼩鼱也有变化:

    $ git diff <hash-of-A> <hash-of-C>
    ...
    @@ ... @@
     blah blah
    -something about Cats
    +something different about Cats
     blah blah
    @@ ... @@
     etc
    -the Shrew flies through the air, carefree
    +the Shrew swims the ocean, carefree
     etc
    

    在提交 EA 相比,关于大象有一个新的或不同的声明。所以A-to-E 的差异显示了大象的这种变化。对 Shrews 有一个不同的、冲突的变化——我们将省略除此之外的所有内容:

    @@ ... @@
     etc
    -the Shrew flies through the air, carefree
    +the Shrew eats rhinos, though it causes indigestion
     etc
    

    当 Git 进入 merge 这些更改时,它将接收从 A-to-C 到 Cats 的更改以及从 A-to-@987654365 到 Elephants 的更改@。 这些更改不冲突,因此将它们组合起来没有问题。但它也会看到关于鼩鼱谎言的变化,这些变化相互冲突,不能合并。

    -X 选项指示 Git 选择冲突的一方或另一方

    因为这里有冲突,Git 需要帮助解决合并。默认情况下,Git 只是声明“存在冲突”,将 both 更改写入工作树,并在发生冲突时停止,让您自己清理混乱。 (在幕后还有很多工作可以帮助您解决冲突,但现在这些都不重要。)

    但是,使用其中一个 -X 选项,Git 被告知选择我们的,或者选择他们的,然后继续前进。但这只会影响有冲突的更改。 Git 仍然会像往常一样组合不冲突的更改:在上面的示例中,无论您选择哪个 -X,Git 都会接受 Cat 和 Elephant 的更改,-X 将简单地选择要选择的 Shrew 更改。

    合并策略不同

    有一个名为ours 的Git 合并策略,调用为git merge -s ours,这与-X ours 选项非常不同。这个策略告诉 Git 毕竟不要为合并基础而烦恼,而只是让源代码树远离“我们的”提交。 Git 将在不做任何工作以产生正确的合并之后进行合并提交。效果是“杀死”另一个分支,同时保留其提交;这允许您删除另一个分支名称,保留提交以防您想在某个时候查看它们,同时声明您打算永远不再使用它们(但显然不太确定:-))。

    如果git merge -s theirs 存在,它会做同样的事情,除了它会“杀死” 当前 分支以支持新分支。有时您可能想要这个,当它们出现时,如果 Git 有 git merge -s theirs 可能会很好,但无论出于何种原因,它只有 git merge -s ours

    替代品有效,git commit-tree 的技巧允许您使用附加到任何现有提交的任意现有源代码树将任意多个提交捆绑在一起——因此它涵盖了-s ours(使用HEAD^{tree})和@987654379 @。生成的提交对象没有名称,但是是 HEAD 提交的直接后代,因此 git merge --ff-only 会将其添加到当前分支。

    【讨论】:

      【解决方案2】:

      首先,请注意:您想要的结果是“邪恶合并”的示例,它可能会导致某些问题。如果这是你需要的,那就是你所需要的;但请注意,在某些 rebase 操作(可能还有其他事情)期间,结果与默认值不同的合并(尤其是在默认值不冲突时)可能会被误解。

      也就是说:存在一些混淆,因为默认的合并策略采用-X 参数“我们的”或“他们的”来告诉它如何解决冲突;但这并没有改变合并操作从双方进行非冲突更改。更令人困惑的是,还有还有一个名为“ours”的合并策略,它完全忽略了来自其他分支的更改——但没有相应的“theirs”策略。

      所以你可以做一些事情。一种选择是

      git checkout dev
      git merge -s ours master
      git checkout master
      git merge dev
      

      (您希望最后一次合并是快进的;默认情况下会这样,但如果您的设置更改了默认设置,您可能需要一个 --ff 选项。)

      这与将dev 合并到master 不完全相同,因为父级将被颠倒;但如果这对你来说不重要,那么这可能是最简单的方法。

      另一种选择是手动编辑合并。

      git checkout master
      git merge --no-commit dev
      

      现在你需要让你的工作树和索引看起来像 commit E。我想

      git checkout dev -- .
      git clean
      

      当然你也可以使用git diff来验证。

      这种方法在合并时“按预期”保持父级的顺序 - 但不要忘记,它仍然是一个邪恶的合并。

      【讨论】:

        【解决方案3】:

        git merge-X theirs 选项告诉它在使用默认策略 (-s recursive) 时如何解决合并冲突。可以合并干净的更改不受影响。合并后的文件将包含在两个分支上操作的更改。

        ours strategy 可用于创建忽略合并分支上的更改的合并提交。为了使F 看起来与E 相同,您必须将master 合并到dev 中,但这不是您要求的。

        您的请求的可能解决方案(我没有测试过)可能是这样的:

        • 像现在一样进行合并,但在命令行中添加--no-commit 选项;这样,Git 会准备合并提交,但允许您查看(并可能更改)它;
        • (强制)签出工作树文件以匹配 E 提交上的 repo 状态;
        • 将所有内容添加到索引并提交。

        命令是:

        git checkout master
        git merge -X theirs --no-commit dev
        git checkout --force E -- .
        git add .
        git commit
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-09-29
          • 1970-01-01
          • 2015-05-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多