
# Egyesítés

Az Pitmaster ágakat egyesít háromutas módszerrel, az `git merge`-hez hasonlóan. Észleli a gyors előretekerési lehetőségeket, kiszámítja az egyesítési alapokat, végrehajtja a fa- és tartalomszintű egyesítést, és szabványos konfliktusjelölőket hoz létre, ha a fájlok ütköznek.

## Alapvető egyesítés

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

Az `MergeResult` osztály csak olvasható értékobjektum:

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

Ha az egyesítés konfliktusok miatt leáll, a konfliktus állapota az indexbe és a munkafába kerül. Oldja fel a konfliktusokat, `add()` őket, majd hívja meg az `$repo->mergeContinue()` metódust, vagy hívja meg az `$repo->mergeAbort()` metódust az egyesítés előtti állapot visszaállításához.

Egyesítés előtt véglegesítse vagy tegye félre a helyi módosításokat: az egyesítés az eredményből építi újra a munkafát, és felülírja a követett fájlok nem véglegesített módosításait ([#2](https://github.com/inline0/pitmaster/issues/2)).

## Gyors előretekeréses egyesítések

Ha az aktuális HEAD a célág őse, az Pitmaster gyors előretekeréses egyesítést végez: egyszerűen a cél commitra állítja a HEAD-et, és nem hoz létre egyesítési commitot.

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

A fordított esetet még nem észleli: ha olyan ágat egyesít, amely már szerepel a HEAD előzményei között, „Already up to date” jelentése helyett új, változatlan fát tartalmazó egyesítési commitot hoz létre ([#6](https://github.com/inline0/pitmaster/issues/6)).

## Háromutas egyesítés

Ha az ágak eltérő irányban fejlődtek, az Pitmaster háromutas egyesítést végez:

1. Megkeresi az egyesítési alapot (a legközelebbi közös őst)
2. Fa szinten összehasonlítja az alapot a saját és a másik ág állapotával
3. Minden olyan fájlon, amely mindkét ágban módosult, tartalomszintű egyesítést végez
4. Létrehoz egy két szülővel rendelkező egyesítési commitot

```
Before:              After merge:

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

## Egyesítési alap

Az összeolvasztási alap két véglegesítés legutóbbi közös őse. A Pitmaster a véglegesítési gráf bejárásával találja meg. Ha több összeolvasztási alap is van (kereszteződő történetben), először a `merge()` egyesíti őket, majd az eredményt használja alapként.

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

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

Az `MergeBase` algoritmus közvetlen eléréséhez:

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

## Ágegyesítés ellenőrzése

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

## Konfliktusjelölők

Ha egy fájl mindkét ágban módosult, és a módosítások átfedik egymást, az Pitmaster szabványos konfliktusjelölőket hoz létre. Az `merge()` az `HEAD` névvel, valamint az egyesített ág nevével címkézi az oldalakat:

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

Az `ConflictMarker` osztály két verziót fog közre jelölőkkel:

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

## Tartalomszintű háromutas egyesítés

Az `ThreeWayMerge` osztály blobok tartalmát egyesíti (nem fájlokat):

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

Három sztringet kap (az alapverziót, a saját verziót és a másik verziót), opcionális címkéket, valamint egy konfliktusstílust (alapértelmezés szerint `'merge'`, vagy `'diff3'` az alap szakasz hozzáadásához).

## Egyesítési stratégiák

### Rekurzív egyesítés

Az alapértelmezett stratégia (`$repo->merge($branch)`) több egyesítési alappal rendelkező, kereszteződő egyesítési előzményeket is kezel. Az CLI ehhez az `-s ort` és az `-s recursive` értéket is elfogadja.

### Saját ágat megtartó egyesítés

Olyan egyesítési commitot rögzít, amely változatlanul hagyja a HEAD fáját. Hasznos az ág előzményeinek megőrzéséhez a módosítások beemelése nélkül.

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

### Polipágú egyesítés

Két vagy több ágat egyesít egyetlen egyesítési commitba. Ha bármelyik ágban konfliktus van, nem tiszta eredményt ad vissza, és nem ír egyesítési állapotot vagy hoz létre commitot.

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

Az `RecursiveMerge`, `OursMerge` és `OctopusMerge` osztályok az `Pitmaster\Merge` tartalomszintű segédeszközei; az `Repository` valósítja meg a fenti stratégiákat.

## Rerere (rögzített feloldás újrafelhasználása)

Az Pitmaster tartalmaz egy önálló `Rerere` osztályt, amely a feloldásokat a git `rr-cache` elrendezésében tárolja:

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

Az `merge()` nem hívja meg: a feloldások rögzítése és visszajátszása csak akkor történik meg, ha a kód ezt végzi. Az `isEnabled()` a szó szerinti `rerere.enabled = true` szöveget keresi az `.git/config` fájlban, és nem ismeri fel az `git config rerere.enabled true` által kiírt `[rerere]` szakaszt ([#20](https://github.com/inline0/pitmaster/issues/20)).

## Fa szintű egyesítési folyamat

A teljes egyesítési folyamat:

1. Az `MergeBase::findAll()` megkeresi a közös ősöket
2. Az alap, a saját és a másik ág fáit útvonalanként hasonlítja össze
3. A csak az egyik ágban módosult fájloknál átveszi az adott módosítást
4. A mindkét ágban módosult fájloknál:
   - Ha mindkét ág ugyanúgy módosította: bármelyik változatot átveszi, mert megegyeznek
   - Ha a módosítások eltérnek: az `ThreeWayMerge::merge()` metódust futtatja a tartalmon
5. Ha minden egyesítés konfliktusmentes: felépíti az egyesített fát, és létrehoz egy egyesítési commitot
6. Ha van konfliktus: `clean = false` értékkel és az ütköző útvonalakkal rendelkező `MergeResult` értéket adja vissza

## CLI használata

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

Sikeres művelet kimenete:
```
Merge successful.
```

Kimenet konfliktus esetén:
```
CONFLICT in: src/File.php, src/Other.php
```
