在上一篇文章中,我們解釋了Git的數據模型。在這一篇文章,我們來看看Git另一個重要的方面:index。Index空間,也叫做stage空間,或者cache空間,集中了很多人對Git的誤解。在這篇文章里,讓我來嘗試解開大家關于這方面的疑惑。
之前的我也對這部分有不少誤解。我曾經以為,當你新checkout一個commit以后,你的index將是一片空白。然后你通過git add往index添加需要更新的文件。當你commit以后,你的新修改文件會被加入到Git數據庫,然后你的index又會回到一片空白的狀態(tài)。
我的理解實際上包含了幾個錯誤的地方:
-
Index并不是臨時存放新文件的地方 - 新文件并不是在
git commit的時候才被加入到Git的數據庫。實際上在git add的時候就已經被加入到數據庫里了。 -
Index包含的并不是當前commit和新commit的diff
為了糾正這些錯誤觀念,我們就要研究一下index里面到底存放著什么信息。我們可以通過查看./.git/index這個文件來得到答案。讓我們繼續(xù)使用上一篇文章的例子:
$ git checkout master
$ git ls-files --stage
100644 bf97e71de76bcff2bd8aba44710aa5e665eacb99 0 dir/inside.txt
100644 8231f0fdc862f06b2bd7b7bfd2f42082d3086b71 0 index.txt
上面的命令列出index中存放的信息。其中我們看到,即使我們是新checkout一個branch,index也不是空白的。它目前有兩個條目,每個條目分別指向一個blob對象,而且還包含該blob對象所對應的文件的路徑(分別是dir/inside.txt和index.txt)。而且這兩個blob對象對應的都是當前commit中的文件版本。
讓我們現在來做點小試驗。讓我們再次修改index.txt并添加到index中:
$ echo "version 3" > index.txt
$ git add index.txt
$ git ls-files --stage
100644 bf97e71de76bcff2bd8aba44710aa5e665eacb99 0 dir/inside.txt
100644 cd80567970da17e3c5d718e5b3db8700dbf320bf 0 index.txt
其實嚴格來說,“把index.txt添加到index中”這個表述并不準確。因為實際上我們看到的是,當我們執(zhí)行git add時,新的index.txt已經被作為一個新的blob對象添加到了Git數據庫中了,而并不是被添加到index中。Git只是更新了index中這個文件的指針來指向新版本的blob對象。如果我們此時執(zhí)行git commit,那么,Git將會基于index里面所指向的這些blob對象以及他們對應的路徑來生成tree對象,最終生成commit對象(下圖)。也就是說,實際上index空間是新的commit的一個寫照(snapshot)。

如果我們真正地明白這一點,那么很多本來覺得不自然的操作都顯得理所應當了。例如,如果我們想讓Git不再追蹤一個文件,那么我們該怎么做呢?每當想到index是新commit的寫照,那么我們就應該想到要移除index里面關于這個文件的條目。例如在我們的例子中,如果我想停止跟蹤dir/inside.txt:
$ git rm --cache dir/inside.txt
$ git ls-files --stage
100644 cd80567970da17e3c5d718e5b3db8700dbf320bf 0 index.txt
又例如,我們有如下的歷史:
HEAD → commit 5 → commit 4 → commit 3 → commit 2 → commit 1
如果我們想要合并commit 5, commit 4和commit 3,其中一種簡單的方法就是使用git reset --soft如下:
$ git reset --soft [commit 2]
$ git commit -m "commit 6"
這樣會得到以下結果:
HEAD → commit 6 → commit 2 → commit 1
原因就是,git reset --soft只把HEAD移到了commit 2,index并沒有變化,所以新執(zhí)行的commit是基于commit 2的(因為HEAD指向commit 2),但卻會保留commit 5的內容。
解釋了那么多,其實我想說的是,理解index的工作原理有助于我們自主去想解決的辦法,而不是生搬硬套網上搜來的解決方法,是提高水平的一大重要途徑。