Git的儲存原理

小江的学习日记發表於2024-07-27

目錄
  • Git 設計原理
    • Git vs SVN
    • Git 儲存模型
      • .git 目錄結構
      • Git 基本資料物件
      • Git 包檔案
      • Git 引用

Git 設計原理

概括的講,Git 就是一個基於快照的內容定址檔案系統。 往下慢慢看。

Git vs SVN

Git 出現前,主流版本控制系統(SVN...)一般為基於增量(delta-based)的系統,如下圖:

img

Git 則是基於快照(snapshot),即針對每一個被修改的檔案生成一個快照,沒被修改的則不再重新生成快照,如下圖:

img

直覺上講,似乎基於增量的方式要更好些?

畢竟針對被修改的檔案,Git 生成的是完全的快照,而其他系統只是生成增量檔案。沒錯,但是當需要回滾版本或者比對多個版本間的差異時,Git 只需要取出對應版本的快照檔案進行對比即可,而基於增量的系統則需要從頭開始一步步應用增量檔案來回溯,Git 的速度優勢就很明顯了。

Git 儲存模型

.git 目錄結構

當用git init 或者 git clone 獲取一個 git 倉庫時,可以發現目錄下有一個隱藏目錄。git,它的基本結構類似如下:

├── COMMIT_EDITMSG 倉庫最後一次commit的message
├── FETCH_HEAD  每個分支的最後一次commit的SHA1值
├── HEAD 記錄了HEAD指標的指向位置
├── ORIG_HEAD 針對某些危險操作,該指標記錄了上一次安全版本的HEAD指標的位置,方便回退
├── config git的相關配置
├── index 暫存區,索引檔案
├── packed-refs 已經壓縮的分支,記錄了每個分支的最後一次commit的SHA1
├── logs/ 操作日誌,包括本地遠端的
├── objects/ 物件儲存資料夾
|   ├── ... 資料夾名稱根據object的SHA1值的前2個字元確定
|   ├── ...
|   ├── info/
|   ├── pack/ 壓縮後的資料
└── refs/ 記錄本地和遠端的最後一次commit的SHA1值
    ├── heads/ 分支引用
    ├── remotes/ 遠端地址
    └── tags/ 標籤引用

這個目錄下包含了 Git 所有資訊,且都是用檔案的形式儲存,所以說 Git 是一個檔案系統。

Git 基本資料物件

  • blob(二進位制大物件):也就是前面說的基於快照儲存的檔案
  • tree:目錄,代表了 blob 物件的集合
  • commit:提交,包含了 blob、tree 的集合
  • tag:標籤物件(指 annotation 標籤),還有一種輕量標籤不記錄建立標籤人等額外資訊,不需要再單獨建立標籤物件

上述 4 種資料物件均儲存在。git/object/目錄下,git 會對每一種資料物件計算雜湊值來確定具體的儲存路徑,下面來舉個例子。

> echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4 
//40位的SHA-1雜湊值,前2位位目錄名,其他38位為檔名,儲存路徑即.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
> git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
 test content 
> git cat-file -p master^{tree} // 輸出master最新提交包含內容
100644 blob a906cb2a4a904a152e80877d4088654daad0c859      README  
100644 blob 8f94139338f9404f26296befa88755fc2598c289      Rakefile  
040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0      lib
// 包含了2個檔案的修改和1個目錄的修改
> git cat-file -p 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0
100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b      simplegit.rb    

git hash-object 命令可以用於計算檔案的雜湊值

-w 表示把將物件寫入到 git 資料庫中

--stdin 表示從標準輸入讀取內容

git cat-file 命令可以根據傳入雜湊值取出 git 儲存的物件

-p 自動判斷內容的型別

一次提交的資料結構可以用下圖來概括:

img

Git 包檔案

可能有的小夥伴透過上述方式在自己專案中嘗試時,發現在。git/objects/下找不到對應檔案,這是什麼原因呢?

可能真的不是操作出了問題,而是 Git 進行了壓縮操作。

Git 最初儲存物件時使用的時"鬆散(loose)"物件格式,即儲存在。git/objects/下。

但是,Git 會時不時(或者當你手動執行git gc 命令後)地將這些物件打包成一個稱為“包檔案(packfile)”的二進位制檔案(儲存在。git/objects/pack),以節省空間和提高效率。

Git 引用

引用類似於指標,除了 HEAD 儲存在。git/HEAD 以外,其他指標儲存在。git/refs 目錄下

  • 分支
  • HEAD:一種特殊的指標,用於指向目前所在的 commit,。git/HEAD 檔案裡儲存的就是引用的 commit 的雜湊值
  • 標籤(輕量標籤)

可以看出,所謂的引用只是一個記錄了 commit 雜湊值的檔案,非常的輕量,這也是為什麼分支/標籤的建立、刪除速度能這麼快的原因。

相關文章