
# Sammanslagning på cellnivå

Sammanslagning på kolumnnivå upptäcker när båda sidorna har ändrat samma kolumn. Sammanslagning på cellnivå går djupare och slår samman innehållet i kolumnen. Två personer som redigerar olika rader i ett fält av typen TEXT, eller olika nycklar i ett objekt av typen JSON, kan slå samman ändringarna utan konflikt.

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

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

## Aktivera sammanslagning på cellnivå

Sammanslagning på cellnivå är valfri. Den körs endast i en `ThreeWayMerge` som konstruerats med en `CellMergeConfig`. Utan en sådan är varje kolumn som båda sidor ändrat till olika värden en `update_update`-konflikt.

Gränssnittet `Merql` tar ingen konfiguration: `Merql::merge()`, `Merql::patch()` och `Merql::plan()` konstruerar `new ThreeWayMerge()` utan någon, och kommandot `merge` i CLI går via `Merql::merge()`. De slår alltid samman på kolumnnivå ([#7](https://github.com/inline0/merql/issues/7)).

Om du vill slå samman ögonblicksbilder som sparats av gränssnittet eller CLI med sammanslagning på cellnivå läser du in dem och anropar själv `ThreeWayMerge`. Resultatet är en vanlig `MergeResult`, så gränssnittet kan fortfarande tillämpa det, och `MergePlanBuilder` omvandlar det till en [sammanslagningsplan](https://merql.inline0.dev/sv/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);
```

Cell-sammanslagare körs endast för kolumner som båda sidorna har ändrat. Om bara den ena sidan har ändrat en kolumn används dess värde som det är.

## CellMergeConfig

`CellMergeConfig` mappar kolumner eller kolumntyper till implementationer av cell-sammanslagare. Fabriken `auto()` tillhandahåller lämpliga standardinställningar:

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

Prioritetsordning för sammanslagare:

1. Kvalificerat kolumnnamn (`table.column`).
2. Okvalificerat kolumnnamn (`column`).
3. Matchning av kolumntypmönster (skiftlägesokänslig `str_contains`).
4. Standard: `OpaqueCellMerger` (rapporterar alltid en konflikt när värden skiljer sig).

## TextCellMerger

Slår samman flerradig text med Myers diff, samma algoritm som git använder för filinnehåll. Varje rad behandlas som en enhet, och ändringar på olika rader slås samman utan konflikt.

```php
use Merql\CellMerge\TextCellMerger;

$merger = new TextCellMerger();
```

### Exempel: konfliktfri textsammanslagning

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

Olika rader redigerades. Sammanslagningen ger `"LINE ONE\nline two\nLINE THREE"` utan konflikt.

### Exempel: textkonflikt

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

Båda sidorna ändrade samma rad till olika värden. Sammanslagaren returnerar ett konfliktresultat. Det sammanslagna innehållet innehåller konfliktmarkörer som liknar git.

### Så fungerar det

`TextCellMerger` använder pitmasters `ThreeWayMerge::merge()`, som beräknar en Myers diff mellan basen och varje sida och sedan kombinerar de två redigeringssekvenserna. Om ändringarna överlappar i samma radområde rapporteras en konflikt.

## JsonCellMerger

Slår samman objekt av typen JSON genom att jämföra toppnivånycklar var för sig. Varje nyckel slås samman enligt de vanliga trevägsreglerna.

```php
use Merql\CellMerge\JsonCellMerger;

$merger = new JsonCellMerger();
```

### Exempel: konfliktfri sammanslagning av JSON

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

Olika nycklar ändrades. Det sammanslagna resultatet är:

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

### Exempel: konflikt i JSON

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

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

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

Båda sidorna ändrade nyckeln `color` till olika värden. Det är en konflikt. Sammanslagaren använder som standard vårt värde och rapporterar en konflikt.

### Regler på nyckelnivå

| Bas | Vår | Deras | Resultat |
|---|---|---|---|
| Nyckel oförändrad | Nyckel oförändrad | Acceptera basen |
| Nyckel oförändrad | Nyckel ändrad | Acceptera deras |
| Nyckel ändrad | Nyckel oförändrad | Acceptera vårt |
| Nyckel ändrad (samma) | Nyckel ändrad (samma) | Acceptera (överens) |
| Nyckel ändrad (olika) | Nyckel ändrad (olika) | **Konflikt** (standard: vårt) |
| Nyckel finns | Nyckel finns | Nyckel borttagen | Acceptera borttagningen |
| Nyckel finns | Nyckel borttagen | Nyckel finns | Acceptera borttagningen |
| Nyckel saknas | Nyckel tillagd | Nyckel saknas | Acceptera tillägget |
| Nyckel saknas | Nyckel saknas | Nyckel tillagd | Acceptera tillägget |

Kapslade objekt jämförs som ogenomskinliga strängar av typen JSON, inte rekursivt. Om ett kapslat objekt skiljer sig åt jämförs hela värdet för nyckeln, inte dess undernycklar.

### Krav

Alla tre värden måste vara giltiga objekt eller arrayer av typen JSON. Om något värde inte är giltig JSON, eller är en skalär av typen JSON (sträng, tal, booleskt värde), faller sammanslagaren tillbaka till en konflikt.

## OpaqueCellMerger

Standardsammanslagaren när ingen strategi för cellnivå har konfigurerats. Den behandlar kolumnvärdet som en ogenomskinlig blob och rapporterar alltid en konflikt när de båda sidornas värden skiljer sig åt.

```php
use Merql\CellMerge\OpaqueCellMerger;

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

## CellMergeResult

Alla cell-sammanslagare returnerar en `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
```

Fabriksmetoder:

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

## Anpassade cell-sammanslagare

Implementera gränssnittet `CellMerger` för att lägga till en egen sammanslagningsstrategi:

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