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:
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:
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
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:
- Kontrollib
PACKpäist ja versiooninumbrit - Leiab nihke
.idxfaili abil ning parsib kirje tüübi/suuruse varinti - Pakib zlib-iga tihendatud andmed lahti
- Baasobjektide korral tagastab sisu otse
- 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
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)
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:
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
use Pitmaster\Pack\DeltaApplier;
$result = DeltaApplier::apply($baseContent, $deltaInstructions);
DeltaApplier:
- Loeb delta päisest oodatava baassuuruse ja tulemuse suuruse (LEB128-kodeeringus)
- Käib juhised läbi:
- Kopeerimine: eraldab nihke ja suuruse ning kopeerib baassisu baidid
- Lisamine: eraldab pikkuse ja kopeerib delta andmetest literaalbaidid
- 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:
- Loeb kirje. Kui see on baasobjekti tüüpi (commit, puu, blob, silt), tagastab selle.
- Kui tegemist on OFS_DELTA-ga: arvutab baasi nihke (praegune nihe miinus kodeeritud negatiivne nihe), loeb baaskirje ja kordab toimingut rekursiivselt.
- Kui tegemist on REF_DELTA-ga: otsib baasi sama paki indeksist räsi järgi ja kordab toimingut rekursiivselt.
- Rakendab lahendatud baasile delta juhised.
- Piirab ahela pikkuse väärtusega
PITMASTER_MAX_DELTA_CHAIN(vaikimisi 50), et vältida lõputuid ahelaid.
// 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.
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:
- Kogub pakki kaasatavad objektid
- Serialiseerib iga objekti tüübi/suuruse varindi päisega
- Tihendab sisu zlib-iga
- 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:
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.
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.
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:
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:
use Pitmaster\Encoding\Leb128;
$value = Leb128::decodeUnsigned($reader); // a BinaryReader
$bytes = Leb128::encodeUnsigned($value);
BinaryReader
Madalatasemeline baidivoog koos asukoha jälgimisega:
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);