Paketo failai
Paketo failai yra git naudojamas efektyvaus saugojimo ir perdavimo būdas. Užuot saugojus kiekvieną objektą atskirame zlib suspaustame faile, daug objektų sujungiama į vieną failą, kuriame pasirinktinai taikomas deltinis suspaudimas. Beveik visose klonuotose saugyklose naudojami paketo failai.
Paketo failo struktūra
Paketo faile (.git/objects/pack/pack-{hash}.pack) yra:
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)
Objektų tipo kodavimas
Kiekvienas įrašas prasideda kintamo ilgio sveikąja reikšme, kurioje persipina tipas ir dydis:
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
Tipo reikšmės:
| Reikšmė | Tipas |
|---|---|
| 1 | Commit |
| 2 | Tree |
| 3 | Blob |
| 4 | Tag |
| 6 | OFS_DELTA (deltos poslinkiu) |
| 7 | REF_DELTA (deltos maiša) |
Paketo failų skaitymas
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();
Klasė PackFile:
- Patikrina
PACKantraštę ir versijos numerį - Randa poslinkį per
.idxfailą ir išanalizuoja įrašo tipo bei dydžio varintą - Išskleidžia zlib suspaustus duomenis
- Baziniams objektams: tiesiogiai grąžina turinį
- Deltiniams objektams: suranda bazinį objektą (sekdama grandines) ir pritaiko deltą, išspręstus objektus išsaugodama podėlyje
Paketo indekso failai
Kiekvienas .pack failas turi atitinkamą .idx failą, skirtą greitai paieškai. Be indekso objekto paieškai reikia peržiūrėti visą paketą.
v2 indekso formatas
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();
Išskleidimo lentelė leidžia atlikti O(log n) dvejetainę paiešką: 256 įrašai nurodo, kiek maišų prasideda kiekvienu galimu pirmuoju baitu.
v1 indekso formatas
Senesnis formatas be CRC32 kontrolinių sumų. Pitmaster palaiko abu formatus: PackIndex::open() perskaito v1 failą, jei nėra v2 magiškosios reikšmės, todėl tas pats kodas veikia su abiem formatais.
Deltų sprendimas
Deltomis saugomuose objektuose pateikiamos instrukcijos, kaip atkurti turinį iš bazinio objekto, užuot saugojus visą turinį. Taip gerokai suspaudžiami panašūs objektai (e.g., paeiliui einančios to paties failo versijos).
Deltų tipai
OFS_DELTA (6 tipas): bazinis objektas identifikuojamas neigiamu poslinkiu tame pačiame paketo faile. Šis tipas dažnesnis, nes nereikia žinoti bazinio objekto maišos.
REF_DELTA (7 tipas): bazinis objektas identifikuojamas pagal maišą. git bazinis objektas gali būti ir už paketo ribų (plonieji paketai). Pitmaster bazinių objektų ieško tik tame pačiame pakete, todėl plonieji paketai nepalaikomi (REF_DELTA#16).
Deltos instrukcijos
Deltos turinys yra kopijavimo ir įterpimo instrukcijų seka:
Copy: 1XXXXXXX [offset bytes] [size bytes] (copy from base)
Insert: 0LLLLLLL <L bytes of literal data> (insert new data)
Kopijavimo instrukcijos: 7 X bitai nurodo, kurie poslinkio ir dydžio baitai pateikti. Poslinkį sudaro iki 4 baitų, dydį – iki 3 baitų. Jei dydis yra nulis, tai reiškia 0x10000 (65536) baitų.
Įterpimo instrukcijos: 7 L bitai nurodo pažodinių duomenų ilgį (1–127 baitų). L=0 yra rezervuota ir negaliojanti reikšmė.
DeltaApplier
use Pitmaster\Pack\DeltaApplier;
$result = DeltaApplier::apply($baseContent, $deltaInstructions);
DeltaApplier:
- Iš deltos antraštės perskaito laukiamą bazės ir rezultato dydį (koduotus LEB128 formatu)
- Pereina instrukcijas:
- Kopijavimas: išskiria poslinkį ir dydį, nukopijuoja baitus iš bazinio turinio
- Įterpimas: išskiria ilgį, nukopijuoja pažodinius baitus iš deltos duomenų
- Patikrina, ar rezultato dydis sutampa su lauktu dydžiu
Deltų grandinės
Deltomis saugomo objekto bazė taip pat gali būti saugoma delta. PackFile::read() ir readAtOffset() seka grandinę:
- Perskaito įrašą. Jei tai bazinis tipas (commit, tree, blob, tag), grąžina jį.
- Jei OFS_DELTA: apskaičiuoja bazės poslinkį (dabartinis poslinkis minus užkoduotas neigiamas poslinkis), perskaito bazinį įrašą ir rekursyviai kartoja veiksmą.
- Jei REF_DELTA: ieško bazinio objekto pagal maišą to paties paketo indekse ir rekursyviai kartoja veiksmą.
- Išspręstai bazei pritaiko deltos instrukcijas.
- Taiko
PITMASTER_MAX_DELTA_CHAINribą (numatytoji reikšmė – 50), kad grandinės netęstųsi be galo.
// Configure max chain depth (integer)
define('PITMASTER_MAX_DELTA_CHAIN', 50);
// Read the effective limit
$max = \Pitmaster\Pack\DeltaResolver::maxChainDepth();
PackWriter
Pitmaster gali kurti paketo failus tinklo operacijoms (push).
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:
- Surenka į paketą įtrauktinus objektus
- Serializuoja kiekvieną objektą su tipo ir dydžio varinto antrašte
- Suglaudina turinį naudodama zlib
- Įrašo PACK antraštę, įrašus ir SHA-1 pabaigos duomenis
Šiuo metu įrašomi tik baziniai objektai (deltinis suspaudimas netaikomas). Deltinis suspaudimas yra optimizavimas, kurį galima pridėti vėliau; saugojimo efektyvumui pagerinti leiskite git gc pergrupuoti objektus.
PackEnumerator
Pereikite per visus paketo objektus indekso tvarka:
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";
}
Kiekvienas elementas yra išspręstas GitObject.
MultiPackIndex
Saugyklose, kuriose yra daug paketo failų, kelių paketų indeksas (MIDX) leidžia atlikti vieną paiešką visuose paketuose.
use Pitmaster\Pack\MultiPackIndex;
MIDX failą (.git/objects/pack/multi-pack-index) sudaro:
- Išskleidimo lentelė (kaip paketo indekso v2)
- Visų paketų objektų ID, surikiuoti pagal tvarką
- Paketų failų pavadinimai ir kiekvienam objektui priskirtas paketas
- Poslinkiai į atskirus paketo failus
MultiPackIndex::open($path) analizuoja MIDX failą ir suteikia findObject($hex) bei packNames(). Saugykloje objektų paieška šio indekso nenaudoja: objektų duomenų bazė visada skaito atskirus .idx failus.
CommitGraph
Commit grafas pagreitina commit istorijos peržiūrą ir sujungimo bazės apskaičiavimą.
use Pitmaster\Pack\CommitGraph;
Jis yra .git/objects/info/commit-graph ir jame pateikiama:
- Surikiuoti commit OID
- Pirminės nuorodos (indeksuotos, todėl nereikia skaityti objektų)
- Generavimo numeriai (greitesnėms pasiekiamumo užklausoms)
- Commit laiko žymos
Jei commit grafas yra, sujungimo bazės apskaičiuojant Repository::merge(), mergeBase(), isBranchMerged() ir push greitojo pirmyn perkėlimo patikrai, pirminių commit nuorodos skaitomos iš commit grafo, o ne iš atskirų commit objektų. CommitWalker (log) jo nenaudoja.
Kintamo ilgio sveikieji skaičiai
Paketo failuose naudojami keli kintamo ilgio sveikųjų skaičių kodavimo būdai:
Git varintas (tipo ir dydžio antraštės)
MSB tęstinis kodavimas, kuriame reikšmingiausias bitas nurodo, kad toliau eina daugiau baitų:
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 (deltų antraštės)
Naudojamas deltos instrukcijų antraštėse bazės ir rezultato dydžiui nurodyti:
use Pitmaster\Encoding\Leb128;
$value = Leb128::decodeUnsigned($reader); // a BinaryReader
$bytes = Leb128::encodeUnsigned($value);
BinaryReader
Žemo lygio baitų srautas su pozicijos sekimu:
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);