
# Zásobník zmien

Funkcia Git ukladá zmeny pracovného adresára a indexu, aby ste mohli prejsť na inú úlohu a neskôr ich obnoviť. Pitmaster implementuje túto funkciu pomocou `refs/stash` a reflogu v rozložení úložiska git. Trieda `Repository` nemá metódu zásobníka zmien; použite nižšie uvedenú triedu `Stash`.

Známe sú dve chyby: ak je položiek viac než jedna, `drop()` (a teda aj `pop()`) ponechá položku v zozname ([#10](https://github.com/inline0/pitmaster/issues/10)) a `apply()` prepíše nepotvrdené zmeny v súboroch, ktorých sa uložená zmena netýka ([#11](https://github.com/inline0/pitmaster/issues/11)). Zmeny zo zásobníka použite na čistom pracovnom strome.

## Ako funguje zásobník zmien

Každá položka zásobníka zmien je revízia s dvoma rodičmi:
- **Rodič 0**: revízia HEAD v čase uloženia (základný stav)
- **Rodič 1**: revízia zachytávajúca stav indexu (pripravené zmeny)

Vlastný strom revízie zásobníka zmien zachytáva stav pracovného stromu (vrátane nepripravených zmien).

„Zásobník“ zmien tvorí reflog pre `refs/stash`. Najnovšia uložená zmena je samotná `refs/stash`. Staršie položky sú záznamy reflogu: `stash@{0}`, `stash@{1}` atď.

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

## Vytvorenie objektu zásobníka zmien

```php
use Pitmaster\Stash\Stash;

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

## Push (uloženie zmien)

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

Operácia Push vykoná tieto kroky:
1. Načíta aktuálnu revíziu HEAD
2. Vytvorí strom z aktuálneho indexu (stav pripravených zmien)
3. Vytvorí „revíziu indexu“, ktorej rodičom je HEAD
4. Vytvorí strom z aktuálneho pracovného adresára (vrátane nepripravených zmien)
5. Vytvorí revíziu zásobníka zmien (strom pracovného adresára, rodičia = HEAD + revízia indexu)
6. Aktualizuje `refs/stash` tak, aby ukazoval na revíziu zásobníka zmien
7. Pridá záznam reflogu zásobníka
8. Obnoví pracovný strom a index do stavu HEAD

Po operácii push sa sledované súbory zhodujú so stavom HEAD. Nesledované súbory zostanú, pokiaľ nie je nastavená možnosť `includeUntracked`; v takom prípade sa odstránia a uložia do vlastného stromu revízie zásobníka zmien (git ich namiesto toho uloží ako tretieho rodiča, takže git ich považuje za sledované zmeny). Funkcia `push()` vytvorí položku aj vtedy, keď sa nič nezmenilo; v takom prípade git oznámi „Nie sú žiadne miestne zmeny na uloženie“.

### S predvolenou správou

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

Pri odpojenom HEAD má `<branch>` hodnotu `HEAD`.

## Pop (použitie a odstránenie)

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

Použije najvrchnejšiu položku zásobníka zmien na pracovný adresár a odstráni ju zo zásobníka. Zodpovedá operácii `apply()`, po ktorej nasleduje `drop()`.

### Použitie konkrétnej položky

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

## Apply (obnovenie bez odstránenia)

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

Operácia Apply zlučuje uloženú zmenu so súbormi aktuálneho HEAD, a to po jednotlivých súboroch (základ uloženia, aktuálny HEAD, strom uloženia). Položka zostáva v zásobníku a index sa nemení. Pri konfliktoch sa v súboroch ponechajú značky `Updated upstream` a `Stashed changes` a vyvolá sa `Pitmaster\Exceptions\MergeConflictException`; `pop()` potom položku ponechá. Operácia Apply najprv nekontroluje, či existujú miestne zmeny (pozrite poznámku na začiatku).

## Zoznam položiek

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

Každá položka obsahuje:
- `index`: pozíciu v zásobníku zmien (0 = najnovšia)
- `message`: správu o uloženej zmene
- `hash`: hašovací súčet revízie zásobníka zmien

## Drop (odstránenie bez použitia)

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

Ak odstránite jedinú položku, odstráni sa `refs/stash` aj jeho reflog. Ak je položiek viac, `drop()` zatiaľ iba presunie `refs/stash` a reflog nezmení, takže položka zostane v `listEntries()` ([#10](https://github.com/inline0/pitmaster/issues/10)).

## Podrobnosti o úložisku

### Štruktúra revízie zásobníka zmien

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

<stash message>
```

### Štruktúra revízie indexu

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

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

Identita sa určí postupne z `GIT_COMMITTER_NAME`, `GIT_AUTHOR_NAME` a `user.name` v konfigurácii repozitára a potom z `Pitmaster` (rovnaké poradie platí aj pre e-mail; posledná možnosť je `pitmaster@example.invalid`). Dátum sa určí z `GIT_COMMITTER_DATE`, potom `GIT_AUTHOR_DATE` a napokon z aktuálneho času. Na rozdiel od revízií, zásobník zmien nečíta konštanty `PITMASTER_*`.

### Úložisko Reflog

Položky zásobníka zmien sa ukladajú do `.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
```

Reflog sa číta v chronologickom poradí. Zoznam zásobníka zmien ho zoradí opačne (od najnovšej položky). Reflog zásobníka zmien sa zapisuje bez ohľadu na `core.logallrefupdates`.

## Používanie CLI

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

Správa je argument za `push` (alebo `save`); možnosť `-m` nie je podporovaná. Operácie `pop`, `apply` a `drop` vždy pracujú s `stash@{0}`; CLI nemá `--include-untracked`.

## Obmedzenia

- Podporuje sa iba uloženie celého pracovného adresára (bez `--keep-index`); nesledované súbory možno uložiť iba pomocou `push($message, includeUntracked: true)`
- Režim `--patch` (interaktívny výber častí zmien) nie je k dispozícii
- Pri operácii Pop alebo Apply sa obnoví stav pracovného stromu, ale nie indexu. Pripravené úpravy sa obnovia ako nepripravené a novopripravené súbory ako nesledované
- `stash@{n}` operácia `Repository::resolve()` neprijíma
- Uloženie zmien v prepojenom pracovnom strome používa nesprávne umiestnenie konfigurácie a reflogu ([#14](https://github.com/inline0/pitmaster/issues/14))
