
# Merge

Pitmaster udfører branch-merges med en trevejs-merge, ligesom `git merge`. Den registrerer muligheder for fast-forward, beregner merge-baser, udfører merges på tree- og indholdsniveau og genererer standardkonfliktmarkører, når filer er i konflikt.

## Grundlæggende merge

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

if ($result->clean) {
    echo "Merged successfully: {$result->commitId->hex}\n";
} else {
    echo "Conflicts:\n";
    foreach ($result->conflictPaths as $path) {
        echo "  {$path}\n";
    }
}
```

## MergeResult

Klassen `MergeResult` er et skrivebeskyttet værdiobjekt:

```php
use Pitmaster\Merge\MergeResult;

$result->clean;           // bool: true if merge completed without conflicts
$result->commitId;        // ?ObjectId: the merge commit (null if conflicts)
$result->conflictPaths;   // array: list of file paths with conflicts
$result->mergedContents;  // array: path => content with conflict markers (if conflicts)
```

Når merge-processen stopper på grund af konflikter, skrives konfliktstatussen til indekset og arbejdstræet. Løs konflikterne i filerne, `add()` dem, og kald `$repo->mergeContinue()`, eller kald `$repo->mergeAbort()` for at gendanne tilstanden fra før merge-processen.

Før du merger, skal du committe eller stash'e lokale ændringer: merge-processen genopbygger arbejdstræet ud fra resultatet og overskriver ikke-committede ændringer i sporede filer ([#2](https://github.com/inline0/pitmaster/issues/2)).

## Fast-forward-merges

Når den aktuelle HEAD er en forfader til target-branchen, udfører Pitmaster en fast-forward-merge: den flytter blot HEAD til target-committen uden at oprette en merge-commit.

```
Before:         After fast-forward:

A---B (main)    A---B---C---D (main)
     \
      C---D (feature)
```

```php
$result = $repo->merge('feature');
// $result->clean === true
// $result->commitId points to D
// No merge commit created
```

Det omvendte tilfælde registreres endnu ikke: Hvis du merger en branch, som HEAD allerede indeholder, oprettes der en ny merge-commit med et uændret tree i stedet for at rapportere "Already up to date" ([#6](https://github.com/inline0/pitmaster/issues/6)).

## Trevejsfletning

Når brancher er divergeret, udfører Pitmaster en trevejs-merge:

1. Find merge-basen (den laveste fælles forfader)
2. Sammenlign base-to-ours og base-to-theirs på tree-niveau
3. Udfør en merge på indholdsniveau for hver fil, der er ændret i begge brancher
4. Opret en merge-commit med to forældre

```
Before:              After merge:

      C---D (feature)         C---D
     /                       /     \
A---B---E---F (main)   A---B---E---F---G (main)
```

## Merge-base

Merge-basen er den seneste fælles forfader til to commits. Pitmaster finder den ved at gennemgå commit-grafen. Hvis der er flere merge-baser (krydsende historik), merger `merge()` dem først og bruger resultatet som base.

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

if ($base !== null) {
    echo "Common ancestor: {$base->hex}\n";
}
```

Sådan får du direkte adgang til algoritmen `MergeBase`:

```php
use Pitmaster\Merge\MergeBase;

$finder = new MergeBase($repo->objectDatabase());
$base = $finder->find($commitA, $commitB);

// All merge bases (criss-cross histories can have more than one)
$bases = $finder->findAll($commitA, $commitB);

// Check ancestry
$isAncestor = $finder->isAncestor($oldCommit, $newCommit);
```

## Kontrol af branch-merge

```php
if ($repo->isBranchMerged('feature/done')) {
    // All commits in feature/done are reachable from default branch
    $repo->deleteBranch('feature/done');
}

// Check against a specific target
if ($repo->isBranchMerged('feature/done', 'develop')) {
    // Merged into develop
}
```

## Konfliktmarkører

Når en fil er ændret i begge brancher, og ændringerne overlapper, genererer Pitmaster standardkonfliktmarkører. `merge()` angiver siderne som `HEAD` og navnet på den mergede branch:

```
<<<<<<< HEAD
    return $this->value + 1;
=======
    return $this->value * 2;
>>>>>>> feature
```

Klassen `ConflictMarker` indrammer to versioner med markører:

```php
use Pitmaster\Merge\ConflictMarker;

$marked = ConflictMarker::mark($oursContent, $theirsContent, 'HEAD', 'feature');

// diff3 style, with the base section
$marked = ConflictMarker::mark($oursContent, $theirsContent, 'HEAD', 'feature', $baseContent, 'base');
```

## Trevejs-merge på indholdsniveau

Klassen `ThreeWayMerge` merger blob-indhold (ikke filer):

```php
use Pitmaster\Merge\ThreeWayMerge;

$result = ThreeWayMerge::merge($baseContent, $oursContent, $theirsContent, 'HEAD', 'feature');

$result['content'];   // merged text, with conflict markers if needed
$result['clean'];     // true when nothing conflicted
$result['conflicts']; // number of conflict regions
```

Den tager tre strenge (baseversionen, vores version og deres version) samt valgfrie etiketter og en konfliktstil (`'merge'` som standard eller `'diff3'` for også at inkludere basissektionen).

## Merge-strategier

### Rekursiv merge

Standardstrategien (`$repo->merge($branch)`) håndterer krydsende merge-historik med flere merge-baser. CLI accepterer også `-s ort` og `-s recursive` til den.

### Ours-merge

Registrerer en merge-commit, der bevarer HEAD's tree uændret. Nyttigt til at vedligeholde branchhistorik uden at indarbejde ændringer.

```php
$result = $repo->merge('feature/old', 'ours');
```

### Octopus-merge

Merger to eller flere brancher i én merge-commit. Hvis en branch er i konflikt, returneres et urent resultat uden at skrive mergestatus eller oprette en commit.

```php
$result = $repo->mergeOctopus(['feature/a', 'feature/b', 'feature/c']);
```

Klasserne `RecursiveMerge`, `OursMerge` og `OctopusMerge` i `Pitmaster\Merge` er hjælpere til indholdsniveau; `Repository` implementerer selv strategierne ovenfor.

## Rerere (genbrug af registreret konfliktløsning)

Pitmaster indeholder en selvstændig `Rerere`-klasse, der gemmer konfliktløsninger i git's `rr-cache`-layout:

```php
use Pitmaster\Merge\Rerere;

$rerere = new Rerere($repo->gitDir());

if ($rerere->isEnabled()) {                     // rerere.enabled = true in .git/config
    $rerere->record($conflicted, $resolved);    // remember a resolution
    $resolved = $rerere->resolve($conflicted);  // replay it, or null
}
```

`merge()` kalder den ikke: Konfliktløsninger registreres og afspilles kun, når din kode gør det. `isEnabled()` leder efter den bogstavelige tekst `rerere.enabled = true` i `.git/config` og genkender ikke sektionen `[rerere]`, som `git config rerere.enabled true` skriver ([#20](https://github.com/inline0/pitmaster/issues/20)).

## Merge-forløb på tree-niveau

Hele merge-processen:

1. `MergeBase::findAll()` finder de fælles forfædre
2. Base-, ours- og theirs-trees sammenlignes sti for sti
3. For filer, der kun er ændret i én branch: Brug den ændring
4. For filer, der er ændret i begge brancher:
   - Hvis begge er ændret på samme måde: Brug en af dem (de stemmer overens)
   - Hvis ændringerne er forskellige: Kør `ThreeWayMerge::merge()` på indholdet
5. Hvis alle merges er uden konflikter: Byg det mergede tree, og opret en merge-commit
6. Hvis der er konflikter: Returner `MergeResult` med `clean = false` og konfliktstierne

## Brug af CLI

```bash
./vendor/bin/pitmaster merge feature/login
```

Output ved succes:
```
Merge successful.
```

Output ved konflikt:
```
CONFLICT in: src/File.php, src/Other.php
```
