Git 是如何存儲文件的


01、存儲方式

Git 從核心上來看不過是簡單地存儲鍵值對(key-value)。它允許插入任意類型的內容,並會返回一個鍵值,通過該鍵值可以在任何時候再取出該內容。

Git 存儲數據內容的方式,為每份內容生成一個文件,取得該內容與頭信息的 SHA-1 校驗和,創建以該校驗和前兩個字符為名稱的子目錄,並以 (校驗和) 剩下 38 個字符為文件命名 (保存至子目錄下)。

寫入對象
$ echo 'version 2' > test.txt $ git hash-object -w test.txt 取回對象 $ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4 返回對象的類型 $ git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a

02、Git 對象

數據對象 blob object

樹對象 tree object
解決文件名的保存問題,允許我們將多個文件組織到一起,Git 以一種類似於 UNIX 文件系統的方式存儲內容,但作了些許簡化。

所有內容均以樹對象和數據對象的形式存儲,其中樹對象對應了 UNIX 中的目錄項,數據對象則大致上對應了 inodes 或文件內容。 一個樹對象包含了一條或多條樹對象記錄(tree entry),每條記錄含有一個指向數據對象或者子樹對象的 SHA-1 指針,以及相應的模式、類型、文件名信息。

$ git cat-file -p master^{tree}
100644 blob a906cb2a4a904a152e80877d4088654daad0c859 README 100644 blob 8f94139338f9404f26296befa88755fc2598c289 Rakefile 040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0 lib (這是一個文件夾)

你可以自己創建 tree 。通常 Git 根據你的暫存區域或 index 來創建並寫入一個 tree 。因此要創建一個 tree 對象的話首先要通過將一些文件暫存從而創建一個 index 。

$ git write-tree  $ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579

image

commit 對象
commit 對象有格式很簡單:指明了該時間點項目快照的頂層樹對象、作者/提交者信息(從 Git 設置的 user.name 和 user.email中獲得)以及當前時間戳、一個空行,以及提交注釋信息。

每一個 commit 對象都指向了你創建的樹對象快照。

當你執行git add, git commit, Git其實就是保存修改了的文件的 blob,更新索引,創建 tree 對象,最后創建 commit 對象,這些 commit 對象指向了頂層 tree 對象以及先前的 commit 對象。

這三類 Git 對象 ── blob,tree 以及 commit ── 都各自以文件的方式保存在 .git/objects 目錄下。

image

03、對象存儲

Git是如何存儲對象的?

構造文件頭

對象類型+空格+數據內容的長度+空字節(null byte)

>> header = "blob #{content.length}\0" => "blob 16\000"

計算SHA-1 校驗和

Git 將文件頭與原始數據內容拼接起來,並計算拼接后的新內容的 SHA-1 校驗和:

>> store = header + content
=> "blob 16\000what is up, doc?" >> require 'digest/sha1' => true >> sha1 = Digest::SHA1.hexdigest(store) => "bd9dbf5aae1a3862dd1526723246b20206e5fc37"

用 zlib 對數據內容進行壓縮

在 Ruby 中可以用 zlib 庫來實現。首先需要導入該庫,然后用 Zlib::Deflate.deflate() 對數據進行壓縮:

>> require 'zlib'
=> true >> zlib_content = Zlib::Deflate.deflate(store) => "x\234K\312\311OR04c(\317H,Q\310,V(-\320QH\311O\266\a\000_\034\a\235"

將用 zlib 壓縮后的內容寫入磁盤

需要指定保存對象的路徑 (SHA-1 值的頭兩個字符作為子目錄名稱,剩余 38 個字符作為文件名保存至該子目錄中)。

>> path = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38] => ".git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37" >> require 'fileutils' => true >> FileUtils.mkdir_p(File.dirname(path)) => ".git/objects/bd" >> File.open(path, 'w') { |f| f.write zlib_content } => 32

所有的 Git 對象都以這種方式存儲,惟一的區別是類型不同 ── 除了字符串 blob,文件頭起始內容還可以是 commit 或 tree 。不過雖然 blob 幾乎可以是任意內容,commit 和 tree 的數據卻是有固定格式的。

04、參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM