
# Cellaszintű egyesítés

Az oszlopszintű egyesítés észleli, ha mindkét oldal ugyanazt az oszlopot módosította. A cellaszintű egyesítés mélyebbre hatol, és az oszlopon belüli tartalmat egyesíti. Két ember különböző sorokat szerkeszthet egy TEXT mezőben, vagy különböző kulcsokat egy JSON objektumban, és a módosítások ütközésmentesen egyesíthetők.

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

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

## Cellaszintű egyesítés engedélyezése

A cellaszintű egyesítés választható. Csak olyan `ThreeWayMerge` esetén fut, amelyet `CellMergeConfig` értékkel hoztak létre. Enélkül minden olyan oszlop, amelyet mindkét oldal eltérő értékre módosított, `update_update` ütközésnek számít.

A `Merql` homlokzat nem fogad el konfigurációt: a `Merql::merge()`, `Merql::patch()` és `Merql::plan()` konfiguráció nélkül hozza létre a `new ThreeWayMerge()` példányát, a CLI `merge` parancsa pedig a `Merql::merge()` útján fut. Ezek mindig oszlopszinten egyesítenek ([#7](https://github.com/inline0/merql/issues/7)).

Ha a homlokzat vagy a CLI által mentett pillanatképeket cellaszintű egyesítéssel szeretné egyesíteni, töltse be őket, majd hívja meg saját maga a `ThreeWayMerge` műveletet. Az eredmény egy szokványos `MergeResult`, ezért a homlokzat továbbra is alkalmazhatja, a `MergePlanBuilder` pedig [egyesítési tervvé](https://merql.inline0.dev/hu/docs/merge/plans) alakítja:

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

A cellaegyesítők csak azoknál az oszlopoknál futnak le, amelyeket mindkét oldal módosított. Ha csak az egyik oldal módosított egy oszlopot, az értéke változatlanul kerül át.

## CellMergeConfig

A `CellMergeConfig` oszlopokat vagy oszloptípusokat rendel cellaegyesítő-megvalósításokhoz. A `auto()` gyár alapértelmezésként ésszerű beállításokat ad:

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

Az egyesítő feloldásának prioritása:

1. Minősített oszlopnév (`table.column`).
2. Nem minősített oszlopnév (`column`).
3. Oszloptípus szerinti illesztés (kis- és nagybetűket figyelmen kívül hagyó `str_contains`).
4. Alapértelmezés: `OpaqueCellMerger` (eltérő értékek esetén mindig ütközést jelez).

## TextCellMerger

Többsoros szöveget egyesít a Myers-féle különbségvizsgálattal, ugyanazzal az algoritmussal, amelyet a git használ a fájltartalomhoz. Minden sor önálló egységnek számít, így a különböző sorok módosításai ütközésmentesen egyesíthetők.

```php
use Merql\CellMerge\TextCellMerger;

$merger = new TextCellMerger();
```

### Példa: ütközésmentes szövegegyesítés

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

Különböző sorokat szerkesztettek. Az egyesítés eredménye `"LINE ONE\nline two\nLINE THREE"`, ütközés nélkül.

### Példa: szövegütközés

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

Mindkét oldal ugyanazt a sort módosította eltérő értékre. Az egyesítő ütközési eredményt ad vissza. Az egyesített tartalom a gitben használthoz hasonló ütközésjelölőket tartalmaz.

### Működés

A `TextCellMerger` a pitmaster `ThreeWayMerge::merge()` megoldására bízza a feladatot, amely Myers-féle különbségvizsgálattal összehasonlítja a base állapotot mindkét oldallal, majd egyesíti a két módosítási listát. Ha a módosítások ugyanazon a sorszakaszon fedik egymást, ütközést jelez.

## JsonCellMerger

JSON objektumokat egyesít úgy, hogy a legfelső szintű kulcsokat külön-külön hasonlítja össze. Minden kulcs egyesítése a szokásos háromutas szabályok szerint történik.

```php
use Merql\CellMerge\JsonCellMerger;

$merger = new JsonCellMerger();
```

### Példa: ütközésmentes JSON egyesítés

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

A különböző kulcsok módosultak. Az egyesítés eredménye:

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

### Példa: JSON ütközés

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

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

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

Mindkét oldal ugyanazt az `color` kulcsot módosította, eltérő értékre. Ez ütközés. Az egyesítő alapértelmezés szerint az ours értékét választja, és egy ütközést jelez.

### Kulcsszintű szabályok

| Base | Ours | Theirs | Eredmény |
|---|---|---|---|
| Kulcs változatlan | Kulcs változatlan | Az alap elfogadása |
| Kulcs változatlan | Kulcs módosult | A theirs elfogadása |
| Kulcs módosult | Kulcs változatlan | Az ours elfogadása |
| Kulcs módosult (azonos) | Kulcs módosult (azonos) | Elfogadás (egyezés) |
| Kulcs módosult (eltérő) | Kulcs módosult (eltérő) | **Ütközés** (alapértelmezés: ours) |
| Kulcs létezik | Kulcs létezik | Kulcs eltávolítva | Az eltávolítás elfogadása |
| Kulcs létezik | Kulcs eltávolítva | Kulcs létezik | Az eltávolítás elfogadása |
| Kulcs hiányzik | Kulcs hozzáadva | Kulcs hiányzik | A hozzáadás elfogadása |
| Kulcs hiányzik | Kulcs hiányzik | Kulcs hozzáadva | A hozzáadás elfogadása |

A beágyazott objektumokat átlátszatlan JSON karakterláncként hasonlítja össze, rekurzió nélkül. Ha egy beágyazott objektum eltér, a teljes kulcsértéket hasonlítja össze, nem annak alkulcsait.

### Követelmények

Mindhárom értéknek érvényes JSON objektumnak vagy tömbnek kell lennie. Ha bármelyik érték nem érvényes JSON, vagy JSON skalár (karakterlánc, szám, logikai érték), az egyesítő ütközésre vált.

## OpaqueCellMerger

Az alapértelmezett egyesítő, ha nincs beállítva cellaszintű stratégia. Az oszlopértéket átlátszatlan adatként kezeli, és mindig ütközést jelez, ha a két oldal eltér.

```php
use Merql\CellMerge\OpaqueCellMerger;

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

## CellMergeResult

Minden cellaegyesítő egy `CellMergeResult` értéket ad vissza:

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

Gyári metódusok:

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

## Egyéni cellaegyesítők

Saját egyesítési stratégia hozzáadásához valósítsa meg a `CellMerger` interfészt:

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