恢复git合并冲突标志

3
我正在尝试找到一种方法与团队的其他成员共享合并冲突。我们有一些非常大的分支,合并它们会产生许多冲突。我尝试了几种不同的方法,我的当前尝试包括将处于冲突状态的文件推送到远程仓库(保留文件中的合并标记),然后运行一个繁琐的长别名来搜索文件并手动重新创建合并文件(LOCAL,BASE,REMOTE)。
我最近发现了git checkout --conflict=merge -- (file) 命令,在本地分支上工作得很好,但是一旦它被推送到远程,这个命令就不能再恢复合并标志了。
有没有一种方法可以强制Git将文件重新标记为冲突,以便人们可以使用正常的合并工具来解决它们?
1个回答

4
有没有办法强制 git 重新标记一个文件为冲突状态,以便人们可以使用普通的合并工具来解决它们?
不行,除非你自己编写代码。(有人应该编写一些代码,也许这应该是——是时候有一个这样的工具了。有一堆难处理的角落案例。)
问题在于,在 Git 中,冲突合并是由存储在 Git 索引中的状态表示的。
退后一步,让我们定义索引,以及当前提交或 HEAD,以及工作树(或工作目录、工作树和一堆类似变体)。
  • 当前提交,也称为HEAD或HEAD,非常简单。 (我喜欢在计算机文本布局中使用HEAD来指代Git名称HEAD。您还可以在Git版本1.8.5或更高版本中使用@。此特殊名称引用当前分支(如果有当前分支),然后当前分支定位该分支的尖端提交,即当前提交。或者,在“分离的HEAD”模式下,HEAD直接包含当前提交的哈希ID。无论哪种方式,这都命名了当前提交。)

  • 工作树就是您进行工作的地方。 Git内部保存提交和文件版本副本的数据结构不适合其他任何用途,因此Git将版本提取到普通文件中,您可以像平常一样阅读和操作。

    工作树还可以保存您尚未提交并且不想提交的文件。 这些是未跟踪文件。(从技术上讲,未跟踪的文件是工作树中未在索引中的任何文件,但我们还没有定义索引。 :-))Git倾向于抱怨未跟踪的文件正在被跟踪;您可以通过在.gitignore文件中列出文件或其路径名模式来关闭这些投诉。请注意,将文件名添加到.gitignore 不会使该文件未跟踪。如果文件被跟踪,则仍然被跟踪。.gitignore条目主要只是关闭投诉,并且使Git不会自动添加这些未跟踪的文件,当您说“添加所有文件”时。

  • 索引位于这两者之间。通常,当您首次检出提交或分支时,索引内容与HEAD提交内容相匹配,Git也将其提取到工作树中。然后,您可以随意修改工作树,但索引仍然与HEAD匹配。您必须git add文件以将其从工作树复制回索引中。

    因此,索引基本上代表了您将要进行的下一个提交。当您运行git commit时,Git将索引转换为新的提交(自动成为新的HEAD提交)。只有您复制回索引的内容才会被提交,这意味着您可以通过仅逐个git add几个文件来将更改拆分为几个提交。(并且,您可以使用git add -p仅添加文件的部分,而不是整个文件,以便索引版本本身在HEAD提交版本和工作树版本之间。)

如果您从未执行过任何合并,或者从未遇到过任何合并冲突,我们可以在此结束并完成索引。当然,您正在执行合并操作,并且它们会出现冲突,因此我们需要仔细查看。
索引每个条目有四个“阶段插槽”
索引按照工作树中的路径名记录文件。如果您修改其中某些文件,并将其添加到git add以准备进行下一次提交,则会更新文件的索引版本。但是,如果在发生冲突的合并期间运行git ls-files --stage则会显示秘密。这通常只会在发生冲突的合并期间显示。秘密在于每个文件可以在索引中最多出现三次,在编号为阶段插槽的位置上。插槽零是正常的、日常的插槽:
$ git ls-files --stage
[snip]
100644 d8d18736e74c7a5f61d794770a2dd94786501d12 0   Makefile
100644 046dcab7645305cbf4b94adef54a859234ac3caa 0   README
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0   lib/__init__.py

所有零值列表示这些文件都在第零个槽位中。

然而,在冲突合并期间,README或其他名称可能会出现在1、2和3号槽位中。在这种情况下,Git知道存在合并冲突。第1个槽位保存文件的合并基础版本,第2个槽位保存HEAD--ours版本,第3个槽位保存MERGE_HEAD--theirs版本,这些版本均属于相同的文件。(根据定义,此时槽位0为空。)

这些槽位中有几个可能是空的。(我曾说过最多只能有一个空槽位,但我错了:在重命名/重命名或重命名/删除冲突中,我们实际上可以看到多个空槽位。)空槽位表示在合并的三个输入之一或多个输入中没有任何文件使用该名称。然而,任何高编号条目的存在都表明存在合并冲突。

正如您所见,您的工作是解决这些冲突。命名文件的工作树版本通常包含Git在此时的最佳合并尝试,但由于Git无法解决冲突,工作树版本中有冲突标记。解决冲突后,您应像往常一样在路径上运行git add(如果要删除文件,则运行git rm)。这会清除高级别槽位,并将工作树文件复制到槽0中,除非该文件真的被删除。现在,冲突已经解决。

如果您正在进行合并,尚未提交结果,并且已编辑或甚至解决了某个文件,但希望将其恢复为原始未合并状态,则可以使用:

git checkout -m -- <path>

你可以使用=<style>来指定冲突风格:mergediff3(我更喜欢diff3,因为它包括文件的合并基础版本的文本)。这将移除阶段零的条目(如果你有的话),并恢复更高阶段的冲突条目。这种特殊形式的git checkout需要在索引中保留原始未合并的条目。
无论如何,只有当所有较高阶段的索引条目都解决后,才能创建新的提交。也就是说,如果git ls-files --stage显示任何不为零阶段的条目,则不能进行新的提交。

... 一旦推送到远程仓库,此 [git checkout -m] 命令将不再有效。

事实上,在那之前就已经失效了。提交后,恢复冲突的能力将永久清除所有高阶段的索引条目。但你无法推送索引,也无法推送文件,只能推送提交。这意味着要推送部分合并(让其他人来处理),你必须解决冲突并提交。现在它已经不在进展中,也无法再次进展了。
需要的是一个工具,可以将完整的索引状态、工作树文件、合并状态(包括HEADMERGE_HEAD两个提交的ID,这意味着合并基础的ID),甚至未跟踪和/或被忽略的文件(类似于git stash)保存到特殊的提交或一组不属于分支引用的提交中。这个提交,或这些提交,然后可以从一个仓库转移到另一个仓库。构建这样一个工具所需的所有组件都已经存在(因为git ls-files --stagegit update-index都存在)。但编写这个工具对可能会像git stash脚本一样复杂。

虽然这不是我所期望的“解决方案”,但这可能是我能得到的最好的“答案”。感谢您详细的回复。在发布此问题后,我开始阅读有关git的“管道”和“瓷器”的内容,并开始意识到问题可能比我想象的要大,而您的答案证实了这一点。不过,有一件事让我困惑,就是在我的测试中,我能够在提交后使用[git checkout -m]命令。即使在切换分支后,我也可以使用该命令来恢复冲突。只有在推送到远程存储库后,该命令才停止工作。 - Melangeaddict
你可以使用 git checkout -m 命令将一个提交与另一个提交合并。效果与运行 git merge 命令大致相同,只是没有记录合并的信息...所以有点像git merge --squash,但也不完全一样。最终类似于运行 git read-tree -m 命令。(Git 就是这样有趣。 :-)) - torek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接