
# Csomagfájlok

A csomagfájlok a git hatékony tárolást és átvitelt szolgáló mechanizmusa. Ahelyett, hogy minden objektumot külön zlib-tömörített fájlban tárolna, több objektumot egyetlen fájlba egyesít, opcionális deltatömörítéssel. Szinte minden klónozott tároló csomagfájlokat használ.

## A csomagfájl felépítése

A csomagfájl (`.git/objects/pack/pack-{hash}.pack`) tartalma:

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

### Objektumtípus-kódolás

Minden bejegyzés változó hosszúságú egész számmal kezdődik, amelyben a típus és a méret bitjei összefonódnak:

```
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ípusértékek:
| Érték | Típus |
|-------|------|
| 1 | Commit |
| 2 | Tree |
| 3 | Blob |
| 4 | Tag |
| 6 | OFS_DELTA (eltolásalapú delta) |
| 7 | REF_DELTA (hashalapú delta) |

## Csomagfájlok beolvasása

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

A `PackFile` osztály:
1. Ellenőrzi a `PACK` fejlécet és verziószámot
2. Megkeresi az eltolást a `.idx` fájlban, és feldolgozza a bejegyzés típusát és méretét tartalmazó varint értéket
3. Kitömöríti a zlib-tömörített adatokat
4. Alapobjektumok esetén közvetlenül visszaadja a tartalmat
5. Deltaobjektumok esetén feloldja az alapot (a láncokat követve), alkalmazza a deltát, majd gyorsítótárazza a feloldott objektumokat

## Csomagindexfájlok

Minden `.pack` fájlhoz tartozik egy `.idx` fájl a gyors kereséshez. Index nélkül egy objektum megtalálásához a teljes csomagot végig kell olvasni.

### v2 indexformátum

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

A fanout tábla O(log n) bináris keresést tesz lehetővé: a 256 bejegyzések megadják, hogy az egyes lehetséges első bájtokkal hány hash kezdődik.

### v1 indexformátum

Régebbi formátum CRC32-ellenőrző összegek nélkül. A Pitmaster mindkettőt támogatja: a `PackIndex::open()` akkor olvas be v1 fájlt, ha hiányzik a v2 mágikus érték, így ugyanaz a kód mindkét formátummal működik.

## Deltafeloldás

A deltásított objektumok a teljes tartalom helyett az alapobjektumból történő visszaállításhoz szükséges utasításokat tárolják. Ez hasonló objektumok esetén jelentős tömörítést eredményez (e.g, például ugyanazon fájl egymást követő verziói).

### Deltatípusok

**OFS_DELTA** (6-os típus): az alapobjektumot az ugyanazon csomagfájlon belüli negatív eltolás azonosítja. Ez gyakoribb, mert nem kell ismerni az alapobjektum hash értékét.

**REF_DELTA** (7-es típus): az alapobjektumot a hash értéke azonosítja. A gitben az alap a csomagon kívül is lehet (vékony csomagok). A Pitmaster csak az ugyanazon csomagon belüli REF_DELTA alapokat keresi, ezért a vékony csomagok nem támogatottak ([#16](https://github.com/inline0/pitmaster/issues/16)).

### Deltautasítások

A delta tartalma másolási és beszúrási utasítások sorozata:

```
Copy:   1XXXXXXX [offset bytes] [size bytes]   (copy from base)
Insert: 0LLLLLLL <L bytes of literal data>       (insert new data)
```

Másolási utasítások: a 7 X bit jelzi, mely eltolás- és méretbájtok vannak jelen. Az eltolás legfeljebb 4, a méret legfeljebb 3 bájt lehet. Ha a méret nulla, az 0x10000 (65536) bájtot jelent.

Beszúrási utasítások: a 7 L bit adja meg a szó szerinti adatok hosszát (1–127 bájt). Az L=0 érték fenntartott/érvénytelen.

### DeltaApplier

```php
use Pitmaster\Pack\DeltaApplier;

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

A `DeltaApplier`:
1. Beolvassa a várt alapméretet és az eredmény méretét a delta fejlécéből (LEB128-kódolással)
2. Végigmegy az utasításokon:
   - Másolás: kinyeri az eltolást és a méretet, majd bájtokat másol az alaptartalomból
   - Beszúrás: kinyeri a hosszt, majd a szó szerinti bájtokat másolja a deltaadatokból
3. Ellenőrzi, hogy az eredmény mérete megfelel-e a várt méretnek

### Deltaláncok

Egy deltásított objektum alapja maga is deltásított lehet. A `PackFile::read()` és a `readAtOffset()` követi a láncot:

1. Beolvassa a bejegyzést. Ha alaptípusú (commit, fa, blob, címke), visszaadja.
2. Ha OFS_DELTA: kiszámítja az alap eltolását (az aktuális eltolásból kivonja a kódolt negatív eltolást), beolvassa az alapbejegyzést, majd rekurzívan folytatja.
3. Ha REF_DELTA: megkeresi az alapot hash szerint ugyanazon csomag indexében, majd rekurzívan folytatja.
4. Alkalmazza a deltautasításokat a feloldott alapon.
5. Érvényesíti a `PITMASTER_MAX_DELTA_CHAIN` korlátot (alapértelmezés szerint 50), hogy megelőzze a túl hosszú láncokat.

```php
// Configure max chain depth (integer)
define('PITMASTER_MAX_DELTA_CHAIN', 50);

// Read the effective limit
$max = \Pitmaster\Pack\DeltaResolver::maxChainDepth();
```

## PackWriter

A Pitmaster hálózati műveletekhez (push) csomagfájlokat tud írni.

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

A `PackWriter`:
1. Összegyűjti a csomagba kerülő objektumokat
2. Minden objektumot típus- és méretfejlécet tartalmazó varint értékkel szerializál
3. zlib segítségével tömöríti a tartalmat
4. Kiírja a PACK fejlécet, a bejegyzéseket és a SHA-1 lezáró részt

Jelenleg csak alapobjektumokat ír (deltatömörítés nélkül). A deltatömörítés később hozzáadható optimalizálás; a tárolási hatékonyság érdekében bízza a csomagok újrarendezését a `git gc` eszközre.

## PackEnumerator

A csomag minden objektumának bejárása indexsorrendben:

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

Minden elem egy feloldott `GitObject`.

## MultiPackIndex

A sok csomagfájlt tartalmazó tárolókban a többcsomagos index (MIDX) egyetlen keresési lehetőséget biztosít az összes csomagon át.

```php
use Pitmaster\Pack\MultiPackIndex;
```

A MIDX fájl (`.git/objects/pack/multi-pack-index`) tartalma:
- Fanout tábla (a csomagindex v2 táblájához hasonlóan)
- Az összes csomagon át rendezett objektumazonosítók
- Csomagfájlnevek és objektumonkénti csomag-hozzárendelések
- Eltolások az egyes csomagfájlokon belül

A `MultiPackIndex::open($path)` feldolgozza a MIDX fájlt, és biztosítja a `findObject($hex)` és a `packNames()` lehetőséget. A tárolóban végzett objektumkeresések nem használják: az objektum-adatbázis mindig az egyedi `.idx` fájlokat olvassa be.

## CommitGraph

A commitgráffájl felgyorsítja a commitelőzmények bejárását és az egyesítési alap kiszámítását.

```php
use Pitmaster\Pack\CommitGraph;
```

Helye: `.git/objects/info/commit-graph`. Tartalma:
- Rendezett commit OID-k
- Szülőkre vonatkozó információk (indexelve, az objektumok beolvasása nélkül)
- Generációs számok (a gyorsabb elérhetőségi lekérdezésekhez)
- Commit-időbélyegek

Ha rendelkezésre áll, a `Repository::merge()`, `mergeBase()`, `isBranchMerged()` és a push gyors előretekerési ellenőrzése mögötti egyesítési alap kiszámítása egyedi commitobjektumok beolvasása helyett a commitgráfból olvassa ki a szülőkapcsolatokat. A `CommitWalker` (log) nem használja.

## Változó hosszúságú egész számok

A csomagfájlok többféle változó hosszúságú egészszám-kódolást használnak:

### Git varint (típus-/méretfejlécek)

MSB-folytatásos kódolás, amelyben a legjelentősebb bit jelzi, hogy további bájtok következnek:

```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 (deltafejlécek)

A deltautasítás-fejlécekben az alapmérethez és az eredménymérethez használatos:

```php
use Pitmaster\Encoding\Leb128;

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

### BinaryReader

Alacsony szintű bájtfolyam pozíciókövetéssel:

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