
# Worktrees

Git worktrees maken het mogelijk meerdere werkdirectories te delen binnen één repository. Elke worktree heeft een eigen checkout (HEAD, index, werkbestanden), maar deelt de objectopslag, configuratie en packed-refs met de hoofdrepository. Pitmaster kan gekoppelde worktrees aanmaken, weergeven, vergrendelen en verwijderen, en ze als repositories openen. Opschonen, verplaatsen en repareren van worktrees zijn niet geïmplementeerd.

## Concepten

Een reguliere git-repository heeft één werkdirectory:

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

Met worktrees kun je extra checkouts hebben:

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

Het bestand `.git` van de gekoppelde worktree bevat (in plaats van een map) een pad relatief aan de worktree:
```
gitdir: ../../path/to/project/.git/worktrees/review
```

## Een worktree aanmaken

```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()` doet het volgende:
1. Maakt de branch aan als deze niet bestaat (op basis van HEAD of de opgegeven commit `$from`)
2. Maakt de metadatamap aan in `.git/worktrees/<name>/`
3. Schrijft het bestand `.git` in de worktree-map
4. Schrijft de metadatabestanden `HEAD`, `commondir` en `gitdir`
5. Checkt de tree van de branch uit naar de worktree-map
6. Schrijft de index voor de worktree

Als de branch al bestaat, wordt `$from` genegeerd. In tegenstelling tot git weigert `addWorktree()` geen branch die al in een andere worktree is uitgecheckt en voert het de hook `post-checkout` niet uit.

### Met een expliciete metadatanaam

Gebruik het optionele argument `name` wanneer meerdere gekoppelde worktrees dezelfde checkout-basismapnaam kunnen hebben.

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

Die metadatanaam wordt gebruikt onder `.git/worktrees/<name>/` in plaats van `basename($path)`.

### Vanaf een specifieke commit

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

## Worktrees weergeven

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

De lijst bevat altijd eerst de hoofd-worktree, gevolgd door gekoppelde worktrees. Roep `worktrees()` aan op een handle voor de hoofdrepository: bij een handle voor een gekoppelde worktree geeft de hoofdvermelding momenteel het eigen pad van die gekoppelde worktree weer ([#14](https://github.com/inline0/pitmaster/issues/14)).

## Een worktree verwijderen

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

Een worktree verwijderen:
1. Controleert of de worktree vergrendeld is (mislukt tenzij `$force` true is)
2. Verwijdert het bestand `.git` uit de worktree-map
3. Verwijdert de metadatamap uit `.git/worktrees/`

De bestanden in de werkdirectory worden niet automatisch verwijderd. De branch blijft bestaan.

## Een gekoppelde worktree openen

Je kunt een gekoppelde worktree net als elke repository openen:

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

Repositorybewerkingen lossen paden als volgt op:
- **Objecten**: gelezen uit en geschreven naar de gemeenschappelijke git-map (`project/.git/objects/`)
- **Configuratie**: gelezen uit de gemeenschappelijke git-map (`project/.git/config`)
- **Packed-refs**: gelezen uit de gemeenschappelijke git-map
- **HEAD**: per worktree (elke worktree houdt de eigen branch bij), samen met `ORIG_HEAD`, `MERGE_HEAD` en de andere bewerkingsrefs
- **Index**: per worktree (elke worktree heeft een eigen staginggebied)
- **Losse refs**: branches en tags worden gedeeld in de gemeenschappelijke map (`project/.git/refs/`)
- **Hooks**: gelezen uit de git-map per worktree, dus de gedeelde hooks worden niet uitgevoerd ([#12](https://github.com/inline0/pitmaster/issues/12))

Stash ondersteunt gekoppelde worktrees nog niet: het leest de identiteit uit de map per worktree, die geen configuratie bevat, en schrijft de reflog daar ([#14](https://github.com/inline0/pitmaster/issues/14)).

## Worktree-waardeobject

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

Voor werktreebeheer op laag niveau. `WorktreeManager::add()` schrijft alleen de metadata en het bestand `.git`: het maakt geen branch aan, checkt geen bestanden uit en schrijft geen index. Gebruik `$repo->addWorktree()` voor een bruikbare checkout. De branch moet al bestaan (of een volledige ref-naam zijn zoals `refs/tags/v1.0.0`, waarmee HEAD wordt losgekoppeld).

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

## Worktrees vergrendelen

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

De vergrendeling wordt opgeslagen als bestand in `.git/worktrees/<name>/locked`. De inhoud van het bestand is de reden voor de vergrendeling (mag leeg zijn).

## Worktrees detecteren

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

## Bestandsindeling

### Metadata van de hoofd-worktree voor gekoppelde worktrees

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

### Het .git-bestand van een gekoppelde worktree

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

### Resources delen

| Resource | Locatie | Gedeeld? |
|----------|----------|----------|
| Objecten | `$commonDir/objects/` | Ja |
| Configuratie | `$commonDir/config` | Ja |
| Packed-refs | `$commonDir/packed-refs` | Ja |
| Hooks | `$gitDir/hooks/` | Nee: Pitmaster voert de gedeelde hooks niet uit in gekoppelde worktrees |
| HEAD | `$gitDir/HEAD` | Nee (per worktree) |
| Index | `$gitDir/index` | Nee (per worktree) |
| Losse refs | `$commonDir/refs/` | Ja (HEAD en bewerkingsrefs zoals `MERGE_HEAD` zijn per worktree) |
