
# Kolme osapoole liitmine

Kolme osapoole liitmisel võrreldakse kahte muudatuste kogumit ühise eelkäijaga ja ühendatakse need üheks tulemuseks. merql rakendab seda algoritmi failiridade asemel andmebaasiridadele.

```php
use Merql\Merge\ThreeWayMerge;

$merge = new ThreeWayMerge();
$result = $merge->merge($base, $ours, $theirs);

echo $result->operationCount(); // resolved operations
echo $result->conflictCount();  // unresolved conflicts
```

## Kolm olekut

Iga liitmine nõuab täpselt kolme hetktõmmist:

- **Alus** -- andmebaasi olek hetkel, mil mõlemad harud lahknesid.
- **Meie** -- üks haru pärast muudatusi, näiteks muudetud liivakastikoopia.
- **Nende** -- teine haru pärast muudatusi, näiteks tööd jätkanud aktiivne andmebaas.

Meie ja nende hetktõmmis tuleb jäädvustada vastava haru andmebaasist. Liitmisreeglid käsitlevad mõlemat poolt võrdselt; nimetused määravad iga tehte `source`, konfliktitüüpide nimed (`update_delete` tähendab, et meie uuendasime ja nemad kustutasid) ning selle, kumba poolt eelistavad `ConflictPolicy::OursWins` ja `TheirsWins`.

```
         Base (snapshot at fork time)
        /                              \
   Ours (our changes)                   Theirs (their changes)
        \                              /
         --------- MERGE ----------
                     |
              Merged result
              + conflicts (if any)
```

Ilma ühise aluseta ei saa eristada „lisatud” ja „muutmata” väärtust. Kahesuunaline võrdlus näitab, et poolel A on väärtus ja poolel B seda pole, kuid ei tuvasta, kas A lisas selle või B kustutas selle. Alus annab sellele küsimusele vastuse.

## Kuidas muudatuskogumid arvutatakse

`Differ` võrdleb iga hetktõmmist alusega, et luua kaks muudatuskogumit. Iga muudatuskogum sisaldab kolme tüüpi tehteid:

| Alus | Praegune | Tehe |
|---|---|---|
| Rida on olemas | Rida on olemas, sama sõrmejälg | Muutusi pole |
| Rida on olemas | Rida on olemas, erinev sõrmejälg | **UPDATE** (veeruvahe) |
| Rida on olemas | Rida puudub | **DELETE** |
| Rida puudub | Rida on olemas | **INSERT** |

Sõrmejäljed (SHA-256 normaliseeritud rea sisu räsi) pakuvad kiiret teed. Kui kahe sõrmejälje väärtus on sama, pole rida muutunud ja veerutaseme võrdlust pole vaja.

```php
use Merql\Diff\Differ;

$differ = new Differ();
$oursChangeset = $differ->diff($base, $ours);
$theirsChangeset = $differ->diff($base, $theirs);
```

## Liitmisreeglid

Seejärel liidetakse kaks muudatuskogumit tehe tehte haaval. Järgmine tabel määrab tulemuse iga kombinatsiooni korral:

| Meie | Nemad | Tulemus |
|---|---|---|
| Muutusi pole | Muutusi pole | Jäta alus alles |
| Muutusi pole | Uuendatud | Võta nende muudatus vastu |
| Uuendatud | Muutusi pole | Võta meie muudatus vastu |
| Uuendatud (sama väärtus) | Uuendatud (sama väärtus) | Võta vastu (mõlemad nõustuvad) |
| Uuendatud (erinev väärtus) | Uuendatud (erinev väärtus) | **Konflikt** |
| Muutusi pole | Kustutatud | Võta kustutamine vastu |
| Kustutatud | Muutusi pole | Võta kustutamine vastu |
| Kustutatud | Kustutatud | Võta kustutamine vastu (mõlemad nõustuvad) |
| Uuendatud | Kustutatud | **Konflikt** |
| Kustutatud | Uuendatud | **Konflikt** |
| Lisatud (ainult meie) | -- | Võta lisamine vastu |
| -- | Lisatud (ainult nemad) | Võta lisamine vastu |
| Lisatud (sama PK) | Lisatud (sama PK) | **Konflikt** |

Kui mõlemad pooled uuendavad sama rida, ei kuuluta merql seda kohe konfliktseks. See liigub veerutaseme liitmise juurde, võrreldes iga veergu eraldi. Üksikasju vaadake jaotisest [Veerutaseme liitmine](https://merql.inline0.dev/et/docs/merge/column-level).

## Fassadi kasutamine

`Merql`-fassad töötab nimega hetktõmmistega, mis on kettale salvestatud. Enne hetktõmmise loomist kutsuge `Merql::init()` välja igas olekus olevat andmebaasi kasutades:

```php
use Merql\Connection;
use Merql\Merql;

$live    = Connection::sqlite('/data/live.sqlite');
$sandbox = Connection::sqlite('/data/sandbox.sqlite');

Merql::init($live);
Merql::snapshot('base');      // before the sandbox is copied from live

// ... copy live.sqlite to sandbox.sqlite, then both databases change ...

Merql::init($sandbox);
Merql::snapshot('ours');
Merql::init($live);
Merql::snapshot('theirs');

$result = Merql::merge('base', 'ours', 'theirs');
```

Ärge jäädvustage kõiki kolme hetktõmmist järjest ühest andmebaasist. Nende hetktõmmis sisaldaks siis ka meie muudatusi, mistõttu paistaks iga meie lisatud rida olevat lisatud mõlemal poolel ja tekitaks konflikti `insert_insert`.

`Merql::merge()`, `Merql::patch()` ja `Merql::plan()` loovad `ThreeWayMerge` ilma objektita `CellMergeConfig`, seega toimub liitmine ainult veerutasemel. Lahtritaseme liitmiseks laadige hetktõmmised ja kasutage otse allpool näidatud klassi.

## Klasside otsekasutus

Suurema kontrolli saamiseks looge komponendid ise. `Snapshotter::capture()` ei salvesta midagi, seega hoidke hetktõmmiseid mälus või salvestage need `SnapshotStore::save()` abil:

```php
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;
use Merql\Snapshot\Snapshotter;
use Merql\Snapshot\SnapshotStore;

$base   = (new Snapshotter($live))->capture('base');
// ... copy live to the sandbox, then both databases change ...
$ours   = (new Snapshotter($sandbox))->capture('ours');
$theirs = (new Snapshotter($live))->capture('theirs');

$merge = new ThreeWayMerge();
$result = $merge->merge($base, $ours, $theirs);

// Snapshots saved earlier, merged with cell-level merge for TEXT and JSON columns.
$merge = new ThreeWayMerge(CellMergeConfig::auto());
$result = $merge->merge(
    SnapshotStore::load('base'),
    SnapshotStore::load('ours'),
    SnapshotStore::load('theirs'),
);
```

## Kiirtee paiga rakendamiseks

Kui muudatusi on ainult ühel poolel (samaaegseid muudatusi pole), kasutage `patch()`. See on kahesuunaline liitmine, kus meie võrdub alusega, seega konflikte tekkida ei saa:

```php
$result = $merge->patch($base, $changes);
// Always clean: ours is identical to base.
```

Fassaadi kaudu:

```php
$result = Merql::patch('base', 'changes');
```

`patch()` on samaväärne objektiga `merge($base, $base, $changes)`. Kuna meie haru ei lahknenud kunagi alusest, võetakse kõik nende muudatused konfliktita vastu.

## Skeemierinevuste tuvastamine

Kui hetktõmmiste vahel tabelisse veerge lisati või sealt eemaldati, jätkub liitmine, kuid erinevused salvestatakse. Muudetud veerutüüpe ei võrrelda ja ümbernimetatud veerg kuvatakse ühe eemaldatud ning ühe lisatud veeruna; vaadake jaotist [Skeemi valideerimine](https://merql.inline0.dev/et/docs/advanced/schema).

```php
$result = $merge->merge($base, $ours, $theirs);

if ($result->hasSchemaMismatches()) {
    foreach ($result->schemaMismatches() as $mismatch) {
        echo $mismatch->getMessage() . "\n";
    }
}
```

merql ei migreeri skeeme automaatselt. Skeemimuudatuste eest vastutab kutsuja. Liitmine annab neist teada, et saaksite otsustada, kuidas edasi toimida.

## MergeResult

Tulemusobjekt sisaldab kõike vajalikku:

```php
$result->isClean();            // true if no conflicts
$result->operations();         // list of MergeOperation objects
$result->conflicts();          // list of Conflict objects
$result->operationCount();     // number of resolved operations
$result->conflictCount();      // number of unresolved conflicts
$result->hasSchemaMismatches(); // true if schemas differ
$result->baseSnapshot();       // the base snapshot used for the merge
```

Igal `MergeOperation`-il on tüüp (`insert`, `update` või `delete`), tabeli nimi, reavõti, veeruväärtused ja allikas (`ours`, `theirs` või `merged`). `merged` märgib rea, mida mõlemad pooled uuendasid (veerud liideti eraldi) või mille mõlemad kustutasid.

## Kus liitmise tulemust rakendada

Liitmise tulemus kirjeldab, kuidas muuta alus liidetud olekuks. Selle tehted sisaldavad mõlema poole muudatusi: nii meie kui ka nende lisatud read ilmuvad lisamistena.

- `Merql::apply()` (või `Applier`) käivitab kõik tehted ühendatud andmebaasis. Seda saab puhtalt rakendada andmebaasile, kus on endiselt alus, näiteks aluse värskele koopiale. Kui andmebaasis on juba ühe poole muudatused, nurjub olemasoleva rea lisamine ja tehing pööratakse tagasi.
- Meie muudatuste toomiseks andmebaasi, kus on juba nende muudatused, näiteks liivakasti muudatuste aktiivsesse keskkonda viimiseks, rakendage ainult need tehted, mis andmebaasis veel puuduvad: allikaga `ours` tehted ja `merged` uuendused. Kasutage nende valimiseks [liitmisplaani](https://merql.inline0.dev/et/docs/merge/plans) ja nende kirjutamiseks koos aktiivse rea eeltingimustega jaotist [Kaitstud rakendamine](https://merql.inline0.dev/et/docs/apply/guarded).
