有没有办法强制 git 将文件重新标记为冲突,以便人们可以使用正常的合并工具来解决它们?
不,无论如何都要编写自己的代码。 (应该有人写一些代码,也许就应该这样——现在是时候为它准备一个工具了。不过,有很多极端情况很难。)
这里的问题是,在 Git 中,冲突合并由存储在 Git 的 index 中的状态表示。
退一步,让我们定义索引,以及当前提交或HEAD,以及工作树(或工作树、工作树和一堆类似的变体):
当前提交,也称为 HEAD 或 HEAD,非常简单。 (我喜欢使用HEAD,在这样的计算机文本布局中,专门表示Git名称HEAD。您也可以在Git 1.8.5或更高版本中使用@。这个特殊名称指的是current branch,如果有当前分支,则当前分支定位到那个分支的tip commit,也就是当前提交。或者,在“分离HEAD”模式下, HEAD 直接包含当前提交的哈希ID。无论哪种方式,这都会命名当前提交。)
-
工作树很简单,就是你工作的地方。 Git 保存提交和版本控制的文件副本的内部数据结构不适用于其他任何东西,因此 Git 将版本提取到普通文件中,然后您可以照常读取和操作这些文件。
工作树还可以保存您尚未提交且不想提交的文件。这些是未跟踪文件。 (从技术上讲,未跟踪的文件是工作树中尚未在 index 中的任何文件,但我们尚未定义索引。:-))
Git 倾向于抱怨未跟踪的文件未被跟踪;您可以通过在.gitignore 文件中列出文件或其路径名模式来关闭这些投诉。请注意,将文件名添加到 .gitignore 不会 使文件无法跟踪。如果文件被跟踪,它将保持跟踪。 .gitignore 条目主要只是关闭投诉,并且还使 Git 在您说“添加所有文件”时不会自动添加这些未跟踪的文件。
-
index 位于这两者之间。通常,当您第一次签出提交或分支时,索引内容与HEAD 提交内容匹配,Git 也会将其提取到工作树中。然后,您可以随意修改工作树,但索引继续匹配 HEAD。您必须git add 文件才能将它们从工作树复制回索引中。
因此,索引本质上代表您将进行的下一次提交。当你运行git commit 时,Git 会将索引变成一个新的提交(它会自动成为新的 HEAD 提交)。只有您复制回 索引的内容才会被提交,这意味着您可以通过一次 git adding 几个文件将更改拆分为多个提交。 (并且,您可以使用git add -p 仅添加文件的部分,而不是整个文件,以便索引版本本身位于 HEAD 提交版本和工作树版本之间。 )
如果您从未进行任何合并,或者从未有任何合并冲突,我们可以在此停止并完成索引。当然,您正在进行合并,它们会发生冲突,因此我们需要仔细观察。
索引每个索引条目有四个“阶段槽”
索引按文件在工作树中的路径名记录文件。如果您修改其中一些文件,并且git add 为下一次提交做好准备,这将更新文件的索引版本。但是,如果您在冲突合并期间运行git ls-files --stage,则会出现一个秘密。这通常不会出现在其他任何时候——它只是在有冲突的合并期间。秘诀是每个文件在索引中最多可以三次,在编号的stage slot中。插槽 0 是正常的日常插槽:
$ 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 未被占用。)
其中几个插槽可以是空的。 (我以前说最多可以有一个是空的,但我错了:重命名/重命名或重命名/删除冲突,我们实际上可以看到多个空槽。)空槽表示 no 文件在合并的三个输入中的一个或多个中具有该名称。但是,任何更高编号的条目的存在表明存在冲突的合并。
正如您所见,解决这些冲突是您的工作。命名文件的工作树版本通常包含 Git 迄今为止解决合并的最佳尝试,但由于 Git 无法解决冲突,因此工作树版本中包含冲突标记。解决冲突后,您应该像往常一样在路径上运行git add(如果解决方案是删除文件,则运行git rm)。这将清除更高阶段的槽,同时还将工作树文件复制到槽零,除非该文件被真正删除。现在冲突解决了。
如果您正在合并,尚未提交结果,并且已经编辑甚至解决了文件但希望将其恢复到原始未合并状态,您可以,如您所述,使用:
git checkout -m -- <path>
(或与--conflict 相同)。您可以添加=<style>,它允许您指定冲突样式:merge 或diff3(我更喜欢diff3,其中包括来自文件合并基础版本的文本)。如果您做了一个,这将删除阶段零条目,并恢复更高阶段的冲突条目。但是,git checkout 的这种特殊形式要求原始未合并条目在索引中可用。
在任何情况下,您不能在解决所有更高阶段的索引条目之前进行新的提交。也就是说,如果git ls-files --stage 显示任何非阶段零的条目,您可能不会进行新的提交。
...一旦它被推送到远程,这个 [git checkout -m] 命令不再用于恢复合并标志。
事实上,早在那之前。一旦你提交,恢复冲突的能力就会消失。这将永久清除所有更高阶段的索引条目。但是你不能推送索引,也不能推送文件:你只能推送提交。这意味着要推送部分合并(让其他人处理它),您必须解决合并和提交。现在它不再在进行中,也不能再在进行中了。
需要一个可以保存完整索引状态、工作树文件、合并状态的工具,包括两个提交的 ID HEAD 和 MERGE_HEAD——这意味着合并基础的 ID——甚至可能将未跟踪和/或忽略的文件(la git stash)放入存储在非分支引用上的特殊提交或一组提交中。然后可以将这个提交或这些提交从一个存储库转移到另一个存储库。同一工具的反向版本可以恢复合并状态、索引状态和工作树。构建此类工具所需的所有组件都存在(因为git ls-files --stage 和git update-index 都存在)。但是编写这个工具对会很复杂,可能至少和git stash 脚本一样困难。