【问题标题】:git merge issues - force mergegit 合并问题 - 强制合并
【发布时间】:2021-04-25 17:17:11
【问题描述】:

我在 git 中有两个分支。

分支开发和我的功能分支 1300。

Branch 1300 是对分支 dev 的完整重构。现在我已经完成了 1300 并且想将它合并到 dev 分支中。

如何将 1300 合并到 dev 中。

当我在 intellij(这是 1300)中进行正常合并并接受他们的合并时,我在 1300 中删除的文件仍在开发中。

有任何强制合并的提示吗?

提前致谢

【问题讨论】:

  • 您可以使用--ours 选项,但最好根据您的理解手动清理合并冲突/问题。
  • 我们的保留来自开发者的东西?我想保留 1300 的东西。

标签: git merge git-merge


【解决方案1】:

Git 中没有“强制合并”之类的东西。幸运的是,这不是你想要的。你想要的是git merge -s theirs。不幸的是,这不存在。幸运的是,有办法伪造它。另请参阅Is there a "theirs" version of "git merge -s ours"? 我可以将其作为副本关闭,但我将写一个长格式的答案。

Git 提交,包括合并提交

merge 这个词有两个或多个含义,具体取决于您是用作动词、合并,还是形容词或名词:a merge commit(形容词)或a merge(形容词变成名词,如adjectives are wont to do in English: this is called nominalization)。名词版本 a merge 只是作为提交存在于您的存储库中,就像任何其他提交一样,只是它不是通常的单亲,而是有两个。1支持>

请记住,在 Git 中,提交由两部分组成:快照——一组已保存的文件——以及一些关于提交的元数据:信息,例如是谁做的,什么时候做的,为什么做的。在元数据中,Git 存储了一些较早提交的哈希 ID。这些是相关提交的父母。大多数普通提交都有一个父哈希 ID;使提交成为 merge 提交的原因在于它有两个父级,而不是通常的一个。但它只有一个快照。该快照就是合并结果。

我发现图片在这里很有帮助。首先,让我们在 Git 中绘制一个简单的提交字符串(如果愿意,可以称为“分支”):

... <-F <-G <-H

这里,H 代表某个提交链中last 提交的实际哈希 ID。由于H 是一个提交,它既有数据——快照——也有元数据。快照以您或任何人进行提交时的形式保存所有文件。 H 的元数据包含先前提交 G 的实际原始哈希 ID。我们说H 指向 G

G 当然也是一个提交:一个快照(当时的文件)和元数据。此元数据指向更早的提交 FFG 一样,有快照和元数据,所以它指向背面。

这一切的意思是,只要我们能找到last提交,我们就能找到所有链中的提交。这就是 Git 分支名称的作用:它让我们在链中找到 last 提交。它只保存该提交的哈希 ID。无论 name 持有什么哈希 ID,这就是 last 提交 在那个链中。所以这可能看起来像:

...--F--G--H   <-- main
            \
             I--J   <-- feature

这里,分支名feature指向commit J,它又指向commit I,又指向commit H,又指向G,以此类推。事实上,还有另一个名称 main 指向 H,这并不能阻止 所有我刚刚列出的提交出现在 两个分支上。然而,main 指向H 的事实确实 停止提交IJ 在该分支上。通过并包括H 的提交在两个分支上,而最后两个仅在feature 上。

我们可以随时添加和删除我们喜欢的任何名称。只有几个限制。例如,名称必须满足某些测试:mainfeature 可以,但hello..world 不是因为双点被禁止。名称还需要指向实际提交。2 我不会涵盖所有内容,但您应该明白:我们可以在特定(合理)限制内创建和销毁任何名称。所以我们可以在上图中添加一个名字dev,像这样:

...--F--G--H   <-- dev, main
            \
             I--J   <-- feature

如果我们现在 git checkout devgit switch dev,这将成为 当前名称—我们将提交 H 作为 当前提交 in这个过程——然后进行一些新的提交,我们需要重新绘制图表。在这里,我将feature 放在顶部,dev 放在下面:

             I--J   <-- feature
            /
...--F--G--H   <-- main
            \
             K--L   <-- dev (HEAD)

这张图向我们展示了我们有两个提交 (I-J),它们 onlyfeature 和两个 onlydev(HEAD) 表示法表明我们当前正在使用名称dev,因此使用提交L,这是dev 上的only之一。


1从技术上讲,任何具有两个或更多父级的提交都是合并提交,但两个就足够了,通常有两个。

2这没有技术原因,Git 正在获得一项功能,其中将对某些名称放宽此限制,但目前所有名称都需要它,并且仍然需要分支名称。


merge-as-a-verb 的机制

当您选择进行合并提交时(通常是通过运行 git merge),您可以选择两个(或者,根据脚注 1,更多)提交捆绑在一起。两者之一是当前提交,您在运行git merge 时选择之前,通常使用git checkout <em>branch</em>git switch <em>branch</em>。另一个是您在命令行上命名的提交。因此我们可以运行:

git checkout dev
git merge feature

例如。3 Git 然后会自行查找第三次提交。这第三次提交是合并基础,合并基础提供了合并动作的起点:合并过程,或作为动词合并。

你的问题是,如果你的图片是这样的:

             I--J   <-- feature
            /
...--F--G--H
            \
             K--L   <-- dev (HEAD)

——我已经删除了 main 这个名字,以免它弄乱图片;它是否存在并不重要——git merge 动作,作为动词合并过程,将通过:

  • 定位 both 分支上的最佳提交,在这种情况下显然是提交 H;
  • H 中的快照与L (dev) 中的快照进行比较,看看我们有什么变化;
  • H 中的快照与J (feature) 中的快照进行比较,看看它们发生了什么变化;
  • 合并这些更改并将合并后的更改应用到H 中的快照;和
  • 通过使用这些组合更改来生成最终的合并结果。

结果将如下所示:

             I--J   <-- feature
            /    \
...--F--G--H      M   <-- dev (HEAD)
            \    /
             K--L

注意分支 name dev 现在如何指向新的提交 M,这是执行此合并过程的结果。


3您可以添加git merge --no-ff 以防止Git 进行快进而不是合并。我想有人可以认为这是强制合并——但这仍然不是你想要的。 :-)


有一个ours 策略

Tim Biegeleisen refers to in a comment策略 是 Git 实际实现合并作为动词过程的方式。当您运行git merge 时,它会进行一系列初步测试并处理各种命令行选项。允许的选项之一是 -s-s 采用 strategy 参数。

Git 内置的开箱即用策略是:

  • -s recursive:这是通常的默认设置。它完成了我们上面谈到的组合。
  • -s resolve:这是-s recursive 的变体;它也这样做了。我们不需要在这里讨论它们之间的技术差异,因为您的问题是您希望避免合并。
  • -s octopus:这是一种特殊用途的算法,用于合并两个以上的提交。这不是您想要的答案。
  • -s ours:这是另一种特殊用途的算法。这几乎是你想要的!

ours 策略的作用很简单:为了制作新快照,它完全忽略您使用 git merge 命令指定的其他提交。然后它说新快照应该与当前提交完全匹配。因此,如果我们在dev 上运行git merge feature,我们会得到:

             I--J   <-- feature
            /    \
...--F--G--H      M   <-- dev (HEAD)
            \    /
             K--L

这看起来和以前完全一样,直到我们查看提交 M 中的 snapshot 之前,@中的 snapshot 987654403@ 是合并两个分支上的工作 并将合并的更改应用到来自H 的合并基础快照的结果。现在,M 中的 快照 与提交 L 中的快照完全相同。

如此接近仍然不是你想要的,因为你说你想要的是像往常一样制作M,但使用来自提交J的快照。 如果 Git 有一种开箱即用的方法,它会拼写为 -s theirs。显然 Git 曾经确实-s theirs(这一定是 Git-1.6 之前的版本),但现在没有 -s theirs

有很多方法可以伪造它。请参阅链接的答案(应该是重复的)以了解这些不同的方法。 确定你真的想伪造它。你最终会得到一个合并提交M,它的快照匹配你选择的“side”文件。

正如那里的几个答案所指出的,请注意 -X ours-X theirs,它们的含义非常不同。这些选项——我喜欢称它们为扩展选项,以明确它们不是策略 (-s) 选项——被传递到 随心所欲的策略。默认的递归/解决代码使用它来自动解决冲突。它不会完全忽略一方的变化。相反,它需要任何一方的更改,但是当这些更改发生冲突时,解决使用一方的更改的冲突,仅针对特定的差异块丢弃另一方的更改。

【讨论】:

    猜你喜欢
    • 2019-04-25
    • 1970-01-01
    • 2015-10-07
    • 2012-01-18
    • 1970-01-01
    • 2020-01-16
    • 2017-03-23
    • 2012-07-16
    相关资源
    最近更新 更多