
# Merge

Pitmaster führt Branch-Merges mit einem Drei-Wege-Merge aus, wie `git merge`. Es erkennt Fast-Forward-Möglichkeiten, berechnet Merge-Basen, führt Merges auf Tree- und Inhaltsebene durch und erzeugt bei Dateikonflikten standardmäßige Konfliktmarkierungen.

## Einfacher 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

Die Klasse `MergeResult` ist ein schreibgeschütztes Wertobjekt:

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

Wenn der Merge wegen Konflikten angehalten wird, wird der Konfliktstatus in den Index und den Arbeitsbaum geschrieben. Löse die Konflikte in den Dateien, `add()` sie und rufe `$repo->mergeContinue()` auf, oder rufe `$repo->mergeAbort()` auf, um den Zustand vor dem Merge wiederherzustellen.

Übernimm lokale Änderungen vor dem Merge in einen Commit oder stash sie: Der Merge erstellt den Arbeitsbaum anhand des Ergebnisses neu und überschreibt nicht übernommene Änderungen an getrackten Dateien ([#2](https://github.com/inline0/pitmaster/issues/2)).

## Fast-Forward-Merges

Wenn der aktuelle HEAD ein Vorfahr des Ziel-Branchs ist, führt Pitmaster einen Fast-Forward-Merge aus: Es verschiebt HEAD einfach zum Ziel-Commit, ohne einen Merge-Commit zu erstellen.

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

Der umgekehrte Fall wird noch nicht erkannt: Beim Mergen eines Branchs, den HEAD bereits enthält, wird ein neuer Merge-Commit mit unverändertem Tree erstellt, statt „Bereits aktuell“ zu melden ([#6](https://github.com/inline0/pitmaster/issues/6)).

## Drei-Wege-Merge

Wenn Branches auseinanderlaufen, führt Pitmaster einen Drei-Wege-Merge aus:

1. Merge-Basis finden (niedrigster gemeinsamer Vorfahr)
2. Basis mit unserem und deren Tree auf Tree-Ebene vergleichen
3. Für jede Datei, die in beiden Branches geändert wurde, einen Merge auf Inhaltsebene ausführen
4. Einen Merge-Commit mit zwei Eltern erstellen

```
Before:              After merge:

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

## Merge-Basis

Die Merge-Basis ist der jüngste gemeinsame Vorfahr zweier Commits. Pitmaster findet sie, indem es den Commit-Graphen durchläuft. Bei mehreren Merge-Basen (Criss-Cross-Verlauf) führt `merge()` sie zuerst zusammen und verwendet das Ergebnis als Basis.

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

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

Für den direkten Zugriff auf den `MergeBase`-Algorithmus:

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

## Branch-Merge-Prüfung

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

## Konfliktmarkierungen

Wenn eine Datei in beiden Branches geändert wurde und sich die Änderungen überschneiden, erzeugt Pitmaster standardmäßige Konfliktmarkierungen. `merge()` kennzeichnet die Seiten mit `HEAD` und dem Namen des zusammengeführten Branchs:

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

Die Klasse `ConflictMarker` schließt zwei Versionen in Markierungen ein:

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

## Drei-Wege-Merge auf Inhaltsebene

Die Klasse `ThreeWayMerge` führt Blob-Inhalte zusammen (keine Dateien):

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

Sie nimmt drei Strings entgegen (die Basisversion, unsere Version und deren Version), außerdem optionale Bezeichnungen und einen Konfliktstil (standardmäßig `'merge'` oder `'diff3'`, um den Basisabschnitt einzuschließen).

## Merge-Strategien

### Rekursiver Merge

Die Standardstrategie (`$repo->merge($branch)`) verarbeitet Criss-Cross-Merge-Verläufe mit mehreren Merge-Basen. Die CLI akzeptiert dafür auch `-s ort` und `-s recursive`.

### Ours-Merge

Erstellt einen Merge-Commit, der den Tree von HEAD unverändert lässt. Nützlich, um den Branch-Verlauf zu erhalten, ohne Änderungen zu übernehmen.

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

### Octopus-Merge

Führt zwei oder mehr Branches in einem Merge-Commit zusammen. Bei Konflikten in einem Branch gibt die Strategie ein nicht bereinigtes Ergebnis zurück, ohne den Merge-Status zu speichern oder einen Commit zu erstellen.

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

Die Klassen `RecursiveMerge`, `OursMerge` und `OctopusMerge` in `Pitmaster\Merge` sind Hilfsklassen für Merges auf Inhaltsebene; `Repository` implementiert die oben genannten Strategien selbst.

## Rerere (aufgezeichnete Konfliktlösung wiederverwenden)

Pitmaster enthält eine eigenständige Klasse `Rerere`, die Konfliktlösungen im Layout von gits `rr-cache` speichert:

```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()` ruft sie nicht auf: Konfliktlösungen werden nur aufgezeichnet und erneut angewendet, wenn dein Code das veranlasst. `isEnabled()` sucht in `.git/config` nach dem wörtlichen Text `rerere.enabled = true` und erkennt den Abschnitt `[rerere]`, den `git config rerere.enabled true` schreibt, nicht ([#20](https://github.com/inline0/pitmaster/issues/20)).

## Merge-Ablauf auf Tree-Ebene

Der vollständige Merge-Prozess:

1. `MergeBase::findAll()` ermittelt die gemeinsamen Vorfahren
2. Die Trees der Basis, unserer Version und deren Version werden Pfad für Pfad verglichen
3. Bei Dateien, die nur in einem Branch geändert wurden: Diese Änderung übernehmen
4. Bei Dateien, die in beiden Branches geändert wurden:
   - Bei identischen Änderungen eine der beiden übernehmen
   - Bei unterschiedlichen Änderungen `ThreeWayMerge::merge()` auf den Inhalt anwenden
5. Wenn alle Merges konfliktfrei sind: den zusammengeführten Tree erstellen und einen Merge-Commit anlegen
6. Bei Konflikten: `MergeResult` mit `clean = false` und den Konfliktpfaden zurückgeben

## Verwendung von CLI

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

Ausgabe bei Erfolg:
```
Merge successful.
```

Ausgabe bei Konflikten:
```
CONFLICT in: src/File.php, src/Other.php
```
