我对git
索引包含的内容有一个模糊的概念,因为一个人执行git-add
s 和git-commit
s,但我不知道当一个人执行 s 时这些内容会发生什么git-merge
。我对了解合并失败时索引的内容特别感兴趣(例如,由于某些冲突)。
1 回答
对于任何给定的路径,索引中最多有四个“版本号”,编号从 0(零)到 3。我将它们称为“插槽”,就好像它们实际上存在于每个条目中一样,然后很容易索引(这个使它们更容易思考),尽管实际上只有在需要时才动态引入额外的版本。这些“虚拟插槽”可以是“空的”,这意味着文件不存在。
(实际上,一旦在索引中创建了一个条目,如果需要,它就会用一个标志位 , 标记CE_REMOVED
。这很麻烦,因为整个目录充满文件可以标记为“已删除”,然后可以创建一个名为上一个目录并标记为“已添加”。让我们假设我们有固定的插槽,但那里是空的。:-))
插槽#0 是“正常”、无冲突、一切正常的条目。它包含存储在存储库中的文件的一堆缓存数据、路径名和 blob-ID(SHA-1)。
当合并成功时,一切都是“一切照旧”,所以唯一的特殊情况是冲突合并。当插槽 1、2 和/或 3 非空时,合并是“冲突的”。跳过大部分机制,会发生什么。合并使用所有插槽的“最新”名称,并且:
- 插槽 0 留空(在解决冲突之前,您不能“提交”,除非您真的希望删除文件,否则该插槽将不再为空)。
- 插槽 1(“基础”)填充了共同祖先版本。如果文件是新文件(在两个版本中),则此插槽为空。
- 插槽 2(“我们的”)填充了目标(
HEAD
,除非您手动调用一些底层合并机制)版本。如果文件在HEAD
/target-of-merge 中被删除,则此插槽为空。 - 插槽 3(“他们的”)填充了正在合并的版本。如果文件在正在合并的修订中被删除,则此插槽为空。
一旦你解决了冲突并“git add”,#0 槽就会被你“添加”的任何东西填充,清除 #1 到 #3 中的条目——或者,如果你“git rm”冲突的文件,另一个阶段条目仍然被删除,但现在 #0 插槽仍然是空的,这也解决了冲突。
那么,更具体地说,假设您有一个共同的祖先(其中包括)这两个文件:
gronk
flibby
您在分支上cleanup
,您已重命名gronk
为breem
,并编辑了 和flibby
。您决定git merge work
,他们修改gronk
但没有重命名的地方,并删除了flibby
. 其他一些文件干净地合并了。
该索引将包含 的三个版本bleem
和两个版本flibby
:
$ git checkout cleanup
Switched to branch 'cleanup'
$ git merge work
CONFLICT (modify/delete): flibby deleted in work and modified
in HEAD. Version HEAD of flibby left in tree.
Auto-merging bleem
CONFLICT (content): Merge conflict in bleem
Automatic merge failed; fix conflicts and then commit the result.
$ git ls-files --stage
100644 4362aba7f3b7abf2da0d0ed558cbf5bc0d12e4b0 1 bleem
100644 49db92a61392e9fd691c4af6e1221f408452a128 2 bleem
100644 04b399c8fe321902ce97a1538248878756678ca2 3 bleem
100644 366b52546711401122b791457793a38c033838dd 1 flibby
100644 6fecb1480f45faaabc31b18c91262d03d3767cde 2 flibby
100644 7129c6edb96d08bb44ca1025eb5ae41d41be8903 0 x.txt
您可以看到bleem
with的原始(基本)版本git show :1:bleem
。这是gronk
在基本版本中调用的(work
在这种情况下也是 in ),但现在调用它是bleem
因为 git 认为你重命名gronk
为bleem
in cleanup
。(Git 在合并库之间找到重命名HEAD
,然后在work
必要时应用相同的重命名,如本例所示。)
同样,您可以看到work
带有git show :3:bleem
orgit show work:gronk
的版本,以及HEAD
带有以下任一选项的版本:git show HEAD:bleem
、git show cleanup:bleem
或git show :2:bleem
(插槽 2 包含HEAD
akacleanup
版本,并根据 中的名称命名HEAD
)。
但是,对于flibby
,由于它已在 中删除work
,因此没有“他们的”(插槽 3)版本。
要解决冲突,您只需告知git add
或git rm
更新槽零条目并删除 1 到 3 条目。当然,使用git add
,进入slot 0 的内容就是现在工作目录中的内容,因此您通常必须先编辑文件。
顺便说一句,我在上面将插槽 2 和 3 标记为“我们的”和“他们的”。这也是git checkout
对待它们的方式(git checkout --ours
并git checkout --theirs
让您将版本 2 或 3 写入插槽 0;这样的结帐,像大多数结帐一样,也会“擦除”其他插槽,从而解决冲突)。但是,在变基中,HEAD
分支实际上是要变基的分支,而“他们的”版本是要变基的分支。因此,在我看来,我们/他们的术语并不是那么好:在变基期间很容易将其倒退。
我还应该注意git checkout -m
,如果您处于冲突合并的中间,则将“重新创建”合并冲突,方法是擦除插槽 0 并根据需要“恢复”插槽 1-3 中的版本(并编写冲突的合并文件到工作目录,同时遵守您的设置的任何更改merge.conflictstyle
)。