
# Indeksin muoto

Indeksitiedosto (`.git/index`) toimii välialueena. Se tallentaa seuraavaan committiin sisällytettävät tiedostot sekä metatiedot tilan tehokasta laskentaa varten. Pitmaster lukee indeksiversiot 2, 3 ja 4 sekä kirjoittaa versioita 2 ja 3. Versiona 4 luetun indeksin kirjoittaminen tuottaa tiedoston, jota git ei pysty lukemaan ([#15](https://github.com/inline0/pitmaster/issues/15)), joten muunna tällaiset tietovarastot komennolla `git update-index --index-version 2` ennen kirjoittamista komennon Pitmaster kautta.

## Indeksin lukeminen

```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()` palauttaa tyhjän indeksin, jos tiedostoa ei ole. `entries()` palauttaa vain vaiheen 0 merkinnät; `allEntries()` sisältää ristiriitojen vaiheet, ja `paths()` luettelee seuratut polut.

## IndexEntry

Jokainen indeksimerkintä on vain luku -arvo-olio:

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

### Merkintöjen luominen

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

## Indeksin muokkaaminen

```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()` käsittelevät useita merkintöjä tai polkuja yhdellä kertaa. Merkintöjen muuttaminen poistaa tallennetut laajennukset (katso alla).

## Indeksin kirjoittaminen

```php
use Pitmaster\Index\IndexWriter;

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

Tai tietovaraston kautta (joka hoitaa tämän automaattisesti):

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

### Otsake

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

### Merkinnän muoto (v2)

Jokainen merkintä on seuraavanlainen:

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

Lipputietue:
- Bitti 15: assume-valid-lippu
- Bitit 13–12: yhdistämisvaihe (0–3)
- Bitit 11–0: polun nimen pituus tai 0xFFF, jos polku on vähintään 4095 tavua pitkä (lukija etsii tällöin NUL-tavua)

### Versioiden erot

**Versio 2**: yllä kuvattu perusmuoto. Yleisin.

**Versio 3**: lisää laajennetut liput (2 lisätavua tavallisten lippujen jälkeen). Tukee bittejä `skip-worktree` ja `intent-to-add`. Käytössä, kun jokin merkintä tarvitsee laajennettuja lippuja.

**Versio 4**: lisää polkujen etuliitteiden pakkauksen. Täysien polkujen tallentamisen sijaan kukin merkintä tallentaa edellisen merkinnän polusta poistettavien tavujen määrän ja sen jälkeen uuden loppuosan. Pienentää huomattavasti syvälle sisäkkäisten puiden indeksin kokoa.

## Ristiriitojen vaiheet

Yhdistämisristiriidan aikana yhdellä polulla voi olla useita indeksimerkintöjä eri vaiheissa:

| Vaihe | Merkitys |
|-------|---------|
| 0 | Tavallinen (ei ristiriitaa) |
| 1 | Perusversio (yhteinen kantaisä) |
| 2 | Meidän versiomme (nykyinen haara) |
| 3 | Heidän versionsa (yhdistettävä haara) |

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

## Indeksin laajennukset

Laajennukset lisätään merkintöjen jälkeen ja ennen tarkistussummaa. Kussakin laajennuksessa on 4 tavun tunniste, 4 tavun koko ja vaihtelevan pituista dataa.

Pitmaster säilyttää laajennuslohkot raak tavuina (`$index->extensions()` palauttaa `signature`- ja `data`-pareja) ja kirjoittaa ne takaisin muuttamattomina, kun merkintöjä ei ole muokattu. Se ei tulkitse tai luo laajennuksia, ja merkintöjen muuttaminen poistaa ne kaikki. Tämän git hyväksyy ja rakentaa tarvittaessa uudelleen. Alla kuvataan viitteeksi git-laajennukset.

### TREE-laajennus

Tallentaa hakemistojen valmiiksi lasketut puuobjektit välimuistiin. Nopeuttaa komentoja `git status` ja `git commit` välttämällä puiden uudelleenlaskennan, kun vain indeksi on muuttunut.

Tunniste: `TREE` (0x54524545)

### REUC-laajennus

Tallentaa ristiriitojen ratkaisemisen kumoamiseen tarvittavat tiedot. Kun ristiriita on ratkaistu, alkuperäiset vaiheiden 1/2/3 merkinnät siirretään tänne, jotta ne voi palauttaa komennolla `git checkout -m`.

Tunniste: `REUC` (0x52455543)

### Muut laajennukset

git kirjoittaa myös seuraavat:
- `EOIE` (indeksimerkinnän loppu): ilmaisee, mihin merkinnät päättyvät ja laajennukset alkavat
- `IEOT` (indeksimerkintöjen siirtymätaulukko): mahdollistaa indeksin rinnakkaisen lataamisen
- `link` (jaettu indeksi): hyvin suurille tietovarastoille, joissa käytetään jaettua indeksiä

git-muodossa isolla kirjaimella alkavan allekirjoituksen laajennukset ovat valinnaisia ja pienellä alkavat pakollisia. Pitmaster hyväksyy molemmat tyypit tarkistamatta niitä. Jaetun indeksin (`link`) tietoja ei ladata, joten sitä käyttävät repositoriot eivät ole tuettuja.

## Tarkistussumma

Indeksitiedoston lopussa on tarkistussumma kaikista sitä edeltävistä tavuista tietovaraston tiivistealgoritmilla (20 tavua SHA-1-algoritmilla, 32 tavua SHA-256-algoritmilla). Pitmaster kirjoittaa sen tallennuksen yhteydessä, mutta ei tarkista sitä luettaessa.

`Index::open()` ja `Index::parse()` heittävät `IndexParseException`-poikkeuksen, jos `DIRC`-tunniste puuttuu tai versio ei ole tuettu.

## Indeksi ja tila

Indeksi on keskeinen osa tilan laskentaa. `WorkingTreeStatus` vertaa:
1. HEAD-puuta indeksiin: havaitsee indeksiin lisätyt muutokset
2. Indeksiä työpuuhun: havaitsee indeksiin lisäämättömät muutokset

Tilakentät (ctime, mtime, dev, ino, size, mode, uid, gid) mahdollistavat nopeat tarkistukset ilman tiedoston sisällön lukemista. Jos tilakentät täsmäävät ja tiedoston viimeisin muokkaus on ennen tilan tarkistuksen alkua, tiedoston oletetaan olevan muuttumaton. Eri koko merkitsee tiedoston heti muokatuksi. Muussa tapauksessa Pitmaster lukee tiedoston ja laskee sen tiivisteen sisällön muutosten tarkistamiseksi.

## Oleta kelvolliseksi -lippu

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

Tämä lippu käskee gitiä ohittamaan tiedoston tarkistuksen tilatietoja laskettaessa, vaikka tilakentät olisivat muuttuneet (`git update-index --assume-unchanged`). Pitmaster säilyttää bitin kirjoittaessaan merkinnät uudelleen, mutta sen tilatarkistus ei huomioi sitä, vaan vertaa tiedostoa silti.
