簡體中文 ▾ 主題 ▾ 最新版本 ▾ multi-pack-index 上次更新於 2.50.0

Git 物件目錄包含一個 pack 目錄,其中包含包檔案(字尾為 ".pack")和包索引(字尾為 ".idx")。包索引提供了一種查詢物件及其在包內偏移量的方法,但這些必須與包檔案成對出現。這種配對取決於檔名,因為包索引與包檔案僅在後綴上有所不同。雖然包索引為每個包檔案提供快速查詢,但隨著包檔案數量的增加,這種效能會下降,因為摘要需要檢查每個包檔案,並且我們更有可能在最近使用的包檔案中發生未命中。對於某些大型倉庫,由於儲存空間或過多的打包時間,將其重新打包成單個包檔案是不可行的。

多包索引(簡稱 MIDX)儲存物件列表及其在多個包檔案中的偏移量。它包含:

  • 包檔名列表。

  • 物件 ID 的排序列表。

  • 第 i 個物件 ID 的元資料列表,包括:

    • 一個值 j,指向第 j 個包檔案。

    • 物件在第 j 個包檔案中的偏移量。

  • 如果需要大偏移量,我們使用另一個大偏移量列表,類似於版本 2 包索引。

    • 物件在偽包順序中的可選列表(與 MIDX 點陣圖一起使用)。

因此,我們可以為任意數量的包檔案提供 O(log N) 的查詢時間。

設計細節

  • MIDX 儲存在 .git/objects/pack 目錄中名為 multi-pack-index 的檔案中。這可以儲存在備用目錄的包目錄中。它僅引用同一目錄中的包檔案。

  • 必須啟用 core.multiPackIndex 配置設定(這是預設設定)才能使用 MIDX 檔案。將其設定為 false 會阻止 Git 讀取 MIDX 檔案,即使它存在。

  • 檔案格式包含物件 ID 雜湊函式的引數,因此將來更改雜湊演算法不需要更改格式。

  • MIDX 每個物件 ID 只保留一條記錄。如果一個物件出現在多個包檔案中,則 MIDX 會選擇首選包檔案中的副本,否則從最近修改的包檔案中選擇。

  • 如果包目錄中存在未在 MIDX 中註冊的包檔案,那麼這些包檔案將被載入到 packed_git 列表和 packed_git_mru 快取中。

  • 包索引(.idx 檔案)保留在包目錄中,以便我們可以刪除 MIDX 檔案,設定 core.midx 為 false,或降級而不會丟失任何資訊。

  • MIDX 檔案格式使用基於塊的方法(類似於 commit-graph 檔案),允許新增可選資料。

增量多包索引

隨著倉庫規模的增長,編寫包含所有包檔案的多包索引(MIDX)變得更加昂貴。為了適應這一點,“增量多包索引”功能允許組合一個多包索引的“鏈”。

鏈中的每個單獨元件只需要包含少量包檔案。向鏈中新增內容不會使鏈的早期部分失效,因此倉庫可以透過確定 MIDX 鏈的每一層中的包數量來控制更新 MIDX 鏈所花費的時間。

設計狀態

目前,增量多包索引功能缺少兩個重要元件:

  • 重寫 MIDX 鏈早期部分的能力(即,將一些相鄰的 MIDX 層“壓縮”成一個 MIDX)。目前,縮小 MIDX 鏈的唯一支援方法是從頭開始重寫整個鏈,而不使用 --split 標誌。

    這方面沒有根本性的限制。它被省略在最初的實現中是為了降低複雜性,但將在以後新增。

  • 支援可達性點陣圖。經典的單 MIDX 實現支援可達性點陣圖(有關更多詳細資訊,請參見 gitformat-pack[5] 中“multi-pack-index reverse indexes”部分)。

    如上所述,將增量 MIDX 格式擴充套件到支援可達性點陣圖沒有任何根本性的限制。下面的設計特別考慮了這一點,並且將在將來的補丁系列中新增對可達性點陣圖的支援。出於同樣的原因,它被省略在當前實現中。

    簡而言之,為了支援增量 MIDX 功能的可達性點陣圖,偽包順序的概念被擴充套件到增量 MIDX 鏈的每個層,形成一個連線的偽包順序。這種連線以鏈本身的順序進行(換句話說,鏈 {$H1, $H2, $H3} 的連線偽包順序將是 $H1 的偽包順序,然後是 $H2 的偽包順序,然後是 $H3 的偽包順序)。

    然後將佈局擴充套件,以便增量 MIDX 鏈的每一層都可以寫入一個 *.bitmap 檔案。每一層點陣圖中的物件都透過鏈中先前圖層的物件數量進行偏移。

檔案佈局

增量 MIDX 不再將單個 multi-pack-index 檔案(帶有可選的 .rev.bitmap 副檔名)儲存在 $GIT_DIR/objects/pack 中,而是採用以下佈局:

$GIT_DIR/objects/pack/multi-pack-index.d/
$GIT_DIR/objects/pack/multi-pack-index.d/multi-pack-index-chain
$GIT_DIR/objects/pack/multi-pack-index.d/multi-pack-index-$H1.midx
$GIT_DIR/objects/pack/multi-pack-index.d/multi-pack-index-$H2.midx
$GIT_DIR/objects/pack/multi-pack-index.d/multi-pack-index-$H3.midx

multi-pack-index-chain 檔案包含鏈中增量 MIDX 檔案的列表,按順序排列。上面的示例顯示了一個鏈,其 multi-pack-index-chain 檔案將包含以下行:

$H1
$H2
$H3

multi-pack-index-$H1.midx 檔案包含多包索引鏈的第一層。multi-pack-index-$H2.midx 檔案包含鏈的第二層,依此類推。

當同時存在增量和非增量 MIDX 時,總是先讀取非增量 MIDX。

增量 MIDX 的物件位置

在原始的多包索引設計中,我們透過物件 ID 在倉庫的單個多包索引中的字典序位置來引用物件。在增量多包索引設計中,我們透過物件在 MIDX 鏈中每個元件的連線字典序排序中的索引來引用物件。

如果 objects_nr() 是一個返回給定 MIDX 層中物件數量的函式,那麼位於字典序位置 i 的物件在(例如)$H3 中的索引定義為:

objects_nr($H2) + objects_nr($H1) + i

(在 C 實現中,這通常計算為 i + m->num_objects_in_base)。

增量 MIDX 的偽包順序

多包可達性點陣圖的原始實現定義了 gitformat-pack[5] 中的偽包順序(參見“multi-pack-index reverse indexes”部分),大致如下:

簡而言之,MIDX 的偽包是 MIDX 儲存的包中物件的去重連線,按包順序排列,包按 MIDX 順序排列(首選包在前)。

在增量 MIDX 設計中,我們將此定義擴充套件到包含來自 MIDX 鏈多個層中的物件。增量 MIDX 的偽包順序是透過按順序連線 MIDX 鏈的每個層的偽包順序來確定的。形式上,兩個物件 o1o2 的比較如下:

  1. 如果 o1 出現在 MIDX 鏈的較早層而不是 o2,則 o1 排在 o2 之前。

  2. 否則,如果 o1o2 出現在同一個 MIDX 層,並且該 MIDX 層沒有基礎,那麼如果 pack(o1)pack(o2) 中的一個被首選而另一個不是,那麼首選的排在非首選的前面。如果存在基礎層(即 MIDX 層不是鏈中的第一層),那麼如果 pack(o1) 出現在該 MIDX 層的包順序中更早,則 o1 排在 o2 之前。同樣,如果 pack(o2) 出現在更早,則情況相反。

  3. 否則,o1o2 出現在同一個包中,因此在同一個 MIDX 層中。按它們在包含的包檔案中的偏移量對 o1o2 進行排序。

請注意,首選包是 MIDX 鏈的屬性,而不是單獨的層。從根本上說,我們可以引入每個層的首選包,但這現在不太相關了,因為我們可以跨 MIDX 中的包集執行多包重用。

可達性點陣圖和增量 MIDX

增量 MIDX 鏈的每一層都可以用自己的 *.bitmap 檔案表示其物件(以及同一 MIDX 鏈中任何先前層的物件)。

屬於增量 MIDX 鏈的 *.bitmap 檔案的結構與非增量 MIDX 點陣圖或經典單包點陣圖相同。由於物件被新增到增量 MIDX 的偽包順序的末尾(見上文),因此可以在 MIDX 鏈的末尾附加點陣圖。

(注意:也可以將連續的 MIDX 增量層及其 *.bitmap 檔案壓縮成一個層和 *.bitmap,但這尚未實現。)

使用的物件位置在偽包順序中是全域性的,因此後續層將在其四個型別的點陣圖中具有例如 m->num_objects_in_base0 位。這是因為我們只為點陣圖對應的層中的物件編寫型別點陣圖條目)。

另請注意,在可達性查詢中,只有增量 MIDX 鏈中最新層的點陣圖用於儲存有趣和不有趣物件的 P 可達性資訊。較早的點陣圖層僅用於查詢該層中的提交和偽合併點陣圖,以及該層中物件的型別級別點陣圖。

為了簡化實現,型別級別點陣圖被同時迭代,並且將它們的結果 OR 起來,以避免遞迴呼叫內部點陣圖函式。

未來工作

  • 如果多包索引被擴充套件以儲存“穩定的物件順序”(一個對於給定雜湊來說是常數的函式 Order(hash) = integer,即使多包索引被更新),那麼 MIDX 點陣圖可以獨立於 MIDX 進行更新。

  • 可以透過空檔案將包檔案標記為“特殊”,這些檔案共享初始名稱,但將 ".pack" 替換為 ".keep" 或 ".promisor"。我們可以向多包索引新增一個可選資料塊,該資料塊記錄有關包檔案的資訊標誌。這允許新的狀態,例如 repackedredeltified,這有助於在多包環境中進行包維護。按物件型別(提交、樹、blob 等)組織包檔案並使用這些元資料來幫助維護也可能很有用。

[0] https://bugs.chromium.org/p/git/issues/detail?id=6 Chromium 工作項:多包索引 (MIDX)

[2] https://lore.kernel.org/git/alpine.DEB.2.20.1803091557510.23109@alexmv-linux/ Git Merge 2018 貢獻者峰會筆記(包括 MIDX 討論)