
# 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

```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

```
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](https://github.com/inline0/pitmaster/issues/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

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