
# Worktree

Git I worktree consentono a più directory di lavoro di condividere un unico repository. Ogni worktree ha il proprio checkout (HEAD, indice, file di lavoro), ma condivide l'archivio degli oggetti, la configurazione e packed-refs con il repository principale. Pitmaster può creare, elencare, bloccare e rimuovere worktree collegati, oltre che aprirli come repository. Le operazioni di pulizia, spostamento e riparazione dei worktree non sono implementate.

## Concetti

Un repository git normale ha un'unica directory di lavoro:

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

Con i worktree, potete avere checkout aggiuntivi:

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

Il `.git` del worktree collegato è un file (non una directory) che contiene un percorso relativo al worktree:
```
gitdir: ../../path/to/project/.git/worktrees/review
```

## Creare 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()` esegue le seguenti operazioni:
1. Crea il branch se non esiste (da HEAD o dal commit `$from` specificato)
2. Crea la directory dei metadati in `.git/worktrees/<name>/`
3. Scrive il file `.git` nella directory del worktree
4. Scrive i file di metadati `HEAD`, `commondir` e `gitdir`
5. Estrae l'albero del branch nella directory del worktree
6. Scrive l'indice del worktree

Se il branch esiste già, `$from` viene ignorato. A differenza di git, `addWorktree()` non rifiuta un branch già estratto in un altro worktree e non esegue l'hook `post-checkout`.

### Con un nome esplicito per i metadati

Usate l'argomento facoltativo `name` quando più worktree collegati possono condividere lo stesso nome di base del checkout.

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

Il nome dei metadati viene usato in `.git/worktrees/<name>/` al posto di `basename($path)`.

### Da un commit specifico

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

## Elencare i worktree

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

L'elenco include sempre prima il worktree principale, seguito da quelli collegati. Chiamate `worktrees()` su un handle del repository principale: con un handle di un worktree collegato, la voce principale riporta attualmente il percorso del worktree collegato stesso ([#14](https://github.com/inline0/pitmaster/issues/14)).

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

Rimozione di un worktree:
1. Verifica se il worktree è bloccato (fallisce a meno che `$force` non sia true)
2. Rimuove il file `.git` dalla directory del worktree
3. Rimuove la directory dei metadati da `.git/worktrees/`

I file della directory di lavoro non vengono eliminati automaticamente. Il branch continua a esistere.

## Aprire un worktree collegato

Potete aprire un worktree collegato come qualsiasi altro repository:

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

Le operazioni sul repository risolvono i percorsi così:
- **Oggetti**: letti e scritti nella directory git comune (`project/.git/objects/`)
- **Configurazione**: letta dalla directory git comune (`project/.git/config`)
- **Packed-refs**: lette dalla directory git comune
- **HEAD**: specifico del worktree (ognuno tiene traccia del proprio branch), insieme a `ORIG_HEAD`, `MERGE_HEAD` e agli altri riferimenti delle operazioni
- **Indice**: specifico del worktree (ognuno ha la propria area di staging)
- **Riferimenti sciolti**: branch e tag sono condivisi nella directory comune (`project/.git/refs/`)
- **Hook**: letti dalla directory git specifica del worktree; gli hook condivisi non vengono eseguiti ([#12](https://github.com/inline0/pitmaster/issues/12))

Stash non gestisce ancora i worktree collegati: legge l'identità dalla directory specifica del worktree, che non contiene configurazione, e vi scrive il reflog ([#14](https://github.com/inline0/pitmaster/issues/14)).

## Oggetto valore del 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

Per la gestione dei worktree a basso livello. `WorktreeManager::add()` scrive soltanto i metadati e il file `.git`: non crea il branch, non estrae i file e non scrive un indice. Usate `$repo->addWorktree()` per ottenere un checkout utilizzabile. Il branch deve già esistere (oppure essere un nome completo di riferimento come `refs/tags/v1.0.0`, che scollega HEAD).

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

## Bloccare i worktree

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

Il blocco viene memorizzato in un file in `.git/worktrees/<name>/locked`. Il contenuto del file è il motivo del blocco (può essere vuoto).

## Rilevare i worktree

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

## Struttura dei file

### Metadati del worktree principale per i worktree collegati

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

### File .git del worktree collegato

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

### Condivisione delle risorse

| Risorsa | Posizione | Condivisa? |
|----------|----------|---------|
| Oggetti | `$commonDir/objects/` | Sì |
| Configurazione | `$commonDir/config` | Sì |
| Packed-refs | `$commonDir/packed-refs` | Sì |
| Hook | `$gitDir/hooks/` | No: Pitmaster non esegue gli hook condivisi nei worktree collegati |
| HEAD | `$gitDir/HEAD` | No (specifico del worktree) |
| Indice | `$gitDir/index` | No (specifico del worktree) |
| Riferimenti sciolti | `$commonDir/refs/` | Sì (HEAD e riferimenti delle operazioni come `MERGE_HEAD` sono specifici del worktree) |
