1 前言
- Git使用比較靈活,達(dá)到相同結(jié)果有多種方式醒颖。
- 靠記憶不同場(chǎng)景下的命令組合妻怎,會(huì)停留在“知其然,不知其所以然”的層次泞歉。
- 只有理解Git內(nèi)部原理和Git命令的底層操作逼侦,才能深入淺出的靈活運(yùn)用Git。
2 Git內(nèi)部原理
Git是由C語言開發(fā)的一套內(nèi)容尋址文件系統(tǒng)腰耙,并在此之上提供了一個(gè)VCS用戶界面榛丢。
2.1 Git目錄結(jié)構(gòu)
使用git init
命令初始化當(dāng)前目錄,生成.git文件夾挺庞。
- 工作區(qū)晰赞、暫存區(qū)和Git倉庫
- 工作區(qū)是當(dāng)前目錄(除去.git/),所有的編輯操作都在該目錄進(jìn)行选侨。
- 暫存區(qū)對(duì)應(yīng).git/index文件掖鱼,它包含了當(dāng)前暫存區(qū)的信息,由它可生成git的tree對(duì)象援制。(
git init
執(zhí)行后并沒有產(chǎn)生.git/index戏挡,而是在首次執(zhí)行git add
命令后才生成,并把由更新文件生成的blob對(duì)象放入.git/objects/內(nèi)晨仑。)git add生成.git/index - git倉庫對(duì)應(yīng).git/褐墅,它存儲(chǔ)了項(xiàng)目的所有歷史快照拆檬,以供需要的時(shí)候使用。
- .git/目錄
.git/包含了以下目錄和文件:branches/:新版本不再使用- description:僅供GitWeb程序使用
- config:當(dāng)前項(xiàng)目的配置選項(xiàng)
- info/:不同于.gitignore文件掌栅,可配置本地的文件忽略模式,不會(huì)push到remote庫而影響其他人码泛。
- hooks/:目錄存放鉤子腳本
- objects/:目錄存儲(chǔ)所有數(shù)據(jù)內(nèi)容
- refs/:目錄存儲(chǔ)指向數(shù)據(jù)的commit對(duì)象的指針
- HEAD:文件內(nèi)容為當(dāng)前分支
- index:文件內(nèi)容為暫存區(qū)的信息
2.2 Git命令
Git包含底層命令(Plumbing)和高層命令(Procelain)猾封。
- 用戶平時(shí)使用的Git命令一般為高層命令,如add噪珊、commit晌缘、checkout等;高層命令對(duì)用戶友好痢站,便于理解和操作磷箕。
-
Git起初被設(shè)計(jì)為供VCS使用的工具集,這些工具也稱為底層命令阵难;底層命令一般不被用戶直接使用岳枷,而是被shell或腳本調(diào)用。Git部分底層命令
此處列舉幾個(gè)底層命令簡(jiǎn)要說明:
- checkout-index:Copy files from the index to the working tree.
- cat-file:Provide content or type and size information for repository objects.
- hash-object:Compute object ID and optionally creates a blob from a file.
- update-index:Register file contents in the working tree to the index.
- write-tree:Create a tree object from the current index.
- commit-tree:Create a new commit object.
2.3 Git對(duì)象
Git定義了4種對(duì)象:blob呜叫、tree空繁、commit和tag,它們都位于.git/objects/目錄下朱庆。git對(duì)象在原文件的基礎(chǔ)上增加了一個(gè)頭部盛泡,即對(duì)象內(nèi)容 = 對(duì)象頭 + 文件內(nèi)容
。這種格式無法直接通過cat
命令讀取娱颊,需要使用git cat-file
這個(gè)底層命令才能正確讀取傲诵。
對(duì)象頭的格式為:對(duì)象頭 = 對(duì)象類型 + 空格 + 數(shù)據(jù)內(nèi)容長度 + null byte
,例如一個(gè)文件內(nèi)容為“hello world”箱硕,其blob對(duì)象頭為"blob 11\000"拴竹。
- blob:工作區(qū)的文件以blob對(duì)象的形式進(jìn)入git倉庫,相當(dāng)于UNIX中的inodes或文件內(nèi)容剧罩。
- tree:tree對(duì)象包含對(duì)blob對(duì)象以及其他tree對(duì)象的引用殖熟,相當(dāng)于UNIX中的目錄。
-
commit:包含了上一次commit對(duì)象的Hash串引用斑响、該時(shí)間點(diǎn)項(xiàng)目快照的頂層tree對(duì)象的Hash串引用菱属、作者/提交者信息、時(shí)間戳舰罚、空行纽门,以及提交的注釋信息。commit营罢、tree赏陵、blob的引用關(guān)系
- tag:包含一個(gè)commit的Hash串引用饼齿、標(biāo)簽名,以及其他信息(由標(biāo)簽類型決定)蝙搔。
2.4 內(nèi)容尋址
- 依賴底層命令
git hash-object
命令缕溉,對(duì)文件內(nèi)容增加頭信息后計(jì)算hash值并返回,增加-w
參數(shù)后在git倉庫內(nèi)創(chuàng)建blob對(duì)象(blob對(duì)象 = 對(duì)象頭 + 文件內(nèi)容)吃型。 - blob對(duì)象存儲(chǔ)到git倉庫目錄(.git/objects/)時(shí)证鸥,依據(jù)40位(16進(jìn)制字符)長度的hash串指定存儲(chǔ)目錄(hash串前2位)和命名文件(hash串后38位)。例如某blob對(duì)象的hash值為
62/0d4582bfbf773ef15f9b52ac434906a3cdf9c3
勤晚,那么它在git倉庫中的路徑為.git/objects/62/0d4582bfbf773ef15f9b52ac434906a3cdf9c3
枉层。 -
Git內(nèi)容尋址本質(zhì)是:Git根據(jù)由文件內(nèi)容(增加文件頭)產(chǎn)生的Hash值來標(biāo)識(shí)和索引文件,另外進(jìn)行命令操作時(shí)沒有必要寫完整的hash串赐写,只要輸入的hash串長度是唯一可識(shí)別和索引的即可鸟蜡。根據(jù)文件內(nèi)容的hash值索引文件
- 無需考慮Hash碰撞的情況,在大型項(xiàng)目上也可以放心使用Git挺邀。因?yàn)樵诟怕噬蟂HA-1產(chǎn)生的哈希值碰撞的機(jī)會(huì)可以小到忽略揉忘。
2.5 Git版本機(jī)制
- HEAD指向當(dāng)前分支。若master是當(dāng)前分支端铛,則HEAD文件內(nèi)容為
ref: refs/heads/master
癌淮。HEAD指向當(dāng)前分支 -
分支(本地分支、遠(yuǎn)程分支沦补、遠(yuǎn)程跟蹤分支汇荐、跟蹤分支)和標(biāo)簽(tag對(duì)象)都包含了對(duì)commit對(duì)象的引用但金。master分支引用了hash為1ad0的commit對(duì)象
-
commit對(duì)象包含了上次commit對(duì)象的引用(類似單鏈表)和本次提交的頂級(jí)tree對(duì)象的引用晴及。commit對(duì)象引用了tree對(duì)象
- 每個(gè)頂級(jí)tree對(duì)象可看做是一個(gè)完整的版本诊笤。
- 通過commit對(duì)象的鏈?zhǔn)浇Y(jié)構(gòu)進(jìn)行串聯(lián),形成提交歷史和版本歷史产舞。
-
總之:git的分支和標(biāo)簽通過引用commit對(duì)象來標(biāo)注當(dāng)前分支的版本信息魂奥。git的版本歷史機(jī)制
Note:凡是對(duì)Git對(duì)象的引用,都指的是Git對(duì)象的40位長度的Hash串易猫。
2.6 引用規(guī)格(refspec)
引用規(guī)格指的是遠(yuǎn)程倉庫分支和本地分支的映射耻煤,可表示為<src>:<dst>
,這也暗示了數(shù)據(jù)流向?yàn)?code>src →
dst准颓。
- fetch和push命令
# 兩命令都包含引用規(guī)格(refspec)來指定數(shù)據(jù)流向哈蝇。
git fetch [remote repository] [remote branch]:[local branch]
git push [remote repository] [local branch]:[remote branch]
- config文件配置refspec
當(dāng)使用缺省的fetch/push命令時(shí),Git會(huì)根據(jù).git/config中的refspec配置進(jìn)行操作攘已。
- 當(dāng)通過
git remote add
命令添加一個(gè)遠(yuǎn)程分支的同時(shí)炮赦,會(huì)在.git/config文件中添加一個(gè)配置結(jié)點(diǎn)。git remote addgit fetch orgin
這個(gè)缺省命令時(shí),會(huì)拉取origin遠(yuǎn)程倉庫的所有分支剧防。 - 可通過
git log origin/master
來查看從遠(yuǎn)程倉庫fetch的master分支植锉。
# 以下三個(gè)命令是等價(jià)的,Git會(huì)把他們都擴(kuò)展為refs/remote/origin/master
git log origin/master
git log remote/origin/master
git log refs/remote/origin/master
- refspec指定分支映射
1)可通過改寫fetch行為fetch = +refs/heads/master:refs/remotes/origin/mymaster
峭拘,指定把遠(yuǎn)程的master分支映射為本地的origin/mymaster分支俊庇。
2)也可指定多個(gè)映射,一次拉取多個(gè)指定分支棚唆。
[remote "origin"]
url = git@github.com:kivihub/test.git
fetch = +refs/heads/master:refs/remotes/origin/master
fetch = +refs/heads/experiment:refs/remotes/origin/experiment
fetch = +refs/heads/qa/*:refs/remote/orgin/qa/*
3)可同時(shí)指定push的refspec
若要把本地的master分支push到遠(yuǎn)程的qa/master分支暇赤,可配置如下:
[remote "origin"]
url = git@github.com:kivihub/test.git
fetch = +refs/heads/master:refs/remotes/origin/master
fetch = +refs/heads/experiment:refs/remotes/origin/experiment
fetch = +refs/heads/qa/*:refs/remote/orgin/qa/*
push = refs/heads/master:refs/heads/qa/master
4)刪除遠(yuǎn)程分支
通過命令git push origin :master
可以刪除遠(yuǎn)程origin庫的master分支心例。因?yàn)閞efspec的格式為<src>:<dst>宵凌,通過把<src>置空表示把遠(yuǎn)程分支變?yōu)榭眨簿褪莿h除它止后。
2.7 其他
-
git gc
垃圾回收命令用于壓縮或刪除數(shù)據(jù)瞎惫,節(jié)省磁盤空間。- 將松散對(duì)象進(jìn)行打包存入packfile译株。
- 將不被任何commit引用并且已存在一段時(shí)間(數(shù)月)的對(duì)象刪除瓜喇。
參考文章
- GIT科普系列5:index in git
- 《Pro Git》相關(guān)章節(jié)內(nèi)容
2.1 Git 內(nèi)部原理 - 底層命令和高層命令
2.2 Git 內(nèi)部原理 - Git 對(duì)象
2.3 Git 內(nèi)部原理 - Git 引用
2.4 Git 內(nèi)部原理 - 包文件
2.5 Git 內(nèi)部原理 - 引用規(guī)格
2.6 Git 內(nèi)部原理 - 傳輸協(xié)議
2.7 Git 內(nèi)部原理 - 維護(hù)與數(shù)據(jù)恢復(fù)
2.8 Git 內(nèi)部原理 - 環(huán)境變量 - Git命令列表
- .gitignore和exclude