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

kjo*_*kjo 5 git git-merge git-index

我对git索引包含的内容有一个模糊的概念,就像git-adds和git-commits一样,但我不知道当一个人做a时这些内容会发生什么git-merge.我特别感兴趣的是在合并失败时(例如由于某些冲突)了解索引的含义.

tor*_*rek 6

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

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

插槽#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
Run Code Online (Sandbox Code Playgroud)

你分支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
Run Code Online (Sandbox Code Playgroud)

您可以看到bleemwith 的原始(基本)版本git show :1:bleem.这被称为gronk在基础版本(在work为好,在这种情况下),但现在它被称为bleem是因为混帐相信你改名gronk,以bleemcleanup.(Git在merge-base之间找到重命名HEAD,然后在work必要时应用相同的重命名,如本例所示.)

同样,你可以看到work有版本git show :3:bleemgit show work:gronkHEAD任何的版本:git show HEAD:bleem,git show cleanup:bleemgit show :2:bleem(插槽2包含HEAD又名cleanup版本,并根据名字命名的HEAD).

对于flibby,虽然,因为它是在删除work,不存在"他们"(插槽3)版本.

要解决冲突,您只需要告知git addgit rm更新slot-zero条目并删除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设置中的任何更改.