
# Združevanje

Pitmaster združuje veje s trosmernim združevanjem, tako kot `git merge`. Zazna možnosti hitrega pomika naprej, izračuna osnovne različice združitve, izvede združevanje na ravni dreves in vsebine ter ob sporih v datotekah ustvari standardne oznake sporov.

## Osnovno združevanje

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

Razred `MergeResult` je objekt vrednosti samo za branje:

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

Ko se združevanje ustavi zaradi sporov, se stanje spora zapiše v indeks in delovno drevo. Razrešite spore v datotekah, jih dodajte v pripravo z `add()` in pokličite `$repo->mergeContinue()` ali pa pokličite `$repo->mergeAbort()`, da obnovite stanje pred združevanjem.

Pred združevanjem potrdite lokalne spremembe ali jih shranite na stran: združevanje znova sestavi delovno drevo na podlagi rezultata in prepiše nepotrjene spremembe v datotekah pod nadzorom različic ([#2](https://github.com/inline0/pitmaster/issues/2)).

## Združevanja s hitrim pomikom naprej

Če je trenutni HEAD prednik ciljne veje, Pitmaster izvede združevanje s hitrim pomikom naprej: HEAD preprosto premakne na ciljno potrditev, ne da bi ustvaril potrditev združitve.

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

Obratni primer še ni zaznan: združevanje veje, ki jo HEAD že vsebuje, ustvari novo potrditev združitve z nespremenjenim drevesom, namesto da bi sporočilo »Že posodobljeno« ([#6](https://github.com/inline0/pitmaster/issues/6)).

## Tristranska združitev

Ko se veje razidejo, Pitmaster izvede trosmerno združevanje:

1. Poišče osnovno različico združitve (najnižjega skupnega prednika)
2. Na ravni dreves primerja osnovo z našo in njihovo različico
3. Za vsako datoteko, spremenjeno v obeh vejah, izvede združevanje na ravni vsebine
4. Ustvari potrditev združitve z dvema nadrejenima potrditvama

```
Before:              After merge:

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

## Osnovna različica združitve

Osnovna različica združitve je najnovejši skupni prednik dveh potrditev. Pitmaster jo poišče s sprehodom po grafu potrditev. Če je osnovnih različic združitve več (križajoča se zgodovina), jih `merge()` najprej združi in rezultat uporabi kot osnovo.

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

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

Za neposreden dostop do algoritma `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);
```

## Preverjanje združitve vej

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

## Oznake sporov

Če je datoteka spremenjena v obeh vejah in se spremembe prekrivajo, Pitmaster ustvari standardne oznake sporov. `merge()` označi strani z `HEAD` in imenom združene veje:

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

Razred `ConflictMarker` ovije dve različici z oznakami:

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

## Trosmerno združevanje vsebine

Razred `ThreeWayMerge` združuje vsebino objektov blob (ne datotek):

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

Sprejme tri nize (osnovno različico, našo različico in njihovo različico), izbirne oznake in slog sporov (privzeto `'merge'` ali `'diff3'`, da vključi razdelek z osnovno različico).

## Strategije združevanja

### Rekurzivno združevanje

Privzeta strategija (`$repo->merge($branch)`) obravnava zgodovino križajočih se združitev z več osnovnimi različicami. Razred CLI za to sprejema tudi `-s ort` in `-s recursive`.

### Združevanje po naši različici

Zabeleži potrditev združitve, ki ohrani drevo HEAD nespremenjeno. Uporabno za vzdrževanje zgodovine veje brez vključevanja sprememb.

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

### Združevanje Octopus

Združi dve ali več vej v eno samo potrditev združitve. Če pride do spora v kateri koli veji, vrne nečist rezultat, ne da bi zapisalo stanje združitve ali ustvarilo potrditev.

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

Razredi `RecursiveMerge`, `OursMerge` in `OctopusMerge` v `Pitmaster\Merge` so pomočniki za združevanje na ravni vsebine; `Repository` samega izvaja zgornje strategije.

## Rerere (ponovna uporaba zabeležene razrešitve)

Pitmaster vključuje samostojen razred `Rerere`, ki shranjuje razrešitve v postavitvi `rr-cache` za 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()` ga ne pokliče: razrešitve se zabeležijo in znova uporabijo samo, če to stori vaša koda. `isEnabled()` v `.git/config` išče dobesedno besedilo `rerere.enabled = true` in ne prepozna razdelka `[rerere]`, ki ga zapiše `git config rerere.enabled true` ([#20](https://github.com/inline0/pitmaster/issues/20)).

## Potek združevanja na ravni dreves

Celoten postopek združevanja:

1. `MergeBase::findAll()` poišče skupne prednike
2. Drevesa osnove, naše in njihove različice se primerjajo po poteh
3. Pri datotekah, spremenjenih samo v eni veji: uporabi to spremembo
4. Pri datotekah, spremenjenih v obeh vejah:
   - Če sta bili spremenjeni enako: uporabi katero koli različico (ujemata se)
   - Če se spremembe razlikujejo: za vsebino zažene `ThreeWayMerge::merge()`
5. Če vsa združevanja uspejo brez sporov: sestavi združeno drevo in ustvari potrditev združitve
6. Če pride do sporov: vrne `MergeResult` z `clean = false` in potmi do datotek v sporu

## Uporaba CLI

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

Izhod ob uspehu:
```
Merge successful.
```

Izhod ob sporu:
```
CONFLICT in: src/File.php, src/Other.php
```
