告诉 git 解决与第三个文件的冲突,这是答案

Tell git to resolve conflict with a third file which is the answer

我们有以下情况: 有人在没有任何 git 合并工具的情况下手动解决了很多(数万)合并冲突,但手动解决并保存到不同的文件中 在尝试解决此问题时,我正在进行适当的合并。现在我知道如何解决所有冲突 - 采取旧的手动解决文件中的冲突。 有没有办法自动执行此操作 - 通过 "third"?

解决

对该问题的评论并不完全有意义;所以我怀疑某些评论可能已被删除(或者在任何情况下都没有出现在我面前),我希望我没有遗漏任何相关内容。也就是说:

在"individual merge of an individual file"级别,最好的解决方案是开始合并,当提示解决冲突时,将预先解决的文件复制到工作副本上;然后 git add 它和 commit 完成合并。 [1]

如果您只是对单个文件进行一次合并,那么这就是您应该做的;因为任何更自动化的设置都将花费更多的时间。

另一方面,如果您有很多文件(可能跨越多个合并),那么自动化程度更高可能是有意义的。如果您可以为您的工作树设置一个并行目录结构,并将预先合并的文件放在该并行结构中各自的位置,那么 cp -R 就可以工作。或者,如果您在 git 中提交了它们,您可以使用 git checkout <commit> -- <path> 语法来获取固定版本。

我想如果你真的想要花哨的话,你可以设置一个合并驱动程序来复制预合并的文件并避免首先报告冲突;但是使它正常工作的设置仍然同样乏味(更糟糕的是,事实上,因为创建合并驱动程序的额外步骤),而自动化只是意味着你在有机会之前就已经提交了合并做最后的完整性检查...老实说,我不会理会这个想法。


[1] 在您的评论中,您说您担心日志显示的是个别更改而不是完全重写的文件,这就是我觉得缺少某些上下文的地方。无论如何,复制预先解决的文件来解决冲突本身不会导致 git 将文件视为 "completely rewritten"。日志输出是计算出的补丁。它显示了文件之间的差异,无论用于生成每个版本的过程如何。

因此,如果它显示 "completely rewritten file",则意味着它在每一行上(或多或少)看到了差异。这可能是由于白色 space 的变化(尤其是在某些情况下行结束变化往往会偷偷摸摸)。或者某种无偿的重新格式化。

如果发生类似的事情,并且您无法撤消对预合并副本的 "unnecessary" 更改,那么您可能必须决定是手动重做合并更好,还是忍受日志中的一个小问题。

除了 (我认为这是正确的并且我已投赞成票)之外,还值得添加另一个注释。 (因此这应该是一条评论,除了不能格式化评论并且有一个小的长度限制,这超过了,嗯,"a lot"。)

一个合并提交只是一个至少有两个parent的提交。也就是说,"normal" 或 non-merge 提交是一个提交,或者在极少数情况下,no parents. 1 这是一个这样的实际提交:

$ git rev-parse HEAD
ccdcbd54c4475c2238b310f7113ab3075b5abc9c
$ git cat-file -p HEAD | sed 's/@/ /'
tree 191f960868564ef1f0978328589aa191219f1ab8
parent 96f29521a3908eb80b9552f11f2b75ca34475686
author Junio C Hamano <gitster pobox.com> 1525762230 +0900
committer Junio C Hamano <gitster pobox.com> 1525762789 +0900

The fifth batch for 2.18

Signed-off-by: Junio C Hamano <gitster pobox.com>

合并提交看起来是一样的,但有两行或更多行 parent(当然还有不同的日志消息)。 tree 行是 Git 记录提交的 源树快照 的方式。

Git 使用 Git 的 indexany 提交中制作源树快照,这是一个数据结构,要么非常重要,要么最初命名如此糟糕,2,它有三个名称:index暂存区,以及缓存。在正常情况下,no-merge-conflict 情况下,索引存储每个文件的一个副本。索引的版本采用特殊的 Git-only 格式,而您使用的 work-tree 版本采用计算机上的普通日常文件格式。您在 work-tree 版本上工作,然后使用 git add 复制 work-tree 版本 之上索引版本,将普通格式文件转换为新的、特殊的、Git-only 版本准备提交。

不过,在冲突合并期间,索引扮演了更大的角色:现在它不仅包含文件的 一个 副本,还包含 三个 个冲突文件的副本,每个文件名使用三个额外的 "slots"。一个正常的、无冲突的文件位于插槽 0 中。但是,当存在合并冲突时,Git 将文件的 base 版本放入插槽 1,即当前(--ours,或左或本地)版本在插槽 2 中,另一个(--theirs,或右或远程)版本在插槽 3 中。因此索引中有 个文件副本,当然还有work-tree 中的一个带有冲突标记。

,作为一个人,在处理合并冲突时for Git所做的是操纵索引,直到它只有 一个 版本的冲突文件,而不是存储所有三个版本,使用索引中的三个版本 and the work-tree以您喜欢的任何方式复制。无论您如何进行合并都是如此:Git 为您提供所有四种不同的文件,其中三种在索引中,一种在 work-tree 中,最后,您告诉 Git 将某些内容放入 Git 的索引中的零槽中,而完全忘记其他三个版本。

一旦你这样做了—一旦你消除了索引中的三个 higher-stage 槽条目,通过解决合并冲突,使用你的编辑器或合并工具或通过 numerology or whatevermancy 和 运行 git add 将最终版本复制到正常 slot-zero— 只有这样 你可以 git commit结果。 当你这样做时,Git 制作与 any 提交相同类型的 tree 快照。 这棵树 object 是像任何提交树一样的快照,因此合并就像任何其他提交一样保存快照。 换句话说,合并提交与任何其他提交没有区别*,除了它至少有两个 parent 提交。

使合并提交 看起来 不同的是,当您要求 Git 告诉您有关提交时 —any提交—Git 通过将提交与其 parent 进行比较来实现。但是一个merge至少有两个parent,所以Git怎么能和比较(单)parent? Git 的答案各不相同:

  • git log 默认情况下根本不去比较它。
  • 默认情况下,
  • git show 生成 Git 所谓的 组合差异 。 (组合差异故意遗漏了很多东西,所以它们在弄清楚发生了什么方面并不是很有用。它们主要表明存在一些冲突并且通过除选择一个以外的其他方式解决了冲突 "side"合并。)

其他命令通常遵循这两个选项之一。可以显示合并的命令通常也有一个 -m 选项,它告诉 Git 将 拆分 合并,至少为了比较目的,分成多个单独的 "virtual commits":每个这样的提交都有与合并相同的,但只有一个parent。现在 "virtual commit" 有一个 parent 而不是 two-or-more,Git 可以用通常的方式显示它,通过比较(diffing)虚拟提交与其(单个)parent.

这不会改变 合并提交中的内容,这是一个普通快照,使用与任何普通 non-merge 提交相同的方式制作,使用任何方式您在提交时在索引中。所以从这个意义上说没有什么特别的关于解决冲突!您只需将包含内容的文件写入 work-tree,使用 git add 将其放入索引,然后使用 git commit 将其快照到提交中——即使最终提交有两个 parents.


1没有 parents 的提交是 root 提交,存储库中的第一个提交是始终是根提交,因为它之前没有提交到 attach-to。大多数存储库往往只有一个根提交,尽管由于 Git 在某种程度上只是一个图形处理系统,您可以根据需要创建任意数量的根提交。大多数合并提交都有两个 parent,因为正常的合并过程是合并一个特定的提交——通常通过分支名称,但 Git 只关心 提交 在这里——进入你当前的分支,这样 "current commit" 作为 parent #1,"other commit" 作为 parent #2。

Git 可以使 Git 调用的 octopus 合并 ,使用当前提交作为 parent #1,以及一大堆使用 Git 的 -s octopus 合并策略 作为 parent 的其余部分进行额外提交。然而,章鱼合并策略拒绝处理冲突,因为索引没有空间容纳冲突文件的额外副本。只有三个 "sides" 可以进入索引:base、left/local/ours 和 right/remote/other/theirs.

2我个人认为答案是两者兼而有之:poorly-named都很重要。 :-)