
# Solutason yhdistäminen

Saraketason yhdistäminen havaitsee, kun molemmat puolet ovat muuttaneet samaa saraketta. Solutason yhdistäminen menee syvemmälle ja yhdistää kyseisen sarakkeen sisällön. Kahden henkilön eri riveihin tekemät muutokset TEXT-kentässä tai eri avaimiin tekemät muutokset JSON-objektissa voidaan yhdistää ongelmitta.

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

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

## Solutason yhdistämisen käyttöönotto

Solutason yhdistäminen on valinnainen. Se toimii vain `CellMergeConfig`-määrityksellä luodussa kohteessa `ThreeWayMerge`. Ilman sitä jokainen sarake, jonka molemmat puolet muuttivat eri arvoiksi, aiheuttaa `update_update`-ristiriidan.

`Merql`-julkisivu ei ota asetuksia: `Merql::merge()`, `Merql::patch()` ja `Merql::plan()` luovat `new ThreeWayMerge()`-instanssin ilman niitä, ja CLI-ohjelman `merge`-komento kulkee `Merql::merge()`-kohteen kautta. Ne yhdistävät aina vain saraketasolla ([#7](https://github.com/inline0/merql/issues/7)).

Jos haluat yhdistää julkisivulla tai CLI-ohjelmalla tallennettuja tilannevedoksia solutason yhdistämisellä, lataa ne ja kutsu itse `ThreeWayMerge`-toimintoa. Tulos on tavallinen `MergeResult`, joten julkisivu voi yhä käyttää sitä, ja `MergePlanBuilder` muuntaa sen [yhdistämissuunnitelmaksi](https://merql.inline0.dev/fi/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);
```

Soluyhdistimet suoritetaan vain sarakkeille, joita molemmat puolet ovat muuttaneet. Jos vain toinen puoli muutti saraketta, sen arvo otetaan sellaisenaan.

## CellMergeConfig

`CellMergeConfig` yhdistää sarakkeet tai saraketyypit soluyhdistimen toteutuksiin. `auto()`-tehdas tarjoaa järkevät oletusarvot:

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

Yhdistimen valintajärjestys:

1. Täysin määritetty sarakkeen nimi (`table.column`).
2. Määrittämätön sarakkeen nimi (`column`).
3. Saraketyypin mallivastaavuus (kirjainkoosta riippumaton `str_contains`).
4. Oletusarvo: `OpaqueCellMerger` (ilmoittaa aina ristiriidan, kun arvot eroavat).

## TextCellMerger

Yhdistää monirivistä tekstiä Myers-erotusalgoritmilla, samalla algoritmilla, jota git käyttää tiedostosisällölle. Kukin rivi käsitellään yhtenä yksikkönä, ja eri riveihin tehdyt muutokset yhdistyvät ongelmitta.

```php
use Merql\CellMerge\TextCellMerger;

$merger = new TextCellMerger();
```

### Esimerkki: ristiriidaton tekstin yhdistäminen

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

Eri rivejä muokattiin. Yhdistäminen tuottaa tuloksen `"LINE ONE\nline two\nLINE THREE"` ilman ristiriitaa.

### Esimerkki: tekstiristiriita

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

Molemmat puolet muuttivat saman rivin eri arvoiksi. Yhdistin palauttaa ristiriitatuloksen. Yhdistetty sisältö sisältää gitin kaltaiset ristiriitamerkinnät.

### Näin se toimii

`TextCellMerger` käyttää pitmasterin `ThreeWayMerge::merge()`-toteutusta, joka laskee Myers-erotuksen basen ja kummankin puolen välillä ja yhdistää sitten muokkauskomentosarjat. Jos muokkaukset menevät päällekkäin samalla rivialueella, tuloksena ilmoitetaan ristiriita.

## JsonCellMerger

Yhdistää JSON-objektit vertaamalla ylimmän tason avaimia erikseen. Kukin avain yhdistetään tavanomaisilla kolmen osapuolen säännöillä.

```php
use Merql\CellMerge\JsonCellMerger;

$merger = new JsonCellMerger();
```

### Esimerkki: ristiriidaton JSON-yhdistäminen

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

Eri avaimia muutettiin. Yhdistetty tulos on:

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

### Esimerkki: JSON-ristiriita

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

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

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

Molemmat puolet muuttivat avaimen `color` eri arvoiksi. Kyseessä on ristiriita. Yhdistin valitsee oletusarvoisesti ours-puolen ja ilmoittaa yhden ristiriidan.

### Avaintason säännöt

| Lähtötila | Ours | Theirs | Tulos |
|---|---|---|---|
| Avain ennallaan | Avain ennallaan | Hyväksy lähtötila |
| Avain ennallaan | Avain muuttunut | Hyväksy theirs |
| Avain muuttunut | Avain ennallaan | Hyväksy ours |
| Avain muuttunut (sama) | Avain muuttunut (sama) | Hyväksy (samaa mieltä) |
| Avain muuttunut (eri) | Avain muuttunut (eri) | **Ristiriita** (oletus: ours) |
| Avain on olemassa | Avain on olemassa | Avain poistettu | Hyväksy poisto |
| Avain on olemassa | Avain poistettu | Avain on olemassa | Hyväksy poisto |
| Avain puuttuu | Avain lisätty | Avain puuttuu | Hyväksy lisäys |
| Avain puuttuu | Avain puuttuu | Avain lisätty | Hyväksy lisäys |

Sisäkkäisiä objekteja verrataan käsittelemällä niitä läpinäkymättöminä JSON-merkkijonoina, ei rekursiivisesti. Jos sisäkkäinen objekti eroaa, verrataan kyseisen avaimen koko arvoa, ei sen aliavaimia.

### Vaatimukset

Kaikkien kolmen arvon on oltava kelvollisia JSON-objekteja tai taulukoita. Jos jokin arvo ei ole kelvollista JSON-dataa tai on JSON-tyyppinen alkeisarvo (merkkijono, luku tai totuusarvo), yhdistin palautuu ristiriitaan.

## OpaqueCellMerger

Oletusyhdistin, kun solutason strategiaa ei ole määritetty. Se käsittelee sarakkeen arvoa läpinäkymättömänä tietona ja ilmoittaa aina ristiriidasta, kun molemmat puolet ovat muuttaneet arvoa eri tavalla.

```php
use Merql\CellMerge\OpaqueCellMerger;

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

## CellMergeResult

Kaikki soluyhdistimet palauttavat arvon `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
```

Tehdasmetodit:

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

## Mukautetut soluyhdistimet

Toteuta rajapinta `CellMerger` lisätäksesi oman yhdistämisstrategiasi:

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