
# Alijo

El alijo Git guarda los cambios del directorio de trabajo y del índice para que puedas cambiar a otra tarea y restaurarlos más tarde. Pitmaster implementa el alijo mediante `refs/stash` y el reflog, en el diseño de almacenamiento de git. `Repository` no tiene un método de alijo; usa la clase `Stash` que aparece a continuación.

Hay dos problemas conocidos: con más de una entrada, `drop()` (y, por tanto, `pop()`) deja la entrada en la lista ([#10](https://github.com/inline0/pitmaster/issues/10)), y `apply()` sobrescribe los cambios sin confirmar en archivos que el alijo no toca ([#11](https://github.com/inline0/pitmaster/issues/11)). Aplica los alijos en un árbol de trabajo limpio.

## Cómo funciona el alijo

Cada entrada del alijo es una confirmación con dos padres:
- **Padre 0**: la confirmación HEAD al crear el alijo (el estado base)
- **Padre 1**: una confirmación que captura el estado del índice (cambios preparados)

El árbol de la propia confirmación del alijo captura el estado del árbol de trabajo (incluidos los cambios sin preparar).

La «pila» del alijo es el reflog de `refs/stash`. El alijo más reciente es el propio `refs/stash`. Las entradas anteriores son entradas del 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
```

## Crear el objeto Stash

```php
use Pitmaster\Stash\Stash;

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

## Push (guardar cambios)

```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 hace lo siguiente:
1. Lee la confirmación HEAD actual
2. Crea un árbol a partir del índice actual (estado preparado)
3. Crea una «confirmación del índice» con HEAD como padre
4. Crea un árbol a partir del directorio de trabajo actual (incluidos los cambios sin preparar)
5. Crea la confirmación del alijo (árbol del árbol de trabajo, padres = HEAD + confirmación del índice)
6. Actualiza `refs/stash` para que apunte a la confirmación del alijo
7. Añade una entrada del reflog a la pila
8. Restablece el árbol de trabajo y el índice a HEAD

Después de hacer push, los archivos bajo seguimiento coinciden con HEAD. Los archivos sin seguimiento permanecen salvo que se establezca `includeUntracked`; en ese caso, se eliminan y se guardan en el árbol propio de la confirmación del alijo (git los guarda en un tercer padre, por lo que git los considera cambios bajo seguimiento). `push()` crea una entrada incluso si no hubo cambios, mientras que git informa «No local changes to save».

### Con el mensaje predeterminado

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

Con HEAD separado, `<branch>` es `HEAD`.

## Pop (aplicar y eliminar)

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

Aplica la entrada superior del alijo al directorio de trabajo y la elimina de la pila. Equivale a `apply()` seguido de `drop()`.

### Extraer una entrada específica

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

## Apply (restaurar sin eliminar)

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

Apply fusiona el alijo con cada archivo de HEAD actual (base del alijo, HEAD actual, árbol del alijo). La entrada permanece en la pila y el índice no se modifica. Los conflictos dejan marcadores etiquetados `Updated upstream` y `Stashed changes` en los archivos y generan `Pitmaster\Exceptions\MergeConflictException`; `pop()` conserva entonces la entrada. Apply no comprueba primero si hay cambios locales (consulta la nota inicial).

## Enumerar entradas

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

Cada entrada contiene:
- `index`: la posición del alijo (0 = el más reciente)
- `message`: el mensaje del alijo
- `hash`: el hash de la confirmación del alijo

## Drop (eliminar sin aplicar)

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

Si se elimina la única entrada, se eliminan `refs/stash` y su reflog. Si hay más entradas, actualmente `drop()` solo mueve `refs/stash` y deja el reflog intacto, por lo que la entrada permanece en `listEntries()` ([#10](https://github.com/inline0/pitmaster/issues/10)).

## Detalles de almacenamiento

### Estructura de la confirmación del alijo

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

<stash message>
```

### Estructura de la confirmación del índice

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

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

La identidad se obtiene de `GIT_COMMITTER_NAME`, luego `GIT_AUTHOR_NAME`, después `user.name` en la configuración del repositorio y, por último, `Pitmaster` (y se sigue el mismo orden para el correo electrónico, que termina en `pitmaster@example.invalid`). La fecha se obtiene de `GIT_COMMITTER_DATE`, luego `GIT_AUTHOR_DATE` y, por último, de la hora actual. A diferencia de las confirmaciones, el alijo no lee las constantes `PITMASTER_*`.

### Almacenamiento de Reflog

Las entradas del alijo se almacenan en `.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
```

El reflog se lee en orden cronológico. La lista de alijos lo invierte (del más reciente al más antiguo). El reflog del alijo se escribe independientemente de `core.logallrefupdates`.

## Uso de 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
```

El mensaje es el argumento que sigue a `push` (o `save`); `-m` no es compatible. `pop`, `apply` y `drop` siempre operan en `stash@{0}`; CLI no tiene `--include-untracked`.

## Limitaciones

- Solo se puede guardar el directorio de trabajo completo (sin `--keep-index`); los archivos sin seguimiento solo mediante `push($message, includeUntracked: true)`
- No hay modo `--patch` (selección interactiva de fragmentos)
- Pop/apply restaura el estado del árbol de trabajo, pero no el índice. Las modificaciones preparadas vuelven como modificaciones sin preparar, y los archivos recién preparados vuelven como archivos sin seguimiento
- `stash@{n}` no se acepta en `Repository::resolve()`
- El alijo en un árbol de trabajo vinculado usa una ubicación incorrecta para la configuración y el reflog ([#14](https://github.com/inline0/pitmaster/issues/14))
