? ? git本質(zhì)上是一個內(nèi)容尋址的文件系統(tǒng),并在此基礎(chǔ)上提供了一個版本控制系統(tǒng)的界面。
? ? 一個已經(jīng)初始化后的git目錄結(jié)構(gòu)如圖:
其中我們需要重點關(guān)注以下四個文件:
1. HEAD:保存了當(dāng)前被檢出的分支
2. objects:存儲所有的數(shù)據(jù)內(nèi)容
3. index:存儲暫存區(qū)信息
4. refs:存儲指向commit對象的指針
1. Git對象
? ? objects目錄下存儲了git中所有的數(shù)據(jù)對象,其目錄結(jié)構(gòu)如下:
objects? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????0d? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????????7f012a2181ab54c95b4078674da7b31b580bff? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????1c? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????????c469fe42468071e2b1823650fb0a3206985281
????objects目錄中存儲的對象擁有一個40位的id,其中前2位作為文件夾名,后38位作為文件名,文件內(nèi)容即為該對象的值。git中擁有四種對象類型:blob,tree,commit和tag。
1.1 blob對象
? ? blob對象存儲了用戶工作區(qū)域內(nèi)的文件內(nèi)容。當(dāng)執(zhí)行g(shù)it add命令時,objects下會生成新的blob對象,保存了add命令的文件內(nèi)容。
? ? 使用git cat-file命令可以從指定的對象中取得數(shù)據(jù),加上-p選項
? ? git構(gòu)造一個對象時,首先添加一個header,header以對象類型開頭。下面的例子時git構(gòu)造了一個內(nèi)容為字符串"blob",類型為blob的對象。header首先以blob開頭,然后添加一個空格,隨后是數(shù)據(jù)內(nèi)容的長度,最后是一個空字節(jié)。因此一個blob對象包含一個blob header和原始數(shù)據(jù)內(nèi)容,其形式如下:
>> header = "blob #{content.length}\0"
=> "blob 16\u0000"
? ? Git會將上述header和原始數(shù)據(jù)內(nèi)容為這個blob對象計算出SHA-1校驗和。此SHA-1校驗和就是前文所說的40位長度的對象id。然后git根據(jù)此id值確定此對象的存儲位置(前2位作為文件夾,后38位作為文件名),使用zlib壓縮此blob對象然后存儲到相應(yīng)位置。
? ? commit對象,tree對象的文件header則以"commit"和"tree"開頭。
1.2 tree對象
? ? tree對象,顧名思義是一棵樹,它的每個節(jié)點被稱為tree entry。每個tree entry有一個指向blob對象或子樹對象的SHA-1指針,以及相應(yīng)的模式,類型,文件名信息等。例如,某個tree對象的信息如下:
? ? 每一行代表此tree對象的一個節(jié)點,即tree entry。第一列代表文件模式,其中040000表示這是一個目錄,而100644表示這時一個普通文件。第二列表示此節(jié)點的對象類型。第三類是此節(jié)點的對象SHA-1值,通過此SHA-1值來定位數(shù)據(jù)內(nèi)容,可以通過git cat-file -p <SHA-1>來查看。第四列則代表了文件名。因此,此tree對象的樹形結(jié)構(gòu)圖如下:
tree對象同blob對象一樣,擁有一個SHA-1值,并且通過SHA-1值來確定文件位置,并將添加了header的tree對象內(nèi)容寫入到objects目錄下。
1.3 commit對象
????一個commit對象保存了一次commit的信息,包括作者,提交注釋,并且指向父commit對象和一個tree對象。這個tree對象是當(dāng)前項目快照。如圖所示:
2. 再談git文件夾結(jié)構(gòu)
2.1 index文件
????index文件實際上就代表了git中暫存區(qū)。它存儲了一個tree對象,表示當(dāng)前項目的快照。當(dāng)運行g(shù)it add命令時,首先在objects目錄下生成一個新的blob對象,然后更新index文件中的tree對象。
? ? 當(dāng)運行g(shù)it status命令時,git會對比工作區(qū)和暫存區(qū)的文件,具體流程如下:
1. 首先比較文件的時間戳和長度,如果相同則認為文件沒有改變。
2. 如果發(fā)現(xiàn)時間戳不同,則比較文件內(nèi)容,如果內(nèi)容相同,則將工作區(qū)文件的時間戳更新到index文件(暫存區(qū))中。
3. 如果工作區(qū)文件和暫存區(qū)文件內(nèi)容不相同,則提示有changes
? ? 當(dāng)運行g(shù)it commit命令時,首先使用index文件中tree對象在objects目錄下創(chuàng)建一個tree對象,然后在objects目錄下創(chuàng)建一個commit對象,commit對象指向此tree對象以及父commit對象,最后更新.git/refs/heads/master(commit時的分支名)文件內(nèi)容為此commit的SHA-1值。
2.2 refs文件夾
? ? refs文件夾結(jié)構(gòu)如下:
? ? 其中子文件夾heads目錄為每一個分支創(chuàng)建了一個文件,文件中只保存了在該分支下最近一次提交的commit id。
????打開master文件,內(nèi)容只是一個commit的SHA-1值。
2.3 HEAD文件
? ? HEAD文件指向當(dāng)前活躍的分支。打開HEAD文件,文件內(nèi)容如下:
? ??可以看到文件內(nèi)容是本地的某一個代表了feture2分支的文件,而該分支文件的內(nèi)容是最近的一次commit id。因此,我們常用的命令諸如 git reset HEAD等命令中出現(xiàn)的HEAD,能夠正確的定位到具體的commit。