
# 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

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

Klasė `PackFile`:
1. Patikrina `PACK` antraštę ir versijos numerį
2. Randa poslinkį per `.idx` failą ir išanalizuoja įrašo tipo bei dydžio varintą
3. Išskleidžia zlib suspaustus duomenis
4. Baziniams objektams: tiesiogiai grąžina turinį
5. 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)
```

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

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

```php
use Pitmaster\Pack\DeltaApplier;

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

`DeltaApplier`:
1. Iš deltos antraštės perskaito laukiamą bazės ir rezultato dydį (koduotus LEB128 formatu)
2. Pereina instrukcijas:
   - Kopijavimas: išskiria poslinkį ir dydį, nukopijuoja baitus iš bazinio turinio
   - Įterpimas: išskiria ilgį, nukopijuoja pažodinius baitus iš deltos duomenų
3. 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ę:

1. Perskaito įrašą. Jei tai bazinis tipas (commit, tree, blob, tag), grąžina jį.
2. Jei OFS_DELTA: apskaičiuoja bazės poslinkį (dabartinis poslinkis minus užkoduotas neigiamas poslinkis), perskaito bazinį įrašą ir rekursyviai kartoja veiksmą.
3. Jei REF_DELTA: ieško bazinio objekto pagal maišą to paties paketo indekse ir rekursyviai kartoja veiksmą.
4. Išspręstai bazei pritaiko deltos instrukcijas.
5. Taiko `PITMASTER_MAX_DELTA_CHAIN` ribą (numatytoji reikšmė – 50), kad grandinės netęstųsi be galo.

```php
// 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).

```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. Surenka į paketą įtrauktinus objektus
2. Serializuoja kiekvieną objektą su tipo ir dydžio varinto antrašte
3. Suglaudina turinį naudodama zlib
4. Į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:

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

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.

```php
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ą.

```php
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ų:

```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 (deltų antraštės)

Naudojamas deltos instrukcijų antraštėse bazės ir rezultato dydžiui nurodyti:

```php
use Pitmaster\Encoding\Leb128;

$value = Leb128::decodeUnsigned($reader);   // a BinaryReader
$bytes = Leb128::encodeUnsigned($value);
```

### BinaryReader

Žemo lygio baitų srautas su pozicijos sekimu:

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