按照官方文檔的描述,Git是這樣定義的
- 內(nèi)容尋址(content-addressable)文件系統(tǒng),在此之上提供了一個(gè)VCS用戶界面
- Git采用HashTable的方式進(jìn)行查找,通過簡(jiǎn)單的存儲(chǔ)鍵值對(duì)的方式來實(shí)現(xiàn)內(nèi)容尋址,key是文件頭和內(nèi)容組成的40位hash值,value是壓縮過后的文件內(nèi)容
當(dāng)然,這句話寫得并不是很容易讓人明白,通過查看git的目錄結(jié)構(gòu)以及細(xì)致地分析一次Git提交可以更好地幫助理解Git
.git目錄
.git目錄是Git的核心,每一個(gè)變動(dòng)都會(huì)存儲(chǔ)在.git文件夾中,Git的相關(guān)命令本質(zhì)上也是讀取.git文件夾下的內(nèi)容
.git目錄下有幾個(gè)重要的文件/文件夾
- config文件,主要存儲(chǔ)項(xiàng)目的一些配置信息
- objects文件夾, 存儲(chǔ)git對(duì)象
- HEAD文件,記錄當(dāng)前的頭指針
- index文件,存儲(chǔ)暫存區(qū)的信息
- refs文件夾, 存儲(chǔ)分支的指針
git對(duì)象
提交和文件是Git中的主要組成,也叫g(shù)it對(duì)象,Git中的許多命令都和git對(duì)象有關(guān)
git對(duì)象分為下面3類

git對(duì)象存儲(chǔ)在.git目錄下的objects文件夾中,Git會(huì)將git對(duì)象壓縮成二進(jìn)制文件,git對(duì)象的文件名即sha-1算法得到的hash值,按照2/38的形式保存(前兩位是文件夾的名稱,剩下38位是文件名,這樣做可以防止文件夾的內(nèi)容過多,提高查找效率)
對(duì)于commit對(duì)象,hash值也被稱為commitid
可以使用以下命令查看git對(duì)象中的內(nèi)容
git cat-file -p <hash>
通過查看三種git對(duì)象的內(nèi)容,不難發(fā)現(xiàn)如下的組織關(guān)系

- 每個(gè)commit的對(duì)象包含了tree和blob對(duì)象的hash
- 每個(gè)tree對(duì)象包含了blob文件的hash
- 每個(gè)blob對(duì)象是真正文件的二進(jìn)制保存
其實(shí)可以吧hash看成每個(gè)對(duì)象的指針,Git通過指針將眾多git對(duì)象串聯(lián)起來,來實(shí)現(xiàn)對(duì)項(xiàng)目的版本控制
從Git命令看一次提交的完整過程
用戶通過Git命令讀寫.git文件夾,達(dá)到獲取信息或變更版本的目的
Git一開始被設(shè)計(jì)成供VCS使用的工具集合而不是一整套用戶有好的VCS,它還包含了許多的底層命令,一般被稱為plumbing命令(底層命令),而用戶日常使用Git命令被稱為porcelain命令(高層命令),porcelain命令實(shí)際是是對(duì)plumbing命令的封裝

一次完整的提交過程會(huì)包含如下過程
- 保存二進(jìn)制對(duì)象(即生成blob對(duì)象)
- 寫入暫存區(qū)
- 保存目錄結(jié)構(gòu)(生成tree對(duì)象)
- 提交目錄結(jié)構(gòu) (指定上一個(gè)提交的hash并生成commit對(duì)象)
- 更新分支(更新分支指向的hash)
使用porcelain命令的話是非常簡(jiǎn)單的
git add <file>
git commit -m "commit message"
如果使用plumbing命令就會(huì)復(fù)雜很多,但是可以更好地理解其背后的工作原理
git hash-object -w <file>
git update-index <file>
git write-tree
echo "commit message" | git commit-tree writetreehash -p <last commit hash>
echo <commit hash> .git/refs/heads/<branchname>
Git分支和HEAD
通過前文的內(nèi)容不難發(fā)現(xiàn),每次生成的commit對(duì)象會(huì)包含上一個(gè)commit對(duì)象的hash,即當(dāng)前的commit包含上一個(gè)commit的指針,許多個(gè)commit對(duì)象串聯(lián)起來就形成了分支
所以,Git的分支本質(zhì)上是指向commit對(duì)象的可變指針
而HEAD代表當(dāng)前commit的指向,.git/refs/heads/<branchname>文件的內(nèi)容就是該commit對(duì)象的hash