
# Lahtritasemel ühendamine

Veerutasemel ühendamine tuvastab, kui mõlemad pooled muutsid sama veergu. Lahtritasemel ühendamine läheb sügavamale ja ühendab selle veeru sisu. Kaks inimest saavad muuta puhtalt sama TEXT-välja eri ridu või JSON-objekti eri võtmeid.

```php
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;

$config = CellMergeConfig::auto();
$merge = new ThreeWayMerge($config);
$result = $merge->merge($base, $ours, $theirs);
```

## Lahtritasemel ühendamise lubamine

Lahtritasemel ühendamine on valikuline. See töötab ainult `CellMergeConfig` abil loodud `ThreeWayMerge` puhul. Ilma selleta tekitab iga veerg, mida mõlemad pooled on eri väärtuseks muutnud, konflikti `update_update`.

`Merql`-fassaad ei võta seadistust vastu: `Merql::merge()`, `Merql::patch()` ja `Merql::plan()` loovad `new ThreeWayMerge()` ilma selleta ning CLI käsk `merge` kaudu kasutab `Merql::merge()`-i. Need ühendavad alati veerutasemel ([#7](https://github.com/inline0/merql/issues/7)).

Fassaadi või CLI abil salvestatud hetktõmmiste ühendamiseks lahtritasemel laadige need ja kutsuge ise välja `ThreeWayMerge`. Tulemuseks on tavaline `MergeResult`, seega saab fassaad seda endiselt rakendada ja `MergePlanBuilder` muudab selle [ühendamisplaaniks](https://merql.inline0.dev/et/docs/merge/plans):

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

$base   = SnapshotStore::load('base');
$ours   = SnapshotStore::load('ours');
$theirs = SnapshotStore::load('theirs');

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

$plan = (new MergePlanBuilder())->build('plan-1', $base, $ours, $theirs, $result);
```

Lahtrite ühendajad töötavad ainult nende veergude puhul, mida mõlemad pooled muutsid. Kui veergu muutis ainult üks pool, kasutatakse selle väärtust muutmata kujul.

## CellMergeConfig

`CellMergeConfig` seostab veerud või veerutüübid lahtriühendajate teostustega. Tehas `auto()` pakub sobivaid vaikeväärtusi:

```php
use Merql\CellMerge\CellMergeConfig;
use Merql\CellMerge\JsonCellMerger;
use Merql\CellMerge\TextCellMerger;

// Auto: column types containing "text" (TEXT, MEDIUMTEXT, LONGTEXT, ...) use
// TextCellMerger, column types containing "json" use JsonCellMerger.
$config = CellMergeConfig::auto();

// Custom: assign mergers to specific columns.
$config = (new CellMergeConfig())
    ->forColumn('posts.content', new TextCellMerger())
    ->forColumn('settings', new JsonCellMerger())
    ->forType('json', new JsonCellMerger());
```

Ühendaja valimise tähtsusjärjekord:

1. Täielikult kvalifitseeritud veerunimi (`table.column`).
2. Kvalifitseerimata veerunimi (`column`).
3. Veerutüübi mustrile vastavus (tõstutundetu `str_contains`).
4. Vaikeväärtus: `OpaqueCellMerger` (annab alati konflikti, kui väärtused erinevad).

## TextCellMerger

Ühendab mitmerealise teksti Myersi erinevuste leidmise algoritmiga, mida git kasutab failisisu jaoks. Iga rida käsitletakse eraldi ühikuna ning eri ridade muudatused ühendatakse puhtalt.

```php
use Merql\CellMerge\TextCellMerger;

$merger = new TextCellMerger();
```

### Näide: puhas tekstiühendamine

```
Base:    "line one\nline two\nline three"
Ours:    "LINE ONE\nline two\nline three"    // changed line 1
Theirs:  "line one\nline two\nLINE THREE"    // changed line 3
```

Muudeti eri ridu. Ühendamise tulemuseks on `"LINE ONE\nline two\nLINE THREE"` ilma konfliktita.

### Näide: tekstikonflikt

```
Base:    "line one\nline two"
Ours:    "changed by us\nline two"     // changed line 1
Theirs:  "changed by them\nline two"   // also changed line 1
```

Mõlemad pooled muutsid sama rea väärtuse erinevaks. Ühendaja tagastab konfliktitulemuse. Ühendatud sisu sisaldab git-i omadega sarnaseid konfliktimarkereid.

### Kuidas see töötab

`TextCellMerger` kasutab pitmasteri `ThreeWayMerge::merge()`-i, mis arvutab baasi ja kummagi poole vahel Myersi erinevused ning ühendab seejärel kaks muudatuste skripti. Kui muudatused kattuvad samas reapiirkonnas, teatatakse konfliktist.

## JsonCellMerger

Ühendab JSON-objekte, võrreldes ülataseme võtmeid ükshaaval. Iga võti ühendatakse tavaliste kolmeosalise ühendamise reeglite järgi.

```php
use Merql\CellMerge\JsonCellMerger;

$merger = new JsonCellMerger();
```

### Näide: puhas JSON-ühendamine

```json
// Base
{"name": "Alice", "role": "editor", "active": true}

// Ours: changed "role"
{"name": "Alice", "role": "admin", "active": true}

// Theirs: changed "name"
{"name": "Bob", "role": "editor", "active": true}
```

Muutusid erinevad võtmed. Ühendatud tulemus on järgmine:

```json
{"name": "Bob", "role": "admin", "active": true}
```

### Näide: JSON-konflikt

```json
// Base
{"color": "red"}

// Ours
{"color": "blue"}

// Theirs
{"color": "green"}
```

Mõlemad pooled muutsid võtme `color` väärtuse erinevaks. Tekib konflikt. Vaikimisi eelistab ühendaja meiepoolset väärtust ja teatab ühest konfliktist.

### Võtmetaseme reeglid

| Baas | Meiepoolne | Teine pool | Tulemus |
|---|---|---|---|
| Võti muutmata | Võti muutmata | Võetakse baasi väärtus |
| Võti muutmata | Võtit muudeti | Võetakse teise poole väärtus |
| Võtit muudeti | Võti muutmata | Võetakse meiepoolne väärtus |
| Võtit muudeti (sama) | Võtit muudeti (sama) | Võetakse ühine väärtus |
| Võtit muudeti (erinevaks) | Võtit muudeti (erinevaks) | **Konflikt** (vaikimisi: meiepoolne väärtus) |
| Võti olemas | Võti olemas | Võti eemaldatakse |
| Võti olemas | Võti eemaldati | Võti olemas | Võti eemaldatakse |
| Võti puudub | Võti lisati | Võti puudub | Võti lisatakse |
| Võti puudub | Võti puudub | Võti lisati | Võti lisatakse |

Pesastatud objekte võrreldakse läbipaistmatute JSON-stringidena, mitte rekursiivselt. Kui pesastatud objekt erineb, võrreldakse kogu võtme väärtust, mitte selle alamvõtmeid.

### Nõuded

Kõik kolm väärtust peavad olema kehtivad JSON-objektid või massiivid. Kui mõni väärtus pole kehtiv JSON või on JSON-skalaar (string, arv või tõeväärtus), kasutab ühendaja konflikti korral varukäitumist.

## OpaqueCellMerger

Vaikimisi liitja, mida kasutatakse siis, kui lahtritaseme strateegiat pole seadistatud. See käsitleb veeru väärtust läbipaistmatu andmeplokina ja teatab alati konfliktist, kui mõlemad pooled erinevad.

```php
use Merql\CellMerge\OpaqueCellMerger;

$merger = new OpaqueCellMerger();
$result = $merger->merge($base, $ours, $theirs);
// $result->clean is always false when $ours !== $theirs
```

## CellMergeResult

Kõik lahtriliitjad tagastavad `CellMergeResult`:

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

$result->clean;     // bool: true if merge resolved cleanly
$result->value;     // mixed: the merged value (or ours on conflict)
$result->conflicts; // int: number of conflicts within the cell
```

Tehasemeetodid:

```php
CellMergeResult::resolved($value);           // clean merge
CellMergeResult::conflict($oursValue, $n);   // n conflicts, defaults to ours
```

## Kohandatud lahtriliitjad

Oma liitmisstrateegia lisamiseks rakendage liidest `CellMerger`:

```php
use Merql\CellMerge\CellMerger;
use Merql\CellMerge\CellMergeResult;

class YamlCellMerger implements CellMerger
{
    public function merge(mixed $base, mixed $ours, mixed $theirs): CellMergeResult
    {
        // Parse YAML, merge keys, return result.
        $merged = $this->mergeYaml($base, $ours, $theirs);

        if ($merged['clean']) {
            return CellMergeResult::resolved($merged['value']);
        }

        return CellMergeResult::conflict($ours);
    }
}

$config = (new CellMergeConfig())
    ->forColumn('config_data', new YamlCellMerger());
```
