GITFORMAT-PACK(5) Git Manual GITFORMAT-PACK(5)

gitformat-pack - قالب بسته‌ها (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

قالب بسته در گیت (pack format) روشی است که گیت بخش عمده داده‌های اصلی مخزن خود را در آن ذخیره می‌کند. در طول چرخه عمر یک مخزن، اشیاء مجزا (loose objects ‏- در صورت وجود) و بسته‌های کوچک‌تر در بسته(های) بزرگ‌تر تجمیع می‌شوند. نگاه کنید به git-gc(1) و git-pack-objects(1).

قالب بسته همچنین برای انتقال روی شبکه (over-the-wire) نیز استفاده می‌شود؛ مثلاً نگاه کنید به gitprotocol-v2(5)، و همچنین بخشی از سایر قالب‌های کانتینری مانند gitformat-bundle(5) است.

در مخزنی که از SHA-1 سنتی استفاده می‌کند، چک‌سام‌های بسته، چک‌سام‌های ایندکس و شناسه‌های اشیاء (نام‌های اشیاء) که در ادامه ذکر شده‌اند همگی با استفاده از SHA-1 محاسبه می‌شوند. به همین ترتیب، در مخازن مبتنی بر SHA-256، این مقادیر با استفاده از SHA-256 محاسبه می‌گردند.

چک‌سام‌های CRC32 همواره بر روی کل شیء بسته‌بندی‌شده محاسبه می‌شوند، شامل هدر (نوع و طول n-بایتی)؛ نام یا آفست شیء پایه در صورت وجود؛ و کل شیء فشرده‌شده. الگوریتم CRC32 مورد استفاده، همان الگوریتم zlib است.

•یک هدر در ابتدا قرار دارد و شامل موارد زیر است:
امضای ۴ بایتی:
    امضا عبارت است از: {'P', 'A', 'C', 'K'}
شماره نسخه ۴ بایتی (با ترتیب بایت شبکه - network byte order):
    گیت در حال حاضر نسخه ۲ یا ۳ را می‌پذیرد اما
    فقط نسخه ۲ را تولید می‌کند.
تعداد ۴ بایتی اشیاء موجود در بسته (ترتیب بایت شبکه)
ملاحظه: نمی‌توان بیش از ۴ گیگابایت نسخه داشت ;) و
بیش از ۴ گیگابایت شیء در یک بسته وجود ندارد.
•پس از هدر، تعدادی مدخل شیء قرار دارد که هر یک از آن‌ها به این شکل است:
(نمایش غیردلتا - undeltified)
نوع و طول n-بایتی (نوع ۳ بیتی، طول (n-1)*7+4 بیتی)
داده‌های فشرده‌شده
(نمایش دلتا - deltified)
نوع و طول n-بایتی (نوع ۳ بیتی، طول (n-1)*7+4 بیتی)
نام شیء پایه در صورت OBJ_REF_DELTA یا آفست نسبی منفی
    از موقعیت شیء دلتا در بسته در صورتی که این شیء
    یک شیء OBJ_OFS_DELTA باشد
داده‌های دلتای فشرده‌شده
ملاحظه: طول هر شیء در یک قالب با طول متغیر کدگذاری می‌شود
و به ۳۲ بیتی یا موارد مشابه محدود نیست.
•تریلر (trailer) یک چک‌سام بسته از تمام موارد بالا را ثبت می‌کند.

انواع معتبر اشیاء عبارتند از:

•OBJ_COMMIT (1)
•OBJ_TREE (2)
•OBJ_BLOB (3)
•OBJ_TAG (4)
•OBJ_OFS_DELTA (6)
•OBJ_REF_DELTA (7)

نوع ۵ برای توسعه در آینده رزرو شده است. نوع ۰ نامعتبر است.

برخلاف اشیاء مجزا (loose objects)، اشیاء بسته‌بندی‌شده فاقد پیشوندی حاوی نوع، اندازه و یک بایت NUL هستند. این موارد ضروری نیستند زیرا می‌توان آن‌ها را از روی نوع و طول n-بایتی که پیشوند داده‌هاست تشخیص داد و بنابراین از داده‌های فشرده‌شده و دلتا حذف شده‌اند.

محاسبه شناسه شیء (object ID) همچنان از این پیشوند با بازسازی آن بر اساس نوع و طول در صورت نیاز استفاده می‌کند.

این سند از "کدگذاری اندازه" زیر برای اعداد صحیح غیرمنفی استفاده می‌کند: از هر بایت، هفت بیت کم‌ارزش‌تر برای تشکیل عدد صحیح نهایی استفاده می‌شود. تا زمانی که باارزش‌ترین بیت (MSB) برابر با ۱ باشد، این فرآیند ادامه می‌یابد؛ بایتی که MSB آن ۰ باشد هفت بیت آخر را فراهم می‌کند. تکه‌های هفت‌بیتی به یکدیگر متصل می‌شوند. مقادیر بعدی دارای ارزش مکانی بالاتری هستند.

این کدگذاری اندازه نباید با "کدگذاری آفست"، که آن نیز در این سند استفاده شده است، اشتباه گرفته شود.

هنگام کدگذاری اندازه یک شیء غیردلتا در یک بسته، اندازه برابر با اندازه شیء خام فشرده‌نشده است. برای اشیاء دلتا، اندازه برابر با اندازه دلتای فشرده‌نشده است. نام یا آفست شیء پایه در محاسبه اندازه گنجانده نمی‌شود.

از نظر مفهومی تنها چهار نوع شیء وجود دارد: commit، tree، tag و blob. با این حال برای صرفه‌جویی در فضا، یک شیء می‌تواند به صورت یک "دلتا" از یک شیء "پایه" دیگر ذخیره شود. به این نمایش‌ها انواع جدید ofs-delta و ref-delta اختصاص داده شده است که فقط در یک فایل بسته معتبر هستند.

هر دو نوع ofs-delta و ref-delta مقدار "دلتا" را برای اعمال روی شیء دیگر (که شیء پایه نامیده می‌شود) ذخیره می‌کنند تا شیء بازسازی شود. تفاوت میان آن‌ها این است که ref-delta مستقیماً نام شیء پایه را کدگذاری می‌کند. اگر شیء پایه در همان بسته باشد، ofs-delta در عوض آفست شیء پایه در بسته را کدگذاری می‌کند.

شیء پایه نیز در صورتی که در همان بسته باشد می‌تواند به صورت دلتا باشد. نوع Ref-delta همچنین می‌تواند به شیئی خارج از بسته ارجاع دهد (یعنی اصطلاحاً "بسته نازک" یا thin pack). با این حال هنگام ذخیره‌سازی روی دیسک، بسته باید مستقل و خودبسنده (self contained) باشد تا از وابستگی‌های چرخه‌ای جلوگیری شود.

داده‌های دلتا با اندازه شیء پایه و اندازه شیئی که باید بازسازی شود آغاز می‌شوند. این اندازه‌ها با استفاده از کدگذاری اندازه از بخش بالا کدگذاری می‌شوند. باقی‌مانده داده‌های دلتا دنباله‌ای از دستورالعمل‌ها برای بازسازی شیء از روی شیء پایه است. اگر شیء پایه به صورت دلتا باشد، ابتدا باید به فرم استاندارد تبدیل شود. هر دستورالعمل داده‌های بیشتری را به شیء مقصد اضافه می‌کند تا کامل شود. تا کنون دو دستورالعمل پشتیبانی می‌شود: یکی برای کپی کردن یک بازه بایتی از شیء مبدا و دیگری برای درج داده‌های جدید تعبیه‌شده در خود دستورالعمل.

هر دستورالعمل دارای طول متغیر است. نوع دستورالعمل توسط بیت هفتم از اولین بایت تعیین می‌شود. نمودارهای زیر از قرارداد RFC 1951 (قالب داده‌های فشرده Deflate) پیروی می‌کنند.

دستورالعمل کپی از شیء پایه

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

این قالب دستورالعمل برای کپی کردن یک بازه بایتی از شیء مبدا است. این دستور آفست مبدا برای کپی و تعداد بایت‌ها برای کپی را کدگذاری می‌کند. آفست و اندازه با ترتیب اندیان کوچک (little-endian) هستند.

تمام بایت‌های آفست و اندازه اختیاری هستند. این امر برای کاهش اندازه دستورالعمل هنگام کدگذاری آفست‌ها یا اندازه‌های کوچک است. هفت بیت اول در بایت اول مشخص می‌کنند کدام‌یک از هفت بایت بعدی وجود دارند. اگر بیت صفر تنظیم شده باشد، offset1 وجود دارد. اگر بیت یک تنظیم شده باشد offset2 وجود دارد و به همین ترتیب.

توجه داشته باشید که یک دستورالعمل فشرده‌تر، کدگذاری آفست و اندازه را تغییر نمی‌دهد. برای مثال، اگر فقط offset2 مانند زیر حذف شود، offset3 همچنان شامل بیت‌های ۱۶ تا ۲۳ خواهد بود. این بایت به offset2 تبدیل نمی‌شود و حتی اگر درست در کنار offset1 قرار گیرد شامل بیت‌های ۸ تا ۱۵ نخواهد شد.

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

در فشرده‌ترین حالت خود، این دستورالعمل تنها یک بایت (0x80) فضا اشغال می‌کند که در آن هر دو مقدار آفست و اندازه حذف شده‌اند و مقادیر پیش‌فرض آن‌ها صفر خواهد بود. یک استثنای دیگر نیز وجود دارد: اندازه صفر به طور خودکار به 0x10000 تبدیل می‌شود.

دستورالعمل افزودن داده جدید

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

این دستورالعمل برای ساخت شیء مقصد بدون شیء پایه است. داده‌های زیر به شیء مقصد ضمیمه می‌شوند. هفت بیت اول از اولین بایت اندازه داده‌ها را به بایت مشخص می‌کنند. اندازه باید غیرصفر باشد.

دستورالعمل رزروشده

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

این دستورالعمل برای توسعه در آینده رزرو شده است.

•هدر شامل ۲۵۶ عدد صحیح ۴ بایتی با ترتیب بایت شبکه است. مدخل N-ام از این جدول، تعداد اشیاء موجود در بسته متناظر را ثبت می‌کند که بایت اول نام شیء آن‌ها کوچکتور یا مساوی N است. به این جدول، جدول توزیع سطح اول (first-level fan-out) گفته می‌شود.
•پس از هدر، مدخل‌های مرتب‌شده ۲۴ بایتی قرار دارند، به ازای هر شیء در بسته یک مدخل. هر مدخل عبارت است از:
عدد صحیح ۴ بایتی با ترتیب بایت شبکه، که محل ذخیره شیء
در فایل بسته را به صورت آفست از ابتدای فایل ثبت می‌کند.
یک نام شیء با اندازه مناسب.
•فایل با یک تریلر (بخش پایانی) خاتمه می‌یابد:
یک کپی از چک‌سام بسته در انتهای فایل بسته متناظر.
چک‌سام ایندکس از تمام موارد بالا.

Pack Idx file:

        --  +--------------------------------+
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.

دارای سازمان‌دهی‌های دیگری نیز هستند. آن‌ها دارای قالب زیر می‌باشند:
•یک عدد جادویی ۴ بایتی \377tOc که یک مقدار نامتعارف برای fanout[0] است.
•یک شماره نسخه ۴ بایتی (= 2)
•یک جدول توزیع (fan-out) ۲۵۶ مدخلی درست همانند نسخه ۱.
•یک جدول از نام‌های مرتب‌شده اشیاء. این نام‌ها بدون مقادیر آفست در کنار هم فشرده شده‌اند تا ردپای کش در جستجوی دودویی برای یک نام شیء خاص کاهش یابد.
•یک جدول از مقادیر ۴ بایتی CRC32 داده‌های شیء بسته‌بندی‌شده. این ویژگی در نسخه ۲ جدید است تا داده‌های فشرده بتوانند در طول بسته‌بندی مجدد (repacking) مستقیماً از بسته‌ای به بسته دیگر کپی شوند بدون اینکه خرابی داده‌ها پنهان بماند.
•یک جدول از مقادیر آفست ۴ بایتی (با ترتیب بایت شبکه). این مقادیر معمولاً آفست‌های ۳۱ بیتی فایل بسته هستند، اما آفست‌های بزرگ به صورت یک نمایه به جدول بعدی با بیت msbit تنظیم‌شده کدگذاری می‌شوند.
•یک جدول از مدخل‌های آفست ۸ بایتی (برای فایل‌های بسته با حجم کمتر از ۲ گیبی‌بایت خالی است). فایل‌های بسته به گونه‌ای سازمان‌دهی شده‌اند که اشیاء پرکاربردتر در ابتدا قرار گیرند، بنابراین بیشتر ارجاعات اشیاء نیازی به ارجاع به این جدول نخواهند داشت.
•همان تریلر فایل بسته نسخه ۱:
یک کپی از چک‌سام بسته در انتهای
فایل بسته متناظر.
چک‌سام ایندکس از تمام موارد بالا.

•یک عدد جادویی ۴ بایتی 0x52494458 (RIDX).
•یک شناسه نسخه ۴ بایتی (= 1).
•یک شناسه تابع هش ۴ بایتی (= 1 برای SHA-1، و 2 برای SHA-256).
•یک جدول از موقعیت‌های ایندکس (یک موقعیت به ازای هر شیء بسته‌بندی‌شده، در مجموع num_objects، هر کدام یک عدد صحیح بدون علامت ۴ بایتی با ترتیب شبکه)، که بر اساس آفست‌های متناظر آن‌ها در فایل بسته مرتب شده‌اند.
•یک تریلر، شامل موارد زیر:
چک‌سام فایل بسته متناظر، و
یک چک‌سام از تمام موارد بالا.

تمام اعداد ۴ بایتی با ترتیب شبکه هستند.

تمام اعداد ۴ بایتی با ترتیب بایت شبکه هستند.

•یک عدد جادویی ۴ بایتی 0x4d544d45 (MTME).
•یک شناسه نسخه ۴ بایتی (= 1).
•یک شناسه تابع هش ۴ بایتی (= 1 برای SHA-1، و 2 برای SHA-256).
•یک جدول از اعداد صحیح بدون علامت ۴ بایتی. مقدار i-ام، زمان اصلاح (mtime) شیء i-ام در بسته متناظر بر اساس ترتیب واژگانی (ایندکس) است. این مقادیر mtime بر اساس ثانیه‌های استاندارد مبدا زمانی (epoch) شمارش می‌شوند.
•یک تریلر، شامل چک‌سام فایل بسته متناظر، و یک چک‌سام از تمام موارد بالا (که طول هر کدام بر اساس تابع هش مشخص‌شده تعیین می‌شود).

فایل‌های multi-pack-index به چندین فایل بسته و شیء مجزا ارجاع می‌دهند.

به منظور امکان‌پذیر ساختن افزونه‌هایی که داده‌های اضافی را به MIDX اضافه می‌کنند، بدنه را به "تکه‌ها" (chunks) سازمان‌دهی می‌کنیم و یک جدول جستجو در ابتدای بدنه ارائه می‌دهیم. هدر شامل مقادیر طولی معینی است، مانند تعداد بسته‌ها، تعداد فایل‌های MIDX پایه، طول و انواع هش.

تمام اعداد ۴ بایتی با ترتیب شبکه هستند.

هدر (HEADER):

امضای ۴ بایتی:
    امضا عبارت است از: {'M', 'I', 'D', 'X'}
شماره نسخه ۱ بایتی:
    گیت نسخه‌ای را می‌نویسد که با گزینه پیکربندی "midx.version"
    مشخص شده است، که به طور پیش‌فرض برابر با ۲ است. این سیستم
    هر دو نسخه ۱ و ۲ را تشخیص می‌دهد.
نسخه شناسه شیء ۱ بایتی (Object Id Version):
    ما طول شناسه‌های اشیاء (OIDs) را از روی این مقدار استنتاج می‌کنیم:
        1 => SHA-1
        2 => SHA-256
    اگر نوع هش با الگوریتم هش مخزن مطابقت نداشته باشد،
    فایل multi-pack-index باید نادیده گرفته شده و هشداری
    به کاربر نمایش داده شود.
تعداد "تکه‌ها" به صورت ۱ بایتی
تعداد فایل‌های چندبسته‌ای پایه به صورت ۱ بایتی:
    این مقدار در حال حاضر همواره صفر است.
تعداد فایل‌های بسته به صورت ۴ بایتی

جدول جستجوی تکه (CHUNK LOOKUP):

مقدار (C + 1) * 12 بایت فراهم‌کننده آفست‌های تکه‌ها:
    اولین ۴ بایت شناسه تکه را توصیف می‌کند. مقدار ۰ یک برچسب پایان‌دهنده است.
    ۸ بایت دیگر، آفست در فایل جاری را برای شروع تکه ارائه می‌دهند.
    (تکه‌ها با ترتیب ترتیبی در فایل ارائه می‌شوند، بنابراین در صورت نیاز
    می‌توانید طول را با استفاده از موقعیت تکه بعدی استنتاج کنید.)
جدول CHUNK LOOKUP با جدول فهرست مطالب در
قالب فایلی مبتنی بر تکه مطابقت دارد؛ نگاه کنید به linkgit:gitformat-chunk[5].
داده‌های باقی‌مانده در بدنه هر بار به صورت یک تکه توصیف می‌شوند و
این تکه‌ها ممکن است به هر ترتیبی ارائه گردند. تکه‌ها اجباری هستند مگر
خلاف آن مشخص شده باشد.

داده‌های تکه (CHUNK DATA):

نام‌های فایل‌های بسته (شناسه: {'P', 'N', 'A', 'M'})
    نام‌های فایل‌های بسته را به عنوان دنباله‌ای از رشته‌های پایان‌یافته
    با NUL ذخیره می‌کند. هیچ فاصله پرکننده اضافی میان نام‌های فایل
    وجود ندارد و آن‌ها به ترتیب الفبایی فهرست شده‌اند. خود تکه
    در انتها با بین ۰ تا ۳ بایت NUL پر می‌شود تا اندازه
    تکه مضربی از ۴ بایت شود. نسخه‌های ۱ MIDX ملزم هستند
    بسته‌های خود را به ترتیب الفبایی فهرست کنند، اما نسخه‌های ۲ MIDX می‌توانند
    بسته‌های خود را به هر ترتیب دلخواهی فهرست نمایند.
فایل‌های بسته نقشه‌بیتی (شناسه: {'B', 'T', 'M', 'P'})
    جدولی از دو عدد صحیح بدون علامت ۴ بایتی با ترتیب شبکه ذخیره می‌کند.
    هر مدخل جدول متناظر با یک بسته واحد است (به ترتیبی که
    در بالا در تکه `PNAM` ظاهر شده‌اند). مقادیر برای هر مدخل جدول
    به شرح زیر است:
    - اولین موقعیت بیتی (در ترتیب شبه‌بسته، در ادامه را ببینید) که
      حاوی شیئی از آن بسته باشد.
    - تعداد بیت‌هایی که اشیاء آن‌ها از آن بسته انتخاب شده‌اند.
توزیع OID (شناسه: {'O', 'I', 'D', 'F'})
    مدخل i-ام، F[i]، تعداد OIDهایی را که بایت اول آن‌ها
    حداکثر i است ذخیره می‌کند. بنابراین F[255] تعداد کل
    اشیاء را ذخیره می‌کند.
جستجوی OID (شناسه: {'O', 'I', 'D', 'L'})
    شناسه‌های OID برای تمام اشیاء در MIDX به ترتیب
    الفبایی در این تکه ذخیره می‌شوند.
آفست‌های اشیاء (شناسه: {'O', 'O', 'F', 'F'})
    دو مقدار ۴ بایتی را برای هر شیء ذخیره می‌کند.
    ۱: شناسه pack-int-id برای بسته‌ای که این شیء را ذخیره می‌کند.
    ۲: آفست در داخل بسته.
        اگر تمام آفست‌ها کمتر از 2^32 باشند، تکه آفست بزرگ
        وجود نخواهد داشت و آفست‌ها همانند IDX v1 ذخیره می‌شوند.
        اگر حداقل یک مقدار آفست بزرگتر از 2^32-1 وجود داشته باشد،
        تکه آفست بزرگ باید وجود داشته باشد و آفست‌های بزرگتر از
        2^31-1 باید در عوض در آن ذخیره شوند. اگر تکه آفست بزرگ
        وجود داشته باشد و بیت ۳۱ام فعال باشد، حذف آن بیت ردیفی
        را در آفست‌های بزرگ آشکار می‌کند که حاوی آفست ۸ بایتی
        این شیء است.
[اختیاری] آفست‌های بزرگ اشیاء (شناسه: {'L', 'O', 'F', 'F'})
    آفست‌های ۸ بایتی در فایل‌های بسته بزرگ.
[اختیاری] ترتیب بسته نقشه‌بیت (شناسه: {'R', 'I', 'D', 'X'})
    فهرستی از موقعیت‌های MIDX (یک موقعیت به ازای هر شیء در MIDX، در مجموع num_objects،
    هر کدام یک عدد صحیح بدون علامت ۴ بایتی با ترتیب بایت شبکه)، مرتب‌شده
    بر اساس موقعیت‌های نسبی نقشه‌بیت/شبه‌بسته آن‌ها.

تریلر (TRAILER):

چک‌سام ایندکس از محتویات بالا.

مشابه ایندکس معکوس مبتنی بر بسته، از ایندکس چندبسته‌ای (multi-pack index) نیز می‌توان برای تولید یک ایندکس معکوس استفاده کرد.

به جای نگاشت میان آفست، موقعیت بسته و موقعیت ایندکس، این ایندکس معکوس میان موقعیت یک شیء در MIDX و موقعیت آن شیء در یک شبه‌بسته (pseudo-pack) که MIDX توصیف می‌کند نگاشت انجام می‌دهد (یعنی مدخل i-ام از ایندکس معکوس چندبسته‌ای، موقعیت MIDX مربوط به شیء i-ام را با ترتیب شبه‌بسته نگه‌داری می‌کند).

برای روشن شدن تفاوت میان این ترتیب‌ها، یک نقشه‌بیت دسترسی‌پذیری چندبسته‌ای را در نظر بگیرید (که هنوز وجود ندارد، اما چیزی است که ما در اینجا در جهت آن حرکت می‌کنیم). هر بیت باید متناظر با یک شیء در MIDX باشد، و بنابراین به یک نگاشت کارآمد از موقعیت بیت به موقعیت MIDX نیاز داریم.

یک راه‌حل این است که اجازه دهیم بیت‌ها همان موقعیت را در ایندکس مرتب‌شده بر اساس oid ذخیره‌شده توسط MIDX اشغال کنند. اما چون شناسه‌های oid عملاً تصادفی هستند، نقشه‌بیت‌های دسترسی‌پذیری حاصل فاقد محلی بودن داده (locality) خواهند بود و بنابراین فشرده‌سازی بسیار ضعیفی خواهند داشت. (به همین دلیل است که نقشه‌بیت‌های تک‌بسته‌ای برای همین منظور از ترتیب بسته استفاده می‌کنند، نه از ترتیب .idx).

بنابراین مایل هستیم ترتیبی برای کل MIDX بر پایه ترتیب بسته تعریف کنیم که محلی بودن بسیار بهتری دارد (و در نتیجه کارآمدتر فشرده می‌شود). می‌توانیم یک شبه‌بسته را متصور شویم که از اتصال تمام بسته‌های موجود در MIDX به وجود آمده است. به عنوان مثال، اگر یک MIDX با سه بسته (a، b، c) داشته باشیم که به ترتیب دارای ۱۰، ۱۵ و ۲۰ شیء باشند، می‌توانیم ترتیبی از اشیاء را به این صورت تصور کنیم:

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

که در آن ترتیب بسته‌ها توسط فهرست بسته‌های MIDX تعریف می‌شود، و سپس ترتیب اشیاء در هر بسته با ترتیب موجود در فایل بسته واقعی یکسان است.

با داشتن فهرست بسته‌ها و تعداد اشیاء آن‌ها، می‌توانید این ترتیب شبه‌بسته را به سادگی بازسازی کنید (برای نمونه، شیء در موقعیت ۲۷ باید (c,1) باشد زیرا بسته‌های "a" و "b" ۲۵ جایگاه را اشغال کرده‌اند). اما نکته‌ای وجود دارد: اشیاء ممکن است میان بسته‌ها تکراری باشند، که در این صورت MIDX فقط یک اشاره‌گر به شیء را ذخیره می‌کند (و بنابراین ما فقط یک جایگاه در نقشه‌بیت می‌خواهیم).

فراخوان‌ها می‌توانند با خواندن اشیاء به ترتیب موقعیت بیتی، موارد تکراری را خودشان مدیریت کنند، اما این کار با تعداد اشیاء نسبت خطی دارد و برای جستجوهای معمول در نقشه‌بیت بیش از حد پرهزینه است. ساخت یک ایندکس معکوس این مشکل را حل می‌کند، چرا که ایندکس معکوس وارون منطقی ایندکس است و آن ایندکس از قبل موارد تکراری را حذف کرده است. اما ساخت ایندکس معکوس به صورت بلادرنگ و پویا می‌تواند پرهزینه باشد. از آنجا که ما از قبل یک قالب روی دیسک برای ایندکس‌های معکوس مبتنی بر بسته داریم، اجازه دهید از آن برای شبه‌بسته MIDX نیز مجدداً استفاده کنیم.

اشیاء موجود در MIDX به منظور اتصال در شبه‌بسته به صورت زیر مرتب می‌شوند. فرض کنید pack(o) بسته‌ای را برمی‌گرداند که o توسط MIDX از آن انتخاب شده است، و ترتیبی از بسته‌ها را بر اساس شناسه عددی آن‌ها (به شکلی که توسط MIDX ذخیره شده است) تعریف کنید. فرض کنید offset(o) آفست شیء o را درون pack(o) برمی‌گرداند. سپس o1 و o2 را به صورت زیر مقایسه کنید:

•اگر یکی از pack(o1) و pack(o2) ترجیح‌داده‌شده باشد و دیگری نباشد، آنگاه بسته ترجیح‌داده‌شده اول مرتب می‌شود.

(این جزئیاتی است که به نقشه‌بیت MIDX اجازه می‌دهد مشخص کند کدام بسته باید توسط مکانیزم بازاستفاده بسته استفاده شود، زیرا می‌تواند از MIDX بسته‌ای را که شامل شیء در موقعیت بیت ۰ است درخواست کند).

•اگر pack(o1) ≠ pack(o2) باشد، آنگاه این دو شیء را به ترتیب نزولی بر اساس شناسه بسته مرتب کنید.
•در غیر این صورت، pack(o1) = pack(o2) است، و اشیاء به ترتیب بسته مرتب می‌شوند (یعنی o1 دقیقاً زمانی جلوتر از o2 قرار می‌گیرد که offset(o1) < offset(o2) باشد).

به طور خلاصه، شبه‌بسته یک MIDX اتصال بدون تکرار اشیاء در بسته‌های ذخیره‌شده توسط MIDX است که با ترتیب بسته چیده شده‌اند، و بسته‌ها با ترتیب MIDX مرتب شده‌اند (به طوری که بسته ترجیح‌داده‌شده در ابتدا می‌آید).

ایندکس معکوس MIDX در تکه اختیاری RIDX درون خود MIDX ذخیره می‌شود.

تکه فایل‌های بسته نقشه‌بیتی (BTMP) اطلاعات اضافی را درباره اشیاء در نقشه‌بیت دسترسی‌پذیری ایندکس چندبسته‌ای کدگذاری می‌کند. به یاد داشته باشید که اشیاء موجود در MIDX با ترتیب "شبه‌بسته" (بالا را ببینید) برای نقشه‌بیت‌های دسترسی‌پذیری چیده شده‌اند.

بر اساس مثال بالا، فرض کنید بسته‌های "a"، "b" و "c" را به ترتیب با ۱۰، ۱۵ و ۲۰ شیء داریم. در ترتیب شبه‌بسته، این اشیاء به صورت زیر چیده می‌شوند:

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

هنگام کار با نقشه‌بیت‌های تک‌بسته‌ای (یا به طور معادل، نقشه‌بیت‌های دسترسی‌پذیری چندبسته‌ای با یک بسته ترجیح‌داده‌شده)، دستور git-pack-objects(1) بازاستفاده «عین‌به‌عین» (verbatim) را انجام می‌دهد و تلاش می‌کند تکه‌هایی از فایل بسته نقشه‌بیتی یا ترجیح‌داده‌شده را به جای افزودن اشیاء به فهرست بسته‌بندی، مجدداً استفاده کند.

هنگامی که تکه‌ای از بایت‌ها از یک بسته موجود مجدداً استفاده می‌شود، نیازی نیست اشیاء موجود در آن به فهرست بسته‌بندی اضافه شوند، که این امر در حافظه و زمان پردازنده صرفه‌جویی می‌کند. اما یک تکه از یک فایل بسته موجود تنها زمانی می‌تواند مجدداً استفاده شود که شرایط زیر برقرار باشد:

•تکه فقط حاوی اشیائی باشد که توسط فراخواننده درخواست شده‌اند (یعنی حاوی هیچ شیئی نباشد که فراخواننده صراحتاً یا ضمناً آن را نخواسته باشد).
•تمام اشیائی که در بسته‌های غیرنازک به صورت دلتاهای آفست یا مرجع ذخیره شده‌اند، شیء پایه خود را نیز در بسته نهایی شامل شوند.

تکه BTMP اطلاعات لازم را برای پیاده‌سازی بازاستفاده چندبسته‌ای بر روی مجموعه‌ای از فایل‌های بسته همان‌طور که در بالا شرح داده شد کدگذاری می‌کند. به طور خاص، تکه BTMP سه بخش اطلاعاتی (همگی اعداد صحیح بدون علامت ۳۲ بیتی با ترتیب بایت شبکه) را برای هر فایل بسته p که در MIDX ذخیره شده است به شرح زیر کدگذاری می‌کند:

bitmap_pos

اولین موقعیت بیتی (در ترتیب شبه‌بسته) در نقشه‌بیت دسترسی‌پذیری ایندکس چندبسته‌ای که توسط شیئی از p اشغال شده است.

bitmap_nr

تعداد موقعیت‌های بیتی (شامل موقعیت در bitmap_pos) که اشیاء را از آن بسته p کدگذاری می‌کنند.

برای مثال، تکه BTMP متناظر با مثال بالا (با بسته‌های «a»، «b» و «c») به این صورت خواهد بود:

bitmap_pos bitmap_nr
packfile “a” 0 10
packfile “b” 10 15
packfile “c” 25 20

با وجود این اطلاعات، می‌توانیم با هر فایل بسته به عنوان بسته‌ای با قابلیت بازاستفاده جداگانه به همان شیوه‌ای رفتار کنیم که پیش از پیاده‌سازی تکه BTMP برای بازاستفاده عین‌به‌عین بسته روی بسته‌های جداگانه انجام می‌شد.

قابلیت بسته‌های باطله (cruft packs) جایگزینی برای مکانیزم سنتی گیت در حذف اشیاء غیرقابل‌دسترس ارائه می‌دهد. این سند مروری بر مکانیزم هرس (pruning) گیت ارائه کرده و توضیح می‌دهد که چگونه می‌توان از یک بسته باطله به جای آن برای انجام همین کار استفاده کرد.

برای حذف اشیاء غیرقابل‌دسترس از مخزن، گیت دستور git repack -Ad را ارائه می‌دهد (نگاه کنید به git-repack(1)). به نقل از مستندات:

[...] اشیاء غیرقابل‌دسترس در یک بسته قبلی، به جای باقی ماندن در بسته قدیمی،
به اشیاء مجزا و بازشده تبدیل می‌شوند. [...] اشیاء مجزای غیرقابل‌دسترس بر اساس
قواعد انقضای معمول در اجرای بعدی 'git gc' هرس خواهند شد.

اشیاء غیرقابل‌دسترس بلافاصله حذف نمی‌شوند، زیرا این کار ممکن است با یک ارسال داده (push) ورودی که ممکن است به شیئی در حال حذف ارجاع دهد، تداخل زمانی (race) پیدا کند. در عوض، آن اشیاء غیرقابل‌دسترس به عنوان اشیاء مجزا ذخیره می‌شوند و تا زمانی که سن آن‌ها از پنجره انقضا بگذرد به همان صورت باقی می‌مانند؛ در آن زمان توسط git-prune(1) حذف می‌شوند.

گیت باید این اشیاء غیرقابل‌دسترس را به صورت مجزا ذخیره کند تا بتواند زمان‌های mtime اختصاصی هر شیء را ردیابی نماید. اگر این اشیاء غیرقابل‌دسترس در یک بسته بزرگ نوشته می‌شدند، آنگاه چه با تازه‌سازی آن بسته (به دلیل بازنویسی یک شیء درون آن) و چه با ایجاد یک بسته جدید از اشیاء غیرقابل‌دسترس، مقدار mtime بسته به‌روزرسانی می‌شد و اشیاء درون آن هرگز از پنجره انقضا خارج نمی‌شدند. در عوض، اشیاء به صورت مجزا نگه‌داری می‌شوند تا زمان mtime تک‌تک اشیاء ردیابی شود و از وضعیتی که در آن همه اشیاء باطله به طور همزمان تازه‌سازی می‌شوند جلوگیری گردد.

این امر می‌تواند به شرایط نامطلوبی منجر شود؛ به ویژه زمانی که یک مخزن شامل تعداد زیادی شیء غیرقابل‌دسترس است که هنوز دوره مهلت آن‌ها به پایان نرسیده است. داشتن دایرکتوری‌های بزرگ در بخش‌های مختلف .git/objects می‌تواند موجب افت کارایی در مخزن شود. علاوه بر این، در صورت وجود اشیاء غیرقابل‌دسترس به تعداد کافی، این وضعیت می‌تواند به اتمام inodeها (inode starvation) منجر شده و عملکرد کل سیستم را تضعیف کند. از آنجا که هرگز نمی‌توانیم آن اشیاء را بسته‌بندی کنیم، این مخازن اغلب حجم زیادی از فضای دیسک را اشغال می‌کنند، زیرا ما فقط می‌توانیم آن‌ها را با zlib فشرده سازیم اما امکان ذخیره آن‌ها در زنجیره‌های دلتا وجود ندارد.

یک بسته باطله با گنجاندن مقادیر mtime به ازای هر شیء در یک فایل جداگانه در کنار یک بسته واحد حاوی همه اشیاء مجزا، نیاز به ذخیره اشیاء غیرقابل‌دسترس در حالت مجزا را از بین می‌برد.

یک بسته باطله توسط دستور git repack --cruft هنگام تولید یک بسته جدید نوشته می‌شود (گزینه --cruft در git-pack-objects(1)). توجه داشته باشید که git repack --cruft یک بسته‌بندی مجدد کلاسیک همه‌در‌یکی (all-into-one) است، به این معنی که همه‌چیز در بسته حاصل قابل‌دسترس است و هر چیز دیگری غیرقابل‌دسترس تلقی می‌شود. پس از نوشتن، گزینه --cruft به دستور git repack اعلام می‌کند که بسته دیگری تولید کند که فقط شامل اشیائی باشد که در مرحله قبل بسته‌بندی نشده‌اند (که معادل بسته‌بندی همه اشیاء غیرقابل‌دسترس در کنار یکدیگر است). این فرآیند به صورت زیر پیش می‌رود:

1.شمارش تک‌تک اشیاء، با علامت‌گذاری هر شیئی که (الف) در یک بسته نگه‌داری‌شده (kept-pack) قرار ندارد، و (ب) زمان mtime آن در دوره مهلت قرار دارد، به عنوان یک سرشاخه پیمایش (traversal tip).
2.انجام یک پیمایش دسترسی‌پذیری بر اساس سرشاخه‌های جمع‌آوری‌شده در مرحله قبل، و افزودن تمام اشیاء در طول مسیر به بسته.
3.نوشتن بسته، به همراه یک فایل .mtimes که برچسب‌های زمانی تک‌تک اشیاء را ثبت می‌کند.

این حالت هنگامی که به git-repack(1) دستور نوشتن یک بسته باطله داده شود، به صورت داخلی فراخوانی می‌شود. نکته اساسی این است که مجموعه بسته‌های نگه‌داری‌شده در حافظه (in-core kept packs) دقیقاً همان مجموعه‌ای از بسته‌ها هستند که توسط repack حذف نخواهند شد؛ به عبارت دیگر، آن‌ها حاوی تمام اشیاء قابل‌دسترس مخزن هستند.

هنگامی که یک مخزن از قبل دارای یک بسته باطله باشد، دستور git repack --cruft معمولاً فقط اشیاء را به آن اضافه می‌کند. یک استثنا بر این قاعده زمانی است که به git repack گزینه --cruft-expiration داده شود، که به بسته باطله تولیدشده امکان می‌دهد به جای انتظار برای انقضای بعدی اشیاء توسط git-gc(1)، اشیاء منقضی‌شده را نادیده بگیرد و حذف کند.

به طور معمول این git-gc(1) است که مسئول حذف اشیاء غیرقابل‌دسترس منقضی‌شده می‌باشد.

جایگزین‌های قابل‌توجه برای این طرح عبارتند از:

•مکان داده‌های mtime اختصاصی هر شیء.

درباره مکان داده‌های mtime، یک فایل کمکی جدید متصل به بسته انتخاب شد تا از پیچیده شدن قالب .idx جلوگیری شود. اگر قالب .idx روزی پشتیبانی از تکه‌های اختیاری داده را به دست آورد، ادغام قالب .mtimes در خود فایل .idx ممکن است منطقی باشد.

بخشی از مجموعه git(1)

2026-06-29 Git 2.55.0