
# Stash

A Git stash elmenti a munkakönyvtár és az index módosításait, így átválthat egy másik feladatra, majd később visszaállíthatja őket. A Pitmaster a stash-t a `refs/stash` és a reflog használatával, a git tárolási elrendezésében valósítja meg. A `Repository` osztálynak nincs stash metódusa; használja az alábbi `Stash` osztályt.

Két ismert probléma: egynél több bejegyzés esetén a `drop()` (és így a `pop()` is) a listában hagyja a bejegyzést ([#10](https://github.com/inline0/pitmaster/issues/10)), a `apply()` pedig felülírja a nem commitolt módosításokat azokban a fájlokban, amelyeket a stash nem érint ([#11](https://github.com/inline0/pitmaster/issues/11)). A stash-t tiszta munkafán alkalmazza.

## A stash működése

Minden stash-bejegyzés két szülővel rendelkező commit:
- **0. szülő**: a stash készítésekor érvényes HEAD commit (az alapállapot)
- **1. szülő**: az index állapotát rögzítő commit (az előkészített módosítások)

A stash commit saját fája a munkafa állapotát rögzíti (a még elő nem készített módosításokkal együtt).

A stash „verem” a `refs/stash` reflogja. A legutóbbi stash maga a `refs/stash`. A régebbi bejegyzések reflog-bejegyzések: `stash@{0}`, `stash@{1}` stb.

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

## A Stash objektum létrehozása

```php
use Pitmaster\Stash\Stash;

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

## Push (módosítások mentése)

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

A Push a következőket végzi:
1. Beolvassa az aktuális HEAD commitot
2. Fát épít az aktuális indexből (staged állapot)
3. Létrehoz egy „index commitot”, amelynek szülője a HEAD
4. Fát épít az aktuális munkakönyvtárból (az unstaged módosításokat is beleértve)
5. Létrehozza a stash commitot (munkafa, szülők = HEAD + index commit)
6. A `refs/stash` értékét a stash commitra állítja
7. Reflog-bejegyzést fűz a veremhez
8. Visszaállítja a munkafát és az indexet a HEAD állapotára

A push után a követett fájlok megegyeznek a HEAD állapotával. A nem követett fájlok megmaradnak, hacsak nincs beállítva a `includeUntracked`; ekkor eltávolítja és a stash commit saját fájában tárolja őket (a git ehelyett egy harmadik szülőben tárolja őket, így a git követett módosításként látja ezeket). A `push()` akkor is létrehoz egy bejegyzést, ha semmi sem változott; ilyenkor a git ezt jelzi: „Nincs mentendő helyi módosítás”.

### Alapértelmezett üzenettel

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

Leválasztott HEAD esetén a `<branch>` értéke `HEAD`.

## Pop (alkalmazás és eltávolítás)

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

Alkalmazza a legfelső stash-bejegyzést a munkakönyvtárra, majd eltávolítja a veremből. A `apply()`, majd a `drop()` művelettel egyenértékű.

### Adott bejegyzés Pop művelete

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

## Apply (visszaállítás eltávolítás nélkül)

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

Az Apply fájlonként egyesíti a stash tartalmát az aktuális HEAD fájljaival (stash-alap, aktuális HEAD, stash-fa). A stash-bejegyzés a veremben marad, az index pedig érintetlen marad. Ütközés esetén a fájlokban `Updated upstream` és `Stashed changes` címkéjű jelölők maradnak, és a művelet `Pitmaster\Exceptions\MergeConflictException` kivételt dob; a `pop()` ezután is megtartja a bejegyzést. Az Apply előzetesen nem ellenőrzi a helyi módosításokat (lásd a fenti megjegyzést).

## Bejegyzések listázása

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

Minden bejegyzés a következőket tartalmazza:
- `index`: a stash pozíciója (0 = legújabb)
- `message`: a stash üzenete
- `hash`: a stash commit hash-e

## Drop (eltávolítás alkalmazás nélkül)

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

Ha az egyetlen bejegyzést dobja el, a `refs/stash` és a reflog is törlődik. Több bejegyzés esetén a `drop()` jelenleg csak a `refs/stash` értékét módosítja, a reflogot változatlanul hagyja, ezért a bejegyzés a `listEntries()` részében marad ([#10](https://github.com/inline0/pitmaster/issues/10)).

## Tárolási részletek

### A stash commit felépítése

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

<stash message>
```

### Az index commit felépítése

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

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

Az identitás a `GIT_COMMITTER_NAME`, majd a `GIT_AUTHOR_NAME`, aztán a tároló konfigurációjában szereplő `user.name`, végül a `Pitmaster` értékéből származik (az e-mail-címnél ugyanez a sorrend érvényes, amely a `pitmaster@example.invalid` értékkel zárul). A dátum a `GIT_COMMITTER_DATE`, majd a `GIT_AUTHOR_DATE` értékéből, végül az aktuális időből származik. A commitokkal ellentétben a stash nem olvassa be a `PITMASTER_*` konstansokat.

### Reflog tárolása

A stash-bejegyzések tárolási helye: `.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
```

A reflog időrendi sorrendben olvasható. A stash-lista megfordítja ezt a sorrendet (a legújabbal kezdve). A stash reflogja a `core.logallrefupdates` értékétől függetlenül íródik.

## CLI használata

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

Az üzenet a `push` (vagy a `save`) utáni argumentum; a `-m` nem támogatott. A `pop`, a `apply` és a `drop` mindig a `stash@{0}` elemen működik; a CLI nem rendelkezik `--include-untracked` értékkel.

## Korlátozások

- Csak teljes munkakönyvtár menthető el stashként (nincs `--keep-index`); nem követett fájlok csak a `push($message, includeUntracked: true)` segítségével
- Nincs `--patch` mód (interaktív hunkkiválasztás)
- A Pop/Apply visszaállítja a munkafa állapotát, az indexet azonban nem. A stage-elt módosítások unstaged módosításként térnek vissza, az újonnan stage-elt fájlok pedig nem követett fájlokként
- A `stash@{n}` nem fogadható el a `Repository::resolve()` által
- A stash egy összekapcsolt munkafában rossz konfigurációs és reflog-helyet használ ([#14](https://github.com/inline0/pitmaster/issues/14))
