Git 是一套內(nèi)容尋址文件系統(tǒng),那么Git是怎么進(jìn)行尋址呢爽撒?其實(shí)蕊唐,尋址無(wú)非就是查找,而Git采用HashTable的方式進(jìn)行查找简十,也就是說(shuō)檬某,Git只是通過(guò)簡(jiǎn)單的存儲(chǔ)鍵值對(duì)(key-value pair)的方式來(lái)實(shí)現(xiàn)內(nèi)容尋址的,而key就是文件(頭+內(nèi)容)的哈希值(采用sha-1的方式螟蝙,40位)恢恼,value就是經(jīng)過(guò)壓縮后的文件內(nèi)容。因此胰默,在接下來(lái)的實(shí)踐中场斑,會(huì)經(jīng)常通過(guò)40位的hash值來(lái)進(jìn)行底層命令操作,幾乎每一個(gè)底層命令都需要通過(guò)key來(lái)指定所要操作的對(duì)象牵署。
一.Git的三種對(duì)象類型
1.BLOB(binary large object)對(duì)象:BLOB對(duì)象可以存儲(chǔ)幾乎所有的文件類型
2.tree對(duì)象:用來(lái)組織BLOB對(duì)象的一種數(shù)據(jù)類型
3.commit對(duì)象:每一次的提交操作漏隐,由tree對(duì)象衍生,每一個(gè)commit對(duì)象表示一次提交奴迅,在創(chuàng)建的過(guò)程中可以指定該commit對(duì)象的父節(jié)點(diǎn)青责,這樣所有的commit操作便可以連接在一起,而這些commit對(duì)象便組成了提交樹(shù),branch只不過(guò)是這個(gè)樹(shù)中的某一個(gè)子樹(shù)罷了爽柒。
二.Git對(duì)象存儲(chǔ)方式為:key-value
key=sha1(file_header + file_content)
value=zlib(file_content)
Git 將文件頭與原始數(shù)據(jù)內(nèi)容拼接起來(lái)吴菠,并計(jì)算拼接后的新內(nèi)容的 40位的sha-1校驗(yàn)和,將該校驗(yàn)和的前2位作為object目錄中的子目錄的名稱浩村,后38位作為子目錄中的文件名做葵;然后,Git 用zlib的方式對(duì)數(shù)據(jù)內(nèi)容進(jìn)行壓縮心墅,最后將用 zlib 壓縮后的內(nèi)容寫入磁盤.
三.對(duì)象暫存(緩存)區(qū)---------->了解git add的背后.Git如何使用底層命令完成文件索引操作的!
git add命令在底層命令中被分為兩步:
1.通過(guò)git?hash-object命令將需要暫存的文件進(jìn)行key-value化轉(zhuǎn)換成Git對(duì)象酿矢,并進(jìn)行存儲(chǔ),拿到這些文件的key
git hash-object?#獲取指定文件的key怎燥,如果帶上-w選項(xiàng)瘫筐,則會(huì)將該對(duì)象的value進(jìn)行存儲(chǔ)
2.通過(guò)git? update-index命令將這些對(duì)象加入到暫存區(qū)進(jìn)行暫存,這樣便完成了Git文件的暫存操作
git update-index?#將指定的object加入索引庫(kù)铐姚,需要帶 --add選項(xiàng)
查看key對(duì)應(yīng)的文件信息:
git cat-file –p? -t key?#獲取指定key的對(duì)象信息策肝,-p打印詳細(xì)信息,-t打印對(duì)象的類型
四.實(shí)驗(yàn)驗(yàn)證-------->使用底層命令實(shí)現(xiàn)git add的操作
1)mkdir test? && cd test && git init?
2)該目錄下會(huì)生成.git目錄,查看.git/objects 目錄下面沒(méi)有文件 --------->find .git/objects -type f
Git 初始化了?objects?目錄隐绵,同時(shí)在該目錄下自動(dòng)創(chuàng)建 pack?和?info?子目錄之众,但是該目錄下沒(méi)有其他常規(guī)文件
3)將文件轉(zhuǎn)換成git對(duì)象:
解釋:
A) 參數(shù)w 指示hash-object命令存儲(chǔ)數(shù)據(jù),若不指定這個(gè)參數(shù)該命令僅僅返回鍵值
B)該命令輸出長(zhǎng)度為 40 個(gè)字符的校驗(yàn)和
C)objects?目錄下會(huì)生成一個(gè)文件,這便是 Git 存儲(chǔ)數(shù)據(jù)內(nèi)容的方式
hash-objec命令會(huì)返回該Git對(duì)象的key值,這時(shí)到.git目錄的objects目錄下會(huì)發(fā)現(xiàn),多一個(gè)19的目錄,該目錄下有一個(gè)0a18037c64c43e6b11489df4bf0b9eb6d2c9bf的文件
4).查看存儲(chǔ)文件的內(nèi)容和git對(duì)象的類型:
5)將git對(duì)象加入到暫存庫(kù)中:
--cacheinfo? 指定文件類型: 100644代表普通文件
此時(shí).git目錄下會(huì)多一個(gè)index文件,以后每次每次update-index命令執(zhí)行之后,該index文件的內(nèi)容都會(huì)發(fā)生變化.
以上git add操作的底層命令已經(jīng)完成.
五.index文件簡(jiǎn)談
index是一個(gè)索引文件依许,存放的是暫存區(qū)的整個(gè)目錄樹(shù)的信息棺禾,并且為目錄樹(shù)中的每個(gè)文件都保存了時(shí)間戳和長(zhǎng)度
1.index文件的格式為:?
Index魔數(shù)(DIRC) + 版本號(hào) + 暫存的文件個(gè)數(shù) + 每個(gè)文件的時(shí)間戳和長(zhǎng)度
2.?Index索引庫(kù)記錄從項(xiàng)目初始化到目前為止,項(xiàng)目倉(cāng)庫(kù)中所有文件最后一次修改時(shí)刻的時(shí)間戳以及對(duì)應(yīng)的長(zhǎng)度信息峭跳,因此隨著加入倉(cāng)庫(kù)中的文件不斷增多膘婶,index文件也會(huì)不斷增大.
3.git status命令,則會(huì)把每一個(gè)文件的索引信息和上次提交的索引信息進(jìn)行比較蛀醉,如果發(fā)生了變化悬襟,就會(huì)顯示出來(lái).
4.暫存操作會(huì)對(duì)每一個(gè)文件計(jì)算校驗(yàn)和(即前面提到的 SHA-1 哈希字串),然后把當(dāng)前版本的文件快照保存到 Git 倉(cāng)庫(kù)中(Git 使用 blob 類型的對(duì)象存儲(chǔ)這些快照)滞欠,并將校驗(yàn)和加入暫存區(qū)域.
六.創(chuàng)建樹(shù)節(jié)點(diǎn)
1.在Git中古胆,所有的內(nèi)容以tree或者BLOB對(duì)象進(jìn)行存儲(chǔ).,其中?tree 對(duì)象對(duì)應(yīng)于 UNIX 中的目錄筛璧,blob 對(duì)象則大致對(duì)應(yīng)于 inodes 或文件內(nèi)容.
一個(gè)單獨(dú)的tree對(duì)象包含一條或多條tree記錄逸绎,每一條記錄含有一個(gè)指向BLOB對(duì)象或子tree對(duì)象的sha-1指針(也就是一個(gè)40位的key值),并附有該對(duì)象的權(quán)限模式 夭谤、類型和文件名信息.
創(chuàng)建tree對(duì)象只是add和commit中間的一個(gè)緩沖步驟棺牧,因?yàn)閏ommit對(duì)象要根據(jù)tree對(duì)象來(lái)創(chuàng)建
2.創(chuàng)建tree對(duì)象:
解釋: cat-file -t顯示該對(duì)象的類型是tree,表明tree對(duì)象創(chuàng)建成功.至此,tree節(jié)點(diǎn)創(chuàng)建完成.
由于index暫存區(qū)包括了項(xiàng)目倉(cāng)庫(kù)中所有的文件,因此commit對(duì)象所對(duì)應(yīng)的tree對(duì)象朗儒,永遠(yuǎn)都是工作目錄的根tree對(duì)象颊乘。也就是說(shuō)参淹,每次commit,都是把工作目錄的根目錄所對(duì)應(yīng)的tree對(duì)象乏悄,鏈接給此次的commit對(duì)象浙值;而且,在Git中檩小,每個(gè)子目錄都對(duì)應(yīng)一個(gè)tree對(duì)象开呐,每個(gè)文件對(duì)應(yīng)一個(gè)BLOB對(duì)象,因此整個(gè)工作目錄對(duì)應(yīng)一棵Git對(duì)象樹(shù)规求,根節(jié)點(diǎn)就是commit對(duì)象所引用的tree節(jié)點(diǎn)筐付,而每個(gè)子文件夾又分別對(duì)應(yīng)一棵子樹(shù)。所以任何一個(gè)文件的更改阻肿,都會(huì)導(dǎo)致其上層所有父對(duì)象的更改和重新存儲(chǔ)
七.commit對(duì)象
在Git中瓦戚,每一次commit都對(duì)應(yīng)一個(gè)commit對(duì)象,而一個(gè)commit對(duì)象對(duì)應(yīng)一個(gè)tree對(duì)象,該tree對(duì)象是工作目錄的根tree對(duì)象.
1.創(chuàng)建commit對(duì)象
解釋:?
1)由于這是第一次提交丛塌,因此不需要帶上-p選項(xiàng)來(lái)指明父節(jié)點(diǎn)
2)該commit對(duì)象中包含了與之關(guān)聯(lián)的tree對(duì)象的key值较解,以及author和committer的信息
3)git log --stat key命令,該命令會(huì)打印指定commit對(duì)象之前的所有提交記錄
2.目前為止blob對(duì)象,tree對(duì)象,commit對(duì)象之間的關(guān)系:
八.提交樹(shù)---->commit tree
操作要求:在第一次提交的基礎(chǔ)上完成第二次提交和第三次提交,
第二次提交會(huì)提交ceshi的第二個(gè)版本和增加一個(gè)新的文件并提交赴邻;
第三次提交會(huì)演示在tree對(duì)象中構(gòu)造子tree對(duì)象并提交.
在下面的每一次提交中哨坪,還需要指定每一次提交的前繼提交對(duì)象,這樣commit對(duì)象便連接在一起乍楚,形成一棵提交樹(shù).
第二次提交操作:
1.修改ceshi文件和創(chuàng)建newfile文件并將文件轉(zhuǎn)變成git對(duì)象:
此時(shí).git/object目錄下會(huì)存在a5和1e目錄.
2.將git對(duì)象添加早暫存區(qū)中:
3.利用暫存區(qū)創(chuàng)建tree對(duì)象
4.根據(jù)該tree對(duì)象創(chuàng)建commit對(duì)象
5.查看commit對(duì)象的內(nèi)容:
6.目前位置git對(duì)象關(guān)系:
第三次提交(演示在tree對(duì)象中構(gòu)造子tree對(duì)象并提交):
1.將版本1中的tree對(duì)象讀取到暫存區(qū)中:
git read-tree --prefix=bak 39314df08ba40c2cd50eb0276fc80129189efaa2
解釋:在讀取的過(guò)程中,需要加--prefix選項(xiàng)届慈,否則無(wú)法成功讀取徒溪,這是因?yàn)?b>在index中相同路徑的文件只能出現(xiàn)一次,由于ceshi已經(jīng)存在于index索引庫(kù)了金顿,因此如果想把第一個(gè)版本的tree對(duì)象讀取進(jìn)來(lái)臊泌,需要將該版本的ceshi放在文件夾bak中
2.創(chuàng)建tree對(duì)象并提交commit對(duì)象:
3.查看tree對(duì)象包含的內(nèi)容:
4.git對(duì)象之間的關(guān)系: