2014-01-23 57 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

這是一個非常好的答案。 –