
# PHP API

## `Pitmaster\Pitmaster`

Klassen `Pitmaster\Pitmaster` er det statiske indgangspunkt. Den indeholder factory-metoder til at åbne, oprette og klone repositories samt hjælperfunktioner til registrering.

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

### `open`

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

Åbn et eksisterende repository på `$path`. Stien skal være arbejdstræets rod (der indeholder `.git/`). Understøtter også tilknyttede worktrees (hvor `.git` er en fil, der indeholder `gitdir: <path>`) og bare repositories (hvor `$path` er selve mappen `.git`).

`$options` accepterer to booleske værdier, begge `true` som standard:

- `hooks`: kør `.git/hooks`-scripts. Angiv `false` for at springe dem over for dette handle.
- `processes`: tillad alt, der starter en proces eller åbner en netværkstransport. Angiv `false` for at deaktivere hooks, fsmonitor-hooken, clone, fetch, push og SSH-procestransport. Lokale læsninger og skrivninger fungerer fortsat.

Kaster `InvalidArgumentException`, hvis stien ikke er et git-repository.

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

Initialiser et nyt repository på `$path`. Opretter mappestrukturen `.git` med `objects/`, `refs/heads/`, `refs/tags/`, `hooks/`, `info/`, `HEAD` (der peger på `refs/heads/main`) og en standard-`config`.

`$objectFormat` er `'sha1'` eller `'sha256'`. Et SHA-256-repository får `repositoryformatversion = 1` og `extensions.objectformat = sha256` i sin konfiguration. Enhver anden værdi kaster `InvalidArgumentException`. `$options` bruger de samme nøgler som `open()`.

Kaster `RuntimeException`, hvis der allerede findes et repository på stien.

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

Klon et eksternt repository. Initialiserer et nyt repo, finder eksterne refs, henter pack-filen, opsætter remote-tracking-branches og materialiserer arbejdstræet. URL'er af typen SSH (`ssh://...` eller `user@host:path`) bruger transporten SSH; andre URL'er bruger smart HTTP og falder tilbage til dumb HTTP, når den smarte udveksling mislykkes. Angiv `$depth` for en shallow clone.

Kaster `RuntimeException`, når `$options['processes']` er `false`. Hvis kloningen mislykkes, og `$path` ikke fandtes i forvejen, fjernes den delvist oprettede mappe.

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

Returnerer `['processes' => false, 'hooks' => false]` til runtime-miljøer, der ikke må starte værtsprocesser. Giv den videre til `open()` eller `init()`.

### `isRepository`

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

Kontrollér, om en sti er et git-repository. Returnerer `true` for almindelige repositories (mappen `.git/`), tilknyttede worktrees (filen `.git`) og bare repositories (hvor filen `HEAD` findes).

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

### `isWorktree`

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

Kontrollér, om en sti er et tilknyttet worktree (ikke hovedrepositoriet). Returnerer kun `true`, hvis `.git` er en fil, der indeholder en `gitdir:`-reference.

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

### `commonGitDir`

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

Find den fælles git-mappe ud fra en vilkårlig checkout-sti. For almindelige repositories er dette mappen `.git`. For tilknyttede worktrees er det den delte git-mappe med objekter, konfiguration og packed-refs. Returnerer `null`, hvis stien ikke er et repository.

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

---

## `Pitmaster\Repository`

Klassen `Pitmaster\Repository` er hovedhåndtaget til alle git-operationer. Hent et via `Pitmaster::open()`, `Pitmaster::init()` eller `Pitmaster::clone()`.

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

### Stiadgang

#### `gitDir`

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

Returnerer git-mappen for det enkelte worktree. For almindelige repositories er det `.git/`. For tilknyttede worktrees er det `.git/worktrees/<name>/`.

#### `commonGitDir`

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

Returnerer den delte git-mappe. Det samme som `gitDir()` for almindelige repositories. For tilknyttede worktrees er det hovedrepositoriets mappe `.git/` (hvor objekter, konfiguration og packed-refs ligger).

#### `workDir`

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

Returnerer arbejdstræets rodsti.

#### `isLinkedWorktree`

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

Returnerer `true`, hvis dette repository-handle er et tilknyttet worktree.

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

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

Angiver, om repositoryet ikke har noget arbejdstræ, og hvilke af valgene `hooks` / `processes` der gælder for dette handle. `hooksEnabled()` er `false`, når processer er deaktiveret.

---

### Objekter

#### `readObject`

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

Læs et vilkårligt objekt via dets fulde hex-hash (40 tegn eller 64 i et SHA-256-repository). Forkortede hasher accepteres ikke. Returnerer en instans af `Blob`, `Tree`, `Commit` eller `Tag`. Søger først i løse objekter og derefter i pack-filer.

Kaster `ObjectNotFoundException`, hvis hashen ikke findes.

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

#### `writeObject`

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

Skriv et objekt til lageret for løse objekter. Returnerer den beregnede `ObjectId`.

```php
use Pitmaster\Object\Blob;

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

#### `catFile`

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

Returnerer objektets rå indhold, svarende til `git cat-file -p`.

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

#### `objectExists`

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

Kontrollér, om et objekt findes i repositoryet (løst eller pakket).

#### `listObjects`

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

Vis alle objekthashes i repositoryet. Returnerer et array af hex-strenge.

---

### Refs

#### `head`

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

Returnerer den aktuelle HEAD-commit. Følger symbolske refs (HEAD -> refs/heads/main -> commit-hash).

Kaster `RuntimeException`, hvis HEAD ikke peger på en gyldig commit (e.g., tomt repository).

#### `branch`

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

Uden argumenter returneres det aktuelle branchnavn (e.g., `'main'`) eller `null`, hvis HEAD er detached.

Med et branchnavn løses det til dets commit-hash (hex-streng), eller `null` returneres, hvis branchen ikke findes.

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

#### `branches`

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

Returnerer et sorteret array med alle branchnavne (uden præfikset `refs/heads/`).

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

#### `tags`

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

Returnerer et sorteret array med alle tagnavne (uden præfikset `refs/tags/`).

#### `resolve`

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

Løs et revisionsudtryk til et `ObjectId`. Understøtter:
- Fulde hex-hashes (40 tegn eller 64 for SHA-256). Forkortede hashes som `abc1234` løses ikke.
- `HEAD`
- Branchnavne (`main`, `feature/login`)
- Tagnavne (`v1.0.0`)
- Fulde ref-stier (`refs/heads/main`)
- Revisionsudtryk (`HEAD~3`, `main^2`)

Kaster `RuntimeException`, hvis revisionen ikke kan løses.

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

#### `allRefs`

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

Returnerer alle refs som et associativt array fra ref-navn til 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
```

Opdatér en ref, så den peger på et nyt mål.

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

#### `createBranch`

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

Opret en ny branch. Hvis `$from` er null, oprettes branchen ud fra den aktuelle HEAD.

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

#### `deleteBranch`

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

Slet en gren efter navn. Kaster `RuntimeException`, hvis grenen er tjekket ud eller ikke findes. I modsætning til `git branch -d` kontrollerer den ikke, om grenen er flettet ind.

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

#### `createLightweightTag`

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

Opret et letvægtstag. Hvis `$target` er null, tagges den aktuelle HEAD direkte.

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

Opret et annoteret tag. Hvis `$target` er null, tagges den aktuelle HEAD. Returnerer tagobjektets `ObjectId`.

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

#### `deleteTag`

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

Slet et tag efter navn.

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

#### `packRefs`

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

Skriv den delte `packed-refs`-fil om ud fra det aktuelle sæt af løse refs, og fjern de tilsvarende delte løse refs. For sammenkædede worktrees skrives der til det fælles git-bibliotek.

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

#### `defaultBranch`

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

Find repositoryets standardgren. Kontrollerer først den eksterne HEAD-symref, derefter den lokale HEAD og falder til sidst tilbage på `main` eller `master`.

#### `isBranchMerged`

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

Kontrollér, om en gren er fuldt flettet ind i en anden gren (standard er standardgrenen).

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

#### `checkout`

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

Skift gren, eller frigør HEAD. Opdaterer HEAD, indekset og arbejdstræet. `$target` er et grennavn, et tag, en fuld hash eller et revisionsudtryk; alt, der ikke er en gren, frigør HEAD.

Kaster `RuntimeException`, når en lokal ændring (sat i staging eller ej) eller en ikke-sporet fil ligger på en sti, der er forskellig mellem det aktuelle træ og måltræet.

Ikke-committede ændringer i andre sporede filer videreføres ikke: indekset og arbejdstræet genopbygges ud fra måltræet, så redigeringer af filer, der er identiske på begge sider, går tabt, hvor git ville bevare dem ([#2](https://github.com/inline0/pitmaster/issues/2)). Commit eller stash dem først. `merge()`, `cherryPick()`, `revert()`, `rebase()` og bisect-metoderne genopbygger arbejdstræet på samme måde.

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

---

### Indeks

#### `index`

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

Læs og returnér det aktuelle indeks (staging-område) fra `.git/index`.

#### `add`

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

Sæt filer i staging. Læser hver fil fra arbejdstræet, opretter et blobobjekt og opdaterer indekset. Hvert argument skal være en filsti (eller symlinksti) relativ til arbejdstræets rod; mapper og globmønstre udvides ikke, og en manglende fil kaster `RuntimeException`.

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

#### `remove`

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

Fjern sporede stier fra både indekset og arbejdstræet. Understøtter `--cached` og `-r` / `--recursive` på argumentlisten for adfærd som 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
```

Flyt/omdøb en fil i både arbejdstræet og indekset.

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

---

### Commits

#### `commit`

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

Opret et commit ud fra det aktuelle indeks. Bygger et træhierarki ud fra indeksposterne, opretter commitobjektet med HEAD som overordnet commit (hvis der er en) og opdaterer HEAD. Returnerer det nye commits `ObjectId`.

`$message` må kun være `null`, når en merge, cherry-pick, revert eller rebase er stoppet på grund af en konflikt; den forberedte meddelelse bruges da. Ellers kaster en `null`-meddelelse `RuntimeException('Commit message required')`. Et commit kaster også, når indekset indeholder ikke-flettede stier, når indekset er tomt i et repository uden commits, eller når træet er uændret i forhold til HEAD.

Parameteren `$author` er en fuld git-forfatterstreng (`Name <email> timestamp timezone`). Hvis den er null, findes værdien ud fra miljøvariabler, konstanter og `.git/config` i den rækkefølge, der er beskrevet i [Kom godt i gang](https://pitmaster.inline0.dev/da/docs/getting-started).

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

#### `show`

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

Vis et commit-lignende objekt og dets diff i forhold til den første overordnede commit. Når revisionen peger på et annoteret tag, indeholder returværdien også `['tag' => Tag]`, og `commit` er den udpakkede målcommit.

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

Nulstil HEAD til et commit. Tilstande:
- `'soft'`: flyt kun HEAD
- `'mixed'`: flyt HEAD + nulstil indeks (standard)
- `'hard'`: flyt HEAD + nulstil indeks + nulstil arbejdstræ

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

Gendan en sti fra indekset eller en bestemt kilderevision. Hvis ingen af flagene er angivet, gendanner Pitmaster arbejdstræet fra indekset (eller fra `$source`, hvis angivet). `staged: true` gendanner indekset fra HEAD (eller `$source`) og lader arbejdstræet være urørt; angiv `staged: true, worktree: true` for at gendanne begge dele.

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

Anvend et commit som et nyt commit på den aktuelle gren, og bevar den oprindelige forfatter. Hvis konflikter stopper handlingen, skal du løse dem, sætte stierne i staging og derefter kalde `cherryPickContinue()` eller `cherryPickAbort()`.

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

Opret et commit, der ophæver et andet commit. Hvis konflikter stopper revert-handlingen, skal du løse dem, sætte stierne i staging og derefter kalde `revertContinue()` eller `revertAbort()`.

```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 den aktuelle gren oven på en anden revision. Pitmaster understøtter aktuelt lineære rebases med konfliktstoptilstand i formatet Git samt fortsættelse, afbrydelse og udeladelse. HEAD skal pege på en gren.

`rebase()`, `rebaseContinue()` og `rebaseSkip()` returnerer `['success' => bool, 'commits' => int, 'conflicts' => list<string>]`: om rebasen blev færdig, hvor mange commits der blev anvendt, og konfliktstierne, hvis den stoppede.

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

Beregn arbejdstræets status. Returnerer en række `StatusEntry`-objekter, som hver indeholder:
- `path`: filstien
- `index`: `FileStatus`-enum for ændringer fra HEAD til indeks
- `worktree`: `FileStatus`-enum for ændringer fra indeks til arbejdstræ
- `origPath`, `renameScore`: angivet for omdøbte poster, ellers `null`

Ikke-sporede filer har `FileStatus::Untracked` i begge kolonner. Ignorerede filer rapporteres ikke.

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

#### `statusPorcelainV2`

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

Maskinlæsbar status i git's porcelain-format 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
```

Sammenlign arbejdstræet med indekset (ændringer, der ikke er sat i staging). Returnerer en række `DiffResult`-objekter. `$pathspec` filtrerer efter én nøjagtig filsti. `$algorithm` er `'myers'` (standard), `'patience'`, `'histogram'` eller `'minimal'`; alle andre værdier kaster `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
```

Sammenlign indekset med HEAD (ændringer sat i staging). Returnerer `DiffResult[]` med registrering af omdøbninger mellem slettede og tilføjede filer.

Argumentet `$pathspec` ignoreres aktuelt, så alle filer i staging returneres, og metoden returnerer en tom række før det første commit ([#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
```

Sammenlign to træer ud fra deres `ObjectId`. Returnerer `DiffResult[]`. Angiv træ-id'er, ikke commit-id'er: Et commit-id læses ikke som et træ og giver et tomt resultat.

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

---

### Log

#### `log`

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

Gennemgå commit-historikken, nyeste først efter committerens tidsstempel. Returnerer `Commit[]`. Hvis `$from` er null, begynder gennemgangen ved HEAD. Returnerer en tom række, hvis HEAD endnu ikke har et commit.

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

#### `logAll`

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

Gennemgå commits, der kan nås fra hver ref og HEAD, nyeste først. Returnerer `Commit[]`.

#### `logOneline`

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

Returnér loglinjer i formatet `git log --oneline` (`abc1234 Subject`), fra HEAD, fra alle refs (`$all`) eller filtreret efter sti.

#### `logPath`

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

Log filtreret efter en filsti. Returnerer kun commits, hvis diff i forhold til deres første overordnede commit tilføjer, ændrer, sletter eller omdøber netop den fil. Mappestier matcher ikke.

Kun de nyeste `5 * $limit` commits undersøges, så ældre commits, der berører filen, kan mangle i resultatet ([#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
```

Flet en gren (eller en vilkårlig revision) ind i HEAD. Registrerer automatisk fast-forward-merges. Ved merges uden fast-forward registreres konflikter på træniveau, og der oprettes et merge-commit, hvis alt er konfliktfrit. Konflikter stopper skrivningen og efterlader en merge-tilstand i formatet Git i indekset og arbejdstræet; løs stierne, sæt dem i staging, og kald derefter `mergeContinue()` eller `mergeAbort()`.

`$strategy` `'ours'` registrerer et merge-commit, der bevarer HEAD's træ; alle andre værdier bruger standardmergen rekursivt. `mergeOctopus()` fletter to eller flere grene til ét commit. Hvis en gren har konflikter, returneres et resultat med konflikter uden at skrive merge-tilstand eller oprette et commit.

Fletning af en gren, der allerede er indeholdt i HEAD, opretter endnu et merge-commit i stedet for at rapportere "Already up to date" ([#6](https://github.com/inline0/pitmaster/issues/6)). Ikke-committede ændringer i sporede filer overskrives som beskrevet under `checkout()`.

Returnerer en `MergeResult` med:
- `clean`: om mergingen lykkedes uden konflikter
- `commitId`: merge-commit-id'et (hvis konfliktfrit)
- `conflictPaths`: række med konfliktbehæftede filstier (hvis ikke konfliktfrit)
- `mergedContents`: konfliktbehæftet sti til filindhold med konfliktmarkører (hvis ikke konfliktfrit)

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

Find mergebasen (den laveste fælles forfader) for to commits. Returnerer null, hvis der ikke findes nogen fælles forfader.

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

Binærsøg efter det commit, der indførte en ændring. `bisectStart()` registrerer de dårlige og gode revisioner, tjekker den første kandidat ud og returnerer den. `bisectGood()` og `bisectBad()` markerer den aktuelle HEAD (eller `$revision`), tjekker den næste kandidat ud og returnerer den. Når der kun er én mistænkt tilbage, returneres det commit, som forbliver kandidaten: det er det første dårlige commit. `bisectReset()` afslutter sessionen og vender tilbage til den gren eller det commit, hvor sessionen begyndte.

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

---

### Netværk

#### `fetch`

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

Hent fra en remote. Downloader nye objekter og opdaterer refs til fjernsporede grene. Angiv `$depth` for at udvide eller begrænse en overfladisk historik.

`fetch()` og metoderne `push*()` kaster `RuntimeException` på et handle, der er åbnet med `['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 den aktuelle gren (eller en angivet gren) til en remote. Varianterne force-with-lease, atomic og mirror er tilgængelige for strengere eller bredere ref-opdateringer.

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

Tilføj et sammenkædet worktree. Opretter grenen, hvis den ikke findes, opsætter worktree-metadata og materialiserer arbejdstræets filer. Angiv `$name`, når du har brug for en deterministisk metadataslug, der adskiller sig fra checkout-mappens basisnavn.

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

Fjern et sammenkædet worktree. Brug `$force = true` for også at fjerne et låst worktree.

#### `worktrees`

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

Vis alle worktrees (hoved-worktree og sammenkædede). Returnerer `Worktree[]`.

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

---

### Konfiguration

#### `config`

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

Få adgang til repositoryets git-konfiguration. Læs og skriv konfigurationsværdier.

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

---

### Intern adgang

Disse metoder giver adgang til interne komponenter til avanceret brug.

#### `objectDatabase`

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

Få adgang til den rå objektdatabase (løse objekter og pakkelagre).

#### `refDatabase`

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

Få adgang til den rå ref-database (løse og pakkede refs).

---

## Klasser på lavere niveau

Disse klasser er ikke omsluttet af `Repository` og konstrueres ud fra dets interne komponenter.

### `Pitmaster\Graph\Blame`

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

Returnerer én post pr. linje i `$path` ved `$headId`: `['line' => int, 'hash' => string, 'author' => string, 'content' => string]`. `author` er den fulde identitetsstreng (`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
```

Søger i et træs blobs. Returnerer `['path' => string, 'line' => int, 'content' => string]` pr. matchende linje. Valgmuligheder: `regex` (behandl mønsteret som et PCRE-mønster) og `ignore_case`. En ugyldig regex kaster `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
```

Læser og skriver git-noter gemt under en notes-ref. `listAll()` returnerer commit-hash til noteindhold.

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