İçeriğe geç

Paket Dosyaları

Paket dosyaları, git'in verimli depolama ve aktarım mekanizmasıdır. Her nesneyi ayrı bir zlib sıkıştırmalı dosyada saklamak yerine, paket dosyaları birçok nesneyi isteğe bağlı delta sıkıştırmasıyla tek bir dosyada birleştirir. Klonlanan depoların neredeyse tamamı paket dosyalarını kullanır.

Paket dosyası yapısı

Bir paket dosyası (.git/objects/pack/pack-{hash}.pack) şunları içerir:

Kod
Header:   PACK (4 bytes) + version (4 bytes, big-endian) + count (4 bytes, big-endian)
Entries:  [type+size varint] [delta-base] [zlib-compressed data] ...
Trailer:  checksum of the pack (20 bytes SHA-1, 32 bytes SHA-256)

Nesne türü kodlaması

Her girdi, tür ve boyut bilgilerinin iç içe kodlandığı değişken uzunluklu bir tamsayıyla başlar:

Kod
First byte: 1TTTSSSS
  TTT  = object type (1-4 for base objects, 6 for OFS_DELTA, 7 for REF_DELTA)
  SSSS = first 4 bits of uncompressed size
  MSB  = 1 means more size bytes follow

Tür değerleri:

Değer Tür
1 Commit
2 Tree
3 Blob
4 Tag
6 OFS_DELTA (ofset tabanlı delta)
7 REF_DELTA (özet tabanlı delta)

Paket dosyalarını okuma

PHP
use Pitmaster\Pack\PackFile;

$pack = PackFile::open(
    '/path/to/.git/objects/pack/pack-abc123.pack',
    '/path/to/.git/objects/pack/pack-abc123.idx',
);

$pack->has($hex);               // Is the object in this pack?
$object = $pack->read($hex);    // Resolved GitObject, or null
$object = $pack->readAtOffset($offset);
$hashes = $pack->allHashes();

PackFile sınıfı:

  1. PACK başlığını ve sürüm numarasını doğrular
  2. .idx dosyası üzerinden ofseti bulur ve girdinin tür/boyut değişken uzunluklu tamsayısını ayrıştırır
  3. zlib ile sıkıştırılmış veriyi açar
  4. Taban nesneleri için içeriği doğrudan döndürür
  5. Delta nesneleri için tabanı çözümler (zincirleri izleyerek) ve deltayı uygular; çözümlenmiş nesneleri önbelleğe alır

Paket dizin dosyaları

Her .pack dosyasının hızlı aramalar için karşılık gelen bir .idx dosyası vardır. Dizin olmadan bir nesneyi bulmak için tüm paketi taramak gerekir.

v2 dizin biçimi

Kod
Magic:    FF 74 4F 63 (identifies v2)
Version:  00 00 00 02
Fanout:   256 x 4-byte big-endian counts (cumulative)
Names:    N x 20-byte SHA-1 hashes, sorted (32 bytes for SHA-256)
CRC32s:   N x 4-byte CRC32 checksums
Offsets:  N x 4-byte pack offsets (MSB=1 means large offset table)
Large:    M x 8-byte offsets (for packs > 2GB)
Checksums: pack checksum + index checksum (20 bytes each for SHA-1)
PHP
use Pitmaster\Pack\PackIndex;

$index = PackIndex::open('/path/to/pack-abc123.idx');

// Look up an object
$offset = $index->findOffset($hex);  // Pack file offset, or null

// List all objects in the pack
$hashes = $index->allHashes();
$count = $index->objectCount();

Fanout tablosu, O(log n) ikili arama yapılmasını sağlar: 256 girdileri, olası her ilk baytla başlayan kaç özet bulunduğunu belirtir.

v1 dizin biçimi

CRC32 sağlama toplamları olmayan eski biçim. Pitmaster her iki biçimi de destekler: PackIndex::open(), v2 sihirli değeri yoksa bir v1 dosyasını okur; böylece aynı kod her ikisiyle de çalışır.

Delta çözümleme

Delta biçimli nesneler, içeriğin tamamını saklamak yerine içeriği bir taban nesneden yeniden oluşturmak için gereken talimatları saklar. Bu, benzer nesnelerde (e.g, aynı dosyanın ardışık sürümlerinde) önemli ölçüde sıkıştırma sağlar.

Delta türleri

OFS_DELTA (tür 6): taban nesne, aynı paket dosyasındaki negatif bir ofsetle tanımlanır. Taban nesnenin özetini bilmeyi gerektirmediği için daha yaygındır.

REF_DELTA (tür 7): temel nesne, hash değeriyle tanımlanır. git içinde temel nesne paketin dışında bulunabilir (ince paketler). Pitmaster, REF_DELTA temel nesnelerini yalnızca aynı paketin içinde arar; bu nedenle ince paketler desteklenmez (#16).

Delta talimatları

Delta içeriği, kopyalama ve ekleme talimatları dizisidir:

Kod
Copy:   1XXXXXXX [offset bytes] [size bytes]   (copy from base)
Insert: 0LLLLLLL <L bytes of literal data>       (insert new data)

Kopyalama talimatları: 7 X biti, hangi ofset ve boyut baytlarının bulunduğunu gösterir. Ofset en fazla 4 bayt, boyut en fazla 3 bayttır. Boyut sıfırsa 0x10000 (65536) bayt anlamına gelir.

Ekleme talimatları: 7 L biti, değişmez veri uzunluğunu (1-127 bayt) verir. L=0 ayrılmıştır/geçersizdir.

DeltaApplier

PHP
use Pitmaster\Pack\DeltaApplier;

$result = DeltaApplier::apply($baseContent, $deltaInstructions);

DeltaApplier:

  1. Delta başlığından beklenen taban boyutunu ve sonuç boyutunu okur (LEB128 kodlu)
  2. Talimatlar arasında ilerler:
    • Kopyala: ofseti ve boyutu çıkarır, taban içeriğinden baytları kopyalar
    • Ekle: uzunluğu çıkarır, değişmez baytları delta verisinden kopyalar
  3. Sonuç boyutunun beklenen boyutla eşleştiğini doğrular

Delta zincirleri

Delta biçimli bir nesnenin tabanı da delta biçimli olabilir. PackFile::read() ve readAtOffset() zinciri izler:

  1. Girdiyi oku. Taban türündeyse (commit, tree, blob, tag) döndür.
  2. OFS_DELTA ise taban ofsetini hesapla (geçerli ofset eksi kodlanmış negatif ofset), taban girdisini oku ve özyinelemeli olarak devam et.
  3. REF_DELTA ise aynı paketin dizininde tabanı özetiyle ara ve özyinelemeli olarak devam et.
  4. Çözümlenmiş tabana delta talimatlarını uygula.
  5. Zincirlerin denetimsiz büyümesini önlemek için PITMASTER_MAX_DELTA_CHAIN sınırını uygula (varsayılan 50).
PHP
// Configure max chain depth (integer)
define('PITMASTER_MAX_DELTA_CHAIN', 50);

// Read the effective limit
$max = \Pitmaster\Pack\DeltaResolver::maxChainDepth();

PackWriter

Pitmaster, ağ işlemleri (push) için paket dosyaları yazabilir.

PHP
use Pitmaster\Pack\PackWriter;
use Pitmaster\Pack\PackIndexer;

$packData = PackWriter::encode($objects);           // GitObject[] to pack bytes
$paths = PackWriter::write($packDir, $objects);     // ['packPath' => ..., 'idxPath' => ..., 'hash' => ...]

$idxPath = PackIndexer::writeIndex($packPath);      // Build the .idx for a received pack

PackWriter:

  1. Pakete eklenecek nesneleri toplar
  2. Her nesneyi tür/boyut değişken uzunluklu başlığıyla serileştirir
  3. İçeriği zlib ile sıkıştırır
  4. PACK başlığını, girdileri ve SHA-1 son ekini yazar

Şu anda yalnızca taban nesnelerini yazar (delta sıkıştırması yoktur). Delta sıkıştırması daha sonra eklenebilecek bir iyileştirmedir; depolama verimliliği için yeniden paketleme işini git gc'e bırakın.

PackEnumerator

Bir paketteki tüm nesneleri dizin sırasıyla dolaşın:

PHP
use Pitmaster\Pack\PackEnumerator;

$enumerator = new PackEnumerator($packFile); // a PackFile opened with its .idx

foreach ($enumerator->enumerate() as $object) {
    echo "{$object->type->value} {$object->id->hex} {$object->size()}\n";
}

Her öğe çözümlenmiş bir GitObject'dir.

MultiPackIndex

Çok sayıda paket dosyası olan depolarda çoklu paket dizini (MIDX), tüm paketlerde tek bir arama olanağı sağlar.

PHP
use Pitmaster\Pack\MultiPackIndex;

MIDX dosyası (.git/objects/pack/multi-pack-index) şunları içerir:

  • Bir fanout tablosu (paket dizini v2 gibi)
  • Tüm paketler genelinde sıralanmış nesne kimlikleri
  • Paket dosyası adları ve nesne başına paket atamaları
  • Tek tek paket dosyalarındaki ofsetler

MultiPackIndex::open($path), bir MIDX dosyasını ayrıştırır ve findObject($hex) ile packNames() sunar. Depodaki nesne aramaları bunu kullanmaz: nesne veritabanı her zaman tek tek .idx dosyalarını okur.

CommitGraph

Commit grafiği dosyası, commit geçmişinde gezinmeyi ve birleştirme tabanı hesaplamasını hızlandırır.

PHP
use Pitmaster\Pack\CommitGraph;

.git/objects/info/commit-graph konumunda bulunur ve şunları içerir:

  • Sıralanmış commit OID'leri
  • Üst öğe bilgileri (dizinlenmiştir; nesnelerin okunması gerekmez)
  • Daha hızlı erişilebilirlik sorguları için nesil numaraları
  • Commit zaman damgaları

Mevcut olduğunda, Repository::merge(), mergeBase() ve isBranchMerged() arkasındaki birleştirme tabanı hesaplaması ile push hızlı ileri kontrolü, tek tek commit nesnelerini okumak yerine üst öğe bağlantılarını commit grafiğinden okur. CommitWalker (log) bunu kullanmaz.

Değişken uzunluklu tamsayılar

Paket dosyaları birden fazla değişken uzunluklu tamsayı kodlaması kullanır:

Git varint (tür/boyut başlıkları)

En anlamlı bitin daha fazla bayt olduğunu belirttiği MSB-devam kodlaması:

PHP
use Pitmaster\Encoding\VarInt;

$size = VarInt::decodePackSize($reader, $initialBits);  // continues the size from the first header byte
$offset = VarInt::decodeOfsOffset($reader);             // OFS_DELTA base distance
$header = VarInt::encodePackSize($type, $size);

LEB128 (delta başlıkları)

Taban boyutu ve sonuç boyutu için delta talimatı başlıklarında kullanılır:

PHP
use Pitmaster\Encoding\Leb128;

$value = Leb128::decodeUnsigned($reader);   // a BinaryReader
$bytes = Leb128::encodeUnsigned($value);

BinaryReader

Konum takibi olan düşük seviyeli bayt akışı:

PHP
use Pitmaster\Encoding\BinaryReader;

$reader = new BinaryReader($data);
$byte = $reader->readByte();
$int = $reader->readUint32();   // big-endian
$bytes = $reader->read(20);
$hash = $reader->readHash(20);
$reader->position();  // Current offset
$reader->seek(0);