Git 實(shí)質(zhì)
Git 是一個(gè)內(nèi)容尋址文件系統(tǒng)忘苛。 這意味著蝉娜,Git 的 核心部分是一個(gè)簡(jiǎn)單的鍵值對(duì)數(shù)據(jù)庫(kù)。 你可以向該數(shù)據(jù)庫(kù)插入任意類型的內(nèi)容扎唾,它會(huì)返回一個(gè)鍵值召川,通過(guò)該鍵值可以在任意時(shí)刻再次檢索(retrieve)該內(nèi)容。
底層命令
涉及到的底層命令有:
命令 | 解釋 |
---|---|
cat-file | 獲取指定 Git 對(duì)象的信息 |
ls-files | 顯示暫存區(qū)的信息 |
ls-tree | 顯示指定結(jié)點(diǎn)的 tree 對(duì)象 |
hash-object | 用于計(jì)算文件的 sha-1 值 |
update-index | 更新暫存區(qū) |
write-tree | 將暫存區(qū)中的文件寫成 tree 對(duì)象 |
read-tree | 把樹(shù)對(duì)象讀入暫存區(qū) |
commit-tree | 根據(jù)指定的樹(shù)對(duì)象創(chuàng)建一個(gè)提交對(duì)象 |
rev-parse | 解析分支名或標(biāo)簽名對(duì)應(yīng)的提交對(duì)象的 sha-1 值 |
Git 所做的實(shí)質(zhì)工作 —— 將被改寫的文件保存為數(shù)據(jù)對(duì)象(hash-object)胸遇,更新暫存區(qū) ( update-index )荧呐,記錄樹(shù)對(duì)象(write-tree),最后創(chuàng)建一個(gè)指明了頂層樹(shù)對(duì)象和父提交的提交對(duì)象(commit-tree)纸镊。
ls-tree
用于顯示指定結(jié)點(diǎn)對(duì)應(yīng)的 tree 對(duì)象中的內(nèi)容倍阐。
它會(huì)展示所有的文件(即 blob 對(duì)象)。
$ git ls-tree -r HEAD
100644 blob 34f7ae03cc475d78515719c5b6f8c34e46002f7f aa
100644 blob c93ab02d540e388e1b83e23bbd8b49cb97c4b4ce bb
100644 blob 837df2b7ed69104790e279524d51880324064492 cc
100644 blob 9b5cf45dcf51d69998fc7cdab86ea8afb2905047 cid.java
100644 blob 79f6e4824f9578f237633353e8d6939c8bffcca1 dd.txt
100644 blob 7f1021754129446ecef3fecab549015ccbe5e59f ee
100644 blob f0e0aa843620d42bb1e72f4949001d0ca4173dc1 i/i.txt
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 i/ii/xx.txt
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 new
100644 blob 59f2c499c12c1385dd0ff1131a50820e7f6a618c test.html
100644 blob d6459e005434a49a66a3ddec92279a86160ad71f xxx.xxx
從輸出可以看出逗威,其中有幾個(gè)文件是子目錄下的文件峰搪,它也會(huì)直接列出來(lái)。
rev-parse
解析分支凯旭、標(biāo)簽等指向的提交對(duì)象的 sha-1 值概耻。
如:
$ git rev-parse master
9b335a39f65dff232f239c08baeeb9f7d27f193a
$ git tag t1
$ git rev-parse t1
9b335a39f65dff232f239c08baeeb9f7d27f193a
$ git rev-parse HEAD
9b335a39f65dff232f239c08baeeb9f7d27f193a
master 與 HEAD 為分支名, t1 為標(biāo)簽名罐呼。
ls-files
用于獲取暫存區(qū)的信息鞠柄。
其常用的幾個(gè)選項(xiàng)有:
-c : 在輸出中顯示暫存的文件。默認(rèn)值嫉柴。此選項(xiàng)只會(huì)輸出文件名厌杜。
--stage : 顯示文件的詳細(xì)信息。包括文件名计螺,sha-1 值夯尽,文件模式等。
$ git ls-files --stage
100644 6b20baa0a072d5c9578e024942c6ce1d42cf5a2a 0 a.txt
100644 24e27b2a30edadba619d26e2d3662ec307cdddb2 0 test.html
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 xx
上述命令顯示當(dāng)前暫存區(qū)中暫存的有三個(gè)文件登馒。
hash-object 命令
計(jì)算文件的 sha-1 值呐萌,并可選地根據(jù)該文件創(chuàng)建一個(gè) blob 對(duì)象。
注意:該命令只適用于文件谊娇,不能用于目錄肺孤。
選項(xiàng) | 解釋 |
---|---|
-w | 不加該選項(xiàng)則不會(huì)生成 blob 對(duì)象,反之則會(huì)生成 |
--stdin | hash-object 命令會(huì)從標(biāo)準(zhǔn)輸入讀取內(nèi)容 |
-- <path> | 指定 hash-object 操作的文件的路徑 |
注意:如果使用 --stdin 選項(xiàng)济欢,就需要指定文件路徑赠堵。
如:
$ find .git/objects/ -type f
$ git hash-object -- ../appid.txt
a5a06de3b66ee3a609def184e78873c99d6b1221
$ find .git/objects/ -type f
$ git hash-object -w -- ../appid.txt
a5a06de3b66ee3a609def184e78873c99d6b1221
$ find .git/objects/ -type f
.git/objects//a5/a06de3b66ee3a609def184e78873c99d6b1221
find 命令用于查找指定目錄( .git/objects )目錄下的所有文件( 通過(guò) -type f 限定 )。
從上可以看出法褥,起初 objects 目錄下并沒(méi)有文件茫叭。hash-object 不加 -w 操作后,目錄下依舊沒(méi)有文件半等,只不過(guò)輸出了指定文件的 sha-1 值揍愁。
加上 -w 選項(xiàng)后呐萨,objects 目錄下多了一個(gè)父目錄為 a5 的文件。Git 會(huì)截取 sha-1 值的前兩個(gè)字符生成目錄名莽囤,將剩余的部分做為文件名谬擦。因此,objects 目錄下會(huì)有一個(gè) a5 目錄朽缎。
cat-file
獲取指定的 Git 對(duì)象的內(nèi)容等信息惨远。
其常用的選項(xiàng)如下:
選項(xiàng) | 解釋 |
---|---|
-p | 后跟 sha-1 值,則 cat-file 會(huì)返回該 sha-1 值的內(nèi)容 |
-t | 獲取指定對(duì)象的類型 |
-s | 獲取指定對(duì)象的大小 |
在使用時(shí)话肖,可以通過(guò) > <path>
后綴北秽,將 cat-file 的輸出內(nèi)容輸出到指定的文件中。如 git cat-file -p bf43b62de7d99fa2c427dcc82257d81431ba816f > xxx.xxx
就會(huì)將內(nèi)容輸出到同目錄下的 xxx.xxx 文件中最筒。
如 -p 選項(xiàng)的使用:
$ git cat-file -p 9daeafb9864cf43055ae93beb0afd6c7d144bfa4
test
其中 test 為該對(duì)象的內(nèi)容贺氓。
如 -t 選項(xiàng)的使用:
$ git cat-file -t ee1a8365ec2baa3e5a97cb6e44d8d8b5c553fb30
blob
$ git cat-file -t 8bdb01d84c5ad6456acb4d8df8d82a0db5214972
tree
$ git cat-file -t bf43b62de7d99fa2c427dcc82257d81431ba816f
commit
Git 中一共有三種對(duì)象 :commit , tree 與 blob ,所以 -t 只會(huì)輸出這三個(gè)值中的一個(gè)床蜘。
update-index
該選項(xiàng)用于更新暫存區(qū)辙培。
git add
與git rm
等命令都是對(duì)該選項(xiàng)的封裝。
其常用的選項(xiàng)有:
選項(xiàng) | 解釋 |
---|---|
<無(wú)> | 更新已暫存的文件 |
--add | 將未跟蹤的文件添加到暫存區(qū)中 |
--cacheinfo | 將已保存到本地倉(cāng)庫(kù)的文件加入到暫存區(qū)中 |
--remove | 將已刪除的文件從暫存區(qū)中移除 |
各個(gè)選項(xiàng)具體說(shuō)明如下:
-
--add : 如果文件從來(lái)沒(méi)有添加到暫存區(qū)中悄泥,該選項(xiàng)表示將文件第一次添加到暫存區(qū)中虏冻。
$ git ls-files --stag 100644 6b20baa0a072d5c9578e024942c6ce1d42cf5a2a 0 a.txt $ git hash-object yy e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 $ git update-index --add yy $ git ls-files --stage 100644 6b20baa0a072d5c9578e024942c6ce1d42cf5a2a 0 a.txt 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 yy
初始時(shí)肤粱,暫存區(qū)中只有一個(gè) a.txt 文件弹囚。使用 git update-index --add 命令后,暫存區(qū)中多了一個(gè) yy 文件领曼,并且 yy 文件與 update-index 指定的文件的 sha-1 值相同鸥鹉,所以該命令可以將指定的文件添加到暫存區(qū)中。
再查看 .git/objects 目錄下庶骄,可以發(fā)現(xiàn)多了一個(gè)文件(即 yy 文件)毁渗。
-
--cacheinfo:如果要保存的文件已位于 git 倉(cāng)庫(kù)中,需要使用該選項(xiàng)单刁。
$ vim yy $ git hash-object -w -- yy 9be86ae7054f7c477afabe3d971046032851b574 $ git update-index --cacheinfo 100644 9be86ae7054f7c477afabe3d971046032851b574 yy
首先修改 yy 文件灸异,并通過(guò) hash-object 將文件存儲(chǔ)到 git 目錄中。所以更新暫存區(qū)時(shí)不需要 --add 選項(xiàng)羔飞。
在使用 --cacheinfo 修改暫存區(qū)時(shí)肺樟,指定了文件的 sha-1 值,同時(shí)也指定了文件模式為 100644(即普通文件)逻淌。
-
--remove:將指定的文件從索引中刪除么伯,該文件必須已經(jīng)從工作目錄中刪除。
$ git ls-files --stage 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 xx 100644 9be86ae7054f7c477afabe3d971046032851b574 0 yy $ rm xx $ git update-index --remove -- xx $ git ls-files --stage 100644 9be86ae7054f7c477afabe3d971046032851b574 0 yy $ git update-index --remove -- yy $ git ls-files --stage 100644 9be86ae7054f7c477afabe3d971046032851b574 0 yy
不刪除文件直接使用 --remove 選項(xiàng)時(shí)卡儒,并不會(huì)刪除相應(yīng)的記錄(如上例中的 yy 文件)田柔。
write-tree
將暫存區(qū)內(nèi)容寫入一個(gè)樹(shù)對(duì)象俐巴。
通過(guò) update-index 更新暫存區(qū)后,基本的文件并沒(méi)有生成成 tree 對(duì)象硬爆。而 write-tree 可以根據(jù)暫存區(qū)生成一個(gè)樹(shù)對(duì)象欣舵。
$ git write-tree
348eb10c8ce3e5a4561a38aa7e76e61013e153e1
$ git cat-file -t 348eb10c8ce3e5a4561a38aa7e76e61013e153e1
tree
read-tree
將指定的樹(shù)對(duì)象讀入到暫存區(qū)中。
通過(guò) --prefix=name
指定讀入的樹(shù)對(duì)象的名字摆屯。
首先將一個(gè)樹(shù)對(duì)象讀入暫存區(qū)中邻遏,然后通過(guò) write-tree
將暫存區(qū)中的內(nèi)容寫入一個(gè)樹(shù)對(duì)象中。則新生成的樹(shù)對(duì)象會(huì)包含一個(gè)指向通過(guò) read-tree
讀入的樹(shù)對(duì)象的指針虐骑。
$ git read-tree --prefix=namep 348eb10c8ce3e5a4561a38aa7e76e61013e153e1
執(zhí)行上述命令后准验,如果將當(dāng)前暫存區(qū)生成一個(gè) tree 對(duì)象后,該對(duì)象會(huì)包含一個(gè)名為 namep 的指針廷没,它指向的是通過(guò) read-tree
讀入的樹(shù)對(duì)象糊饱。
commit-tree
根據(jù)指定的樹(shù)對(duì)象創(chuàng)建一個(gè)提交對(duì)象。
通過(guò) -p sha-1
指定當(dāng)前對(duì)象的父對(duì)象
$ echo "first commit" | git commit-tree 2aabf73883cc35cef05f3c7919274da2aaebf674
cbc41af8ae9347c21299005f40ce998e58ab357e
$ echo "third" | git commit-tree fa197580163fc711a76726db35488f433968699c -p cbc41af8ae9347c21299005f40ce998e58ab357e
7e0792c1e2b634b4721edc6e3ff6f206d18298a2
echo 指定的是本次提交的說(shuō)明颠黎。
上述命令新建了兩個(gè) commit 對(duì)象另锋,并將第二個(gè) commit 對(duì)象的父結(jié)點(diǎn)指向第一個(gè)對(duì)象。
當(dāng)在第二個(gè) commit 對(duì)象處建立分支后狭归,運(yùn)行 git log 命令夭坪,可以發(fā)現(xiàn)有兩條提交記錄。
目錄
Git 有幾個(gè)非常重要的文件:
objects 目錄存儲(chǔ)所有數(shù)據(jù)內(nèi)容过椎;
-
refs 目錄存儲(chǔ)指向數(shù)據(jù)(分支)的提交對(duì)象的指針室梅。
refs/heads 存儲(chǔ)的是當(dāng)前的是各個(gè)分支,每一個(gè)文件記錄了該分支最后一個(gè)提交結(jié)點(diǎn)的 sha-1 值疚宇。
refs/tags 目錄下記錄的是各個(gè)標(biāo)簽亡鼠。
refs/remotes 存儲(chǔ)當(dāng)前倉(cāng)庫(kù)配置的遠(yuǎn)程服務(wù)器。其每一個(gè)子目錄就對(duì)應(yīng)著一個(gè)遠(yuǎn)程倉(cāng)庫(kù) —— 子目錄名字就是通過(guò)
git remote add <別名> url
中的別名敷待。子目錄下的各個(gè)文件指的是遠(yuǎn)程倉(cāng)庫(kù)的各個(gè)分支间涵。
HEAD 文件指示目前被檢出的分支;
index 文件保存暫存區(qū)信息榜揖。
-
config 文件保存 --local 的配置信息勾哩。例如我們使用
git remote add <別名> url
命令時(shí),會(huì)在 config 文件中添加如下信息举哟。其具體信息可參考 remote一節(jié):[remote "demo"] url = https://github.com/birdandcliff/gitdemo.git fetch = +refs/heads/re:refs/remotes/demo/devlocal