
# Stash

Git stash išsaugo darbinio katalogo ir indekso pakeitimus, kad galėtumėte pereiti prie kitos užduoties ir vėliau juos atkurti. Pitmaster įgyvendina stash naudodama `refs/stash` ir reflog, pagal git saugyklos formatą. `Repository` neturi stash metodo; naudokite toliau pateiktą `Stash` klasę.

Žinomos dvi problemos: kai įrašų daugiau nei vienas, `drop()` (todėl ir `pop()`) palieka įrašą sąraše ([#10](https://github.com/inline0/pitmaster/issues/10)), o `apply()` perrašo neįrašytus pakeitimus failuose, kurių stash neliečia ([#11](https://github.com/inline0/pitmaster/issues/11)). Stash taikykite tik švariame darbiniame medyje.

## Kaip veikia stash

Kiekvienas stash įrašas yra commit su dviem tėviniais commit:
- **Tėvinis commit 0**: stash sukūrimo metu buvęs HEAD commit (pradinė būsena)
- **Tėvinis commit 1**: commit, kuriame užfiksuota indekso būsena (paruošti pakeitimai)

Paties stash commit medis užfiksuoja darbinio medžio būseną (įskaitant neparuoštus pakeitimus).

Stash „dėklas“ yra `refs/stash` reflog. Naujausias stash yra pats `refs/stash`. Senesni įrašai pateikiami reflog įrašuose: `stash@{0}`, `stash@{1}` ir t. t.

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

## Stash objekto kūrimas

```php
use Pitmaster\Stash\Stash;

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

## Push (išsaugoti pakeitimus)

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

Push atlieka šiuos veiksmus:
1. Nuskaito dabartinį HEAD commit
2. Iš dabartinio indekso sukuria medį (paruošta būsena)
3. Sukuria „indekso commit“, kurio tėvinis commit yra HEAD
4. Iš dabartinio darbinio katalogo sukuria medį (įskaitant neparuoštus pakeitimus)
5. Sukuria stash commit (darbinio medžio medis, tėviniai commit = HEAD + indekso commit)
6. Atnaujina `refs/stash`, kad rodytų į stash commit
7. Prideda dėklo reflog įrašą
8. Atkuria darbinį medį ir indeksą į HEAD būseną

Po push sekami failai sutampa su HEAD. Nesekami failai lieka, nebent nustatytas `includeUntracked`; tada jie pašalinami ir išsaugomi paties stash commit medyje (git juos saugo trečiajame tėviniame commit, todėl git juos laiko sekamais pakeitimais). `push()` sukuria įrašą net tada, kai niekas nepasikeitė, nors git praneša „No local changes to save“.

### Su numatytuoju pranešimu

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

Atjungus HEAD, `<branch>` yra `HEAD`.

## Pop (pritaikyti ir pašalinti)

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

Pritaiko viršutinį stash įrašą darbiniam katalogui ir pašalina jį iš dėklo. Tai atitinka `apply()`, po kurio vykdomas `drop()`.

### Konkretaus įrašo pritaikymas ir pašalinimas

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

## Apply (atkurti nepašalinant)

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

Apply sujungia stash su dabartiniu HEAD failas po failo (stash bazė, dabartinis HEAD, stash medis). Stash įrašas lieka dėkle, o indeksas neliečiamas. Kilus konfliktams, failuose paliekami `Updated upstream` ir `Stashed changes` žymekliai ir išmetama `Pitmaster\Exceptions\MergeConflictException`; `pop()` tada įrašo nepašalina. Prieš taikant Apply vietiniai pakeitimai netikrinami (žr. pradžioje pateiktą pastabą).

## Įrašų sąrašas

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

Kiekviename įraše yra:
- `index`: stash pozicija (0 = naujausias)
- `message`: stash pranešimas
- `hash`: stash commit maiša

## Drop (pašalinti nepritaikius)

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

Pašalinus vienintelį įrašą, pašalinami `refs/stash` ir jo reflog. Kai įrašų daugiau, `drop()` šiuo metu tik perkelia `refs/stash`, o reflog palieka nepakeistą, todėl įrašas lieka `listEntries()` ([#10](https://github.com/inline0/pitmaster/issues/10)).

## Saugojimo informacija

### Stash commit struktūra

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

<stash message>
```

### Indekso commit struktūra

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

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

Tapatybė nustatoma pagal `GIT_COMMITTER_NAME`, tada `GIT_AUTHOR_NAME`, tada `user.name` saugyklos konfigūracijoje, o galiausiai `Pitmaster` (el. paštas nustatomas ta pačia tvarka, paskutinis – `pitmaster@example.invalid`). Data nustatoma pagal `GIT_COMMITTER_DATE`, tada `GIT_AUTHOR_DATE`, o galiausiai pagal dabartinį laiką. Skirtingai nei commit, stash neskaito `PITMASTER_*` konstantų.

### Reflog saugykla

Stash įrašai saugomi `.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 skaitomas chronologine tvarka. Stash sąraše įrašai rikiuojami atvirkščiai – naujausi pateikiami pirmiausia. Stash reflog įrašomas nepriklausomai nuo `core.logallrefupdates`.

## CLI naudojimas

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

Pranešimas yra argumentas po `push` (arba `save`); `-m` nepalaikomas. `pop`, `apply` ir `drop` visada veikia su `stash@{0}`; CLI neturi `--include-untracked`.

## Apribojimai

- Galimas tik viso darbinio katalogo stash (nėra `--keep-index`); nesekami failai įtraukiami tik per `push($message, includeUntracked: true)`
- Nėra `--patch` režimo (interaktyvaus pakeitimų gabalų pasirinkimo)
- Pop/apply atkuria darbinio medžio būseną, bet ne indeksą. Paruošti pakeitimai grįžta kaip neparuošti, o naujai paruošti failai – kaip nesekami
- `stash@{n}` nepriima `Repository::resolve()`
- Stash susietame darbiniame medyje naudoja netinkamą konfigūracijos ir reflog vietą ([#14](https://github.com/inline0/pitmaster/issues/14))
