Pakotņu faili
Pakotņu faili ir git mehānisms efektīvai glabāšanai un pārsūtīšanai. Tā vietā, lai katru objektu glabātu atsevišķā, ar zlib saspiestā failā, pakotņu faili apvieno daudzus objektus vienā failā, kurā pēc izvēles var izmantot delta saspiešanu. Pakotņu failus izmanto gandrīz visi klonētie repozitoriji.
Pakotnes faila struktūra
Pakotnes fails (.git/objects/pack/pack-{hash}.pack) satur:
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)
Objektu veidu kodējums
Katrs ieraksts sākas ar mainīga garuma veselu skaitli, kurā veids un izmērs ir savīti:
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
Veidu vērtības:
| Vērtība | Veids |
|---|---|
| 1 | Commit |
| 2 | Tree |
| 3 | Blob |
| 4 | Tag |
| 6 | OFS_DELTA (uz nobīdi balstīta delta) |
| 7 | REF_DELTA (uz jaucējvērtību balstīta delta) |
Pakotņu failu nolasīšana
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();
Klase PackFile:
- Validē
PACKgalveni un versijas numuru - Atrod nobīdi
.idxfailā un parsē ieraksta tipa/izmēra varint - Atspiež ar zlib saspiestos datus
- Pamata objektiem: tieši atgriež saturu
- Delta objektiem: atrisina pamatu (sekojot ķēdēm) un piemēro delta, kešojot atrisinātos objektus
Pakotņu indeksu faili
Katram .pack failam atbilst .idx fails ātrai uzmeklēšanai. Bez indeksa, lai atrastu objektu, ir jāpārmeklē visa pakotne.
v2 indeksa formāts
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();
Fanu izkliedes tabula nodrošina O(log n) bināro meklēšanu: 256 ieraksti norāda, cik jaucējvērtību sākas ar katru iespējamo pirmo baitu.
v1 indeksa formāts
Vecāks formāts bez CRC32 kontrolsummām. Pitmaster atbalsta abus: ja trūkst v2 maģiskās vērtības, PackIndex::open() nolasa v1 failu, tāpēc viens un tas pats kods darbojas abiem formātiem.
Delta atrisināšana
Delta formātā saglabātie objekti glabā instrukcijas satura atjaunošanai no pamata objekta, nevis pilnu saturu. Tas nodrošina ievērojamu saspiešanu līdzīgiem objektiem (e.g., secīgām viena faila versijām).
Delta veidi
OFS_DELTA (6. tips): pamata objekts tiek identificēts pēc negatīvas nobīdes tajā pašā pakotnes failā. Šis veids ir biežāk sastopams, jo nav jāzina pamata objekta jaucējvērtība.
REF_DELTA (7. tips): pamata objekts tiek identificēts pēc tā jaucējvērtības. Git gadījumā pamats var atrasties ārpus pakotnes (plānās pakotnes). Pitmaster meklē REF_DELTA pamatus tikai tajā pašā pakotnē, tāpēc plānās pakotnes netiek atbalstītas (#16).
Delta instrukcijas
Delta saturs ir kopēšanas un ievietošanas instrukciju secība:
Copy: 1XXXXXXX [offset bytes] [size bytes] (copy from base)
Insert: 0LLLLLLL <L bytes of literal data> (insert new data)
Kopēšanas instrukcijas: 7 X biti norāda, kuri nobīdes un izmēra baiti ir klātesoši. Nobīde ir līdz 4 baitiem, izmērs — līdz 3 baitiem. Ja izmērs ir nulle, tas nozīmē 0x10000 (65536) baitus.
Ievietošanas instrukcijas: 7 L biti norāda burtisko datu garumu (1–127 baiti). L=0 ir rezervēts/nederīgs.
DeltaApplier
use Pitmaster\Pack\DeltaApplier;
$result = DeltaApplier::apply($baseContent, $deltaInstructions);
DeltaApplier:
- Nolasa paredzēto pamata un rezultāta izmēru no delta galvenes (kodēts ar LEB128)
- Iziet cauri instrukcijām:
- Kopēšana: iegūst nobīdi un izmēru, kopē baitus no pamata satura
- Ievietošana: iegūst garumu, kopē burtiskos baitus no delta datiem
- Pārbauda, vai rezultāta izmērs atbilst paredzētajam izmēram
Delta ķēdes
Delta formātā saglabāta objekta pamats pats var būt delta formātā. PackFile::read() un readAtOffset() seko ķēdei:
- Nolasa ierakstu. Ja tas ir pamata tips (commit, tree, blob, tag), atgriež to.
- Ja OFS_DELTA: aprēķina pamata nobīdi (pašreizējā nobīde mīnus kodētā negatīvā nobīde), nolasa pamata ierakstu un izsauc rekursiju.
- Ja REF_DELTA: uzmeklē pamatu pēc jaucējvērtības tās pašas pakotnes indeksā un izsauc rekursiju.
- Atrisinātajam pamatam piemēro delta instrukcijas.
- Nosaka
PITMASTER_MAX_DELTA_CHAIN(pēc noklusējuma 50), lai novērstu nekontrolētu ķēžu veidošanos.
// Configure max chain depth (integer)
define('PITMASTER_MAX_DELTA_CHAIN', 50);
// Read the effective limit
$max = \Pitmaster\Pack\DeltaResolver::maxChainDepth();
PackWriter
Pitmaster var rakstīt pakotņu failus tīkla darbībām (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:
- Apkopo pakotnē iekļaujamos objektus
- Serializē katru objektu ar tipa/izmēra varint galveni
- Saspiež saturu ar zlib
- Ieraksta PACK galveni, ierakstus un SHA-1 noslēguma daļu
Pašlaik raksta tikai pamata objektus (bez delta saspiešanas). Delta saspiešanu var pievienot vēlāk kā optimizāciju; ļaujiet git gc pārpakot datus, lai efektīvāk izmantotu krātuvi.
PackEnumerator
Iziet cauri visiem pakotnes objektiem indeksa secībā:
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";
}
Katrs vienums ir atrisināts GitObject.
MultiPackIndex
Repozitorijiem ar daudziem pakotņu failiem vairāku pakotņu indekss (MIDX) nodrošina vienotu uzmeklēšanu visās pakotnēs.
use Pitmaster\Pack\MultiPackIndex;
MIDX fails (.git/objects/pack/multi-pack-index) satur:
- Fanu izkliedes tabulu (tādu pašu kā pakotnes indeksā v2)
- Objektu ID, sakārtotus visās pakotnēs
- Pakotņu failu nosaukumus un katra objekta pakotnes piešķīrumus
- Nobīdes atsevišķajos pakotņu failos
MultiPackIndex::open($path) parsē MIDX failu un nodrošina findObject($hex) un packNames(). Objektu uzmeklēšanai repozitorijā tas netiek izmantots: objektu datubāze vienmēr nolasa atsevišķos .idx failus.
CommitGraph
Commit grafu fails paātrina commit vēstures pārlūkošanu un sapludināšanas bāzes aprēķināšanu.
use Pitmaster\Pack\CommitGraph;
Atrodas vietā .git/objects/info/commit-graph, un satur:
- Commit OID sakārtotā secībā
- Informāciju par vecākobjektiem (indeksētu, bez nepieciešamības nolasīt objektus)
- Paaudžu numurus (ātrākiem sasniedzamības vaicājumiem)
- Commit laikspiedolus
Ja tas ir pieejams, sapludināšanas bāzes aprēķins, ko veic Repository::merge(), mergeBase(), isBranchMerged() un push ātrās pārsūtīšanas pārbaude, nolasa vecākobjektu saites no commit grafa, nevis atsevišķus commit objektus. CommitWalker (log) to neizmanto.
Mainīga garuma veseli skaitļi
Pakotņu faili izmanto vairākus mainīga garuma veselu skaitļu kodējumus:
Git varint (tipa/izmēra galvenes)
MSB turpinājuma kodējums, kur visnozīmīgākais bits norāda, ka seko vēl citi baiti:
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 galvenes)
Izmanto delta instrukciju galvenēs pamata un rezultāta izmēram:
use Pitmaster\Encoding\Leb128;
$value = Leb128::decodeUnsigned($reader); // a BinaryReader
$bytes = Leb128::encodeUnsigned($value);
BinaryReader
Zema līmeņa baitu plūsma ar pozīcijas uzskaiti:
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);