前面一篇文章介紹了Git對象模型,接下來咱們就進入".git"目錄看看到底有什麼東西,目錄中哪些東西又跟Git對象模型相關。結合這個目錄,咱們將進一步瞭解Git的工做原理。git
下面就開始進入.git目錄了,經過"ls"命令能夠看到.git目錄中的文件和子目錄:web
對於這些文件和目錄,下面給出了一些基本的描述。在後面後有logs、objects、refs、index和HEAD更詳細的介紹shell
Git中的引用是個很是重要的概念,對於理解分支(branch)、HEAD指針以及reflog很是有幫助。app
Git系統中的分支名、遠程分支名、tag等都是指向某個commit的引用。好比master分支,origin/master遠程分支,命名爲V1.0.0.0的tag等都是引用,它們經過保存某個commit的SHA1哈希值指向某個commit。spa
HEAD也是一個引用,通常狀況下間接指向你當前所在的分支的最新的commit上。HEAD跟Git中通常的引用不一樣,它並不包含某個commit的SHA1哈希值,而是包含當前所在的分支,因此HEAD直接指向當前所在的分支,而後間接指向當前所在分支的最新提交。3d
爲了更形象的解釋上面的描述,咱們首先查看".git/HEAD"的內容:指針
ref: refs/heads/master
這就表示HEAD是一個指向master分支的引用,而後咱們能夠根據引用路徑打開"refs/heads/master"文件,內容以下:code
4ea6c317a67e73b0befcb83c36b915c1481f2efe
根據前面一片文章的介紹,咱們經過這個哈希值查看對象的類型和內容,能夠看到這個哈希值對應一個commit,而且經過"git log"能夠發現這個commit就是master分支上最新的提交。對象
因此能夠看到,全部的內容都是環環相扣的,咱們經過HEAD找到一個當前分支,而後經過當前分支的引用找到最新的commit,而後經過commit能夠找到整個對象關係模型,看下圖:blog
直到如今咱們都沒有開始介紹分支(branch),這裏也不許備介紹分支,只是想大概展現一下引用和分支的關係。
假設咱們如今除了master分支,又建立了一個release-1.0.0.1的分支,再次查看".git/refs/heads/"目錄,能夠看到除了master文件以外,又多了一個release-1.0.0.1文件,查看給文件的內容也是一個哈希值。
經過"git show-ref --heads"命令就能夠產看全部的頭,這些都是HEAD的候選值:
根據前面的講解,這個commit就是就是release-1.0.0.1分支上最新的提交。一樣,當咱們把當前分支切換到release-1.0.0.1的時候,HEAD文件的內容也會相應的變成:
ref: refs/heads/release-1.0.0.1
看過第二篇文章的同窗必定還記得咱們是怎麼根據reflog去獲得一個commit哈希值,而後把repo退回到一個指定的狀態。
接下了,咱們進入".git/logs"文件夾,能夠看到這個文件夾也有一個HEAD文件和refs目錄,些就是記錄reflog的地方。
查看HEAD文件的內容,發現這個文件將會包含全部分支的reflog記錄:
0000000000000000000000000000000000000000 601b527296fea232c84b3661abcbff0576b1272c WilberTian <Wilber***.com> 1419759347 +0800 commit (initial): add calc.py into repo 601b527296fea232c84b3661abcbff0576b1272c c2163e267380f71373f29f922e7089abbb741772 WilberTian <Wilber***.com> 1419769538 +0800 commit: add sub function in calc.py c2163e267380f71373f29f922e7089abbb741772 4ea6c317a67e73b0befcb83c36b915c1481f2efe WilberTian <Wilber***.com> 1419771391 +0800 commit: add app.py, __init__.py and calc.py 4ea6c317a67e73b0befcb83c36b915c1481f2efe 4ea6c317a67e73b0befcb83c36b915c1481f2efe WilberTian <Wilber***.com> 1419822744 +0800 checkout: moving from master to release-1.0.0.1
進入".git/logs/refs"目錄,一樣會有master和release-1.0.0.1兩個文件,兩個文件將會保存各自分支的reflog記錄
master的內容:
0000000000000000000000000000000000000000 601b527296fea232c84b3661abcbff0576b1272c WilberTian <Wilber***.com> 1419759347 +0800 commit (initial): add calc.py into repo 601b527296fea232c84b3661abcbff0576b1272c c2163e267380f71373f29f922e7089abbb741772 WilberTian <Wilber***.com> 1419769538 +0800 commit: add sub function in calc.py c2163e267380f71373f29f922e7089abbb741772 4ea6c317a67e73b0befcb83c36b915c1481f2efe WilberTian <Wilber***.com> 1419771391 +0800 commit: add app.py, __init__.py and calc.py
release-1.0.0.1的內容:
0000000000000000000000000000000000000000 4ea6c317a67e73b0befcb83c36b915c1481f2efe WilberTian <Wilber***.com> 1419822744 +0800 branch: Created from master
前面文章咱們也提到過index/stage,就是更新的暫存區,下面就來看看index文件。
index(索引)示一個存放了已排序的路徑的二進制文件,而且每一個路徑都對應一個SHA1哈希值。在Git系統中,能夠經過"git ls-files --stage"來顯示index文件的內容:
從命令的輸出能夠看到,全部的記錄都對應倉庫中的文件(包含全路徑)。經過"git cat-file"命令查看app.py對應的哈希值,能夠看到這個哈希值就是表明app.py的blob對象。
如今咱們更新app.py文件,加上一個"div(16, 4)"的調用並經過"git add"添加到暫存區,這時發現index中app.py對象的哈希值已經變化了。
經過這個例子,咱們也能夠理解diff操做應該會有怎樣的輸出了:
前面提到全部的Git對象都會存放在".git/objects"目錄中,對象SHA1哈希值的前兩位是文件夾名稱,後38位做爲對象文件名。
因此,咱們前面提到的master上最新的commit對象的哈希值是"4ea6c317a67e73b0befcb83c36b915c1481f2efe",那麼這個對象會被存儲在".git/objects/4e/a6c317a67e73b0befcb83c36b915c1481f2efe"。進入objects目錄後,咱們確實找到了這個文件。
在Git系統中有兩種對象存儲的方式,鬆散對象存儲和打包對象存儲。
鬆散對象存儲就是前面提到的,每個對象都被寫入一個單獨文件中,對象SHA1哈希值的前兩位是文件夾名稱,後38位做爲對象文件名。
對於鬆散存儲,把每一個文件的每一個版本都做爲一個單獨的對象,它的效率比較低,並且浪費空間。因此就有了經過打包文件(packfile)的存儲方式。
Git使用打包文件(packfile)去節省空間.。在這個格式中,,Git只會保存第二個文件中改變了的部分,而後用一個指針指向類似的那個文件。
通常Git系統會自動完成打包的工做,在已經發生過打包的Git倉庫中,".git/objects/pack"目錄下會成對出現不少"pack-***.idx"和"pack-***.pack"文件。關於打包就介紹這麼多了,暫時尚未去研究兩個文件的內容和原理。
這篇文章結合了前一篇的Git對象模型,探索了.git文件夾,經過引用,reflog以及索引的介紹,相信會對Git的工做原理有了更多的瞭解。
經過這兩篇文章介紹下來,感受對謎同樣的Git也慢慢的熟悉了起來。