Git Step by Step – (4) 探索.git目錄

前面一篇文章介紹了Git對象模型,接下來咱們就進入".git"目錄看看到底有什麼東西,目錄中哪些東西又跟Git對象模型相關。結合這個目錄,咱們將進一步瞭解Git的工做原理。git

.git目錄

下面就開始進入.git目錄了,經過"ls"命令能夠看到.git目錄中的文件和子目錄:web

對於這些文件和目錄,下面給出了一些基本的描述。在後面後有logs、objects、refs、index和HEAD更詳細的介紹shell

  • (D) hooks:這個目錄存放一些shell腳本,能夠設置特定的git命令後出發相應的腳本;在搭建gitweb系統或其餘git託管系統會常常用到hook script
  • (D) info:包含倉庫的一些信息
  • (D) logs:保存全部更新的引用記錄(會在後面介紹引用)
  • (D) objects:全部的Git對象都會存放在這個目錄中,對象的SHA1哈希值的前兩位是文件夾名稱,後38位做爲對象文件名
  • (D) refs:這個目錄通常包括三個子文件夾:heads、remotes和tags,heads中的文件標識了項目中的各個分支指向的當前commit
  • (F) COMMIT_EDITMSG:保存最新的commit message,Git系統不會用到這個文件,只是給用戶一個參考
  • (F) config:這個是Git倉庫的配置文件
  • (F) description:倉庫的描述信息,主要給gitweb等git託管系統使用
  • (F) index:這個文件就是咱們前面文章提到的暫存區(stage),是一個二進制文件
  • (F) HEAD:這個文件包含了一個當前分支(branch)的引用,經過這個文件Git能夠獲得下一次commit的parent
  • (F) ORIG_HEAD:HEAD指針的前一個狀態

 

Git引用

Git中的引用是個很是重要的概念,對於理解分支(branch)、HEAD指針以及reflog很是有幫助。app

Git系統中的分支名、遠程分支名、tag等都是指向某個commit的引用。好比master分支,origin/master遠程分支,命名爲V1.0.0.0的tag等都是引用,它們經過保存某個commit的SHA1哈希值指向某個commit。spa

從新認識HEAD

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

看過第二篇文章的同窗必定還記得咱們是怎麼根據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

  

Git索引(index)

前面文章咱們也提到過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 diff:比較WorkSpace和stage,add以前有diff輸出;add以後沒有diff輸出
  • git diff HEAD:比較WorkSpace和repo,add以前以後都有diff輸出
  • git diff --cached:比較stage和repo,add以前沒有diff輸出;add以後有diff輸出

 

對象的存儲

前面提到全部的Git對象都會存放在".git/objects"目錄中,對象SHA1哈希值的前兩位是文件夾名稱,後38位做爲對象文件名。

因此,咱們前面提到的master上最新的commit對象的哈希值是"4ea6c317a67e73b0befcb83c36b915c1481f2efe",那麼這個對象會被存儲在".git/objects/4e/a6c317a67e73b0befcb83c36b915c1481f2efe"。進入objects目錄後,咱們確實找到了這個文件。

在Git系統中有兩種對象存儲的方式,鬆散對象存儲和打包對象存儲。

鬆散對象(loose object)

鬆散對象存儲就是前面提到的,每個對象都被寫入一個單獨文件中,對象SHA1哈希值的前兩位是文件夾名稱,後38位做爲對象文件名。

打包對象(packed object)

對於鬆散存儲,把每一個文件的每一個版本都做爲一個單獨的對象,它的效率比較低,並且浪費空間。因此就有了經過打包文件(packfile)的存儲方式。

Git使用打包文件(packfile)去節省空間.。在這個格式中,,Git只會保存第二個文件中改變了的部分,而後用一個指針指向類似的那個文件。

通常Git系統會自動完成打包的工做,在已經發生過打包的Git倉庫中,".git/objects/pack"目錄下會成對出現不少"pack-***.idx"和"pack-***.pack"文件。關於打包就介紹這麼多了,暫時尚未去研究兩個文件的內容和原理。

 

總結

這篇文章結合了前一篇的Git對象模型,探索了.git文件夾,經過引用,reflog以及索引的介紹,相信會對Git的工做原理有了更多的瞭解。

經過這兩篇文章介紹下來,感受對謎同樣的Git也慢慢的熟悉了起來。

相關文章
相關標籤/搜索