1. 程式人生 > 其它 >探索Git內部原理

探索Git內部原理

Git是一個開源的分散式版本控制系統,是目前主流的版本控制系統,很多軟體專案都會用它做原始碼管理。Git的常用操作想必很多人都會,但是可能瞭解Git內部原理的人並不多。瞭解一些底層的東西,可以更好的幫你理清思路,遇到問題的時候也可以更好的去解決。

準備工作

在介紹Git如何儲存資料之前,我們先做一些準備工作。
首先新建一個目錄git-test,然後在這個目錄右鍵,開啟Git Bash,

然後,在Git Bash,分別執行以下命令,

$ git init  
$ echo 'a' > a.txt
$ echo 'b' > b.txt
$ git add .

至此,準備工作完成。我們可以看到,git-test目錄下現在有一個.git目錄,以及兩個txt檔案(a.txt、b.txt)。

Git如何儲存資料

.git目錄是git init後在當前目錄生成的一個管理git倉庫的目錄,這裡包含所有git操作所需要的東西。其中objects目錄下存放所有的git物件。經過上面的操作後,objects目錄是這樣的,

其中info和pack目錄是執行git init以後就已經有的,而78和61目錄分別對應著a.txt和b.txt檔案,這兩個目錄是建立了a.txt和b.txt,並執行git add .命令後才生成的。

78目錄下有一個檔案,檢視一下這個檔案的內容,執行以下命令,

$ cat .git/objects/78/981922613b2afb6025042ff6bd878ac1994e85

我們看到檔案的內容是一串亂碼,這是因為Git將資訊壓縮成二進位制檔案。Git提供了一個能夠幫助探索objects的命令:git cat-file [-t] [-p], -t可以檢視object的型別,-p可以檢視object儲存的具體內容。分別執行以下命令,

$ git cat-file -t 7898  
$ git cat-file -p 7898

blob型別的object

7898就是目錄名加上檔名的前兩位。可以看到,這個object是一個blob型別的物件,而這個物件儲存的內容,就是我們寫入到a.txt的文字。因此,上面的亂碼其實就是a.txt的內容,也就是說,這個object儲存著a.txt檔案的內容。

blob型別的object儲存的是一個檔案的內容。然後,git根據這個檔案的內容經過SHA1雜湊演算法得到對應的雜湊值(981922613b2afb6025042ff6bd878ac1994e85),作為這個object在Git倉庫中的唯一id。現在的git儲存是這樣子的,如圖:

tree型別的object

接著執行下一個命令,

$ git commit -m '第一次提交'

執行git commit命令後,objects目錄下又多出了兩個object,如圖:

首先,用git cat-file -t命令檢視f4目錄下的檔案,如圖:

可以看到,這個object的型別是tree,利用git cat-file -p命令檢視這個object的內容,如圖:

可以看到,tree型別的object儲存了一個目錄結構的快照,從左到右分別顯示了每個檔案的許可權、型別、object的id(SHA1值)、以及檔名。現在的Git倉庫是這樣子的,如圖:

commit型別的object

用同樣的方法,檢視3c目錄下的檔案,如圖:

這是一個commit型別的object,而這個object儲存了一個tree型別的object的id,以及提交的一些資訊。現在的Git倉庫是這樣子的,如圖:

分支

實際做專案都會有很多分支,git的分支資訊就儲存在/.git/refs/heads目錄下,如圖:

因為現在只有一個master分支,所以只有一個master檔案。直接開啟master這個檔案,可以看到這個檔案儲存了3c0acd6df4df30074678a2b97967a82efd9c8acf這樣一串字串,這正是上面的commit型別object的id。現在的Git倉庫是這樣子的,如圖:

HEAD

在/.git/HEAD這個檔案下,記錄內容如下:

ref: refs/heads/master

這個內容告訴Git當前修改的內容是基於哪個分支上的,我們可以理解為這是一個指標。現在的Git倉庫是這樣子的,如圖:

至此,一個完整的Git儲存結構就出來了。

Git的三個分割槽

Git有3個分割槽,分別是工作區、暫存區和版本庫。

工作區: 就是專案所在目錄(除去.git目錄),所有程式碼開發編輯都在這上面完成。

暫存區: 英文叫 stage 或 index。一般存放在 .git 目錄下的 index 檔案(.git/index)中,所以我們把暫存區有時也叫作索引(index)。

Git倉庫: 由Git object記錄著每一次提交的快照,以及鏈式結構記錄的提交變更歷史。

有了3個分割槽,整個結構如下圖:

Git的內部運作

現在,我們嘗試修改a.txt檔案,如圖:

這時候,除了工作目錄下的a.txt檔案內容有變化,暫存區和Git倉庫都是沒有變化的。接著,執行git add a.txt命令,這時/.git/objects目錄下又多了一個物件,如圖:

這個新增的object也是blob型別,對應著新修改的a.txt檔案。這時,整個結構如下圖:

最後,我們執行git commit -m '修改a.txt'命令,不出意外,/.git/objects目錄會生成兩個object,根據上面的介紹,這兩個object分別是tree和commit型別,如圖:

這時,整個結構如下圖:

從上圖可以看到,master分支已經指向新的commit object,並且新的commit object記錄著它的parent object,也就是舊的那個commit object,這使得我們可以檢視Git的提交歷史。