
# Shramba

Git shramba shrani spremembe delovnega imenika in indeksa, da lahko preklopite na drugo opravilo in jih pozneje obnovite. Pitmaster izvaja shrambo z uporabo `refs/stash` in refloga, v skladu s postavitvijo shrambe za git. Na `Repository` ni metode za shrambo; uporabite spodnji razred `Stash`.

Znani sta dve težavi: če je vnosov več kot eden, `drop()` (in zato tudi `pop()`) pusti vnos na seznamu ([#10](https://github.com/inline0/pitmaster/issues/10)), `apply()` pa prepiše nepotrjene spremembe v datotekah, ki se jih shramba ne dotika ([#11](https://github.com/inline0/pitmaster/issues/11)). Shrambe uveljavljajte v čistem delovnem drevesu.

## Delovanje shrambe

Vsak vnos shrambe je potrditev z dvema nadrejenima potrditvama:
- **Nadrejena potrditev 0**: potrditev HEAD v času shranjevanja (osnovno stanje)
- **Nadrejena potrditev 1**: potrditev, ki zajame stanje indeksa (pripravljene spremembe)

Lastno drevo potrditve shrambe zajame stanje delovnega drevesa (vključno z nepripravljenimi spremembami).

»Sklad« shrambe je reflog za `refs/stash`. Najnovejša shramba je kar `refs/stash`. Starejši vnosi so vnosi refloga: `stash@{0}`, `stash@{1}` itd.

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

## Ustvarjanje predmeta Stash

```php
use Pitmaster\Stash\Stash;

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

## Potisni (shrani spremembe)

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

Potisni izvede naslednje:
1. Prebere trenutno potrditev HEAD
2. Zgradi drevo iz trenutnega indeksa (pripravljeno stanje)
3. Ustvari »potrditev indeksa« z nadrejeno potrditvijo HEAD
4. Zgradi drevo iz trenutnega delovnega imenika (vključno z nepripravljenimi spremembami)
5. Ustvari potrditev shrambe (drevo delovnega drevesa, nadrejeni potrditvi = HEAD + potrditev indeksa)
6. Posodobi `refs/stash`, da kaže na potrditev shrambe
7. Doda vnos refloga za sklad
8. Ponastavi delovno drevo in indeks na HEAD

Po potisku se sledene datoteke ujemajo s HEAD. Nesledene datoteke ostanejo, razen če je nastavljen `includeUntracked`; v tem primeru se odstranijo in shranijo v lastno drevo potrditve shrambe (git jih namesto tega shrani v tretjo nadrejeno potrditev, zato jih git vidi kot sledene spremembe). `push()` ustvari vnos tudi, če ni bilo sprememb; v tem primeru git sporoči »Ni lokalnih sprememb za shranjevanje«.

### S privzetim sporočilom

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

Pri odpetem HEAD je `<branch>` enak `HEAD`.

## Izprazni (uveljavi in odstrani)

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

Uveljavi vrhnji vnos shrambe v delovnem imeniku in ga odstrani iz sklada. Enakovredno je `apply()`, ki mu sledi `drop()`.

### Izprazni določen vnos

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

## Uveljavi (obnovi brez odstranitve)

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

Uveljavi združi shrambo s trenutno datoteko HEAD po datotekah (osnova shrambe, trenutni HEAD, drevo shrambe). Vnos shrambe ostane v skladu, indeks pa se ne spremeni. Ob sporih v datotekah ostanejo označevalniki `Updated upstream` in `Stashed changes`, sproži pa se `Pitmaster\Exceptions\MergeConflictException`; `pop()` nato vnos ohrani. Uveljavi predhodno ne preveri lokalnih sprememb (glejte opombo na vrhu).

## Seznam vnosov

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

Vsak vnos vsebuje:
- `index`: položaj shrambe (0 = najnovejša)
- `message`: sporočilo shrambe
- `hash`: zgoščena vrednost potrditve shrambe

## Izbriši (odstrani brez uveljavitve)

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

Če izbrišete edini vnos, se odstranita `refs/stash` in njegov reflog. Če je vnosov več, `drop()` trenutno samo premakne `refs/stash` in refloga ne spremeni, zato vnos ostane v `listEntries()` ([#10](https://github.com/inline0/pitmaster/issues/10)).

## Podrobnosti shranjevanja

### Struktura potrditve shrambe

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

<stash message>
```

### Struktura potrditve indeksa

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

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

Identiteta izhaja iz `GIT_COMMITTER_NAME`, nato `GIT_AUTHOR_NAME`, nato `user.name` v konfiguraciji repozitorija, nato `Pitmaster` (enak vrstni red velja za e-pošto, ki se konča z `pitmaster@example.invalid`). Datum izhaja iz `GIT_COMMITTER_DATE`, nato `GIT_AUTHOR_DATE`, nato iz trenutnega časa. Za razliko od potrditev shramba ne bere konstant `PITMASTER_*`.

### Shranjevanje Reflog

Vnosi shrambe so shranjeni v `.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 se bere v kronološkem vrstnem redu. Seznam shrambe ga obrne (najnovejše najprej). Reflog shrambe se zapiše ne glede na `core.logallrefupdates`.

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

Sporočilo je argument za `push` (ali `save`); `-m` ni podprt. `pop`, `apply` in `drop` vedno delujejo na `stash@{0}`; CLI nima `--include-untracked`.

## Omejitve

- Samo shramba celotnega delovnega imenika (brez `--keep-index`); nesledene datoteke so podprte samo prek `push($message, includeUntracked: true)`
- Ni načina `--patch` (interaktivni izbor kosov)
- Izprazni/uveljavi obnovi stanje delovnega drevesa; indeksa ne obnovi. Pripravljene spremembe se vrnejo kot nepripravljene, na novo pripravljene datoteke pa kot nesledene
- `stash@{n}` ne sprejema `Repository::resolve()`
- Shramba v povezanem delovnem drevesu uporablja napačno mesto konfiguracije in refloga ([#14](https://github.com/inline0/pitmaster/issues/14))
