Liigu sisu juurde

Pakifailid

Pakifailid on git-i meetod objektide tõhusaks salvestamiseks ja edastamiseks. Selle asemel et salvestada iga objekt eraldi zlib-iga tihendatud failina, ühendatakse pakifailis mitu objekti, soovi korral kasutatakse delta-tihendust. Peaaegu kõik kloonitud hoidlad kasutavad pakifaile.

Pakifaili struktuur

Pakifail (.git/objects/pack/pack-{hash}.pack) sisaldab järgmist:

Kood
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)

Objekti tüübi kodeerimine

Iga kirje algab muutuva pikkusega täisarvuga, milles tüüp ja suurus on põimitud:

Kood
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üübi väärtused:

Väärtus Tüüp
1 Commit
2 Puu
3 Blob
4 Silt
6 OFS_DELTA (nihkepõhine delta)
7 REF_DELTA (räsipõhine delta)

Pakifailide lugemine

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();

Klass PackFile:

  1. Kontrollib PACK päist ja versiooninumbrit
  2. Leiab nihke .idx faili abil ning parsib kirje tüübi/suuruse varinti
  3. Pakib zlib-iga tihendatud andmed lahti
  4. Baasobjektide korral tagastab sisu otse
  5. Deltaobjektide korral lahendab baasi (järgides ahelaid) ja rakendab deltat, salvestades lahendatud objektid vahemällu

Paki indeksifailid

Igal .pack failil on kiireks otsinguks vastav .idx fail. Indeksita tuleb objekti leidmiseks kogu pakk läbi otsida.

v2 indeksi vorming

Kood
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();

Hajutustabel võimaldab teha O(log n) binaarotsingu: 256 kirjed näitavad, mitu räsi algab iga võimaliku esimese baidiga.

v1 indeksi vorming

Vanem vorming ilma CRC32 kontrollsummadeta. Pitmaster toetab mõlemat: PackIndex::open() loeb faili v1, kui v2 maagiline väärtus puudub, nii et sama kood töötab kummagagi.

Delta lahendamine

Deltaks teisendatud objektid salvestavad sisu rekonstrueerimise juhised baasobjekti põhjal, mitte kogu sisu. Nii saavutatakse märkimisväärne tihendus sarnaste objektide puhul (e.g näiteks sama faili järjestikused versioonid).

Delta tüübid

OFS_DELTA (tüüp 6): baasobjekti määrab negatiivne nihe samas pakifailis. See on levinum, sest baasobjekti räsi pole vaja teada.

REF_DELTA (tüüp 7): baasobjekti määrab selle räsi. git-is võib baas asuda väljaspool pakki (õhukesed pakid). Pitmaster otsib REF_DELTA baase ainult samast pakist, seega õhukesed pakid pole toetatud (#16).

Delta juhised

Delta sisu koosneb kopeerimis- ja lisamisjuhiste jadast:

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

Kopeerimisjuhised: 7 X bitti näitavad, millised nihke- ja suurusebaidid on olemas. Nihe võib olla kuni 4 baiti, suurus kuni 3 baiti. Kui suurus on null, tähendab see 0x10000 (65536) baiti.

Lisamisjuhised: 7 L-bitti määravad literaalandmete pikkuse (1–127 baiti). L=0 on reserveeritud ja kehtetu.

DeltaApplier

PHP
use Pitmaster\Pack\DeltaApplier;

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

DeltaApplier:

  1. Loeb delta päisest oodatava baassuuruse ja tulemuse suuruse (LEB128-kodeeringus)
  2. Käib juhised läbi:
    • Kopeerimine: eraldab nihke ja suuruse ning kopeerib baassisu baidid
    • Lisamine: eraldab pikkuse ja kopeerib delta andmetest literaalbaidid
  3. Kontrollib, et tulemuse suurus vastaks oodatud suurusele

Delta ahelad

Deltaks teisendatud objekti baas võib samuti olla deltaks teisendatud. PackFile::read() ja readAtOffset() järgivad ahelat:

  1. Loeb kirje. Kui see on baasobjekti tüüpi (commit, puu, blob, silt), tagastab selle.
  2. Kui tegemist on OFS_DELTA-ga: arvutab baasi nihke (praegune nihe miinus kodeeritud negatiivne nihe), loeb baaskirje ja kordab toimingut rekursiivselt.
  3. Kui tegemist on REF_DELTA-ga: otsib baasi sama paki indeksist räsi järgi ja kordab toimingut rekursiivselt.
  4. Rakendab lahendatud baasile delta juhised.
  5. Piirab ahela pikkuse väärtusega PITMASTER_MAX_DELTA_CHAIN (vaikimisi 50), et vältida lõputuid ahelaid.
PHP
// Configure max chain depth (integer)
define('PITMASTER_MAX_DELTA_CHAIN', 50);

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

PackWriter

Pitmaster saab võrgutoimingute jaoks (push) pakifaile kirjutada.

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. Kogub pakki kaasatavad objektid
  2. Serialiseerib iga objekti tüübi/suuruse varindi päisega
  3. Tihendab sisu zlib-iga
  4. Kirjutab PACK-päise, kirjed ja SHA-1 lõpplõigu

Praegu kirjutab ainult baasobjekte (delta-tihenduseta). Delta-tihendus on optimeerimisvõimalus, mille saab hiljem lisada; laske git gc-l salvestusruumi tõhusamaks kasutamiseks objektid ümber pakkida.

PackEnumerator

Käib paki kõik objektid indeksi järjekorras läbi:

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";
}

Iga üksus on lahendatud GitObject.

MultiPackIndex

Paljude pakifailidega hoidlates võimaldab mitme paki indeks (MIDX) otsida kõigist pakkidest korraga.

PHP
use Pitmaster\Pack\MultiPackIndex;

MIDX-fail (.git/objects/pack/multi-pack-index) sisaldab järgmist:

  • Hajutustabelit (nagu pakiindeksis v2)
  • Kõigi pakkide vahel sorteeritud objektitunnuseid
  • Pakifailide nimesid ja iga objekti paki määranguid
  • Nihkeid üksikutesse pakifailidesse

MultiPackIndex::open($path) parsib MIDX-faili ning pakub findObject($hex) ja packNames(). Hoidla objektide otsingud seda ei kasuta: objektide andmebaas loeb alati üksikuid .idx faile.

CommitGraph

Commit-graafi fail kiirendab commit-ajaloo läbimist ja ühendamise baasi arvutamist.

PHP
use Pitmaster\Pack\CommitGraph;

See asub asukohas .git/objects/info/commit-graph ning sisaldab järgmist:

  • Sorteeritud järjestuses commit’ide OID-sid
  • Vanemate teavet (indekseeritud, objektide lugemist pole vaja)
  • Põlvkonnanumbreid (kättesaadavuspäringute kiirendamiseks)
  • Commit’ide ajatempleid

Kui commit-graaf on olemas, loetakse Repository::merge(), mergeBase(), isBranchMerged() aluseks oleva merge-base’i arvutamise ja push’i kiiredasiliikumise kontrolli jaoks vanemlingid commit-graafist, mitte üksikutest commit-objektidest. CommitWalker (log) seda ei kasuta.

Muutuva pikkusega täisarvud

Pakifailid kasutavad mitut muutuva pikkusega täisarvu kodeeringut:

Git varint (tüübi/suuruse päised)

MSB-jätku kodeeringus näitab kõige olulisem bitt, et järgnevad veel baidid:

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 päised)

Kasutatakse delta juhiste päistes baassuuruse ja tulemuse suuruse jaoks:

PHP
use Pitmaster\Encoding\Leb128;

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

BinaryReader

Madalatasemeline baidivoog koos asukoha jälgimisega:

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);