
# Indeksi vorming

Indeksifail (`.git/index`) on lavastusala. See salvestab järgmisse kinnistusse lisatavad failid koos metaandmetega, mis kiirendavad oleku arvutamist. Pitmaster loeb indeksiversioone 2, 3 ja 4 ning kirjutab versioone 2 ja 3. Versioonina 4 loetud indeksi kirjutamisel tekib fail, mida git ei suuda lugeda ([#15](https://github.com/inline0/pitmaster/issues/15)), seega teisenda sellised hoidlad käsuga `git update-index --index-version 2` enne, kui kirjutad käsuga Pitmaster.

## Indeksi lugemine

```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()` tagastab tühja indeksi, kui faili pole. `entries()` tagastab ainult 0. etapi kirjed; `allEntries()` kaasab konflikti etapid ja `paths()` loetleb jälgitavad rajad.

## IndexEntry

Iga indeksikirje on kirjutuskaitstud väärtusobjekt:

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

### Kirjete loomine

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

## Indeksi muutmine

```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()` ja `removeEntries()` töötlevad ühe läbimisega mitu kirjet või rada. Kirjete mis tahes muudatus eemaldab salvestatud laiendused (vt allpool).

## Indeksi kirjutamine

```php
use Pitmaster\Index\IndexWriter;

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

Või hoidla kaudu (mis teeb seda automaatselt):

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

## Binaarvorming

### Päis

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

### Kirje vorming (v2)

Iga kirje koosneb järgmisest:

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

Lippude väli:
- Bitt 15: eeldatavalt kehtiva oleku lipp
- Bitid 13–12: liitmise etapp (0–3)
- Bitid 11–0: raja nime pikkus või 0xFFF, kui raja pikkus on vähemalt 4095 baiti (sel juhul otsib lugeja NUL-i)

### Versioonide erinevused

**Versioon 2**: eespool kirjeldatud põhivorming. Kõige levinum.

**Versioon 3**: lisab laiendatud lipud (tavaliste lippude järel 2 lisabaiti). Toetab bitte `skip-worktree` ja `intent-to-add`. Kasutusel, kui mõni kirje vajab laiendatud lippe.

**Versioon 4**: lisab radade eesliidete tihendamise. Täielike radade salvestamise asemel salvestatakse igas kirjes eelmisest rajast eemaldatavate baitide arv ja seejärel uus lõpp. Vähendab oluliselt indeksi suurust sügavalt pesastatud puude korral.

## Konfliktietapid

Liitmis konflikti ajal võib ühel rajal olla mitu eri etappi indeksikirjet:

| Etapp | Tähendus |
|-------|---------|
| 0 | Tavaline (konflikt puudub) |
| 1 | Baasversioon (ühine eellane) |
| 2 | Meie oma (praegune haru) |
| 3 | Nende oma (liidetav haru) |

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

## Indeksi laiendused

Laiendused lisatakse pärast kirjeid ja enne kontrollsummat. Igal laiendusel on 4-baidine signatuur, 4-baidine suurus ja muutuva pikkusega andmed.

Pitmaster säilitab laiendusplokid töötlemata baitidena (`$index->extensions()` tagastab paare `signature` ja `data`) ning kirjutab need muutmata kujul tagasi, kui kirjeid pole muudetud. See ei tõlgenda ega loo laiendusi ning kirjete muutmisel eemaldab need kõik; git aktsepteerib seda ja loob vajaduse korral laiendused uuesti. Allpool kirjeldatakse viiteks git-i kasutatavaid laiendusi.

### TREE-laiendus

Salvestab kataloogide jaoks eelnevalt arvutatud puuobjektid vahemällu. Kiirendab käske `git status` ja `git commit`, vältides puude uuesti arvutamist, kui muutus ainult indeks.

Signatuur: `TREE` (0x54524545)

### REUC-laiendus

Salvestab konflikti lahendamise tagasivõtmise teabe. Pärast konflikti lahendamist tõstetakse algsed 1/2/3 etapi kirjed siia, et need saaks käsuga `git checkout -m` taastada.

Signatuur: `REUC` (0x52455543)

### Muud laiendused

git kirjutab ka:
- `EOIE` (indeksikirje lõpp): märgib, kus kirjed lõpevad ja laiendused algavad
- `IEOT` (indeksikirje nihete tabel): võimaldab indeksi paralleelset laadimist
- `link` (tükeldatud indeks): väga suurte hoidlade jaoks, kus kasutatakse tükeldatud indeksit

git-i vormingus on suurtähega algava signatuuriga laiendused valikulised ja väiketähega algavad kohustuslikud. Pitmaster aktsepteerib mõlemat tüüpi kontrollimata. Tükeldatud indeksit (`link`) ei laadita, seega ei toetata seda kasutavaid hoidlaid.

## Kontrollsumma

Indeksifail lõpeb kõigi eelnevate baitide kontrollsummaga, mis arvutatakse hoidla räsialgoritmiga (20 baiti SHA-1, 32 baiti SHA-256). Pitmaster kirjutab selle salvestamisel, kuid ei kontrolli lugemisel.

`Index::open()` ja `Index::parse()` tekitavad `IndexParseException`, kui signatuur `DIRC` puudub või versiooni ei toetata.

## Indeks ja olek

Indeks on oleku arvutamisel keskse tähtsusega. `WorkingTreeStatus` võrdleb:
1. HEAD-i puud ja indeksit: tuvastab lisatud muudatused
2. Indeksit ja tööpuud: tuvastab lisamata muudatused

Statistikaväljad (ctime, mtime, dev, ino, size, mode, uid, gid) võimaldavad kiiresti kontrollida, kas fail on muutunud, ilma faili sisu lugemata. Kui statistikaväljad kattuvad ja faili muudeti enne oleku skannimise algust, eeldatakse, et fail pole muutunud. Erinev suurus märgib faili kohe muudetuks. Vastasel juhul loeb Pitmaster faili ja arvutab selle räsi, et kontrollida, kas sisu on tegelikult muutunud.

## Kehtivaks eeldamise lipp

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

See lipp käsib git-il oleku kontrollimisel faili vahele jätta, isegi kui statistikaväljad on muutunud (`git update-index --assume-unchanged`). Pitmaster säilitab biti kirjete ümberkirjutamisel, kuid selle olekukontroll seda ei arvesta ja võrdleb faili siiski.
