
# Stash

Git stash salvează modificările directorului de lucru și ale indexului, astfel încât să puteți trece la o altă sarcină și să le restaurați mai târziu. Pitmaster implementează stash-ul folosind `refs/stash` și reflog-ul, în structura de stocare a git. `Repository` nu are o metodă stash; folosiți clasa `Stash` de mai jos.

Două probleme cunoscute: când există mai multe intrări, `drop()` (și, prin urmare, `pop()`) lasă intrarea în listă ([#10](https://github.com/inline0/pitmaster/issues/10)), iar `apply()` suprascrie modificările neconfirmate din fișierele pe care stash-ul nu le atinge ([#11](https://github.com/inline0/pitmaster/issues/11)). Aplicați stash-uri într-un arbore de lucru curat.

## Cum funcționează stash-ul

Fiecare intrare stash este un commit cu doi părinți:
- **Părinte 0**: commit-ul HEAD de la momentul stash-ului (starea de bază)
- **Părinte 1**: un commit care surprinde starea indexului (modificările staged)

Arborele propriu al commit-ului stash surprinde starea arborelui de lucru (inclusiv modificările nestaged).

„Stiva” stash este reflog-ul lui `refs/stash`. Cel mai recent stash este chiar `refs/stash`. Intrările mai vechi sunt înregistrări reflog: `stash@{0}`, `stash@{1}` etc.

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

## Crearea obiectului Stash

```php
use Pitmaster\Stash\Stash;

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

## Push (salvarea modificărilor)

```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 efectuează următoarele:
1. Citește commit-ul HEAD curent
2. Construiește un arbore din indexul curent (starea staged)
3. Creează un „commit de index” cu HEAD ca părinte
4. Construiește un arbore din directorul de lucru curent (inclusiv modificările nestaged)
5. Creează commit-ul stash (arborele de lucru, părinți = HEAD + commit-ul de index)
6. Actualizează `refs/stash` astfel încât să indice commit-ul stash
7. Adaugă o înregistrare reflog pentru stivă
8. Resetează arborele de lucru și indexul la HEAD

După push, fișierele urmărite corespund cu HEAD. Fișierele neurmărite rămân, cu excepția cazului în care este setat `includeUntracked`; atunci sunt eliminate și stocate în arborele propriu al commit-ului stash (git le stochează într-un al treilea părinte, astfel încât git le vede ca modificări urmărite). `push()` creează o intrare chiar și când nu s-a schimbat nimic, caz în care git raportează „No local changes to save”.

### Cu mesaj implicit

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

Cu un HEAD detașat, `<branch>` este `HEAD`.

## Pop (aplicare și eliminare)

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

Aplică intrarea stash din vârf în directorul de lucru și o elimină din stivă. Echivalent cu `apply()` urmat de `drop()`.

### Eliminarea unei intrări anume

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

## Apply (restaurare fără eliminare)

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

Apply îmbină stash-ul în fișierele HEAD curent, unul câte unul (baza stash-ului, HEAD-ul curent, arborele stash-ului). Intrarea stash rămâne în stivă, iar indexul nu este atins. Conflictele lasă în fișiere marcajele `Updated upstream` și `Stashed changes` și aruncă `Pitmaster\Exceptions\MergeConflictException`; apoi `pop()` păstrează intrarea. Apply nu verifică mai întâi dacă există modificări locale (consultați nota de la început).

## Listarea intrărilor

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

Fiecare intrare conține:
- `index`: poziția stash-ului (0 = cel mai recent)
- `message`: mesajul stash-ului
- `hash`: hash-ul commit-ului stash

## Drop (eliminare fără aplicare)

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

Dacă eliminați singura intrare, `refs/stash` și reflog-ul său sunt eliminate. Când există mai multe intrări, `drop()` doar mută în prezent `refs/stash` și lasă reflog-ul neschimbat, așa că intrarea rămâne în `listEntries()` ([#10](https://github.com/inline0/pitmaster/issues/10)).

## Detalii despre stocare

### Structura commit-ului stash

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

<stash message>
```

### Structura commit-ului de index

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

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

Identitatea provine din `GIT_COMMITTER_NAME`, apoi din `GIT_AUTHOR_NAME`, apoi din `user.name` din configurația depozitului, apoi din `Pitmaster` (aceeași ordine se aplică e-mailului, care se încheie cu `pitmaster@example.invalid`). Data provine din `GIT_COMMITTER_DATE`, apoi din `GIT_AUTHOR_DATE`, apoi din ora curentă. Spre deosebire de commit-uri, stash nu citește constantele `PITMASTER_*`.

### Stocare Reflog

Intrările stash sunt stocate în `.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-ul este citit în ordine cronologică. Lista stash-urilor inversează ordinea (cel mai recent primul). Reflog-ul stash este scris indiferent de `core.logallrefupdates`.

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

Mesajul este argumentul de după `push` (sau `save`); `-m` nu este acceptat. `pop`, `apply` și `drop` funcționează întotdeauna pe `stash@{0}`; CLI nu are `--include-untracked`.

## Limitări

- Doar stash-ul întregului director de lucru (fără `--keep-index`); fișierele neurmărite doar prin `push($message, includeUntracked: true)`
- Fără modul `--patch` (selectarea interactivă a fragmentelor)
- Pop/apply restaurează starea arborelui de lucru; nu restaurează indexul. Modificările staged revin ca modificări nestaged, iar fișierele nou staged revin ca fișiere neurmărite
- `stash@{n}` nu este acceptat de `Repository::resolve()`
- Stash-ul într-un worktree asociat folosește locația greșită pentru configurație și reflog ([#14](https://github.com/inline0/pitmaster/issues/14))
