
# PHP API

## `Pitmaster\Pitmaster`

De klasse `Pitmaster\Pitmaster` is het statische toegangspunt. Deze biedt fabrieksmethoden om repositories te openen, aan te maken en te klonen, plus detectiehulpmiddelen.

```php
use Pitmaster\Pitmaster;
```

### `open`

```php
public static function open(string $path, array $options = []): Repository
```

Open een bestaande repository op `$path`. Het pad moet de hoofdmap van de werkdirectory zijn (met `.git/`). Ondersteunt ook gekoppelde worktrees (waarbij `.git` een bestand is met `gitdir: <path>`) en bare repositories (waarbij `$path` de map `.git` zelf is).

`$options` accepteert twee booleans, die standaard beide `true` zijn:

- `hooks`: voer `.git/hooks`-scripts uit. Geef `false` door om ze voor deze handle over te slaan.
- `processes`: sta alles toe wat een proces start of een netwerktransport opent. Geef `false` door om hooks, de fsmonitor-hook, clone, fetch, push en procestransport via SSH uit te schakelen. Lokaal lezen en schrijven blijft werken.

Genereert `InvalidArgumentException` als het pad geen git-repository is.

```php
$repo = Pitmaster::open('/home/user/project');
$repo = Pitmaster::open('/home/user/project', ['hooks' => false]);
$repo = Pitmaster::open('/home/user/project', Pitmaster::processFreeOptions());
```

### `init`

```php
public static function init(string $path, string $objectFormat = 'sha1', array $options = []): Repository
```

Initialiseer een nieuwe repository op `$path`. Maakt de `.git`-mapstructuur aan met `objects/`, `refs/heads/`, `refs/tags/`, `hooks/`, `info/`, `HEAD` (verwijzend naar `refs/heads/main`) en een standaard-`config`.

`$objectFormat` is `'sha1'` of `'sha256'`. Een SHA-256-repository krijgt `repositoryformatversion = 1` en `extensions.objectformat = sha256` in de configuratie. Elke andere waarde genereert `InvalidArgumentException`. `$options` accepteert dezelfde sleutels als `open()`.

Genereert `RuntimeException` als er al een repository op het pad bestaat.

```php
$repo = Pitmaster::init('/home/user/new-project');
$repo = Pitmaster::init('/home/user/sha256-project', 'sha256');
```

### `clone`

```php
public static function clone(string $url, string $path, ?int $depth = null, array $options = []): Repository
```

Kloon een externe repository. Initialiseert een nieuwe repo, zoekt externe refs, haalt het pack-bestand op, stelt remote-trackingbranches in en materialiseert de werkdirectory. URL's met SSH (`ssh://...` of `user@host:path`) gebruiken het transport SSH; andere URL's gebruiken smart HTTP en vallen terug op dumb HTTP als de smart-uitwisseling mislukt. Geef `$depth` door voor een shallow clone.

Genereert `RuntimeException` wanneer `$options['processes']` gelijk is aan `false`. Als het klonen mislukt en `$path` vooraf niet bestond, wordt de gedeeltelijk aangemaakte map verwijderd.

```php
$repo = Pitmaster::clone('https://github.com/user/repo.git', '/tmp/repo');
$repo = Pitmaster::clone('https://github.com/user/repo.git', '/tmp/shallow', 1);
$repo = Pitmaster::clone('https://github.com/user/repo.git', '/tmp/repo', null, ['hooks' => false]);
```

### `processFreeOptions`

```php
public static function processFreeOptions(): array
```

Retourneert `['processes' => false, 'hooks' => false]`, voor runtimes die geen hostprocessen mogen starten. Geef dit door aan `open()` of `init()`.

### `isRepository`

```php
public static function isRepository(string $path): bool
```

Controleer of een pad een git-repository is. Retourneert `true` voor reguliere repositories (map `.git/`), gekoppelde worktrees (bestand `.git`) en bare repositories (bestand `HEAD` aanwezig).

```php
if (Pitmaster::isRepository('/path/to/check')) {
    // It's a git repo
}
```

### `isWorktree`

```php
public static function isWorktree(string $path): bool
```

Controleer of een pad een gekoppelde worktree is (niet de hoofdrepository). Retourneert alleen `true` als `.git` een bestand is met een verwijzing naar `gitdir:`.

```php
if (Pitmaster::isWorktree('/path/to/worktree')) {
    // It's a linked worktree
}
```

### `commonGitDir`

```php
public static function commonGitDir(string $path): ?string
```

Bepaal de gemeenschappelijke git-map vanaf een willekeurig checkoutpad. Voor reguliere repositories is dit de map `.git`. Voor gekoppelde worktrees is dit de gedeelde git-map met objecten, configuratie en packed-refs. Retourneert `null` als het pad geen repository is.

```php
$common = Pitmaster::commonGitDir('/path/to/worktree');
// '/path/to/main-repo/.git'
```

---

## `Pitmaster\Repository`

De klasse `Pitmaster\Repository` is het hoofdobject voor alle git-bewerkingen. Verkrijg er een via `Pitmaster::open()`, `Pitmaster::init()` of `Pitmaster::clone()`.

```php
use Pitmaster\Repository;
```

### Padaccessors

#### `gitDir`

```php
public function gitDir(): string
```

Retourneert de git-map per worktree. Voor reguliere repositories is dit `.git/`. Voor gekoppelde worktrees is dit `.git/worktrees/<name>/`.

#### `commonGitDir`

```php
public function commonGitDir(): string
```

Retourneert de gedeelde git-map. Voor reguliere repositories is deze gelijk aan `gitDir()`. Voor gekoppelde worktrees is dit de map `.git/` van de hoofdrepository (waar objecten, configuratie en packed-refs staan).

#### `workDir`

```php
public function workDir(): string
```

Retourneert het pad naar de hoofdmap van de werkdirectory.

#### `isLinkedWorktree`

```php
public function isLinkedWorktree(): bool
```

Retourneert `true` als deze repositoryhandle een gekoppelde worktree is.

#### `isBare`, `hooksEnabled`, `processesEnabled`

```php
public function isBare(): bool
public function hooksEnabled(): bool
public function processesEnabled(): bool
```

Geeft aan of de repository geen werkdirectory heeft en welke opties `hooks` / `processes` actief zijn voor deze handle. `hooksEnabled()` is `false` wanneer processen zijn uitgeschakeld.

---

### Objecten

#### `readObject`

```php
public function readObject(string $hash): GitObject
```

Lees elk object op basis van de volledige hex-hash (40 tekens, of 64 in een SHA-256-repository). Afgekorte hashes worden niet geaccepteerd. Retourneert een instantie van `Blob`, `Tree`, `Commit` of `Tag`. Zoekt eerst in losse objecten en daarna in pack-bestanden.

Genereert `ObjectNotFoundException` als de hash niet wordt gevonden.

```php
$object = $repo->readObject('a1b2c3d4e5f6...');
```

#### `writeObject`

```php
public function writeObject(GitObject $object): ObjectId
```

Schrijf een object naar de opslag voor losse objecten. Retourneert de berekende `ObjectId`.

```php
use Pitmaster\Object\Blob;

$blob = Blob::fromContent('Hello, world!');
$id = $repo->writeObject($blob);
```

#### `catFile`

```php
public function catFile(string $hash): string
```

Retourneer de ruwe inhoud van een object, gelijkwaardig aan `git cat-file -p`.

```php
echo $repo->catFile('a1b2c3d4...');
```

#### `objectExists`

```php
public function objectExists(string $hash): bool
```

Controleer of een object in de repository bestaat (los of gepackt).

#### `listObjects`

```php
public function listObjects(): array
```

Geef alle object-hashes in de repository weer. Retourneert een array met hex-strings.

---

### Refs

#### `head`

```php
public function head(): Commit
```

Retourneert de huidige HEAD-commit. Volgt symbolische refs (HEAD -> refs/heads/main -> commit-hash).

Genereert `RuntimeException` als HEAD niet naar een geldige commit verwijst (e.g., lege repository).

#### `branch`

```php
public function branch(?string $name = null): ?string
```

Zonder argumenten retourneert dit de huidige branchnnaam (e.g., `'main'`) of `null` als HEAD is losgekoppeld.

Met een branchnaam wordt deze opgelost naar de commit-hash (hex-string), of retourneert dit `null` als de branch niet bestaat.

```php
$current = $repo->branch();        // 'main'
$hash = $repo->branch('feature');   // 'abc123...' or null
```

#### `branches`

```php
public function branches(): array
```

Retourneert een gesorteerde array met alle branchnamen (zonder het prefix `refs/heads/`).

```php
$branches = $repo->branches();
// ['bugfix/typo', 'feature/login', 'main']
```

#### `tags`

```php
public function tags(): array
```

Retourneert een gesorteerde array met alle tagnamen (zonder het prefix `refs/tags/`).

#### `resolve`

```php
public function resolve(string $revision): ObjectId
```

Los een revisie-expressie op naar een `ObjectId`. Ondersteunt:
- Volledige hex-hashes (40 tekens, of 64 voor SHA-256). Afgekorte hashes zoals `abc1234` worden niet opgelost.
- `HEAD`
- Branchnamen (`main`, `feature/login`)
- Tagnamen (`v1.0.0`)
- Volledige ref-paden (`refs/heads/main`)
- Revisie-expressies (`HEAD~3`, `main^2`)

Genereert `RuntimeException` als de revisie niet kan worden opgelost.

```php
$id = $repo->resolve('HEAD~3');
$id = $repo->resolve('v1.0.0');
$id = $repo->resolve('feature/login');
```

#### `allRefs`

```php
public function allRefs(): array
```

Retourneert alle refs als een associatieve array van ref-naam naar hex-hash.

```php
$refs = $repo->allRefs();
// ['refs/heads/main' => 'abc123...', 'refs/tags/v1.0.0' => 'def456...']
```

#### `updateRef`

```php
public function updateRef(string $name, ObjectId $target): void
```

Werk een ref bij zodat deze naar een nieuw doel verwijst.

```php
$repo->updateRef('refs/heads/main', $commitId);
```

#### `createBranch`

```php
public function createBranch(string $name, ?ObjectId $from = null): void
```

Maak een nieuwe branch aan. Als `$from` null is, wordt de branch gemaakt vanaf de huidige HEAD.

```php
$repo->createBranch('feature/new-thing');
$repo->createBranch('hotfix', $repo->resolve('v1.0.0'));
```

#### `deleteBranch`

```php
public function deleteBranch(string $name): void
```

Verwijder een branch op naam. Genereert `RuntimeException` als de branch is uitgecheckt of niet bestaat. In tegenstelling tot `git branch -d` controleert dit niet of de branch is samengevoegd.

```php
$repo->deleteBranch('feature/old');
```

#### `createLightweightTag`

```php
public function createLightweightTag(string $name, ?ObjectId $target = null): void
```

Maak een lichtgewicht tag aan. Als `$target` null is, wordt de huidige HEAD rechtstreeks getagd.

```php
$repo->createLightweightTag('v2.0.0');
$repo->createLightweightTag('hotfix-start', $repo->resolve('main~2'));
```

#### `createTag`

```php
public function createTag(string $name, string $message, ?ObjectId $target = null, ?string $tagger = null): ObjectId
```

Maak een geannoteerde tag. Als `$target` null is, wordt de huidige HEAD getagd. Geeft de `ObjectId` van het tagobject terug.

```php
$tagId = $repo->createTag('v2.0.0', 'Release version 2.0.0');
```

#### `deleteTag`

```php
public function deleteTag(string $name): void
```

Verwijder een tag op naam.

```php
$repo->deleteTag('v1.0.0');
```

#### `packRefs`

```php
public function packRefs(): void
```

Herschrijf het gedeelde bestand `packed-refs` op basis van de huidige set losse refs en verwijder de bijbehorende gedeelde losse refs. Voor gekoppelde worktrees schrijft dit naar de gemeenschappelijke git-map.

```php
$repo->packRefs();
```

#### `defaultBranch`

```php
public function defaultBranch(): string
```

Bepaal de standaardbranch van de repository. Controleert eerst de remote HEAD-symref, dan de lokale HEAD en valt vervolgens terug op `main` of `master`.

#### `isBranchMerged`

```php
public function isBranchMerged(string $branch, ?string $target = null): bool
```

Controleer of een branch volledig is samengevoegd in een andere branch (standaard de standaardbranch).

```php
if ($repo->isBranchMerged('feature/done')) {
    $repo->deleteBranch('feature/done');
}
```

#### `checkout`

```php
public function checkout(string $target): void
```

Wissel van branch of koppel HEAD los. Werkt HEAD, de index en de werkboom bij. `$target` is een branchnaam, een tag, een volledige hash of een revisie-expressie; alles wat geen branch is, koppelt HEAD los.

Gooit `RuntimeException` wanneer een lokale wijziging (al dan niet gestaged) of een niet-getraceerd bestand zich bevindt op een pad dat verschilt tussen de huidige boom en de doelboom.

Niet-vastgelegde wijzigingen in andere getraceerde bestanden worden niet meegenomen: de index en werkboom worden opnieuw opgebouwd vanuit de doelboom, waardoor bewerkingen in bestanden die aan beide kanten identiek zijn verloren gaan, waar git ze zou behouden ([#2](https://github.com/inline0/pitmaster/issues/2)). Leg ze eerst vast of stash ze. `merge()`, `cherryPick()`, `revert()` en `rebase()` en de bisect-methoden bouwen de werkboom op dezelfde manier opnieuw op.

```php
$repo->checkout('feature/login');
$repo->checkout($repo->resolve('v1.0.0')->hex);  // Detached HEAD
```

---

### Index

#### `index`

```php
public function index(): Index
```

Lees de huidige index (staginggebied) uit `.git/index` en geef deze terug.

#### `add`

```php
public function add(string ...$paths): void
```

Stage bestanden. Leest elk bestand uit de werkboom, maakt een blobobject aan en werkt de index bij. Elk argument moet een bestandspad (of symlinkpad) zijn ten opzichte van de hoofdmap van de werkboom; mappen en glob-patronen worden niet uitgebreid, en een ontbrekend bestand geeft `RuntimeException`.

```php
$repo->add('src/Feature.php', 'tests/FeatureTest.php');
```

#### `remove`

```php
public function remove(string ...$paths): void
public function removeCached(string ...$paths): void
```

Verwijder getraceerde paden uit zowel de index als de werkboom. Ondersteunt `--cached` en `-r` / `--recursive` in de argumentenlijst voor gedrag in de stijl van Git.

```php
$repo->remove('old-file.txt');
$repo->remove('-r', 'src');
$repo->remove('--cached', 'old-file.txt');
$repo->removeCached('old-file.txt');
```

#### `mv`

```php
public function mv(string $source, string $destination): void
```

Verplaats/hernoem een bestand in zowel de werkboom als de index.

```php
$repo->mv('old-name.php', 'new-name.php');
```

---

### Commits

#### `commit`

```php
public function commit(?string $message = null, ?string $author = null): ObjectId
```

Maak een commit van de huidige index. Bouwt een boomstructuur op uit de index-items, maakt het commitobject aan met HEAD als ouder (indien aanwezig) en werkt HEAD bij. Geeft de `ObjectId` van de nieuwe commit terug.

`$message` mag alleen `null` zijn wanneer een merge, cherry-pick, revert of rebase is gestopt vanwege een conflict; de voorbereide melding wordt dan gebruikt. Anders geeft een `null`-melding `RuntimeException('Commit message required')`. Een commit geeft ook een fout wanneer de index niet-samengevoegde paden bevat, wanneer de index leeg is in een repository zonder commits en wanneer de boom niet is gewijzigd ten opzichte van HEAD.

De parameter `$author` is een volledige git-auteursreeks (`Name <email> timestamp timezone`). Als deze null is, wordt de waarde bepaald aan de hand van omgevingsvariabelen, constanten en `.git/config`, in de volgorde beschreven in [Aan de slag](https://pitmaster.inline0.dev/nl/docs/getting-started).

```php
$id = $repo->commit('Fix null pointer bug');
```

#### `show`

```php
public function show(string $revision): array
```

Toon een commit-achtige revisie en het verschil met de eerste ouder. Als de revisie wordt opgelost naar een geannoteerde tag, bevat de retourwaarde ook `['tag' => Tag]` en is `commit` de uitgepakte doelcommit.

```php
$result = $repo->show('HEAD');
echo $result['commit']->message;

foreach ($result['diff'] as $diff) {
    echo $diff->format();
}
```

#### `reset`

```php
public function reset(string $revision, string $mode = 'mixed'): void
```

Zet HEAD terug naar een commit. Modi:
- `'soft'`: verplaats alleen HEAD
- `'mixed'`: verplaats HEAD + reset de index (standaard)
- `'hard'`: verplaats HEAD + reset de index + reset de werkboom

```php
$repo->reset('HEAD~1', 'soft');
$repo->reset('main', 'hard');
```

#### `restore`

```php
public function restore(string $path, ?string $source = null, bool $staged = false, bool $worktree = false): void
```

Herstel een pad vanuit de index of een specifieke bronrevisie. Als geen van beide vlaggen is ingesteld, herstelt Pitmaster de werkboom vanuit de index (of vanuit `$source` indien opgegeven). `staged: true` herstelt de index vanuit HEAD (of `$source`) en laat de werkboom ongemoeid; geef `staged: true, worktree: true` mee om beide te herstellen.

```php
$repo->restore('src/File.php');                                        // Worktree from index
$repo->restore('src/File.php', staged: true);                          // Index from HEAD
$repo->restore('src/File.php', 'HEAD~1');                              // Worktree from another commit
$repo->restore('src/File.php', 'HEAD~1', staged: true, worktree: true); // Index + worktree from another commit
```

#### `cherryPick`

```php
public function cherryPick(string $revision): ObjectId
public function cherryPickContinue(): ObjectId
public function cherryPickAbort(): void
```

Pas een commit toe als nieuwe commit op de huidige branch, met behoud van de oorspronkelijke auteur. Als conflicten de bewerking stoppen, los ze dan op, stage de paden en roep vervolgens `cherryPickContinue()` of `cherryPickAbort()` aan.

```php
$newId = $repo->cherryPick('feature~2');

// After resolving conflicts:
$repo->add('src/File.php');
$repo->cherryPickContinue();
```

#### `revert`

```php
public function revert(string $revision): ObjectId
public function revertContinue(): ObjectId
public function revertAbort(): void
```

Maak een commit die een andere commit ongedaan maakt. Als conflicten de revert stoppen, los ze dan op, stage de paden en roep vervolgens `revertContinue()` of `revertAbort()` aan.

```php
$revertId = $repo->revert('HEAD~1');

// After resolving conflicts:
$repo->add('src/File.php');
$repo->revertContinue();
```

#### `rebase`

```php
public function rebase(string $onto): array
public function rebaseContinue(): array
public function rebaseAbort(): void
public function rebaseSkip(): array
```

Rebase de huidige branch op een andere revisie. Pitmaster ondersteunt momenteel lineaire rebases met een stopstatus in de stijl van Git voor conflicten, plus doorgaan, afbreken en overslaan. HEAD moet naar een branch verwijzen.

`rebase()`, `rebaseContinue()` en `rebaseSkip()` geven `['success' => bool, 'commits' => int, 'conflicts' => list<string>]` terug: of de rebase is voltooid, hoeveel commits zijn toegepast en welke paden conflicten bevatten als de rebase is gestopt.

```php
$result = $repo->rebase('main');

if (!$result['success']) {
    // Resolve conflicts, then continue or skip/abort.
    $repo->add('src/File.php');
    $repo->rebaseContinue();
}
```

---

### Status

#### `status`

```php
public function status(): array
```

Bereken de status van de werkboom. Geeft een array van `StatusEntry`-objecten terug, elk met:
- `path`: het bestandspad
- `index`: enum `FileStatus` voor wijzigingen van HEAD naar de index
- `worktree`: enum `FileStatus` voor wijzigingen van de index naar de werkboom
- `origPath`, `renameScore`: ingesteld voor hernoemde items, anders `null`

Niet-getraceerde bestanden hebben in beide kolommen `FileStatus::Untracked`. Genegeerde bestanden worden niet gerapporteerd.

```php
foreach ($repo->status() as $entry) {
    echo $entry->shortFormat() . "\n"; // "M  src/File.php", "?? notes.txt"
}
```

#### `statusPorcelainV2`

```php
public function statusPorcelainV2(): string
```

Statusuitvoer in een machineleesbaar formaat, volgens git's porcelain-formaat v2.

```php
echo $repo->statusPorcelainV2();
// 1 M. N... 000000 000000 000000 0000...0000 0000...0000 modified-file.txt
// ? untracked-file.txt
```

---

### Diff

#### `diff`

```php
public function diff(?string $pathspec = null, string $algorithm = 'myers'): array
```

Vergelijk de werkboom met de index (niet-gestagede wijzigingen). Geeft een array van `DiffResult`-objecten terug. `$pathspec` filtert op één exact bestandspad. `$algorithm` is `'myers'` (standaard), `'patience'`, `'histogram'` of `'minimal'`; elke andere waarde geeft `InvalidArgumentException`.

```php
$diffs = $repo->diff();

foreach ($diffs as $diff) {
    echo $diff->format();  // Unified diff output
}
```

#### `diffStaged`

```php
public function diffStaged(?string $pathspec = null, string $algorithm = 'myers'): array
```

Vergelijk de index met HEAD (gestagede wijzigingen). Geeft `DiffResult[]` terug, met detectie van hernoemingen tussen verwijderde en toegevoegde bestanden.

Het argument `$pathspec` wordt momenteel genegeerd, dus elk gestaged bestand wordt teruggegeven. Vóór de eerste commit geeft de methode een lege array terug ([#5](https://github.com/inline0/pitmaster/issues/5)).

```php
$staged = $repo->diffStaged();
```

#### `diffTree`

```php
public function diffTree(ObjectId $a, ObjectId $b, string $algorithm = 'myers'): array
```

Vergelijk twee bomen aan de hand van hun `ObjectId`. Geeft `DiffResult[]` terug. Geef boom-ID's op, geen commit-ID's: een commit-ID wordt niet als boom gelezen en levert een leeg resultaat op.

```php
$old = $repo->readObject($repo->resolve('HEAD~1')->hex);
$new = $repo->head();
$diffs = $repo->diffTree($old->tree, $new->tree);
```

---

### Logboek

#### `log`

```php
public function log(int $limit = 50, ?ObjectId $from = null): array
```

Doorloop de commitgeschiedenis, nieuwste eerst op basis van de tijdstempel van de committer. Geeft `Commit[]` terug. Als `$from` null is, wordt begonnen bij HEAD. Geeft een lege array terug als HEAD nog geen commit heeft.

```php
$commits = $repo->log(10);
```

#### `logAll`

```php
public function logAll(int $limit = 50): array
```

Doorloop commits die bereikbaar zijn vanaf elke ref en HEAD, nieuwste eerst. Geeft `Commit[]` terug.

#### `logOneline`

```php
public function logOneline(int $limit = 50, bool $all = false, ?string $path = null): array
```

Geef logregels terug in de vorm `git log --oneline` (`abc1234 Subject`), vanaf HEAD, vanaf alle refs (`$all`) of gefilterd op pad.

#### `logPath`

```php
public function logPath(string $path, int $limit = 50): array
```

Logboek gefilterd op een bestandspad. Geeft alleen commits terug waarvan het verschil met hun eerste ouder dat exacte bestand toevoegt, wijzigt, verwijdert of hernoemt. Mappaden komen niet overeen.

Alleen de nieuwste `5 * $limit` commits worden onderzocht, waardoor oudere commits die het bestand wijzigen in het resultaat kunnen ontbreken ([#7](https://github.com/inline0/pitmaster/issues/7)).

```php
$commits = $repo->logPath('src/Repository.php', 20);
```

---

### Merge

#### `merge`

```php
public function merge(string $branch, string $strategy = 'recursive'): MergeResult
public function mergeOctopus(array $branches): MergeResult
public function mergeContinue(): ObjectId
public function mergeAbort(): void
```

Voeg een branch (of een willekeurige revisie) samen met HEAD. Detecteert automatisch merges met fast-forward. Bij merges zonder fast-forward worden conflicten op boomniveau opgespoord en wordt een merge-commit gemaakt als er geen conflicten zijn. Bij conflicten schrijft de bewerking de merge-status in de stijl van Git naar de index en de werkboom; los de paden op, stage ze en roep vervolgens `mergeContinue()` of `mergeAbort()` aan.

`$strategy` `'ours'` registreert een merge-commit die de boom van HEAD behoudt; elke andere waarde gebruikt de standaard recursieve merge. `mergeOctopus()` voegt twee of meer branches samen in één commit. Als een branch conflicten oplevert, wordt een resultaat met conflicten teruggegeven zonder de merge-status weg te schrijven of een commit te maken.

Een branch samenvoegen die al in HEAD is opgenomen, maakt nog een merge-commit aan in plaats van "Already up to date" te melden ([#6](https://github.com/inline0/pitmaster/issues/6)). Niet-vastgelegde wijzigingen in getraceerde bestanden worden overschreven zoals beschreven onder `checkout()`.

Geeft een `MergeResult` terug met:
- `clean`: of de merge zonder conflicten is geslaagd
- `commitId`: de ID van de merge-commit (bij een schone merge)
- `conflictPaths`: array met paden van bestanden met conflicten (bij conflicten)
- `mergedContents`: pad van een bestand met conflicten naar de inhoud met conflictmarkeringen (bij conflicten)

```php
$result = $repo->merge('feature/login');

if ($result->clean) {
    echo "Merged: {$result->commitId->hex}\n";
} else {
    echo "Conflicts in: " . implode(', ', $result->conflictPaths) . "\n";
    $repo->add('src/File.php');
    $repo->mergeContinue();
}
```

#### `mergeBase`

```php
public function mergeBase(ObjectId $a, ObjectId $b): ?ObjectId
```

Zoek de mergebasis (laagste gemeenschappelijke voorouder) van twee commits. Geeft null terug als er geen gemeenschappelijke voorouder bestaat.

```php
$base = $repo->mergeBase(
    $repo->resolve('main'),
    $repo->resolve('feature/login'),
);
```

### Bisect

```php
public function bisectStart(string $bad, string $good): ObjectId
public function bisectGood(?string $revision = null): ?ObjectId
public function bisectBad(?string $revision = null): ?ObjectId
public function bisectReset(): void
```

Zoek met een binaire zoekactie naar de commit die een wijziging heeft geïntroduceerd. `bisectStart()` legt de slechte en goede revisies vast, checkt de eerste kandidaat uit en geeft deze terug. `bisectGood()` en `bisectBad()` markeren de huidige HEAD (of `$revision`), checken de volgende kandidaat uit en geven deze terug. Wanneer er nog maar één verdachte over is, wordt die commit teruggegeven en blijft deze de kandidaat: het is de eerste slechte commit. `bisectReset()` beëindigt de sessie en keert terug naar de branch of commit waarmee deze begon.

```php
$candidate = $repo->bisectStart('HEAD', 'v1.0.0');
// test $candidate ...
$candidate = $repo->bisectBad();
$candidate = $repo->bisectGood();
$repo->bisectReset();
```

---

### Netwerk

#### `fetch`

```php
public function fetch(string $remote = 'origin', ?int $depth = null): void
```

Haal gegevens op van een remote. Downloadt nieuwe objecten en werkt remote-tracking refs bij. Geef `$depth` mee om een ondiepe geschiedenis te verdiepen of te beperken.

`fetch()` en de methoden `push*()` geven `RuntimeException` bij een handle die is geopend met `['processes' => false]`.

```php
$repo->fetch();
$repo->fetch('upstream');
```

#### `push`

```php
public function push(string $remote = 'origin', ?string $branch = null): void
public function pushForceWithLease(string $remote = 'origin', ?string $branch = null, ?ObjectId $expected = null): void
public function pushAtomic(string $remote = 'origin', array $branches = []): void
public function pushMirror(string $remote = 'origin'): void
```

Push de huidige branch (of een opgegeven branch) naar een remote. Varianten met force-with-lease, atomic en mirror zijn beschikbaar voor strengere of uitgebreidere ref-updates.

```php
$repo->push();
$repo->push('origin', 'feature/login');
$repo->pushForceWithLease('origin', 'main', $repo->resolve('refs/remotes/origin/main'));
$repo->pushAtomic('origin', ['main', 'release']);
$repo->pushMirror();
```

---

### Worktrees

#### `addWorktree`

```php
public function addWorktree(string $path, string $branch, ?ObjectId $from = null, ?string $name = null): Worktree
```

Voeg een gekoppelde worktree toe. Maakt de branch aan als deze niet bestaat, stelt de worktree-metadata in en materialiseert de werkboom-bestanden. Geef `$name` mee als u een deterministische metadata-slug nodig hebt die afwijkt van de basisnaam van de checkout-map.

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

#### `removeWorktree`

```php
public function removeWorktree(string $pathOrName, bool $force = false): void
```

Verwijder een gekoppelde worktree. Gebruik `$force = true` om deze ook te verwijderen als deze vergrendeld is.

#### `worktrees`

```php
public function worktrees(): array
```

Toon alle worktrees (hoofd- en gekoppelde). Geeft `Worktree[]` terug.

```php
foreach ($repo->worktrees() as $wt) {
    echo "{$wt->path} [{$wt->branch}]\n";
}
```

---

### Configuratie

#### `config`

```php
public function config(): GitConfig
```

Open de git-configuratie van de repository. Lees en schrijf configuratiewaarden.

```php
$config = $repo->config();
$name = $config->get('user.name');
$config->set('user.email', 'new@example.com');
```

---

### Interne toegang

Deze methoden bieden toegang tot interne componenten voor geavanceerd gebruik.

#### `objectDatabase`

```php
public function objectDatabase(): ObjectDatabase
```

Open de onbewerkte objectdatabase (losse + pack-opslag).

#### `refDatabase`

```php
public function refDatabase(): RefDatabase
```

Open de onbewerkte ref-database (losse + verpakte refs).

---

## Lagere klassen

Deze klassen worden niet door `Repository` omhuld en worden samengesteld uit de interne componenten ervan.

### `Pitmaster\Graph\Blame`

```php
public function blame(ObjectId $headId, string $path): array
```

Geeft één item per regel van `$path` op `$headId` terug: `['line' => int, 'hash' => string, 'author' => string, 'content' => string]`. `author` is de volledige identiteitsreeks (`Name <email> timestamp timezone`).

```php
use Pitmaster\Graph\Blame;

$blame = new Blame($repo->objectDatabase());

foreach ($blame->blame($repo->resolve('HEAD'), 'src/File.php') as $line) {
    echo substr($line['hash'], 0, 8) . " {$line['content']}\n";
}
```

### `Pitmaster\Graph\Grep`

```php
public function grep(ObjectId $treeId, string $pattern, string $prefix = '', array $options = []): array
```

Doorzoekt de blobs van een boom. Geeft `['path' => string, 'line' => int, 'content' => string]` per overeenkomende regel terug. Opties: `regex` (behandel het patroon als een PCRE-patroon) en `ignore_case`. Een ongeldige regex geeft `InvalidArgumentException`.

```php
use Pitmaster\Graph\Grep;

$grep = new Grep($repo->objectDatabase());
$matches = $grep->grep($repo->head()->tree, 'TODO', '', ['ignore_case' => true]);
```

### `Pitmaster\Ref\Notes`

```php
public function get(ObjectId $commitId, string $notesRef = 'refs/notes/commits'): ?string
public function set(ObjectId $commitId, string $noteContent, string $notesRef = 'refs/notes/commits'): void
public function remove(ObjectId $commitId, string $notesRef = 'refs/notes/commits'): void
public function listAll(string $notesRef = 'refs/notes/commits'): array
public function merge(string $sourceRef, string $targetRef = 'refs/notes/commits'): void
```

Leest en schrijft git-notities die zijn opgeslagen onder een notes-ref. `listAll()` geeft een commit-hash naar notitie-inhoud terug.

```php
use Pitmaster\Ref\Notes;

$notes = new Notes($repo->objectDatabase(), $repo->refDatabase());
$notes->set($repo->resolve('HEAD'), "Reviewed\n");
echo $notes->get($repo->resolve('HEAD'));
```
