標籤:

Git由淺入深之存儲原理

本來計劃本篇介紹Git分支的相關知識點與操作,但是準備的過程中發現涉及到很多內部存儲原理,決定先介紹一下Git存儲原理,明白了這些,有助於理解後續內容,對Git的使用也會有很大幫助。

Git存儲目錄結構

在初始化項目倉庫時(git clone 或git init),Git會在根目錄下創建一個.git目錄,其下存放著Git操作和存儲相關的內容,該目錄結構大致如下:

如圖中所述:

  • HEAD文件指向當前分支;index文件存儲著暫存區的內容信息;

  • refs目錄存儲著所有分支指向各自提交對象的指針;

  • objects目錄存儲著Git資料庫的所有內容;

  • config文件包含項目的配置信息;

  • info目錄下的exclude文件包含項目全局忽略匹配模式,與.gitignore文件互補;

  • hooks目錄則存放項目的客戶端或服務端鉤子腳本。

註:其中的ORIG_HEAD記錄的是在進行極端(drastic)操作(如合併merge,回退reset等)時,此操作之前HEAD所指向的位置,便於我們在發生毀滅性失誤時進行回退,如使用

git reset --hard ORIG_HEAD指令可以回退到危險操作之前的狀態,但是對於正常的提交操作,該指針是不會變化的。在1.8.5版本以後,Git使用了鏈表記錄HEAD的所有移動軌跡,

可以使用git reflog查看,使用git reset HEAD@{num}方式可以回退到指定版本,這也是之後介紹Git數據恢復將要介紹的一個指令,推薦使用這種方式替代ORIG_HEAD方式。

更多信息可參考此處

Git存儲

Git是一個內容定址文件系統(content-addressed filesystem),其存儲內容都是通過內容地址維護,可以把它理解成一個鍵值對存儲方式:即給定一個存儲文件,該系統根據文件信息和內容,使用SHA-1演算法計算,返回一個由40個十六進位字元組成的字元串,之後只需要通過該字元串即可訪問該文件,這個字元串就是Git中通常所說的校驗和。

內容定址

在了解Git內部存儲原理之前我們先了解下內容定址:

When being contrasted with content-addressed storage, a typical local or networked

storage device is referred to as location-addressed. In a location-addressed storage device,

each element of data is stored onto the physical medium, and its location recorded for later use.

The storage device often keeps a list, or directory, of these locations.

When a future request is made for a particular item, the request includes only the

location (for example, path and file names) of the data. The storage device can then use this

information to locate the data on the physical medium, and retrieve it. When new information is

written into a location-addressed device, it is simply stored in some available free space,

without regard to its content.

In contrast, when information is stored into a CAS system, the system will record a content address,

which is an identifier uniquely and permanently linked to the information content itself.

A request to retrieve information from a CAS system must provide the content identifier,

from which the system can determine the physical location of the data and retrieve it.

Because the identifiers are based on content, any change to a data element will necessarily

change its content address.

談到內容定址,有必要了解一下的就是本地定址,或者叫物理定址。對於物理定址系統,其所有數據存儲在物理媒介的可用空間,與其內容無關,系統記錄其物理地址(physical location)供隨後使用,這些物理地址通常通過使用一個列表或者目錄來維護,當再次請求特定數據時,需要使用其物理地址,如路徑和文件名。

而對於一個內容定址系統,系統記錄的是一個內容地址(content-address),該內容地址是對應數據的一個唯一且持久的識別符,它是通過加密哈希演算法(如,SHA-1或MD5)計算出來的一串值,當我們需要數據時,提供該內容地址,系統即可通過該地址獲取數據的物理地址,返回數據;同時,對於數據的任何變更都將導致內容地址發生變化。

Git中所有的這些內容地址都存儲在根目錄下的.git/objects/文件夾下,他們按照樹形結構分類存放,每個內容地址的前兩位作為枝幹分別建立子目錄,該目錄下存在所有同類型(開頭相同)的內容地址。

Git對象

下面需要介紹Git存儲相關的幾個對象概念:提交對象,tree對象,blob對象。

git cat-file

Git提供cat-file指令支持將校驗和解密開,如下校驗和是」hello」經過SHA-1演算法加密後得到的,還原後:

提交對象

如下,我們初始化一個本地倉庫,並進入Git存儲目錄.git/objects,這裡除了默認創建的pack和info子目錄,並沒有任何存儲文件或目錄,因為當前倉庫是空的:

我們在倉庫內新建一個文件,並在文件中輸入」hello」內容:

我們看到此次提交的commitId是debaa7e47534af51ef3c10117200e65dd4c658ef,我們根據校驗和的前兩位,即de在.git/objects/下尋找de目錄,進入該目錄:

該值就是之前的提交commitId值,該值也是此次提交對象的校驗和,解密如下:

在上圖中,我們並沒有使用完整的校驗和,因為Git使用SHA-1演算法計算校驗和,前面8位字元完全重疊的情況幾乎不存在,而且對比後發現解密後的內容和之前查看的提交歷史內容吻合,我們再次變更內容,新增幾個文件:

再次根據新提交的commitId686b5e1cbfa83ed0244558d006ee01543fde83f3,解密:

和初始提交對象相比多了一行parent debaa7e47534af51ef3c10117200e65dd4c658ef,該行對應存儲的校驗和代表的是此次提交的父提交對象,此處即之前的初始提交對象校驗和。

通常提交對象包括三部分內容:

  • 提交信息:包括提交者姓名/郵箱和提交備註等相關信息;
  • tree對象的校驗和:關於tree對象在下一節詳細介紹;

  • 父提交對象的校驗和(可能存在多個。如分支發生合併時,提交的父提交可能有多個)

tree對象

解密上例中的tree對象校驗和:

我們發現此tree對象包含三行,前兩行是兩個blob校驗和,指向單個文件,第三行的校驗和是對應一個tree對象,指向一個spa目錄。

還原第一行的blob對象:

可以發現blob校驗和就代表了該文件的內容;再看二級tree對象校驗和解密後:

和一級tree對象類似,其包含了一個blob校驗和,指向spa目錄下的test.js,當然這個校驗和代表的就是test.js文件的內容:

現在,我們知道一個tree對象可以包含:

  • 若干tree對象校驗和: 指向當前目錄的一個子目錄;
  • 若干blob校驗和:指向一個文件

註:這裡的blob說明Git是通過創建Blob對象(二進位方式)存儲數據。

blob對象

BLOB (binary large object),二進位大對象,是一個可以存儲二進位文件的容器,Git中所有數據都是通過該方式存儲。

本篇對Git內部存儲原理作了介紹,讀完相信會有一定的認識,下一篇介紹Git分支的操作相關。

作者:驚鴻三世

鏈接:Git由淺入深之存儲原理

版權歸作者所有,轉載請註明出處

推薦閱讀:

如何搭建私有可協作的 Git 伺服器
一個奇怪的git問題,求大家幫忙分析一下?
在使用git的過程中 為什麼要是使用命令行?
.Net控制Git.exe進程交互遇到的問題?

TAG:Git | 版本控制 |