簡體中文 ▾ 主題 ▾ 最新版本 ▾ gitformat-pack 最後更新於 2.52.0

名稱

gitformat-pack - Git pack 格式

概要

$GIT_DIR/objects/pack/pack-.{pack,idx}
$GIT_DIR/objects/pack/pack-.rev
$GIT_DIR/objects/pack/pack-*.mtimes
$GIT_DIR/objects/pack/multi-pack-index

描述

Git pack 格式是 Git 儲存其大部分原始倉庫資料的方式。在倉庫的生命週期中,散亂物件(如果有)和較小的 pack 會被合併成一個或多個較大的 pack。請參閱 git-gc[1]git-pack-objects[1]

pack 格式也用於網路傳輸,請參見例如 gitprotocol-v2[5],同時在 gitformat-bundle[5] 的情況下,它也是其他容器格式的一部分。

校驗和與物件 ID

在使用傳統 SHA-1 的倉庫中,下面提到的 pack 校驗和、index 校驗和和物件 ID(物件名稱)都是使用 SHA-1 計算的。同樣,在 SHA-256 倉庫中,這些值是使用 SHA-256 計算的。

CRC32 校驗和總是對整個打包的物件進行計算,包括頭部(n 位元組型別和長度);如果存在,則包括基本物件名稱或偏移量;以及整個壓縮物件。使用的 CRC32 演算法是 zlib 的演算法。

pack-*.pack 檔案具有以下格式

  • 檔案開頭有一個頭部,包含以下內容

    4-byte signature:
        The signature is: {'P', 'A', 'C', 'K'}
       4-byte version number (network byte order):
    Git currently accepts version number 2 or 3 but
           generates version 2 only.
    4-byte number of objects contained in the pack (network byte order)
    Observation: we cannot have more than 4G versions ;-) and
    more than 4G objects in a pack.
  • 頭部後面是多個物件條目,每個條目如下所示

    (undeltified representation)
    n-byte type and length (3-bit type, (n-1)*7+4-bit length)
    compressed data
       (deltified representation)
       n-byte type and length (3-bit type, (n-1)*7+4-bit length)
       base object name if OBJ_REF_DELTA or a negative relative
    offset from the delta object's position in the pack if this
    is an OBJ_OFS_DELTA object
       compressed delta data
    Observation: the length of each object is encoded in a variable
    length format and is not constrained to 32-bit or anything.
  • 尾部記錄了所有上述內容的 pack 校驗和。

物件型別

有效物件型別為

  • OBJ_COMMIT (1)

  • OBJ_TREE (2)

  • OBJ_BLOB (3)

  • OBJ_TAG (4)

  • OBJ_OFS_DELTA (6)

  • OBJ_REF_DELTA (7)

型別 5 用於未來擴充套件。型別 0 無效。

物件編碼

與散亂物件不同,打包物件沒有包含型別、大小和 NUL 位元組的頭部。這些不是必需的,因為它們可以從資料前的 n 位元組型別和長度確定,因此它們被從壓縮和增量資料中省略了。

物件 ID 的計算仍然透過根據需要從型別和長度重構此頭部來使用。

大小編碼

本文件使用以下“大小編碼”表示非負整數:對於每個位元組,使用七個最低有效位構成結果整數。只要最高有效位是 1,此過程就繼續;MSB 為 0 的位元組提供最後七位。七位塊被連線起來。後面的值更具代表性。

此大小編碼不應與本文件中也使用的“偏移量編碼”混淆。

在 pack 中對未增量物件的大小進行編碼時,大小是未壓縮的原始物件的大小。對於增量物件,它是未壓縮的增量的大小。基本物件名稱或偏移量不包含在大小計算中。

增量表示

概念上只有四種物件型別:commit、tree、tag 和 blob。但是為了節省空間,物件可以儲存為另一個“基本”物件的“增量”。這些表示被分配了新的型別 ofs-delta 和 ref-delta,它們僅在 pack 檔案中有效。

ofs-delta 和 ref-delta 都儲存要應用於另一個物件(稱為基本物件)以重建物件的“增量”。它們之間的區別在於,ref-delta 直接編碼基本物件名稱。如果基本物件在同一個 pack 中,ofs-delta 則編碼該物件在 pack 中的偏移量。

基本物件也可以被增量化,如果它在同一個 pack 中。Ref-delta 也可以引用 pack 外的物件(即所謂的“thin pack”)。然而,當儲存在磁碟上時,pack 應該是自包含的,以避免迴圈依賴。

增量資料以基本物件的大小和要重建的物件的大小開始。這些大小使用上面的大小編碼進行編碼。增量資料的其餘部分是一系列指令,用於從基本物件重建物件。如果基本物件被增量化,則必須先將其轉換為規範形式。每條指令都會向目標物件新增越來越多的資料,直到完成為止。到目前為止支援兩種指令:一種用於從源物件複製位元組範圍,另一種用於插入嵌入在指令本身中的新資料。

每條指令的長度可變。指令型別由第一個位元組的第七位確定。下面的圖表遵循 RFC 1951(Deflate 壓縮資料格式)的約定。

從基本物件複製的指令

+----------+---------+---------+---------+---------+-------+-------+-------+
| 1xxxxxxx | offset1 | offset2 | offset3 | offset4 | size1 | size2 | size3 |
+----------+---------+---------+---------+---------+-------+-------+-------+

這是從源物件複製位元組範圍的指令格式。它編碼了要複製的偏移量和要複製的位元組數。偏移量和大小以小端順序表示。

所有偏移量和大小位元組都是可選的。這是為了減少編碼小偏移量或大小時的指令大小。第一個位元組的第七位決定了接下來的七個位元組中的哪個位元組存在。如果設定了第零位,則存在 offset1。如果設定了第一位,則存在 offset2,依此類推。

請注意,更緊湊的指令不會改變偏移量和大小的編碼。例如,如果只省略了 offset2,如下所示,offset3 仍然包含 16-23 位。即使它緊鄰 offset1,它也不會變成 offset2 幷包含 8-15 位。

+----------+---------+---------+
| 10000101 | offset1 | offset3 |
+----------+---------+---------+

在其最緊湊的形式中,此指令只佔用一個位元組 (0x80),其中偏移量和大小都被省略,它們將具有預設值零。還有另一個例外:大小零會自動轉換為 0x10000。

新增新資料的指令

+----------+============+
| 0xxxxxxx |    data    |
+----------+============+

這是在沒有基本物件的情況下構造目標物件的指令。以下資料將被附加到目標物件。第一個位元組的前七位確定資料的位元組大小。大小必須非零。

保留指令

+----------+============
| 00000000 |
+----------+============

此指令保留供將來擴充套件使用。

原始 (版本 1) pack-*.idx 檔案具有以下格式

  • 頭部由 256 個 4 位元組網路位元組序整陣列成。此表的第 N 個條目記錄了對應 pack 中物件的數量,其物件名稱的第一個位元組小於或等於 N。這被稱為第一級散佈表。

  • 頭部後面是已排序的 24 位元組條目,每個 pack 中的物件一個條目。每個條目是

    4-byte network byte order integer, recording where the
    object is stored in the packfile as the offset from the
    beginning.
    one object name of the appropriate size.
  • 檔案以尾部結束

    A copy of the pack checksum at the end of the corresponding
    packfile.
    Index checksum of all of the above.

Pack Idx 檔案

	--  +--------------------------------+
fanout	    | fanout[0] = 2 (for example)    |-.
table	    +--------------------------------+ |
	    | fanout[1]                      | |
	    +--------------------------------+ |
	    | fanout[2]                      | |
	    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
	    | fanout[255] = total objects    |---.
	--  +--------------------------------+ | |
main	    | offset                         | | |
index	    | object name 00XXXXXXXXXXXXXXXX | | |
table	    +--------------------------------+ | |
	    | offset                         | | |
	    | object name 00XXXXXXXXXXXXXXXX | | |
	    +--------------------------------+<+ |
	  .-| offset                         |   |
	  | | object name 01XXXXXXXXXXXXXXXX |   |
	  | +--------------------------------+   |
	  | | offset                         |   |
	  | | object name 01XXXXXXXXXXXXXXXX |   |
	  | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   |
	  | | offset                         |   |
	  | | object name FFXXXXXXXXXXXXXXXX |   |
	--| +--------------------------------+<--+
trailer	  | | packfile checksum              |
	  | +--------------------------------+
	  | | idxfile checksum               |
	  | +--------------------------------+
          .-------.
                  |
Pack file entry: <+
    packed object header:
1-byte size extension bit (MSB)
       type (next 3 bit)
       size0 (lower 4-bit)
       n-byte sizeN (as long as MSB is set, each 7-bit)
	size0..sizeN form 4+7+7+..+7 bit integer, size0
	is the least significant part, and sizeN is the
	most significant part.
    packed object data:
       If it is not DELTA, then deflated bytes (the size above
	is the size before compression).
If it is REF_DELTA, then
  base object name (the size above is the
	size of the delta data that follows).
         delta data, deflated.
If it is OFS_DELTA, then
  n-byte offset (see below) interpreted as a negative
	offset from the type-byte of the header of the
	ofs-delta entry (the size above is the size of
	the delta data that follows).
  delta data, deflated.
  offset encoding:
n bytes with MSB set in all but the last one.
The offset is then the number constructed by
concatenating the lower 7 bit of each byte, and
for n >= 2 adding 2^7 + 2^14 + ... + 2^(7*(n-1))
to the result.

版本 2 pack-*.idx 檔案支援大於 4 GiB 的 pack,並且

have some other reorganizations.  They have the format:
  • 一個 4 位元組的魔術數字 \377tOc,這是一個不合理的散佈[0]值。

  • 一個 4 位元組的版本號(= 2)

  • 一個 256 個條目的散佈表,就像 v1 一樣。

  • 一個已排序的物件名稱表。這些物件被打包在一起,沒有偏移量值,以減少二進位制搜尋特定物件名稱的快取佔用空間。

  • 一個 4 位元組的打包物件資料 CRC32 值表。這是 v2 中的新功能,因此在重新打包過程中,可以不受干擾地直接從 pack 複製壓縮資料,以防止資料損壞。

  • 一個 4 位元組的偏移量表(網路位元組序)。這些通常是 31 位 pack 檔案偏移量,但大偏移量被編碼為指向下一個表的索引,並設定了 msbit。

  • 一個 8 位元組偏移量條目表(對於小於 2 GiB 的 pack 檔案為空)。Pack 檔案被組織成將常用的物件放在前面,因此大多數物件引用不需要指向此表。

  • 與 v1 pack 檔案相同的尾部

    A copy of the pack checksum at the end of the
    corresponding packfile.
    Index checksum of all of the above.

pack-*.rev 檔案具有以下格式

  • 一個 4 位元組的魔術數字 0x52494458 (RIDX)。

  • 一個 4 位元組的版本識別符號(= 1)。

  • 一個 4 位元組的雜湊函式識別符號(= 1 表示 SHA-1,2 表示 SHA-256)。

  • 一個索引位置表(每個打包物件一個,總共 num_objects 個,每個是 4 位元組無符號整數,網路序),按其在 packfile 中的相應偏移量排序。

  • 一個尾部,包含一個

    checksum of the corresponding packfile, and
    a checksum of all of the above.

所有 4 位元組數字都採用網路位元組序。

pack-*.mtimes 檔案具有以下格式

所有 4 位元組數字都採用網路位元組序。

  • 一個 4 位元組的魔術數字 0x4d544d45 (MTME)。

  • 一個 4 位元組的版本識別符號(= 1)。

  • 一個 4 位元組的雜湊函式識別符號(= 1 表示 SHA-1,2 表示 SHA-256)。

  • 一個 4 位元組無符號整數表。第 i 個值是對應 pack 中第 i 個物件的修改時間 (mtime),按字典序(索引)排序。mtimes 記錄標準 epoch 秒。

  • 一個尾部,包含對應 packfile 的校驗和,以及所有上述內容的校驗和(每個校驗和的長度根據指定的雜湊函式確定)。

multi-pack-index (MIDX) 檔案具有以下格式

multi-pack-index 檔案引用多個 pack 檔案和散亂物件。

為了允許擴充套件新增額外資料到 MIDX,我們將主體組織成“塊”,並在主體開頭提供一個查詢表。頭部包含一些長度值,例如 pack 的數量、base MIDX 檔案的數量、雜湊長度和型別。

所有 4 位元組數字都採用網路位元組序。

頭部 (HEADER)

4-byte signature:
    The signature is: {'M', 'I', 'D', 'X'}
1-byte version number:
    Git only writes or recognizes version 1.
1-byte Object Id Version
    We infer the length of object IDs (OIDs) from this value:
	1 => SHA-1
	2 => SHA-256
    If the hash type does not match the repository's hash algorithm,
    the multi-pack-index file should be ignored with a warning
    presented to the user.
1-byte number of "chunks"
1-byte number of base multi-pack-index files:
    This value is currently always zero.
4-byte number of pack files

塊查詢表 (CHUNK LOOKUP)

(C + 1) * 12 bytes providing the chunk offsets:
    First 4 bytes describe chunk id. Value 0 is a terminating label.
    Other 8 bytes provide offset in current file for chunk to start.
    (Chunks are provided in file-order, so you can infer the length
    using the next chunk position if necessary.)
The CHUNK LOOKUP matches the table of contents from
the chunk-based file format, see gitformat-chunk[5].
The remaining data in the body is described one chunk at a time, and
these chunks may be given in any order. Chunks are required unless
otherwise specified.

塊資料 (CHUNK DATA)

Packfile Names (ID: {'P', 'N', 'A', 'M'})
    Store the names of packfiles as a sequence of NUL-terminated
    strings. There is no extra padding between the filenames,
    and they are listed in lexicographic order. The chunk itself
    is padded at the end with between 0 and 3 NUL bytes to make the
    chunk size a multiple of 4 bytes.
Bitmapped Packfiles (ID: {'B', 'T', 'M', 'P'})
    Stores a table of two 4-byte unsigned integers in network order.
    Each table entry corresponds to a single pack (in the order that
    they appear above in the `PNAM` chunk). The values for each table
    entry are as follows:
    - The first bit position (in pseudo-pack order, see below) to
      contain an object from that pack.
    - The number of bits whose objects are selected from that pack.
OID Fanout (ID: {'O', 'I', 'D', 'F'})
    The ith entry, F[i], stores the number of OIDs with first
    byte at most i. Thus F[255] stores the total
    number of objects.
OID Lookup (ID: {'O', 'I', 'D', 'L'})
    The OIDs for all objects in the MIDX are stored in lexicographic
    order in this chunk.
Object Offsets (ID: {'O', 'O', 'F', 'F'})
    Stores two 4-byte values for every object.
    1: The pack-int-id for the pack storing this object.
    2: The offset within the pack.
	If all offsets are less than 2^32, then the large offset chunk
	will not exist and offsets are stored as in IDX v1.
	If there is at least one offset value larger than 2^32-1, then
	the large offset chunk must exist, and offsets larger than
	2^31-1 must be stored in it instead. If the large offset chunk
	exists and the 31st bit is on, then removing that bit reveals
	the row in the large offsets containing the 8-byte offset of
	this object.
[Optional] Object Large Offsets (ID: {'L', 'O', 'F', 'F'})
    8-byte offsets into large packfiles.
[Optional] Bitmap pack order (ID: {'R', 'I', 'D', 'X'})
    A list of MIDX positions (one per object in the MIDX, num_objects in
    total, each a 4-byte unsigned integer in network byte order), sorted
    according to their relative bitmap/pseudo-pack positions.

尾部 (TRAILER)

Index checksum of the above contents.

multi-pack-index 反向索引

與基於 pack 的反向索引類似,multi-pack 索引也可用於生成反向索引。

此反向索引不對映偏移量、pack 和索引位置之間的關係,而是對映物件在 MIDX 中的位置與其在 MIDX 所描述的偽 pack 中的位置之間的關係(即,multi-pack 反向索引的第 i 個條目儲存第 i 個物件在偽 pack 順序中的 MIDX 位置)。

為了闡明這些順序之間的區別,可以考慮一個 multi-pack 可達性點陣圖(它尚不存在,但這是我們在此構建的目標)。每個位都需要對應 MIDX 中的一個物件,因此我們需要一個從位位置到 MIDX 位置的高效對映。

一種解決方案是讓位在 MIDX 儲存的 oid 排序索引中佔用相同的位置。但是由於 oid 實際上是隨機的,因此它們的可達性點陣圖將沒有區域性性,從而壓縮效果不佳。(這就是為什麼單 pack 點陣圖為此目的使用 pack 順序,而不是 .idx 順序的原因。)

因此,我們希望為整個 MIDX 定義一個基於 pack 順序的排序,該排序具有更好的區域性性(因此壓縮效率更高)。我們可以想象一個由 MIDX 中所有 pack 連線而成的偽 pack。例如,如果我們有一個包含三個 pack(a、b、c)的 MIDX,分別有 10、15 和 20 個物件,我們可以設想一個物件順序

|a,0|a,1|...|a,9|b,0|b,1|...|b,14|c,0|c,1|...|c,19|

其中 pack 的順序由 MIDX 的 pack 列表定義,然後每個 pack 內物件的順序與實際 pack 檔案中的順序相同。

給定 pack 列表及其物件計數,您可以粗略地重建該偽 pack 順序(例如,位置 27 的物件必須是 (c,1),因為 pack "a" 和 "b" 佔用了 25 個槽)。但有一個問題。物件可能在 pack 之間重複,在這種情況下,MIDX 只儲存一個指向物件的指標(因此我們希望點陣圖中只有一個槽)。

呼叫者可以透過按位位置順序讀取物件來自己處理重複項,但這與物件數量成線性關係,對於普通的點陣圖查詢來說過於昂貴。構建反向索引可以解決這個問題,因為它是索引的邏輯逆運算,而該索引已經刪除了重複項。但是,即時構建反向索引可能會很昂貴。由於我們已經有了基於 pack 的反向索引的磁碟格式,讓我們也將其用於 MIDX 的偽 pack。

MIDX 中的物件按以下方式排序以串聯偽 pack。令 pack(o) 返回 MIDX 選擇 o 的 pack,並定義一個基於 pack ID(由 MIDX 儲存)的 pack 順序。令 offset(o) 返回 opack(o) 中的物件偏移量。然後,按如下方式比較 o1o2

  • 如果 pack(o1)pack(o2) 中有一個是首選的而另一個不是,則首選的排在前面。

    (這是一個允許 MIDX 點陣圖確定哪個 pack 應該被 pack-reuse 機制使用的細節,因為它可以在位位置 0 處詢問 MIDX 包含該物件的 pack)。

  • 如果 pack(o1) ≠ pack(o2),則根據 pack ID 以降序對兩個物件進行排序。

  • 否則,pack(o1) = pack(o2),並且物件按照 pack 順序排序(即,o1 排在 o2 前當且僅當 offset(o1) < offset(o2))。

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

MIDX 的反向索引儲存在 MIDX 本身的可選 RIDX 塊中。

BTMP

點陣圖 Packfiles (BTMP) 塊編碼了 multi-pack index 可達性點陣圖中物件的附加資訊。回想一下,MIDX 中的物件按“偽 pack”順序(見上文)排列以用於可達性點陣圖。

根據上面的例子,假設我們有 pack "a"、"b" 和 "c",分別有 10、15 和 20 個物件。在偽 pack 順序中,它們將按如下方式排列

|a,0|a,1|...|a,9|b,0|b,1|...|b,14|c,0|c,1|...|c,19|

在處理單 pack 點陣圖(或等效地,具有首選 pack 的 multi-pack 可達性點陣圖)時,git-pack-objects[1] 執行“逐字”重用,嘗試重用點陣圖或首選 packfile 的塊,而不是將物件新增到 packing list 中。

當從現有 pack 中重用位元組塊時,其中包含的任何物件都無需新增到 packing list 中,從而節省記憶體和 CPU 時間。但是,來自現有 packfile 的塊只有在滿足以下條件時才能重用

  • 該塊僅包含呼叫者請求的物件(即,不包含任何呼叫者未明確或隱式請求的物件)。

  • 在非 thin pack 中作為偏移量或引用增量儲存的所有物件也包括其基本物件在生成的 pack 中。

BTMP 塊編碼了實現上述 multi-pack 重用所需的資訊。具體來說,BTMP 塊為 MIDX 中儲存的每個 packfile p 編碼三條資訊(均為 32 位無符號整數,網路位元組序),如下所示

bitmap_pos

multi-pack index 可達性點陣圖中來自 p 的物件佔用的第一個位位置(按偽 pack 順序)。

bitmap_nr

編碼來自該 pack p 的物件的位數(包括 bitmap_pos 處的位數)。

例如,與上述示例(包含 pack "a"、"b" 和 "c")對應的 BTMP 塊將如下所示

bitmap_pos (點陣圖位置) bitmap_nr (點陣圖數量)

packfile "a"

0

10

packfile "b"

10

15

packfile "c"

25

20

有了這些資訊,我們可以將每個 packfile 視為可單獨重用,方式與在 BTMP 塊實現之前對單個 pack 執行的逐字 pack 重用方式相同。

cruft packs (垃圾包)

cruft packs 功能提供了一種替代 Git 傳統的移除不可達物件機制的方法。本文件概述了 Git 的修剪機制,以及如何使用 cruft pack 來實現相同的目的。

背景

要從倉庫中刪除不可達物件,Git 提供了 git repack -Ad(請參閱 git-repack[1])。引用文件內容

[...] unreachable objects in a previous pack become loose, unpacked objects,
instead of being left in the old pack. [...] loose unreachable objects will be
pruned according to normal expiry rules with the next 'git gc' invocation.

不可達物件不會立即刪除,因為這樣做可能會與傳入的 push 發生競爭,而該 push 可能引用即將被刪除的物件。相反,這些不可達物件被儲存為散亂物件,並保持該狀態,直到它們超過過期視窗,此時它們將被 git-prune[1] 刪除。

Git 必須將這些不可達物件儲存為散亂物件,以便跟蹤它們的每個物件 mtime。如果這些不可達物件被寫入一個大的 pack 中,那麼無論是因為其中一個物件被重寫而重新整理該 pack( freshening),還是建立了一個新的不可達物件 pack,都會導致 pack 的 mtime 被更新,並且其中的物件將永遠不會離開過期視窗。相反,物件以散亂狀態儲存是為了跟蹤單個物件 mtime,並避免所有 cruft 物件同時被重新整理的情況。

當倉庫包含許多尚未過寬限期的不可達物件時,這可能導致不良情況。在 .git/objects 的分片中擁有大型目錄可能導致倉庫效能下降。但如果不可達物件足夠多,這可能導致 inode 耗盡並降低整個系統的效能。由於我們永遠無法打包這些物件,因此這些倉庫通常佔用大量磁碟空間,因為我們只能進行 zlib 壓縮,但不能將它們儲存在 delta 鏈中。

Cruft packs (垃圾包)

cruft pack 透過將每個物件的 mtime 儲存在一個單獨的檔案中,並結合一個包含所有散亂物件的單個 pack,消除了將不可達物件儲存為散亂狀態的需要。

cruft pack 由 git repack --cruft 在生成新 pack 時寫入。git-pack-objects[1]--cruft 選項。請注意,git repack --cruft 是經典的“全合一”repack,意味著生成 pack 中的所有內容都是可達的,而其他所有內容都是不可達的。寫入後,--cruft 選項指示 git repack 生成另一個只包含上一步未打包的物件(相當於將所有不可達物件打包在一起)的 pack。這按以下步驟進行

  1. 列舉每個物件,標記任何(a)不包含在保留 pack 中,並且(b)其 mtime 在寬限期內的物件作為遍歷的起點。

  2. 基於上一步收集的起點執行可達性遍歷,並將沿途的所有物件新增到 pack 中。

  3. 將 pack 寫出,以及一個 .mtimes 檔案,該檔案記錄了每個物件的修改時間戳。

此模式由 git-repack[1] 在被指示寫入 cruft pack 時內部呼叫。至關重要的是,核心保留的 pack 集就是不會被 repack 刪除的 pack 集;換句話說,它們包含倉庫中所有可達的物件。

當倉庫已包含 cruft pack 時,git repack --cruft 通常只向其中新增物件。一個例外是當 git repack 提供了 --cruft-expiration 選項時,該選項允許生成的 cruft pack 排除過期的物件,而不是等待 git-gc[1] 稍後過期這些物件。

通常由 git-gc[1] 負責刪除過期的不可達物件。

替代方案

此設計的顯著替代方案包括

  • 每個物件 mtime 資料的位置。

關於 mtime 資料的位置,選擇了一個與 pack 相關聯的新輔助檔案,以避免使 .idx 格式複雜化。如果 .idx 格式將來支援可選資料塊,那麼將 .mtimes 格式合併到 .idx 本身中可能會有意義。

GIT

Git[1] 套件的一部分