按照官方文檔的描述,Git是這樣定義的
- 內(nèi)容尋址(content-addressable)文件系統(tǒng),在此之上提供了一個VCS用戶界面
- Git采用HashTable的方式進(jìn)行查找铃将,通過簡單的存儲鍵值對的方式來實現(xiàn)內(nèi)容尋址择懂,key是文件頭和內(nèi)容組成的40位hash值运敢,value是壓縮過后的文件內(nèi)容
當(dāng)然耗帕,這句話寫得并不是很容易讓人明白穆端,通過查看git的目錄結(jié)構(gòu)以及細(xì)致地分析一次Git提交可以更好地幫助理解Git
.git目錄
.git目錄是Git的核心,每一個變動都會存儲在.git文件夾中仿便,Git的相關(guān)命令本質(zhì)上也是讀取.git文件夾下的內(nèi)容
.git目錄下有幾個重要的文件/文件夾
- config文件体啰,主要存儲項目的一些配置信息
- objects文件夾, 存儲git對象
- HEAD文件探越,記錄當(dāng)前的頭指針
- index文件狡赐,存儲暫存區(qū)的信息
- refs文件夾窑业, 存儲分支的指針
git對象
提交和文件是Git中的主要組成钦幔,也叫g(shù)it對象,Git中的許多命令都和git對象有關(guān)
git對象分為下面3類
git對象存儲在.git目錄下的objects文件夾中常柄,Git會將git對象壓縮成二進(jìn)制文件鲤氢,git對象的文件名即sha-1算法得到的hash值,按照2/38的形式保存(前兩位是文件夾的名稱西潘,剩下38位是文件名卷玉,這樣做可以防止文件夾的內(nèi)容過多,提高查找效率)
對于commit對象喷市,hash值也被稱為commitid
可以使用以下命令查看git對象中的內(nèi)容
git cat-file -p <hash>
通過查看三種git對象的內(nèi)容相种,不難發(fā)現(xiàn)如下的組織關(guān)系
- 每個commit的對象包含了tree和blob對象的hash
- 每個tree對象包含了blob文件的hash
- 每個blob對象是真正文件的二進(jìn)制保存
其實可以吧hash看成每個對象的指針,Git通過指針將眾多git對象串聯(lián)起來品姓,來實現(xiàn)對項目的版本控制
從Git命令看一次提交的完整過程
用戶通過Git命令讀寫.git文件夾寝并,達(dá)到獲取信息或變更版本的目的
Git一開始被設(shè)計成供VCS使用的工具集合而不是一整套用戶有好的VCS,它還包含了許多的底層命令腹备,一般被稱為plumbing命令(底層命令)衬潦,而用戶日常使用Git命令被稱為porcelain命令(高層命令),porcelain命令實際是是對plumbing命令的封裝
一次完整的提交過程會包含如下過程
- 保存二進(jìn)制對象(即生成blob對象)
- 寫入暫存區(qū)
- 保存目錄結(jié)構(gòu)(生成tree對象)
- 提交目錄結(jié)構(gòu) (指定上一個提交的hash并生成commit對象)
- 更新分支(更新分支指向的hash)
使用porcelain命令的話是非常簡單的
git add <file>
git commit -m "commit message"
如果使用plumbing命令就會復(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對象會包含上一個commit對象的hash,即當(dāng)前的commit包含上一個commit的指針友驮,許多個commit對象串聯(lián)起來就形成了分支
所以漂羊,Git的分支本質(zhì)上是指向commit對象的可變指針
而HEAD代表當(dāng)前commit的指向,.git/refs/heads/<branchname>文件的內(nèi)容就是該commit對象的hash