
# Sujungimas

Pitmaster sujungia šakas trijų krypčių metodu, kaip `git merge`. Aptinka greitojo perslinkimo galimybes, apskaičiuoja sujungimo bazes, sujungia medžius ir turinį bei failams konfliktuojant įterpia standartinius konfliktų žymeklius.

## Pagrindinis sujungimas

```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` klasė yra tik skaitomas reikšmės objektas:

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

Kai sujungimas sustoja dėl konfliktų, konfliktų būsena įrašoma į indeksą ir darbinį medį. Išspręskite konfliktus failuose, juos `add()` ir iškvieskite `$repo->mergeContinue()` arba iškvieskite `$repo->mergeAbort()`, kad atkurtumėte būseną prieš sujungimą.

Prieš sujungdami patvirtinkite arba padėkite į saugyklą vietinius pakeitimus: sujungimas iš naujo sukuria darbinį medį pagal rezultatą ir perrašo nepatvirtintus sekamų failų pakeitimus ([#2](https://github.com/inline0/pitmaster/issues/2)).

## Greitojo perslinkimo sujungimai

Kai dabartinis HEAD yra tikslinės šakos protėvis, Pitmaster atlieka greitojo perslinkimo sujungimą: tiesiog perkelia HEAD į tikslinį įrašą, nesukurdama sujungimo įrašo.

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

Atvirkštinis atvejis dar neaptinkamas: sujungus šaką, kurią HEAD jau apima, sukuriamas naujas sujungimo įrašas su nepakitusiu medžiu, užuot pranešus „Already up to date“ ([#6](https://github.com/inline0/pitmaster/issues/6)).

## Trijų krypčių sujungimas

Kai šakų istorijos išsiskiria, Pitmaster atlieka trijų krypčių sujungimą:

1. Rasti sujungimo bazę (artimiausią bendrą protėvį)
2. Palyginti bazę su mūsų ir su jų šakomis medžių lygmeniu
3. Kiekvienam failui, pakeistam abiejose šakose, atlikti turinio lygmens sujungimą
4. Sukurti sujungimo įrašą su dviem tėviniais įrašais

```
Before:              After merge:

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

## Sujungimo bazė

Sujungimo bazė yra naujausias bendras dviejų įrašų protėvis. Pitmaster ją randa eidama įrašų grafu. Jei sujungimo bazių yra kelios (kryžminė istorija), `merge()` pirmiausia jas sujungia, o rezultatą naudoja kaip bazę.

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

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

Tiesioginei prieigai prie `MergeBase` algoritmo:

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

## Šakų sujungimo tikrinimas

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

## Konfliktų žymekliai

Kai failas pakeičiamas abiejose šakose ir pakeitimai persidengia, Pitmaster įterpia standartinius konfliktų žymeklius. `merge()` pažymi puses `HEAD` ir sujungtos šakos pavadinimu:

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

`ConflictMarker` klasė apgaubia dvi versijas žymekliais:

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

## Trijų krypčių sujungimas turinio lygmeniu

`ThreeWayMerge` klasė sujungia blob turinį (ne failus):

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

Ji priima tris eilutes (bazinę versiją, mūsų versiją ir jų versiją), pasirinktines etiketes ir konflikto stilių (pagal numatytuosius nustatymus `'merge'`, o `'diff3'` įtraukia bazinę skiltį).

## Sujungimo strategijos

### Rekursinis sujungimas

Numatytoji strategija (`$repo->merge($branch)`) apdoroja kryžmines sujungimų istorijas su keliomis sujungimo bazėmis. Jai CLI taip pat priima `-s ort` ir `-s recursive`.

### Mūsų pusės sujungimas

Sukuria sujungimo įrašą, paliekantį HEAD medį nepakeistą. Naudinga šakų istorijai palaikyti neįtraukiant pakeitimų.

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

### Aštuonkojo sujungimas

Sujungia dvi ar daugiau šakų į vieną sujungimo įrašą. Jei kurioje nors šakoje kyla konfliktų, grąžinamas nešvarus rezultatas, neįrašant sujungimo būsenos ir nesukuriant įrašo.

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

`Pitmaster\Merge` esančios `RecursiveMerge`, `OursMerge` ir `OctopusMerge` klasės yra turinio lygmens pagalbinės priemonės; `Repository` pačios įgyvendina pirmiau nurodytas strategijas.

## Rerere (pakartotinis įrašyto sprendimo panaudojimas)

Pitmaster turi atskirą `Rerere` klasę, kuri saugo sprendimus git `rr-cache` išdėstymu:

```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()` jos neiškviečia: sprendimai įrašomi ir pakartojami tik tada, kai taip nurodo jūsų kodas. `isEnabled()` ieško pažodinio teksto `rerere.enabled = true` faile `.git/config` ir neatpažįsta `[rerere]` skilties, kurią įrašo `git config rerere.enabled true` ([#20](https://github.com/inline0/pitmaster/issues/20)).

## Sujungimo eiga medžių lygmeniu

Visas sujungimo procesas:

1. `MergeBase::findAll()` nustato bendrus protėvius
2. Pagrindinis, mūsų ir jų medžiai lyginami pagal kiekvieną kelią
3. Failams, pakeistiems tik vienoje šakoje: pritaikomas tas pakeitimas
4. Failams, pakeistiems abiejose šakose:
   - Jei pakeista vienodai: pritaikomas bet kuris variantas (jie sutampa)
   - Jei pakeitimai skiriasi: turiniui paleidžiamas `ThreeWayMerge::merge()`
5. Jei visi sujungimai švarūs: sukuriamas sujungtas medis ir sujungimo įrašas
6. Jei yra konfliktų: grąžinamas `MergeResult` su `clean = false` ir konfliktuojančiais keliais

## CLI naudojimas

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

Išvestis sėkmės atveju:
```
Merge successful.
```

Konflikto atveju pateikiama:
```
CONFLICT in: src/File.php, src/Other.php
```
