
# Árboles de trabajo

Git Los árboles de trabajo permiten que varios directorios de trabajo compartan un mismo repositorio. Cada árbol tiene su propia extracción (HEAD, índice, archivos de trabajo), pero comparte con el repositorio principal el almacén de objetos, la configuración y las referencias empaquetadas. Pitmaster puede crear, enumerar, bloquear y eliminar árboles de trabajo vinculados, y abrirlos como repositorios. No se implementan la poda, el traslado ni la reparación de árboles de trabajo.

## Conceptos

Un repositorio git normal tiene un único árbol de trabajo:

```
project/
  .git/          (directory)
  src/
  README.md
```

Con los worktrees, puede tener checkouts adicionales:

```
project/                          (main worktree)
  .git/                           (the actual git directory)
    worktrees/
      review/                     (metadata for linked worktree)
        HEAD
        commondir
        gitdir

/tmp/review/                      (linked worktree)
  .git                            (file, not directory)
  src/
  README.md
```

El `.git` del worktree vinculado es un archivo (no un directorio) que contiene una ruta relativa al worktree:
```
gitdir: ../../path/to/project/.git/worktrees/review
```

## Crear un worktree

```php
$repo = Pitmaster::open('/path/to/project');

// Create a linked worktree on a new branch
$wt = $repo->addWorktree('/tmp/review', 'feature/review');

echo $wt->path;        // '/tmp/review'
echo $wt->branch;      // 'feature/review'
echo $wt->gitDir;      // '/path/to/project/.git/worktrees/review'
var_dump($wt->isMain);      // bool(false)
var_dump($wt->isDetached);  // bool(false)
```

`addWorktree()` hace lo siguiente:
1. Crea la rama si no existe (desde HEAD o desde el commit `$from` especificado)
2. Crea el directorio de metadatos en `.git/worktrees/<name>/`
3. Escribe el archivo `.git` en el directorio del worktree
4. Escribe los archivos de metadatos `HEAD`, `commondir` y `gitdir`
5. Hace checkout del árbol de la rama en el directorio del worktree
6. Escribe el índice del worktree

Si la rama ya existe, se ignora `$from`. A diferencia de git, `addWorktree()` no rechaza una rama que ya tenga checkout en otro worktree ni ejecuta el hook `post-checkout`.

### Con un nombre de metadatos explícito

Use el argumento opcional `name` cuando varios worktrees vinculados puedan compartir el mismo nombre base de checkout.

```php
$wt = $repo->addWorktree(
    '/tmp/sandboxes/app',
    'feature/review',
    name: 'sandbox-app',
);
```

Ese nombre de metadatos se usa en `.git/worktrees/<name>/` en lugar de `basename($path)`.

### Desde un commit específico

```php
$wt = $repo->addWorktree('/tmp/hotfix', 'hotfix/urgent', $repo->resolve('v1.0.0'));
```

## Listar worktrees

```php
$worktrees = $repo->worktrees();

foreach ($worktrees as $wt) {
    $type = $wt->isMain ? 'main' : 'linked';
    $branch = $wt->branch ?? 'detached';
    $locked = $wt->isLocked ? ' [locked]' : '';

    echo "{$wt->path} ({$type}) [{$branch}]{$locked}\n";
}
```

La lista siempre incluye primero el worktree principal, seguido de los worktrees vinculados. Llame a `worktrees()` en un controlador del repositorio principal: en un controlador de un worktree vinculado, la entrada principal informa actualmente la propia ruta del worktree vinculado ([#14](https://github.com/inline0/pitmaster/issues/14)).

## Eliminar un worktree

```php
$repo->removeWorktree('review');          // by metadata name
$repo->removeWorktree('/tmp/review');     // or by worktree path

// Force remove (even if locked)
$repo->removeWorktree('review', force: true);
```

Al eliminar un worktree:
1. Comprueba si está bloqueado (falla a menos que `$force` sea true)
2. Elimina el archivo `.git` del directorio del worktree
3. Elimina el directorio de metadatos de `.git/worktrees/`

Los archivos del árbol de trabajo no se eliminan automáticamente. La rama sigue existiendo.

## Abrir un worktree vinculado

Puede abrir un worktree vinculado igual que cualquier repositorio:

```php
$repo = Pitmaster::open('/tmp/review');

// Pitmaster detects the .git file and follows the gitdir reference
echo $repo->isLinkedWorktree();  // true
echo $repo->gitDir();            // '/path/to/project/.git/worktrees/review'
echo $repo->commonGitDir();      // '/path/to/project/.git'
echo $repo->workDir();           // '/tmp/review'
```

Las operaciones del repositorio resuelven las rutas de esta manera:
- **Objetos**: se leen del directorio git común y se escriben en él (`project/.git/objects/`)
- **Configuración**: se lee del directorio git común (`project/.git/config`)
- **Packed-refs**: se lee del directorio git común
- **HEAD**: por worktree (cada worktree registra su propia rama), junto con `ORIG_HEAD`, `MERGE_HEAD` y las demás referencias de operaciones
- **Índice**: por worktree (cada worktree tiene su propia área de preparación)
- **Referencias sueltas**: las ramas y etiquetas se comparten en el directorio común (`project/.git/refs/`)
- **Hooks**: se leen del directorio git de cada worktree, por lo que los hooks compartidos no se ejecutan ([#12](https://github.com/inline0/pitmaster/issues/12))

Stash aún no admite worktrees vinculados: lee la identidad del directorio de cada worktree, que no tiene configuración, y escribe allí su reflog ([#14](https://github.com/inline0/pitmaster/issues/14)).

## Objeto de valor Worktree

```php
use Pitmaster\Worktree\Worktree;

$wt->name;          // Metadata name (null for the main worktree)
$wt->path;          // Working directory path
$wt->gitDir;        // Per-worktree git dir
$wt->branch;        // Branch name (null if detached)
$wt->head;          // ObjectId of HEAD commit
$wt->isMain;        // true for the main worktree
$wt->isDetached;    // true if HEAD is detached
$wt->isLocked;      // true if worktree is locked
$wt->lockReason;    // Lock reason, or null when there is none (an empty reason is also null)
```

## WorktreeManager

Para la gestión de worktrees de bajo nivel. `WorktreeManager::add()` solo escribe los metadatos y el archivo `.git`: no crea la rama, no hace checkout de archivos ni escribe un índice. Use `$repo->addWorktree()` para obtener un checkout funcional. La rama ya debe existir (o ser un nombre de referencia completo, como `refs/tags/v1.0.0`, que separa HEAD de la rama).

```php
use Pitmaster\Worktree\WorktreeManager;

$manager = new WorktreeManager($repo->commonGitDir(), $mainWorkDir);

// List
$worktrees = $manager->list();

// Add
$wt = $manager->add('/tmp/review', 'feature/review');
$wt = $manager->add('/tmp/review', 'feature/review', 'review-app');

// Remove
$manager->remove('review');
$manager->remove('review', force: true);

// Lock/unlock (metadata name only)
$manager->lock('review', 'Do not delete, code review in progress');
$manager->unlock('review');
```

## Bloquear worktrees

```php
// Lock (prevents removal)
$manager->lock('review', 'Code review in progress');

// Attempt to remove locked worktree
try {
    $manager->remove('review');
} catch (\RuntimeException $e) {
    // "Worktree is locked: review (Code review in progress)"
}

// Force remove ignores the lock
$manager->remove('review', force: true);

// Unlock
$manager->unlock('review');
```

El bloqueo se guarda como un archivo en `.git/worktrees/<name>/locked`. El contenido del archivo es el motivo del bloqueo (puede estar vacío).

## Detectar worktrees

```php
// Check if a path is a linked worktree
if (Pitmaster::isWorktree('/tmp/review')) {
    echo "This is a linked worktree\n";
}

// Resolve the common git dir from any path
$common = Pitmaster::commonGitDir('/tmp/review');
// '/path/to/project/.git'

// Resolve a .git file to the actual git directory
$gitDir = WorktreeManager::resolveGitFile('/tmp/review');
// '/path/to/project/.git/worktrees/review'
```

## Estructura de archivos

### Metadatos del worktree principal para worktrees vinculados

```
.git/worktrees/<name>/
  HEAD         Per-worktree HEAD (symbolic ref or detached hash)
  commondir    Relative path to the shared git dir ("../..")
  gitdir       Path back to the worktree's .git file, as passed to addWorktree()
  locked       Present if locked (content = reason)
  index        Per-worktree index/staging area
```

### Archivo .git del worktree vinculado

```
gitdir: <relative path to>/project/.git/worktrees/<name>
```

### Recursos compartidos

| Recurso | Ubicación | ¿Compartido? |
|----------|----------|---------|
| Objetos | `$commonDir/objects/` | Sí |
| Configuración | `$commonDir/config` | Sí |
| Packed-refs | `$commonDir/packed-refs` | Sí |
| Hooks | `$gitDir/hooks/` | No: Pitmaster no ejecuta los hooks compartidos en worktrees vinculados |
| HEAD | `$gitDir/HEAD` | No (por worktree) |
| Índice | `$gitDir/index` | No (por worktree) |
| Referencias sueltas | `$commonDir/refs/` | Sí (HEAD y referencias de operaciones como `MERGE_HEAD` son por worktree) |
