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