
# Slučování

Pitmaster slučuje větve pomocí třícestného sloučení, podobně jako `git merge`. Rozpozná možnosti rychlého posunu, vypočítá společné předky, provede sloučení na úrovni stromu i obsahu a při konfliktu souborů vytvoří standardní značky konfliktu.

## Základní slučování

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

Třída `MergeResult` je neměnný objekt hodnoty:

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

Když se slučování zastaví kvůli konfliktům, stav konfliktu se zapíše do indexu a pracovního stromu. Vyřešte soubory, proveďte `add()` a zavolejte `$repo->mergeContinue()`, nebo zavolejte `$repo->mergeAbort()`, chcete-li obnovit stav před sloučením.

Před sloučením potvrďte nebo uložte místní změny do zásobníku: slučování znovu sestaví pracovní strom podle výsledku a přepíše nepotvrzené změny ve sledovaných souborech ([#2](https://github.com/inline0/pitmaster/issues/2)).

## Sloučení rychlým posunem

Pokud je aktuální HEAD předkem cílové větve, Pitmaster provede sloučení rychlým posunem: pouze přesune HEAD na cílové potvrzení, aniž by vytvořil potvrzení sloučení.

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

Opačný případ zatím není rozpoznán: sloučení větve, kterou HEAD již obsahuje, vytvoří nové potvrzení sloučení s nezměněným stromem místo hlášení „Již aktuální“ ([#6](https://github.com/inline0/pitmaster/issues/6)).

## Třícestné sloučení

Pokud se větve rozešly, Pitmaster provede třícestné sloučení:

1. Najde společného předka (nejbližšího společného předka)
2. Porovná základní strom se stromem naší větve a stromem jejich větve
3. U každého souboru změněného v obou větvích sloučí obsah
4. Vytvoří potvrzení sloučení se dvěma rodiči

```
Before:              After merge:

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

## Společný předek

Společný předek je nejnovější společný předek dvou potvrzení. Pitmaster ho najde průchodem grafem potvrzení. Pokud existuje několik společných předků (historie s křížením větví), `merge()` je nejprve sloučí a výsledek použije jako základ.

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

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

Přímý přístup k algoritmu `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);
```

## Kontrola sloučení větví

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

## Značky konfliktu

Pokud je soubor upraven v obou větvích a změny se překrývají, Pitmaster vytvoří standardní značky konfliktu. `merge()` označí strany jako `HEAD` a uvede název sloučené větve:

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

Třída `ConflictMarker` uzavírá dvě verze do značek:

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

## Třícestné slučování na úrovni obsahu

Třída `ThreeWayMerge` slučuje obsah blobů (nikoli soubory):

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

Přijímá tři řetězce (základní verzi, naši verzi a jejich verzi), volitelné popisky a styl konfliktů (ve výchozím nastavení `'merge'`, nebo `'diff3'` pro zahrnutí základní části).

## Strategie slučování

### Rekurzivní slučování

Výchozí strategie (`$repo->merge($branch)`) zpracovává historii křížení větví s několika společnými předky. CLI pro ni přijímá také `-s ort` a `-s recursive`.

### Slučování naší větve

Zaznamená potvrzení sloučení, které ponechá strom HEAD beze změny. Hodí se k zachování historie větví bez začlenění změn.

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

### Sloučení Octopus

Sloučí dvě nebo více větví do jediného potvrzení sloučení. Pokud dojde ke konfliktu v některé větvi, vrátí nečistý výsledek, aniž by zapsalo stav sloučení nebo vytvořilo potvrzení.

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

Třídy `RecursiveMerge`, `OursMerge` a `OctopusMerge` v `Pitmaster\Merge` pomáhají se slučováním obsahu; `Repository` výše uvedené strategie implementuje samostatně.

## Rerere (opakované použití zaznamenaného řešení)

Pitmaster obsahuje samostatnou třídu `Rerere`, která ukládá řešení v uspořádání `rr-cache` systému git:

```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()` ji nevolá: řešení se zaznamenávají a znovu používají pouze tehdy, když to udělá váš kód. `isEnabled()` hledá doslovný text `rerere.enabled = true` v `.git/config` a nerozpozná sekci `[rerere]`, kterou zapisuje `git config rerere.enabled true` ([#20](https://github.com/inline0/pitmaster/issues/20)).

## Průběh slučování na úrovni stromu

Celý proces sloučení:

1. `MergeBase::findAll()` vyhledá společné předky
2. Stromy základu, naší větve a jejich větve se porovnají podle jednotlivých cest
3. U souborů změněných pouze v jedné větvi se převezme tato změna
4. U souborů změněných v obou větvích:
   - Pokud se obě změnily stejně, převezme se kterákoli změna (shodují se)
   - Pokud se změny liší, spustí se `ThreeWayMerge::merge()` nad obsahem
5. Pokud všechna sloučení proběhnou čistě, sestaví se sloučený strom a vytvoří potvrzení sloučení
6. Pokud dojde ke konfliktům, vrátí se `MergeResult` s `clean = false` a cestami ke konfliktním souborům

## Použití CLI

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

Výstup při úspěchu:
```
Merge successful.
```

Výstup při konfliktu:
```
CONFLICT in: src/File.php, src/Other.php
```
