
# Yhdistäminen

Pitmaster yhdistää haaroja kolmen suunnan yhdistämisellä, kuten `git merge`. Se tunnistaa pikakelausmahdollisuudet, laskee yhdistämiskohdat, yhdistää puita ja sisältöä sekä tuottaa tavanomaiset konfliktimerkit ristiriitatilanteissa.

## Yhdistämisen perusteet

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

`MergeResult`-luokka on vain luku -arvo-olio:

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

Kun yhdistäminen keskeytyy ristiriitoihin, ristiriitatila kirjataan indeksiin ja työhakemistoon. Ratkaise tiedostot, lisää ne `add()`-komennolla ja kutsu `$repo->mergeContinue()`-komentoa tai palauta yhdistämistä edeltävä tila kutsumalla `$repo->mergeAbort()`-komentoa.

Tee paikallisista muutoksista toimitus tai tallenna ne pinoon ennen yhdistämistä: yhdistäminen rakentaa työhakemiston uudelleen tuloksen pohjalta ja korvaa seurattujen tiedostojen toimittamattomat muutokset ([#2](https://github.com/inline0/pitmaster/issues/2)).

## Pikakelauksella tehtävät yhdistämiset

Kun nykyinen HEAD on kohdehaaran esi-isä, Pitmaster tekee yhdistämisen pikakelauksella: se siirtää HEADin kohdehaaran toimitukseen luomatta yhdistämistoimitusta.

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

Käänteistä tapausta ei vielä tunnisteta: jos yhdistetään haara, jonka HEAD jo sisältää, syntyy uusi yhdistämistoimitus, jonka puu ei muutu, sen sijaan että ilmoitettaisiin ”Already up to date” ([#6](https://github.com/inline0/pitmaster/issues/6)).

## Kolmen osapuolen yhdistäminen

Kun haarat ovat eriytyneet, Pitmaster tekee kolmen suunnan yhdistämisen:

1. Etsi yhdistämiskohta (lähin yhteinen esi-isä)
2. Laske puutasolla erot kantapuusta omiin ja muiden muutoksiin
3. Yhdistä sisältö kunkin tiedoston osalta, jos sitä on muutettu molemmissa haaroissa
4. Luo yhdistämistoimitus, jolla on kaksi vanhempaa

```
Before:              After merge:

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

## Yhdistämiskohta

Yhdistämiskohta on kahden toimituksen viimeisin yhteinen esi-isä. Pitmaster etsii sen käymällä läpi toimitusgraafin. Jos yhdistämiskohtia on useita (ristikkäishistoriassa), `merge()` yhdistää ne ensin ja käyttää tulosta kantana.

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

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

Yhdistämisalgoritmia `MergeBase` voi käyttää suoraan seuraavasti:

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

## Haarojen yhdistämisen tarkistus

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

## Konfliktimerkit

Kun tiedostoa on muutettu molemmissa haaroissa ja muutokset ovat päällekkäisiä, Pitmaster tuottaa tavanomaiset konfliktimerkit. `merge()` merkitsee puolet tunnisteilla `HEAD` ja yhdistetyn haaran nimellä:

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

`ConflictMarker`-luokka ympäröi kaksi versiota merkeillä:

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

## Sisällön kolmen suunnan yhdistäminen

`ThreeWayMerge`-luokka yhdistää blob-sisältöä (ei tiedostoja):

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

Se ottaa kolme merkkijonoa (kantaversion, oman version ja muiden version) sekä valinnaiset tunnisteet ja konfliktityylin (oletuksena `'merge'` tai `'diff3'`, joka sisällyttää kantaversion osion).

## Yhdistämisstrategiat

### Rekursiivinen yhdistäminen

Oletusstrategia (`$repo->merge($branch)`) käsittelee ristikkäisiä yhdistämishistorioita, joissa on useita yhdistämiskohtia. Myös CLI hyväksyy sille arvot `-s ort` ja `-s recursive`.

### Ours-yhdistäminen

Luo yhdistämistoimituksen, jossa HEADin puu säilyy ennallaan. Hyödyllinen haarahistorian ylläpitoon ilman muutosten sisällyttämistä.

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

### Octopus-yhdistäminen

Yhdistää kaksi tai useampia haaroja yhteen yhdistämistoimitukseen. Jos haarassa on ristiriitoja, se palauttaa keskeneräisen tuloksen tallentamatta yhdistämistilaa tai luomatta toimitusta.

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

Luokkien `RecursiveMerge`, `OursMerge` ja `OctopusMerge` toiminnot kohteessa `Pitmaster\Merge` auttavat sisällön yhdistämisessä; `Repository` toteuttaa yllä olevat strategiat itse.

## Rerere (tallennetun ratkaisun uudelleenkäyttö)

Pitmaster sisältää itsenäisen `Rerere`-luokan, joka tallentaa ratkaisut gitin `rr-cache`-asetteluun:

```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()` ei kutsu sitä: ratkaisut tallennetaan ja toistetaan vain, jos koodisi tekee niin. `isEnabled()` etsii kirjaimellista tekstiä `rerere.enabled = true` kohteesta `.git/config` eikä tunnista osiota `[rerere]`, jonka `git config rerere.enabled true` kirjoittaa ([#20](https://github.com/inline0/pitmaster/issues/20)).

## Puiden yhdistämisen vaiheet

Yhdistämisen kaikki vaiheet:

1. `MergeBase::findAll()` etsii yhteiset esi-isät
2. Kantapuun, oman puun ja muiden puun polkuja verrataan yksitellen
3. Jos tiedostoa on muutettu vain yhdessä haarassa, käytetään tätä muutosta
4. Jos tiedostoa on muutettu molemmissa haaroissa:
   - Jos muutokset ovat samanlaiset, käytetään kumpaa tahansa, sillä ne vastaavat toisiaan
   - Jos muutokset eroavat, suoritetaan sisältöön `ThreeWayMerge::merge()`
5. Jos kaikki yhdistämiset onnistuvat, rakennetaan yhdistetty puu ja luodaan yhdistämistoimitus
6. Jos ristiriitoja esiintyy, palautetaan `MergeResult` sekä `clean = false` ja ristiriitapolut

## CLI-käyttö

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

Tuloste onnistuttaessa:
```
Merge successful.
```

Tuloste ristiriitatilanteessa:
```
CONFLICT in: src/File.php, src/Other.php
```
