
# Formát indexu

Soubor indexu (`.git/index`) je přípravná oblast. Zaznamenává soubory, které budou zahrnuty do příštího commitu, spolu s metadaty pro efektivní výpočet stavu. Pitmaster čte verze indexu 2, 3 a 4 a zapisuje verze 2 a 3. Zápisem indexu načteného jako verze 4 vznikne soubor, který git nedokáže přečíst ([#15](https://github.com/inline0/pitmaster/issues/15)), proto taková úložiště před zápisem pomocí Pitmaster převeďte příkazem `git update-index --index-version 2`.

## Čtení 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átí prázdný index, pokud soubor neexistuje. `entries()` vrátí pouze položky fáze 0; `allEntries()` zahrnuje fáze konfliktů a `paths()` vypíše sledované cesty.

## IndexEntry

Každá položka indexu je objekt hodnoty pouze pro čtení:

```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áření položek

```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()` zpracují několik položek nebo cest v jednom průchodu. Každá změna položek odstraní uložená rozšíření (viz níže).

## Zápis indexu

```php
use Pitmaster\Index\IndexWriter;

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

Nebo přes úložiště (které to vyřídí 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ární 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á podobu:

```
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 příznaků:
- Bit 15: příznak assume-valid
- Bity 13–12: fáze sloučení (0–3)
- Bity 11–0: délka názvu cesty, nebo 0xFFF, pokud má cesta 4095 bajtů či více (čtenář pak hledá NUL)

### Rozdíly mezi verzemi

**Verze 2**: základní formát popsaný výše. Nejběžnější.

**Verze 3**: přidává rozšířené příznaky (2 bajty navíc za běžnými příznaky). Podporuje bity `skip-worktree` a `intent-to-add`. Používá se, když některá položka potřebuje rozšířené příznaky.

**Verze 4**: přidává kompresi předpon cest. Místo ukládání celých cest každá položka ukládá počet bajtů, které se mají odebrat z cesty předchozí položky, a poté novou příponu. Výrazně zmenšuje index u hluboce vnořených stromů.

## Fáze konfliktů

Během konfliktu při sloučení může mít jedna cesta více položek indexu v různých fázích:

| Fáze | Význam |
|-------|---------|
| 0 | Normální (bez konfliktu) |
| 1 | Základní verze (společný předek) |
| 2 | Naše (aktuální větev) |
| 3 | Jejich (slučovaná větev) |

```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šíření indexu

Rozšíření se připojují za položky před kontrolní součet. Každé rozšíření má 4bajtový podpis, 4bajtovou velikost a data proměnné délky.

Pitmaster uchovává bloky rozšíření jako surové bajty (`$index->extensions()` vrací dvojice `signature` a `data`) a při nezměněných položkách je zapíše zpět beze změny. Rozšíření neinterpretuje ani nevytváří a každá změna položek všechna rozšíření odstraní; git to přijme a podle potřeby znovu vytvoří. Rozšíření používaná gitem jsou pro informaci popsána níže.

### Rozšíření TREE

Ukládá do mezipaměti předpočítané objekty stromů pro adresáře. Zrychluje `git status` a `git commit`, protože při změně pouze indexu není nutné strom znovu počítat.

Podpis: `TREE` (0x54524545)

### Rozšíření REUC

Zaznamenává informace pro vrácení řešení konfliktu. Po vyřešení konfliktu se sem přesunou původní položky fází 1/2/3, aby je bylo možné obnovit pomocí `git checkout -m`.

Podpis: `REUC` (0x52455543)

### Další rozšíření

git zapisuje také:
- `EOIE` (konec položek indexu): označuje konec položek a začátek rozšíření
- `IEOT` (tabulka offsetů položek indexu): umožňuje paralelní načítání indexu
- `link` (rozdělený index): pro velmi rozsáhlá úložiště používající rozdělený index

Ve formátu git jsou rozšíření, jejichž podpis začíná velkým písmenem, volitelná, zatímco ta s malým písmenem jsou povinná. Pitmaster přijímá oba druhy bez kontroly. Rozdělený index (`link`) se nenačítá, takže úložiště, která jej používají, nejsou podporována.

## Kontrolní součet

Soubor indexu končí kontrolním součtem všech předchozích bajtů, vytvořeným pomocí hashe úložiště (20 bajtů SHA-1, 32 bajtů SHA-256). Pitmaster jej při uložení zapíše, ale při čtení neověřuje.

`Index::open()` a `Index::parse()` vyvolají `IndexParseException`, pokud chybí podpis `DIRC` nebo je verze nepodporovaná.

## Index a stav

Index je zásadní pro výpočet stavu. `WorkingTreeStatus` porovnává:
1. Strom HEAD a index: zjišťuje připravené změny
2. Index a pracovní strom: zjišťuje nepřipravené změny

Statistická pole (ctime, mtime, dev, ino, size, mode, uid, gid) umožňují rychle ověřit, zda se soubor změnil, aniž by bylo nutné číst jeho obsah. Pokud se statistická pole shodují a soubor byl naposledy změněn před začátkem kontroly stavu, považuje se za nezměněný. Odlišná velikost jej okamžitě označí jako změněný. V opačném případě Pitmaster soubor přečte a vypočítá jeho hash, aby ověřil skutečné změny obsahu.

## Příznak assume-valid

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

Tento příznak říká gitu, aby při kontrole stavu soubor nekontroloval, i když se statistická pole změnila (`git update-index --assume-unchanged`). Pitmaster tento bit při přepisování položek zachová, ale jeho kontrola stavu na něj nebere ohled a soubor přesto porovná.
