這一章將詳細描述打包文件(packfile)和打包文件索引(packfile index)的格式。
## 打包文件索引
首先, 我們來看一下打包文件索引, 基本上它只是一系列指向打包文件內位置的書簽。
打包文件索引有兩個版本. 版本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的打包文件。

在兩個版本格式中, fanout(展開)表用于更快地查找某特定的SHA值在索引文件中的位置. offset/sha1表使用SHA1值進行排序(以便于對這個表進行二分搜索), fanout表用一種特殊的方法指向offset/sha1表(因此后一個表中包含某一特定字節開頭的所有Hash的那一部分可以被輕易找到, 而不必經過二分搜索的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`存儲了基對象在同一打包文件中的偏移。 任何情況下, 有兩個約束必須嚴格遵守:
* delta對象和基對象必須位于同一打包文件;
* delta對象和基對象的類型必須一致(即tree對tree, blob對blob, 等等).
- 1. 介紹
- 歡迎使用Git
- GIT對象模型
- Git目錄 與 工作目錄
- Git索引
- 2. 第一步
- 安裝Git
- 安裝與初始化
- 3. 基本用法
- 獲得一個Git倉庫
- 正常的工作流程
- 分支與合并@基礎
- 查看歷史 -Git日志
- 比較提交 - Git Diff
- 分布式的工作流程
- Git標簽
- 4. 中級技能
- 忽略某些文件
- rebase
- 交互式rebase
- 交互式添加
- 儲藏
- Git樹名
- 追蹤分支
- 使用Git Grep進行搜索
- Git的撤消操作 - 重置, 簽出 和 撤消
- 維護Git
- 建立一個公共倉庫
- 建立一個私有倉庫
- 5. 高級技能
- 創建新的空分支
- 修改你的歷史
- 高級分支與合并
- 查找問題的利器 - Git Bisect
- 查找問題的利器 - Git Blame
- Git和Email
- 定制Git
- Git Hooks
- 找回丟失的對象
- 子模塊
- 6. Git生態體系
- Git 與之 Windows
- 使用Git進行系統部署
- 與 Subversion 集成
- 從其他代碼管理工具遷移到Git
- 圖形化的Git
- Git倉庫托管
- Git的其它用法
- Git的腳本支持
- Git 與編輯器
- 7. 原理解析
- Git是如何存儲對象的
- 查看Git對象
- Git引用
- Git索引
- 打包文件
- 更底層的Git
- 傳輸協議
- 術語表