
# Indexindeling

Het indexbestand (`.git/index`) is het staginggebied. Het bevat de bestanden die in de volgende commit worden opgenomen, samen met metadata voor een efficiënte statusberekening. Pitmaster leest indexversies 2, 3 en 4, en schrijft versies 2 en 3. Als u een index schrijft die als versie 4 is gelezen, ontstaat een bestand dat git niet kan lezen ([#15](https://github.com/inline0/pitmaster/issues/15)). Converteer zulke repositories daarom met `git update-index --index-version 2` voordat u ernaar schrijft via Pitmaster.

## De index lezen

```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()` retourneert een lege index als het bestand niet bestaat. `entries()` retourneert alleen vermeldingen van fase 0; `allEntries()` neemt ook conflictfasen op en `paths()` toont de gevolgde paden.

## IndexEntry

Elke vermelding in de index is een alleen-lezen waardeobject:

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

### Vermeldingen aanmaken

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

## De index wijzigen

```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()` en `removeEntries()` verwerken meerdere vermeldingen of paden in één keer. Bij elke wijziging van de vermeldingen worden de opgeslagen extensies verwijderd (zie hieronder).

## De index schrijven

```php
use Pitmaster\Index\IndexWriter;

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

Of via de repository, die dit automatisch afhandelt:

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

## Binair formaat

### Koptekst

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

### Vermeldingsformaat (v2)

Elke vermelding bestaat uit:

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

Het veld met vlaggen:
- Bit 15: vlag assume-valid
- Bits 13-12: mergefase (0-3)
- Bits 11-0: lengte van de padnaam of 0xFFF wanneer het pad 4095 bytes of langer is (de lezer zoekt dan naar de NUL)

### Verschillen tussen versies

**Versie 2**: de hierboven beschreven basisindeling. Meest gangbaar.

**Versie 3**: voegt uitgebreide vlaggen toe (2 extra bytes na de gewone vlaggen). Ondersteunt de bits `skip-worktree` en `intent-to-add`. Wordt gebruikt wanneer een vermelding uitgebreide vlaggen nodig heeft.

**Versie 4**: voegt compressie van padprefixen toe. In plaats van volledige paden op te slaan, slaat elke vermelding het aantal bytes op dat van het vorige pad moet worden verwijderd, gevolgd door het nieuwe achtervoegsel. Dit verkleint de index aanzienlijk bij diep geneste bomen.

## Conflictfasen

Tijdens een mergeconflict kan één pad meerdere indexvermeldingen in verschillende fasen hebben:

| Fase | Betekenis |
|-------|---------|
| 0 | Normaal (geen conflict) |
| 1 | Basisversie (gemeenschappelijke voorouder) |
| 2 | Onze versie (huidige branch) |
| 3 | Hun versie (branch die wordt gemerged) |

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

## Indexextensies

Extensies worden na de vermeldingen en vóór de controlesom toegevoegd. Elke extensie heeft een handtekening van 4 bytes, een grootte van 4 bytes en gegevens met variabele lengte.

Pitmaster bewaart extensieblokken als ruwe bytes (`$index->extensions()` retourneert paren van `signature` en `data`) en schrijft ze ongewijzigd terug als de vermeldingen niet zijn aangepast. Het interpreteert of genereert geen extensies. Elke wijziging van de vermeldingen verwijdert ze allemaal; git accepteert dit en bouwt ze zo nodig opnieuw op. De extensies die git gebruikt, worden hieronder ter referentie beschreven.

### TREE-extensie

Cachet vooraf berekende boomobjecten voor mappen. Versnelt `git status` en `git commit` doordat de boom niet opnieuw hoeft te worden berekend wanneer alleen de index is gewijzigd.

Handtekening: `TREE` (0x54524545)

### REUC-extensie

Registreert informatie om opgeloste wijzigingen ongedaan te maken. Nadat een conflict is opgelost, worden de oorspronkelijke vermeldingen van fase 1/2/3 hierheen verplaatst, zodat ze met `git checkout -m` kunnen worden hersteld.

Handtekening: `REUC` (0x52455543)

### Overige extensies

git schrijft ook:
- `EOIE` (einde van indexvermelding): geeft aan waar vermeldingen eindigen en extensies beginnen
- `IEOT` (offsettabel voor indexvermeldingen): maakt parallel laden van de index mogelijk
- `link` (gesplitste index): voor zeer grote repositories die een gesplitste index gebruiken

In de indeling van git zijn extensies met een handtekening die met een hoofdletter begint optioneel en die met een kleine letter verplicht. Pitmaster accepteert beide soorten zonder ze te controleren. Een gesplitste index (`link`) wordt niet geladen, dus repositories die er een gebruiken, worden niet ondersteund.

## Controlesom

Het indexbestand eindigt met een controlesom van alle voorgaande bytes, berekend met de hashfunctie van de repository (20 bytes SHA-1, 32 bytes SHA-256). Pitmaster schrijft deze bij het opslaan, maar controleert haar niet bij het lezen.

`Index::open()` en `Index::parse()` geven `IndexParseException` als een `DIRC`-handtekening ontbreekt of een niet-ondersteunde versie wordt aangetroffen.

## Index en status

De index speelt een centrale rol bij de statusberekening. `WorkingTreeStatus` vergelijkt:
1. HEAD-boom met index: detecteert wijzigingen die klaarstaan om te worden vastgelegd
2. Index met werkboom: detecteert wijzigingen die nog niet klaarstaan

De stat-velden (ctime, mtime, dev, ino, size, mode, uid, gid) maken snelle controles mogelijk om te bepalen of een bestand is gewijzigd, zonder de bestandsinhoud te lezen. Als de stat-velden overeenkomen en het bestand voor het begin van de statuscontrole voor het laatst is gewijzigd, wordt aangenomen dat het bestand ongewijzigd is. Een andere grootte betekent meteen dat het is gewijzigd. Anders leest Pitmaster het bestand en berekent het de hash om te controleren of de inhoud daadwerkelijk is veranderd.

## Vlag assume-valid

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

Deze vlag vertelt git het bestand tijdens statuscontroles over te slaan, zelfs als de stat-velden zijn gewijzigd (`git update-index --assume-unchanged`). Pitmaster behoudt de bit wanneer het vermeldingen opnieuw schrijft, maar de statuscontrole houdt er geen rekening mee en vergelijkt het bestand toch.
