Pāriet uz saturu

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:

Kods
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:

Kods
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

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

Klase PackFile:

  1. Validē PACK galveni un versijas numuru
  2. Atrod nobīdi .idx failā un parsē ieraksta tipa/izmēra varint
  3. Atspiež ar zlib saspiestos datus
  4. Pamata objektiem: tieši atgriež saturu
  5. 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

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

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:

Kods
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

PHP
use Pitmaster\Pack\DeltaApplier;

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

DeltaApplier:

  1. Nolasa paredzēto pamata un rezultāta izmēru no delta galvenes (kodēts ar LEB128)
  2. 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
  3. 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:

  1. Nolasa ierakstu. Ja tas ir pamata tips (commit, tree, blob, tag), atgriež to.
  2. 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.
  3. Ja REF_DELTA: uzmeklē pamatu pēc jaucējvērtības tās pašas pakotnes indeksā un izsauc rekursiju.
  4. Atrisinātajam pamatam piemēro delta instrukcijas.
  5. Nosaka PITMASTER_MAX_DELTA_CHAIN (pēc noklusējuma 50), lai novērstu nekontrolētu ķēžu veidošanos.
PHP
// 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).

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. Apkopo pakotnē iekļaujamos objektus
  2. Serializē katru objektu ar tipa/izmēra varint galveni
  3. Saspiež saturu ar zlib
  4. 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ā:

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

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.

PHP
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.

PHP
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:

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

Izmanto delta instrukciju galvenēs pamata un rezultāta izmēram:

PHP
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:

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