Git打包文件


原文: http://gitbook.liuhui998.com/7_5.html

一、打包文件索引
首先, 我們來看一下打包文件索引, 基本上它只是一系列指向打包文件內位置的書簽.
打包文件索引有兩個版本.
 版本1的格式用於Git 1.6版本之前, 版本2的格式用於Git 1.6及以后的版本. 
但是版本2可以被Git 1.5.2及以上的Git讀取, 同時也被后向移植(backport)到了1.4.4.5版本.
版本2包含了每個對象的CRC校驗值, 因此在重打包的過程中, 壓縮過的對象可以直接進行包間拷貝(from pack to pack)而不用擔心數據損壞. 版本2的打包文件索引同時亦支持大於4G的打包文件.
  
在兩個版本格式中, sha1表存儲的是對象的SHA1值,並把它們按照其SHA1值進行排序(以便於對這個表進行二分搜索),  offset表存儲的是sha1表中對應位置的對象在打包文件中的偏移值。
為了加速對象的查找,git使用了分段的思想,在打包文件中包含了一個fanout表。fanout表用一種特殊的方法指向offset/sha1表。
簡單的說應該是這樣的,fanout[0]表示的是SHA1值以0x00開頭的所有的對象中SHA1值最小的對象在offset/sha1表中的偏移;fanout[1]表示的是SHA1值以0x01開頭的所有的對象中SHA1值最小的對象在offset/sha1表中的偏移;fanout[2]表示的是SHA1值以0x02開頭的所有的對象中SHA1值最小的對象在offset/sha1表中的偏移。以此類推,fanout[254]表示的是SHA1值以0xfe開頭的所有的對象中SHA1值最小的對象在offset/sha1表中的偏移,fanout[255]表示的是SHA1值以0xff開頭的所有的對象中SHA1值最大的對象在offset/sha1表中的偏移,它同時也表示了當前offset/sha1表的大小。
因此通過SHA1值查找一個對象時,首先通過SHA1值的前兩位在fanout表中,確定它在offset/sha1表的一個區間范圍,然后再在sha1表中根據SHA1值使用二分法進行查找。這樣通過fanout表對於最壞的情況就減少了8次二分搜索迭代。
 
在第1版中, offset(偏移)和SHA值存在在同一位置. 但是在第2版中, SHA值, CRC值和offset被放在不同的表中. 兩個版本的文件最后都是索引文件以及指向的打包文件的CRC校驗值.
 
很重要的一點是, 要從打包文件中提取(extract)出一個對象, 索引文件不是必不可少的. 索引文件的作用是幫助用戶快速地從打包文件中提取對象. 那些"上傳打包"(upload-pack)和"取回打包"(receive-pack)程序(譯注: 實現push和fetch協議的程序)使用打包文件格式(packfile format)去傳輸對象, 但是沒有使用索引 .因為 索引可以在上傳或者取回打包文件之后通過掃描打包文件重新建立.
二、打包文件格式
打包文件格式是很簡單的. 它有一個頭部(header)和一系列打包過的對象(每個都有自己的header和body), 還有一個校驗尾部(trailer). 前4個字節是字符串'PACK', 它用於確保你找到了打包文件的起始位置. 緊接着是4個字節的打包文件版本號, 之后的4個字節指出了此文件中入口(entry)的個數. 你可以用下面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值排序的打包對象, 每一個打包對象包含了頭部和內容. 打包文件的尾部是該文件中所有(已排序)SHA值的SHA1校驗值(20字節長)(譯注: 即按照排序好的順序進行迭代SHA1運算).
 
 對象頭部(object header)由1個或以上的字節按序組成, 它指出了后面所跟數據的類型及展開后的尺寸. 頭部的每一個字節有7位用於數據, 第1位用於說明頭部是否還有后續字節. 如果第1位是'1', 你需要再讀入1個字節(譯注: 即下一字節仍屬於頭部), 否則下一字節就是數據. 第一個字節的前3位指定了數據的類型, 具體含義參見下表.
 
(3個位可以組合成為8個數. 在當前的使用中, 0(000)是'未定義', 5(101)目前未被使用.)
 
這里我們舉一個由兩個字節組成的頭部的例子. 第1個字節的前3位說明了數據的類型是提交(commit), 余下的4位和第2個字節的7位組成的數字是144, 說明數據展開后的長度是144字節.
 
 
值得注意的一點是, 對象頭部中包含的'尺寸'不是后面跟着的數據的長度, 而是數據展開之后的長度. 因此, 打包索引文件中的偏移是很有用的, 有了它你不必展開每一個對象就可以得到下一個頭部的起始位置.
對於非delta對象, 數據部分就只是zlib壓縮后的數據流. 對於那兩種delta對象, 數據部分包含了它所依賴的基對象(base object)以及用於重構對象的delta(差異)數據. 數據的前20個字節稱為ref-delta, 它是基對象SHA值的前20個字節. ofs-delta存儲了基對象在同一打包文件中的偏移. 任何情況下, 有兩個約束必須嚴格遵守:
1、delta對象和基對象必須位於同一打包文件;
2、delta對象和基對象的類型必須一致(即tree對tree, blob對blob, 等等).


免責聲明!

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



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