git索引的内容如何在合并期间发展(以及合并失败后索引中的内容)?

时间:2014-01-23 13:25:55

标签: git git-merge git-index

我对git索引包含的内容有一个模糊的概念git-addgit-commit s,但我不清楚这些内容会发生什么一个做git-merge。当合并失败时(例如由于某些冲突),我特别感兴趣的是学习索引的含义。

1 个答案:

答案 0 :(得分:5)

对于任何给定的路径,索引中最多有四个“版本号”,编号为0(零)到3.我将它们称为“插槽”,就好像它们实际上是每个条目一样,然后很容易索引(这使它们更容易思考),虽然实际上只有在需要时动态引入了额外的版本。这些“虚拟插槽”可以是“空的”,这意味着该文件不存在。

(实际上,一旦在索引中创建了一个条目,它就会标记一个标志位CE_REMOVED,如果需要的话。这会变得多毛,因为整个目录中的文件可以被标记为“已删除”,然后是可以使用上一个目录的名称创建文件并标记为“已添加”。让我们假装我们有固定的插槽,而不是空的,而是。:-))

Slot#0是“正常”,无冲突,全井进入。它包含一堆缓存数据,路径名和存储在存储库中的文件的blob-ID(SHA-1)。

当合并成功时,它就是“一切照旧”,所以唯一的特例是冲突合并。当插槽1,2和/或3非空时,合并是“冲突的”。跳过大多数机制,会发生什么。合并使用所有插槽的“最新”名称,并且:

  • 插槽零保留为空(在解决冲突之前不能“提交”,此时此插槽将不再为空,除非您确实要删除该文件。)
  • 插槽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查看git show :1:bleem的原始(基本)版本。这在基础版本中称为gronk(在本例中也在work中),但现在称为bleem,因为git认为您将gronk重命名为{{在bleem中的1}}。 (Git在合并基础和cleanup之间找到重命名,然后在必要时将相同的重命名应用于HEAD,如本例所示。)

同样,您可以看到work版本workgit show :3:bleem以及git show work:gronk版本中的任何一个版本:HEAD,{{1} },或git show HEAD:bleem(广告位2包含git show cleanup:bleem又名git show :2:bleem版本,并根据HEAD中的名称命名。

对于cleanup,因为它已在HEAD中删除,所以没有“他们的”(第3个插槽)版本。

要解决冲突,您只需告诉flibbywork更新slot-zero条目并删除1到3条目。当然,对于git add进入插槽0的是现在工作目录中的内容,因此您通常必须先编辑文件。

顺便说一下,我在上面标记了第2和第3个“我们的”和“他们的”。这也是git rm对待它们的方式(git addgit checkout允许您将版本2或3写入插槽0;这样的结帐,像大多数结帐一样,“擦除”其他插槽从而解决冲突)。但是,在一个rebase中,git checkout --ours分支实际上是正在重新定位的分支,而“他们的”版本是您的分支正在重新分配。因此,在我看来,我们/他们的术语并不是那么好:在反叛期间很容易让它倒退。

我还应该注意,git checkout --theirs将“重新创建”合并冲突,如果你正处于冲突合并的中间,通过删除插槽0并“恢复”插槽1-3中的版本根据需要(并将冲突的合并文件写入工作目录,同时遵守HEAD设置中的任何更改。)