這一章將詳細(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 對象和基對象必須位于同一打包文件;