2014-01-23 31 views
3

我对git索引包含的内容有一个模糊的想法,其中一个是git-add s和git-commit s,但我不知道在执行git-merge时这些内容会发生什么。我特别有兴趣了解合并失败时(例如,由于某种冲突)索引所持有的内容。合并期间git索引的内容如何演变(合并失败后索引内容是什么)?

+0

合并和rebase在某些点上是相似的。也许你可以通过'git rebase -i'来看看会发生什么,并用'edit'替换所有的'pick',然后观察.git修改 – Asenar

+0

@Asenar:相似性的出现是因为'rebase'是一系列'cherry-pick'内部操作,并且对于每个樱桃选择的提交,git使用合并机制来应用该提交所隐含的更改。 – torek

+0

谢谢你的精度:) – Asenar

回答

2

对于任何给定的路径,索引中最多有四个“版本号”,编号为0(零)至3.我将它们称为“插槽”,就好像它们实际上存在于每个条目中一样,然后容易索引(这使得它们更易于思考),但实际上只有在需要时才会动态引入额外的版本。这些“虚拟插槽”可以是“空的”,这意味着该文件不存在。 (实际上,一旦在索引中创建了一个条目,如果需要,它将被标记为CE_REMOVED,这样会变得毛茸茸,因为整个目录中充满了文件可以标记为“已删除”,然后文件可以是我们只是假装我们有固定的插槽,而不是空的,而是:-))

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

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

  • 插槽零为空(您不能“提交”,直到您解决冲突,此时该插槽不会为空除非你真的想要删除文件)。
  • 插槽1(“基本”)填充了共同的祖先版本。如果文件是新的(在两个版本中),则此插槽为空。
  • 插槽2(“我们的”)填充目标(HEAD,除非您手动调用某些底层合并机器)版本。如果该文件在HEAD /合并目标中被删除,则此槽为空。
  • 插槽3(“他们”)充满了正在合并的版本。如果文件在正在合并版本中被删除,则此插槽为空。

一旦你解决了冲突和“git add”,#0插槽被填充任何你“添加”,擦除#1到#3的条目 - 或者,如果你“git rm”冲突的文件,其他阶段条目仍然被删除,但现在#0插槽保持空白,这也解决了冲突。

更具体,那么,假设你有一个有(其中包括)共同祖先的这两个文件:

gronk 
flibby 

你分支cleanup,你已经改名gronkbreem,都编辑那和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,cleanup。 (GIT发现合并基础和HEAD之间的重命名,然后应用同样的命名规则,以work如果必要的,因为在这种情况下)。

同样,你可以看到work版本git show :3:bleemgit show work:gronk,并且HEAD版本与以下任何一个:git show HEAD:bleem,git show cleanup:bleemgit show :2:bleem(时隙2包含HEAD又名cleanup版本,并且根据HEAD中的名称命名)。

对于flibby,虽然它在work中被删除,但没有“他们”(插槽3)版本。

要解决冲突,您只需要告诉git addgit rm更新插槽零条目并删除1至3条目。当然,对于git add的插槽0是什么在工作目录现在,所以你通常必须先编辑文件。

顺便提一下,我在上面标记了“我们”和“他们的”的插槽2和3。这也是git checkout对待它们的原因(git checkout --oursgit checkout --theirs可让您将第2版或第3版写入第0号槽;与大多数结账一样,此结账“擦除”其他槽,从而解决冲突)。然而,在一个rebase中,HEAD分支实际上是分支正在重新分配,而“他们的”分支是分支重新分配。因此,我认为我们的/他们的术语并不是那么棒:在重新绑定期间很容易让它倒退。

我还应该注意到,git checkout -m如果您处于冲突合并的中间,将通过擦除插槽0和根据需要“复活”插槽1-3中的版本来“重新创建”合并冲突(并将冲突的合并文件写入工作目录,并遵守merge.conflictstyle设置中的任何更改)。

+0

这是一个非常好的答案。 –