鍍金池/ 教程/ Java/ 打包文件
儲藏
Git 樹名
子模塊
使用 Git Grep 進(jìn)行搜索
建立一個私有倉庫
追蹤分支
Git 的撤消操作 - 重置,簽出和撤消
Git 和 Email
定制 Git
查看 Git 對象
Git 是如何存儲對象的
Git 標(biāo)簽
交互式 rebase
獲得一個 Git 倉庫
高級分支與合并
Git 對象模型
Git 日志
建立一個公共倉庫
分布式的工作流程
交互式添加
維護(hù) Git
安裝與初始化
安裝 Git
更底層的 Git
歡迎使用 Git
查找問題的利器 - Git Blame
找回丟失的對象
比較提交
創(chuàng)建新的空分支
查找問題的利器 - Git Bisect
Git 引用
忽略某些文件
傳輸協(xié)議
打包文件
Git 索引
rebase
正常的工作流程
分支合并
Git 目錄與工作目錄

打包文件

這一章將詳細(xì)描述打包文件(packfile)和打包文件索引(packfile index)的格式。

打包文件索引

首先,我們來看一下打包文件索引,基本上它只是一系列指向打包文件內(nèi)位置的書簽。

打包文件索引有兩個版本。版本 1 的格式用于 Git 1.6 版本之前,版本 2 的格式用于 Git 1.6 及以后的版本。 但是版本 2 可以被 Git 1.5.2 及以上的 Git 讀取,同時也被后向移植(backport)到了 1.4.4.5 版本。

版本 2 包含了每個對象的 CRC 校驗值,因此在重打包的過程中,壓縮過的對象可以直接進(jìn)行包間拷貝(from pack to pack)而不用擔(dān)心數(shù)據(jù)損壞。版本 2 的打包文件索引同時亦支持大于 4G 的打包文件。

http://wiki.jikexueyuan.com/project/git-community-book/images/packfile-index.png" alt="" />

在兩個版本格式中,fanout (展開)表用于更快地查找某特定的SHA值在索引文件中的位置。offset/sha1 表使用 SHA1 值進(jìn)行排序(以便于對這個表進(jìn)行二分搜索),fanout 表用一種特殊的方法指向 offset/sha1 表(因此后一個表中包含某一特定字節(jié)開頭的所有 Hash 的那一部分可以被輕易找到,而不必經(jīng)過二分搜索的 8 次迭代)。

在第 1 版中, offset(偏移)和 SHA 值存在在同一位置。但是在第 2 版中,SHA 值,CRC 值和 offset 被放在不同的表中。兩個版本的文件最后都是索引文件以及指向的打包文件的 CRC 校驗值。

很重要的一點(diǎn)是,要從打包文件中提取(extract)出一個對象,索引文件不是必不可少的。索引文件的作用是幫助用戶快速地從打包文件中提取對象。那些‘上傳打包’(upload-pack)和‘取回打包’(receive-pack)程序(譯注:實現(xiàn) push 和 fetch 協(xié)議的程序)使用打包文件格式(packfile format)去傳輸對象,但是沒有使用索引 - 索引可以在上傳或者取回打包文件之后通過掃描打包文件重新建立。

打包文件格式

打包文件格式是很簡單的。它有一個頭部(header)和一系列打包過的對象(每個都有自己的header和body),還有一個校驗尾部(trailer)。前 4 個字節(jié)是字符串‘PACK’,它用于確保你找到了打包文件的起始位置。緊接著是 4 個字節(jié)的打包文件版本號,之后的 4 個字節(jié)指出了此文件中入口(entry)的個數(shù)。你可以用下面 Ruby 程序讀出打包文件的頭部:

def read_pack_header
  sig = @session.recv(4)
  ver = @session.recv(4).unpack("N")[0]
  entries = @session.recv(4).unpack("N")[0]
  [sig, ver, entries]
end

頭部之后是一系列按照 SHA 值排序的打包對象,每一個打包對象包含了頭部和內(nèi)容。打包文件的尾部是該文件中所有(已排序) SHA 值的 SHA1 校驗值(20 字節(jié)長)(譯注:即按照排序好的順序進(jìn)行迭代 SHA1 運(yùn)算)。

http://wiki.jikexueyuan.com/project/git-community-book/images/packfile-format.png" alt="" />

對象頭部(object header)由 1 個或以上的字節(jié)按序組成,它指出了后面所跟數(shù)據(jù)的類型及展開后的尺寸。頭部的每一個字節(jié)有 7 位用于數(shù)據(jù),第 1 位用于說明頭部是否還有后續(xù)字節(jié)。如果第 1 位是‘1’,你需要再讀入 1 個字節(jié)(譯注:即下一字節(jié)仍屬于頭部),否則下一字節(jié)就是數(shù)據(jù)。第一個字節(jié)的前3位指定了數(shù)據(jù)的類型,具體含義參見下表。

(3 個位可以組合成為 8 個數(shù)。在當(dāng)前的使用中,0(000)是‘未定義’,5(101)目前未被使用。)

這里我們舉一個由兩個字節(jié)組成的頭部的例子。第 1 個字節(jié)的前 3 位說明了數(shù)據(jù)的類型是提交(commit),余下的 4 位和第 2 個字節(jié)的 7 位組成的數(shù)字是 144,說明數(shù)據(jù)展開后的長度是 144 字節(jié)。

http://wiki.jikexueyuan.com/project/git-community-book/images/packfile-logic.png" alt="" />

值得注意的一點(diǎn)是,對象頭部中包含的‘尺寸’不是后面跟著的數(shù)據(jù)的長度,而是數(shù)據(jù)展開之后的長度。因此,打包索引文件中的偏移是很有用的,有了它你不必展開每一個對象就可以得到下一個頭部的起始位置。

對于非 delta 對象,數(shù)據(jù)部分就只是 zlib 壓縮后的數(shù)據(jù)流。對于那兩種 delta 對象,數(shù)據(jù)部分包含了它所依賴的基對象(base object)以及用于重構(gòu)對象的 delta(差異)數(shù)據(jù)。數(shù)據(jù)的前 20 個字節(jié)稱為 ref-delta,它是基對象 SHA 值的前 20 個字節(jié)。ofs-delta 存儲了基對象在同一打包文件中的偏移。任何情況下,有兩個約束必須嚴(yán)格遵守:

  • delta 對象和基對象必須位于同一打包文件;

  • delta 對象和基對象的類型必須一致(即 tree 對 tree,blob 對 blob,等等)。
上一篇:忽略某些文件下一篇:交互式 rebase