
# Προσωρινή αποθήκευση

Το Git stash αποθηκεύει τις αλλαγές του καταλόγου εργασίας και του ευρετηρίου, ώστε να μεταβείτε σε άλλη εργασία και να τις επαναφέρετε αργότερα. Το Pitmaster υλοποιεί το stash χρησιμοποιώντας το `refs/stash` και το reflog, στη διάταξη αποθήκευσης του git. Δεν υπάρχει μέθοδος stash στο `Repository`· χρησιμοποιήστε την παρακάτω κλάση `Stash`.

Δύο γνωστά προβλήματα: όταν υπάρχουν περισσότερες από μία εγγραφές, το `drop()` (και επομένως το `pop()`) αφήνει την εγγραφή στη λίστα ([#10](https://github.com/inline0/pitmaster/issues/10)), ενώ το `apply()` αντικαθιστά μη αποθηκευμένες αλλαγές σε αρχεία που δεν αγγίζει το stash ([#11](https://github.com/inline0/pitmaster/issues/11)). Εφαρμόζετε stash μόνο σε καθαρό κατάλογο εργασίας.

## Πώς λειτουργεί το stash

Κάθε εγγραφή stash είναι ένα commit με δύο γονείς:
- **Γονέας 0**: το commit HEAD τη στιγμή του stash (η βασική κατάσταση)
- **Γονέας 1**: ένα commit που καταγράφει την κατάσταση του ευρετηρίου (σταδιοποιημένες αλλαγές)

Το δέντρο του ίδιου του commit stash καταγράφει την κατάσταση του δέντρου εργασίας (μαζί με τις μη σταδιοποιημένες αλλαγές).

Η «στοίβα» stash είναι το reflog του `refs/stash`. Το πιο πρόσφατο stash είναι το ίδιο το `refs/stash`. Οι παλαιότερες εγγραφές είναι καταχωρίσεις reflog: `stash@{0}`, `stash@{1}` κ.λπ.

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

```php
use Pitmaster\Stash\Stash;

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

## Push (αποθήκευση αλλαγών)

```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 εκτελεί τα εξής:
1. Διαβάζει το τρέχον commit HEAD
2. Δημιουργεί ένα δέντρο από το τρέχον ευρετήριο (σταδιοποιημένη κατάσταση)
3. Δημιουργεί ένα «commit ευρετηρίου» με γονέα το HEAD
4. Δημιουργεί ένα δέντρο από τον τρέχοντα κατάλογο εργασίας (μαζί με τις μη σταδιοποιημένες αλλαγές)
5. Δημιουργεί το commit stash (δέντρο worktree, γονείς = HEAD + commit ευρετηρίου)
6. Ενημερώνει το `refs/stash` ώστε να δείχνει στο commit stash
7. Προσθέτει μια εγγραφή reflog για τη στοίβα
8. Επαναφέρει τον κατάλογο εργασίας και το ευρετήριο στην κατάσταση HEAD

Μετά το push, τα παρακολουθούμενα αρχεία αντιστοιχούν στο HEAD. Τα μη παρακολουθούμενα αρχεία παραμένουν, εκτός αν έχει οριστεί το `includeUntracked`· τότε αφαιρούνται και αποθηκεύονται στο δικό του δέντρο του commit του stash (το git τα αποθηκεύει σε τρίτο γονικό commit, οπότε το git τα βλέπει ως παρακολουθούμενες αλλαγές). Το `push()` δημιουργεί μια εγγραφή ακόμη κι όταν δεν έχει αλλάξει τίποτα, οπότε το git αναφέρει «No local changes to save».

### Με προεπιλεγμένο μήνυμα

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

Με αποσυνδεδεμένο HEAD, το `<branch>` είναι `HEAD`.

## Pop (εφαρμογή και αφαίρεση)

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

Εφαρμόζει την κορυφαία εγγραφή stash στον κατάλογο εργασίας και την αφαιρεί από τη στοίβα. Ισοδυναμεί με `apply()` και έπειτα `drop()`.

### Pop συγκεκριμένης εγγραφής

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

## Apply (επαναφορά χωρίς αφαίρεση)

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

Το Apply συγχωνεύει το stash στο αρχείο του τρέχοντος HEAD, αρχείο προς αρχείο (βάση stash, τρέχον HEAD, δέντρο stash). Η εγγραφή stash παραμένει στη στοίβα και το ευρετήριο δεν επηρεάζεται. Οι συγκρούσεις αφήνουν στα αρχεία δείκτες με ετικέτες `Updated upstream` και `Stashed changes` και προκαλούν `Pitmaster\Exceptions\MergeConflictException`· το `pop()` διατηρεί τότε την εγγραφή. Το Apply δεν ελέγχει πρώτα για τοπικές αλλαγές (δείτε τη σημείωση στην αρχή).

## Λίστα εγγραφών

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

Κάθε εγγραφή περιλαμβάνει:
- `index`: τη θέση stash (0 = πιο πρόσφατη)
- `message`: το μήνυμα stash
- `hash`: το hash του commit stash

## Drop (αφαίρεση χωρίς εφαρμογή)

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

Αν αφαιρεθεί η μοναδική εγγραφή, αφαιρούνται το `refs/stash` και το reflog του. Όταν υπάρχουν περισσότερες εγγραφές, το `drop()` μετακινεί προς το παρόν μόνο το `refs/stash` και αφήνει το reflog αμετάβλητο, οπότε η εγγραφή παραμένει στο `listEntries()` ([#10](https://github.com/inline0/pitmaster/issues/10)).

## Λεπτομέρειες αποθήκευσης

### Δομή commit stash

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

<stash message>
```

### Δομή commit ευρετηρίου

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

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

Η ταυτότητα προέρχεται από το `GIT_COMMITTER_NAME`, έπειτα το `GIT_AUTHOR_NAME`, μετά το `user.name` στη ρύθμιση αποθετηρίου και τέλος το `Pitmaster` (με την ίδια σειρά για το email, με κατάληξη στο `pitmaster@example.invalid`). Η ημερομηνία προέρχεται από το `GIT_COMMITTER_DATE`, έπειτα το `GIT_AUTHOR_DATE` και τέλος την τρέχουσα ώρα. Σε αντίθεση με τα commit, το stash δεν διαβάζει τις σταθερές `PITMASTER_*`.

### Αποθήκευση Reflog

Οι εγγραφές stash αποθηκεύονται στο `.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 διαβάζεται με χρονολογική σειρά. Η λίστα stash αντιστρέφει τη σειρά του (νεότερο πρώτα). Το reflog stash γράφεται ανεξάρτητα από το `core.logallrefupdates`.

## Χρήση 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
```

Το μήνυμα είναι το όρισμα μετά το `push` (ή το `save`)· το `-m` δεν υποστηρίζεται. Τα `pop`, `apply` και `drop` λειτουργούν πάντα με το `stash@{0}`· το CLI δεν έχει `--include-untracked`.

## Περιορισμοί

- Υποστηρίζεται μόνο stash ολόκληρου του καταλόγου εργασίας (χωρίς `--keep-index`)· μη παρακολουθούμενα αρχεία μόνο μέσω του `push($message, includeUntracked: true)`
- Δεν υπάρχει λειτουργία `--patch` (διαδραστική επιλογή τμημάτων)
- Τα Pop/apply επαναφέρουν την κατάσταση του καταλόγου εργασίας· δεν επαναφέρουν το ευρετήριο. Οι σταδιοποιημένες αλλαγές επανέρχονται ως μη σταδιοποιημένες και τα νέα σταδιοποιημένα αρχεία επανέρχονται ως μη παρακολουθούμενα
- Το `stash@{n}` δεν γίνεται δεκτό από το `Repository::resolve()`
- Το stash σε συνδεδεμένο worktree χρησιμοποιεί λανθασμένη θέση ρυθμίσεων και reflog ([#14](https://github.com/inline0/pitmaster/issues/14))
