
# Salvestusloend

Git stash salvestab töökausta ja indeksi muudatused, et saaksid liikuda teise ülesande juurde ja need hiljem taastada. Pitmaster rakendab stash'i, kasutades `refs/stash` ja reflog'i, järgides git salvestuspaigutust. Meetodil `Repository` pole stash-funktsiooni; kasuta allpool olevat klassi `Stash`.

Teada on kaks probleemi: mitme kirje korral jätab `drop()` (ja seega ka `pop()`) kirje loendisse ([#10](https://github.com/inline0/pitmaster/issues/10)), ning `apply()` kirjutab üle kinnitamata muudatused failides, mida salvestusloend ei puuduta ([#11](https://github.com/inline0/pitmaster/issues/11)). Rakendage salvestusloendi kirjeid puhtas tööpuus.

## Salvestusloendi toimimine

Iga salvestusloendi kirje on kahe vanemaga commit:
- **Vanem 0**: HEAD-i commit salvestamise ajal (baasolek)
- **Vanem 1**: indeksi oleku (lavastatud muudatuste) jäädvustav commit

Salvestusloendi commiti enda puu jäädvustab tööpuu oleku (sh lavastamata muudatused).

Salvestusloendi „pinu” on asukoha `refs/stash` reflog. Kõige uuem salvestusloend on `refs/stash` ise. Vanemad kirjed on reflogi kirjed: `stash@{0}`, `stash@{1}` jne.

```
refs/stash -> stash commit (worktree state)
                parent[0] -> HEAD at time of stash
                parent[1] -> index state commit
                               parent[0] -> HEAD at time of stash
```

## Salvestusloendi objekti loomine

```php
use Pitmaster\Stash\Stash;

$stash = new Stash(
    $repo->objectDatabase(),
    $repo->refDatabase(),
    $repo->gitDir(),
    $repo->workDir(),
);
```

## Lükake peale (salvestage muudatused)

```php
// public function push(string $message = '', bool $includeUntracked = false): ObjectId
$stashId = $stash->push('Work in progress on login');
echo "Saved: {$stashId->hex}\n";

// Also stash untracked files
$stashId = $stash->push('With new files', includeUntracked: true);
```

Pealelükkamine teeb järgmist:
1. Loeb praeguse HEAD-i commiti
2. Koostab praegusest indeksist puu (lavastatud olek)
3. Loob „indeksi commiti”, mille vanem on HEAD
4. Koostab praegusest töökataloogist puu (sh lavastamata muudatused)
5. Loob salvestusloendi commiti (tööpuu puu, vanemad = HEAD + indeksi commit)
6. Uuendab väärtuse `refs/stash` salvestusloendi commitile
7. Lisab pinule reflogi kirje
8. Lähtestab tööpuu ja indeksi HEAD-i olekusse

Pärast push'i vastavad jälgitavad failid HEAD-ile. Jälgimata failid jäävad alles, kui pole määratud `includeUntracked`; sel juhul need eemaldatakse ja salvestatakse stash-kinnituse oma puusse (git salvestab need hoopis kolmandasse eellasse, seega git näeb neid jälgitavate muudatustena). `push()` loob kirje ka siis, kui midagi pole muutunud; sel juhul teatab git „No local changes to save”.

### Vaikesõnumiga

```php
$stashId = $stash->push();
// Message: "WIP on <branch>: <short hash> <HEAD subject>"
// With a message: "On <branch>: <message>"
```

Eraldatud HEAD-i korral on `<branch>` väärtus `HEAD`.

## Pop (rakendage ja eemaldage)

```php
$stash->pop();
```

Rakendab ülemise salvestusloendi kirje töökausta ja eemaldab selle pinust. Samaväärne toiminguga `apply()`, millele järgneb `drop()`.

### Kindla kirje pop-toiming

```php
$stash->pop(1);  // Pop stash@{1}
```

## Rakenda (taasta eemaldamata)

```php
$stash->apply();      // Apply stash@{0}
$stash->apply(2);     // Apply stash@{2}
```

Rakendamine ühendab salvestusloendi praeguse HEAD-i failidega ükshaaval (salvestusloendi baas, praegune HEAD, salvestusloendi puu). Kirje jääb pinusse ja indeksit ei puudutata. Konfliktide korral lisatakse failidesse märgenditega `Updated upstream` ja `Stashed changes` markerid ning tekib viga `Pitmaster\Exceptions\MergeConflictException`; `pop()` jätab seejärel kirje alles. Rakendamine ei kontrolli enne kohalikke muudatusi (vt ülal olevat märkust).

## Kirjete loetlemine

```php
$entries = $stash->listEntries();

foreach ($entries as $entry) {
    echo "stash@{{$entry['index']}}: {$entry['message']}\n";
    // stash@{0}: On main: Work in progress on login
    // stash@{1}: WIP on main: f707601 Add login form
}
```

Iga kirje sisaldab:
- `index`: salvestusloendi asukoht (0 = kõige uuem)
- `message`: salvestusloendi sõnum
- `hash`: salvestusloendi commiti räsi

## Eemaldamine (eemalda rakendamata)

```php
$stash->drop();       // Drop stash@{0}
$stash->drop(1);      // Drop stash@{1}
```

Ainsa kirje eemaldamisel kustutatakse `refs/stash` ja selle reflog. Kui kirjeid on rohkem, liigutab `drop()` praegu ainult väärtust `refs/stash` ega muuda reflogi, mistõttu kirje jääb asukohta `listEntries()` ([#10](https://github.com/inline0/pitmaster/issues/10)).

## Salvestuse üksikasjad

### Salvestusloendi commiti struktuur

```
commit <hash>
tree <worktree-tree-hash>
parent <HEAD-hash>
parent <index-commit-hash>
author <current identity>
committer <current identity>

<stash message>
```

### Indeksi commiti struktuur

```
commit <hash>
tree <index-tree-hash>
parent <HEAD-hash>
author <current identity>
committer <current identity>

index on <branch>: <short hash> <HEAD subject>
```

Identiteet pärineb hoidla konfiguratsioonis esmalt väärtusest `GIT_COMMITTER_NAME`, seejärel `GIT_AUTHOR_NAME`, siis `user.name` ja lõpuks `Pitmaster` (sama järjekord kehtib e-posti puhul ning lõpus on `pitmaster@example.invalid`). Kuupäev pärineb esmalt väärtusest `GIT_COMMITTER_DATE`, seejärel `GIT_AUTHOR_DATE` ja lõpuks praegusest kellaajast. Erinevalt commitidest ei loe salvestusloend väärtusi `PITMASTER_*`.

### Reflog-i salvestus

Salvestusloendi kirjed asuvad asukohas `.git/logs/refs/stash`:

```
0000000000000000000000000000000000000000 abc123... Name <email> 1700000000 +0000	WIP on main: f707601 Add login form
abc123... def456... Name <email> 1700000001 +0000	On main: Work in progress on login
```

Reflogi loetakse kronoloogilises järjekorras. Salvestusloendi loend pöörab selle ümber (uusim ees). Salvestusloendi reflog kirjutatakse olenemata väärtusest `core.logallrefupdates`.

## CLI kasutamine

```bash
# Save current changes
./vendor/bin/pitmaster stash
./vendor/bin/pitmaster stash push
./vendor/bin/pitmaster stash push "Work in progress on login"

# Apply and remove top entry
./vendor/bin/pitmaster stash pop

# Apply without removing
./vendor/bin/pitmaster stash apply

# List all entries
./vendor/bin/pitmaster stash list

# Remove top entry without applying
./vendor/bin/pitmaster stash drop
```

Sõnum on argument pärast `push` (või `save`); `-m` pole toetatud. `pop`, `apply` ja `drop` toimivad alati asukohas `stash@{0}`; CLI-il pole väärtust `--include-untracked`.

## Piirangud

- Ainult kogu töökausta salvestamine (ilma `--keep-index`-ta); jälgimata failid ainult väärtuse `push($message, includeUntracked: true)` kaudu
- `--patch` režiimi pole (interaktiivne tükkide valik)
- Pop/rakendamine taastab tööpuu oleku, kuid ei taasta indeksit. Lavastatud muudatused taastuvad lavastamata muudatustena ja äsja lavastatud failid jälgimata failidena
- `stash@{n}` ei aktsepteeri `Repository::resolve()`
- Salvestusloendi kasutamine lingitud tööpuus rakendab vale konfiguratsiooni- ja reflogiasukohta ([#14](https://github.com/inline0/pitmaster/issues/14))
