暫存區(qū)
從 gi t的角度來看,文件的修改涉及到以下三個(gè)區(qū)域:工作目錄, stage區(qū)(暫存區(qū))以及本地倉(cāng)庫(kù).

當(dāng)我們對(duì)我們的項(xiàng)目做了一些修改(新增文件,刪除文件,修改文件等),我們處理的就是我們的工作目錄.這個(gè)目錄是存在于我們電腦的文件系統(tǒng)上的.所有的修改都會(huì)保留在工作目錄直到我們把它們加入到暫存區(qū)(通過git add命令)。
暫存區(qū)這是對(duì)下一次提交最好的表示方式,當(dāng)我們執(zhí)行git commit,git會(huì)獲取暫存區(qū)中的修改,并將這些修改作為下一次的提交內(nèi)容.暫存區(qū)的一個(gè)實(shí)際作用就是允許你調(diào)整你的提交,你可以向暫存區(qū)新增和刪除修改直到你對(duì)你下一次的提交滿意,這個(gè)時(shí)候你就可以用git commit提交你的內(nèi)容了。
在提交修改后,它們就會(huì)進(jìn)入.git/objects目錄,在其中被保存為commit,blob以及tree objects(參考[數(shù)據(jù)模型]http://www.itdecent.cn/p/843c8b0aa007)那一篇文章)
把暫存區(qū)認(rèn)為是一個(gè)存儲(chǔ)修改的真實(shí)區(qū)域并不準(zhǔn)確,git沒有專門的stage目錄來存放這些文件的修改(blobs),git有一個(gè)名為index的文件來跟蹤這三個(gè)區(qū)域的修改:工作目錄,暫存區(qū)以及本地倉(cāng)庫(kù)。
當(dāng)我們添加修改到暫存區(qū)的時(shí)候,git會(huì)更新index文件中的信息,并且創(chuàng)建一個(gè)新的blob object,然后將它們放到與之前提交的記錄所產(chǎn)生的其他blob相同的.git/objects目錄中。
index的變化
接下來我們就通過一個(gè)正常的git流程來演示下git如何使用的index
首先在我們的倉(cāng)庫(kù)里面有master以及feature兩個(gè)分支,如果我們執(zhí)行下面的命令,會(huì)有三件事情發(fā)生
git checkout feature
第一,git會(huì)移動(dòng)HEAD指針來指向feature分支,為了更加便于理解,我們只顯示功能分支的最后一次提交

第二,git將獲取feautre分支指向的提交內(nèi)容并將其添加到索引中

我們注意到index是一個(gè)文件而不是目錄,所以git是沒有往其中存儲(chǔ)內(nèi)容的,git只是存儲(chǔ)我們倉(cāng)庫(kù)中每個(gè)文件的信息而已,類似于上面這樣
mtime : 上次更新時(shí)間
file : 文件名稱
wdir : 工作目錄中文件版本
stage : index中文件版本
repo : 倉(cāng)庫(kù)中的文件版本
文件版本以校驗(yàn)和來標(biāo)識(shí),如果兩個(gè)文件有相同的校驗(yàn)和,那么它們就有一樣的內(nèi)容以及版本.
最后,git會(huì)將你的工作目錄和HEAD指向的內(nèi)容相匹配(它將使用樹和blob對(duì)象重新創(chuàng)建項(xiàng)目目錄的內(nèi)容)

所以,當(dāng)你使用checkout的時(shí)候,工作目錄,暫存區(qū)以及倉(cāng)庫(kù)都是相同的版本
我們來看看當(dāng)我們編輯Main.java的時(shí)候會(huì)發(fā)生什么?

現(xiàn)在僅僅只影響了我們的工作目錄,但是我們運(yùn)行下面的命令的時(shí)候
git status
git 首先會(huì)更新index文件中Main.java的工作目錄的版本

然后我們看到Main.java在工作目錄和暫存區(qū)有不同的版本

然后git會(huì)提示我們
On branch feature
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: Main.java
no changes added to commit (use "git add" and/or "git commit -a")
這就表明工作目錄的修改不在暫存區(qū)中(那么下一次的提交就不會(huì)包含Main.java的修改).
所以,執(zhí)行以下命令將Main.java加入到暫存區(qū)
git add Main.java
執(zhí)行了上面這條命令,就又會(huì)發(fā)生兩件事兒,第一,git會(huì)為Main.java創(chuàng)建一個(gè)blob object然后存儲(chǔ)在.git/objects目錄下,第二,會(huì)再次更新index文件

這個(gè)時(shí)候我們?cè)俅螆?zhí)行命令
git status
git會(huì)發(fā)現(xiàn)Main.java的暫存區(qū)的版本和工作目錄版本一致,但是和倉(cāng)庫(kù)的版本不一致

所以git就告知我們
On branch feature
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: Main.java
證明Main.java已經(jīng)在暫存區(qū),但是還沒有提交到倉(cāng)庫(kù).現(xiàn)在我們就可以提交我們的修改了
git commit -m "add some code to Main.java"
git會(huì)做下面幾件事兒:
1. 新增commit object和tree object,并把它們和執(zhí)行g(shù)it add時(shí)創(chuàng)建的blob object連接起來
2. 移動(dòng)feature的指針到新的commit object
3. 更新index

好啦,現(xiàn)在我們的Main.java在所有區(qū)域都有相同的版本了.
無論執(zhí)行 git add還是git commitindex文件都會(huì)變更,這也更好的證明了我們上述模型,當(dāng)然index文件中的內(nèi)容肯定沒有那么清晰,它是一個(gè)二進(jìn)制文件,如果想要查看它的內(nèi)容就需要借助其他工具來實(shí)現(xiàn)
上面就是關(guān)于git index的原理了,現(xiàn)在回過頭來看發(fā)現(xiàn)其實(shí)并不復(fù)雜,但是對(duì)于我們理解在一些在index上操作的命令(add,checkout,revert,commit,add...)卻是至關(guān)重要的