
# Formát indexu

Súbor indexu (`.git/index`) slúži ako oblasť na prípravu zmien. Zaznamenáva súbory, ktoré sa zahrnú do nasledujúceho commitu, spolu s metadátami na efektívny výpočet stavu. Pitmaster číta verzie indexu 2, 3 a 4 a zapisuje verzie 2 a 3. Zápis indexu načítaného ako verzia 4 vytvorí súbor, ktorý git nedokáže prečítať ([#15](https://github.com/inline0/pitmaster/issues/15)), preto pred zápisom cez Pitmaster preveďte takéto úložiská pomocou `git update-index --index-version 2`.

## Čítanie indexu

```php
use Pitmaster\Index\Index;

$index = Index::open('/path/to/.git/index');   // pass 32 as the second argument for SHA-256

// Or through the repository
$index = $repo->index();

// Entry count (all stages)
echo $index->count();

// Iterate stage 0 entries, keyed by path
foreach ($index->entries() as $path => $entry) {
    echo "{$entry->path} {$entry->hash->hex}\n";
}

// Look up a specific entry
$entry = $index->entry('src/File.php');
```

`Index::open()` vráti prázdny index, ak súbor neexistuje. `entries()` vráti iba položky fázy 0; `allEntries()` zahrnie konfliktné fázy a `paths()` vypíše sledované cesty.

## IndexEntry

Každá položka indexu je nemenný objekt s hodnotami:

```php
use Pitmaster\Index\IndexEntry;

$entry->path;          // File path relative to repo root
$entry->hash;          // ObjectId of the blob
$entry->mode;          // File mode as an int (0100644, 0100755, 0120000, 0160000)
$entry->fileSize;      // File size in bytes
$entry->ctimeSec;      // Metadata change time (seconds)
$entry->ctimeNsec;     // Metadata change time nanoseconds
$entry->mtimeSec;      // Modification time (seconds)
$entry->mtimeNsec;     // Modification time nanoseconds
$entry->dev;           // Device number
$entry->ino;           // Inode number
$entry->uid;           // User ID
$entry->gid;           // Group ID
$entry->flags;         // Raw flags field
$entry->extendedFlags; // v3 extended flags (skip-worktree, intent-to-add)
$entry->stage();       // Conflict stage (0 = normal, 1 = base, 2 = ours, 3 = theirs)
$entry->assumeValid(); // Assume-valid bit
$entry->skipWorktree(); // Skip-worktree bit
```

### Vytváranie položiek

```php
// From file stat
$entry = IndexEntry::fromStat('src/File.php', $blobId, '/full/path/to/src/File.php');

// Minimal (no stat info)
$entry = IndexEntry::create('src/File.php', $blobId);

// With explicit mode
$entry = IndexEntry::create('deploy.sh', $blobId, 0100755);
```

## Úprava indexu

```php
// Add or replace an entry
$index->addEntry($entry);

// Remove an entry
$index->removeEntry('src/OldFile.php');

// Replace all stages of a conflicted path with one resolved entry
$index->resolveConflict('src/File.php', $entry);
```

`addEntries()` a `removeEntries()` spracujú viacero položiek alebo ciest v jednom priechode. Každá zmena položiek odstráni uložené rozšírenia (pozrite nižšie).

## Zápis indexu

```php
use Pitmaster\Index\IndexWriter;

IndexWriter::write($index, '/path/to/.git/index');
```

Alebo cez úložisko (ktoré to spracuje automaticky):

```php
$repo->add('src/File.php');    // Reads file, creates blob, updates index, writes
$repo->remove('--cached', 'old-file.txt'); // Removes from index only, writes
```

## Binárny formát

### Hlavička

```
Signature:   DIRC (4 bytes, ASCII)
Version:     4 bytes, big-endian (2, 3, or 4)
Entry count: 4 bytes, big-endian
```

### Formát položky (v2)

Každá položka má tento formát:

```
ctime seconds:     4 bytes, big-endian
ctime nanoseconds: 4 bytes, big-endian
mtime seconds:     4 bytes, big-endian
mtime nanoseconds: 4 bytes, big-endian
dev:               4 bytes, big-endian
ino:               4 bytes, big-endian
mode:              4 bytes, big-endian
uid:               4 bytes, big-endian
gid:               4 bytes, big-endian
file size:         4 bytes, big-endian
object hash:       20 bytes SHA-1 or 32 bytes SHA-256 (raw binary)
flags:             2 bytes, big-endian
path:              variable length, NUL-terminated
padding:           1-8 NUL bytes to align to 8-byte boundary
```

Pole príznakov:
- Bit 15: príznak assume-valid
- Bity 13-12: fáza zlúčenia (0-3)
- Bity 11-0: dĺžka názvu cesty alebo 0xFFF, ak cesta má aspoň 4095 bajtov (čitateľ potom hľadá znak NUL)

### Rozdiely medzi verziami

**Verzia 2**: základný formát opísaný vyššie. Najbežnejšia.

**Verzia 3**: pridáva rozšírené príznaky (2 dodatočné bajty za bežnými príznakmi). Podporuje bity `skip-worktree` a `intent-to-add`. Používa sa, keď niektorá položka vyžaduje rozšírené príznaky.

**Verzia 4**: pridáva kompresiu prefixov ciest. Namiesto ukladania úplných ciest sa pri každej položke ukladá počet bajtov, ktoré treba odstrániť z cesty predchádzajúcej položky, a potom nová prípona. Výrazne zmenšuje veľkosť indexu pri hlboko vnorených stromoch.

## Konfliktné fázy

Počas konfliktu zlúčenia môže mať jedna cesta viacero položiek indexu v rôznych fázach:

| Fáza | Význam |
|-------|---------|
| 0 | Bežná (bez konfliktu) |
| 1 | Základná verzia (spoločný predok) |
| 2 | Naša verzia (aktuálna vetva) |
| 3 | Ich verzia (zlučovaná vetva) |

```php
// During a conflict, entries have non-zero stages
foreach ($index->allEntries() as $entry) {
    if ($entry->stage() > 0) {
        echo "Conflict: {$entry->path} (stage {$entry->stage()})\n";
    }
}

$index->hasUnmerged();            // true while conflicts remain
$index->unmergedPaths();          // conflicted paths
$index->stageEntries('a.txt');    // [1 => base, 2 => ours, 3 => theirs]
```

## Rozšírenia indexu

Rozšírenia sa pridávajú za položky pred kontrolným súčtom. Každé rozšírenie má 4-bajtový podpis, 4-bajtovú veľkosť a dáta premenlivej dĺžky.

Pitmaster uchováva bloky rozšírení ako surové bajty (`$index->extensions()` vracia dvojice `signature` a `data`) a pri nezmenených položkách ich zapisuje späť bez úprav. Nerozpoznáva ani nevytvára žiadne rozšírenia; každá zmena položiek ich odstráni a git ich podľa potreby prijme a znovu vytvorí. Nižšie sú pre informáciu opísané rozšírenia používané v git.

### Rozšírenie TREE

Ukladá do vyrovnávacej pamäte vopred vypočítané objekty stromov pre adresáre. Urýchľuje `git status` a `git commit` tým, že sa strom nemusí znova počítať, keď sa zmenil iba index.

Podpis: `TREE` (0x54524545)

### Rozšírenie REUC

Zaznamenáva informácie na obnovenie zmien. Po vyriešení konfliktu sa sem presunú pôvodné položky 1/2/3, aby sa dali obnoviť pomocou `git checkout -m`.

Podpis: `REUC` (0x52455543)

### Ďalšie rozšírenia

git zapisuje aj:
- `EOIE` (koniec položky indexu): označuje, kde sa položky končia a začínajú rozšírenia
- `IEOT` (tabuľka posunov položiek indexu): umožňuje paralelné načítanie indexu
- `link` (rozdelený index): pre veľmi veľké repozitáre používajúce rozdelený index

Vo formáte git sú rozšírenia, ktorých podpis sa začína veľkým písmenom, voliteľné a tie s malým písmenom povinné. Pitmaster prijíma oba druhy bez kontroly. Rozdelený index (`link`) sa nenačítava, takže repozitáre, ktoré ho používajú, nie sú podporované.

## Kontrolný súčet

Súbor indexu sa končí kontrolným súčtom všetkých predchádzajúcich bajtov vypočítaným pomocou hašovacej funkcie repozitára (20 bajtov SHA-1, 32 bajtov SHA-256). Pitmaster ho pri ukladaní zapisuje, ale pri čítaní ho neoveruje.

`Index::open()` a `Index::parse()` vyvolajú `IndexParseException`, ak chýba podpis `DIRC` alebo ak verzia nie je podporovaná.

## Index a stav

Index je kľúčový pri výpočte stavu. `WorkingTreeStatus` porovnáva:
1. Strom HEAD s indexom: zisťuje zmeny pripravené na potvrdenie
2. Index s pracovným stromom: zisťuje nepripravené zmeny

Štatistické polia (ctime, mtime, dev, ino, size, mode, uid, gid) umožňujú rýchlo overiť, či sa súbor zmenil, bez čítania jeho obsahu. Ak sa štatistické polia zhodujú a súbor bol naposledy upravený pred začiatkom kontroly stavu, považuje sa za nezmenený. Odlišná veľkosť okamžite označí súbor ako upravený. V opačnom prípade Pitmaster súbor načíta a vypočíta jeho hash, aby overil, či sa skutočne zmenil jeho obsah.

## Príznak assume-valid

```php
if ($entry->assumeValid()) {
    // Entry has assume-valid set (flags & 0x8000)
}
```

Tento príznak hovorí git, aby pri kontrole stavu preskočil kontrolu súboru, aj keď sa štatistické polia zmenili (`git update-index --assume-unchanged`). Pitmaster tento bit zachováva pri prepisovaní položiek, ale jeho kontrola stavu ho nerešpektuje a súbor aj tak porovnáva.
