
# Merge a livello di cella

Il merge a livello di colonna rileva quando entrambi i lati hanno modificato la stessa colonna. Quello a livello di cella scende più in profondità e unisce i contenuti all'interno della colonna. Due persone che modificano righe diverse di un campo TEXT, o chiavi diverse di un oggetto JSON, possono unire le modifiche senza conflitti.

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

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

## Abilitazione del merge a livello di cella

Il merge a livello di cella è facoltativo. Viene eseguito solo con un `ThreeWayMerge` costruito con un `CellMergeConfig`. Senza quest'ultimo, ogni colonna modificata da entrambi i lati con valori diversi genera un conflitto `update_update`.

La facciata `Merql` non accetta una configurazione: `Merql::merge()`, `Merql::patch()` e `Merql::plan()` costruiscono `new ThreeWayMerge()` senza configurazione, e il comando `merge` di CLI passa per `Merql::merge()`. Eseguono sempre il merge a livello di colonna ([#7](https://github.com/inline0/merql/issues/7)).

Per eseguire un merge a livello di cella sugli snapshot salvati dalla facciata o da CLI, caricateli e chiamate direttamente `ThreeWayMerge`. Il risultato è un normale `MergeResult`, quindi la facciata può comunque applicarlo e `MergePlanBuilder` lo trasforma in un [piano di merge](https://merql.inline0.dev/it/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);
```

I merger di cella vengono eseguiti solo per le colonne modificate da entrambi i lati. Se una colonna è stata modificata da un solo lato, il suo valore viene mantenuto così com'è.

## CellMergeConfig

`CellMergeConfig` associa colonne o tipi di colonna alle implementazioni dei merger di cella. La factory `auto()` fornisce valori predefiniti ragionevoli:

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

Priorità di risoluzione dei merger:

1. Nome di colonna qualificato (`table.column`).
2. Nome di colonna non qualificato (`column`).
3. Corrispondenza del tipo di colonna (`str_contains` senza distinzione tra maiuscole e minuscole).
4. Predefinito: `OpaqueCellMerger` (se i valori differiscono, segnala sempre un conflitto).

## TextCellMerger

Unisce testi su più righe usando il diff di Myers, lo stesso algoritmo usato da git per i contenuti dei file. Ogni riga viene trattata come un'unità e le modifiche a righe diverse vengono unite senza conflitti.

```php
use Merql\CellMerge\TextCellMerger;

$merger = new TextCellMerger();
```

### Esempio: merge di testo senza conflitti

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

Sono state modificate righe diverse. Il merge produce `"LINE ONE\nline two\nLINE THREE"` senza conflitti.

### Esempio: conflitto nel testo

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

Entrambi i lati hanno modificato la stessa riga assegnandole valori diversi. Il merger restituisce un risultato con conflitto. Il contenuto unito include marcatori di conflitto simili a quelli di git.

### Come funziona

`TextCellMerger` delega a `ThreeWayMerge::merge()` di pitmaster, che calcola un diff di Myers tra la base e ciascun lato, poi combina i due script di modifica. Se le modifiche si sovrappongono nella stessa regione di righe, segnala un conflitto.

## JsonCellMerger

Unisce gli oggetti JSON confrontando separatamente le chiavi di primo livello. Ogni chiave viene unita con le normali regole a tre vie.

```php
use Merql\CellMerge\JsonCellMerger;

$merger = new JsonCellMerger();
```

### Esempio: merge JSON senza conflitti

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

Sono state modificate chiavi diverse. Il risultato del merge è:

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

### Esempio: conflitto JSON

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

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

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

Entrambi i lati hanno modificato la chiave `color` assegnandole valori diversi. Si tratta di un conflitto. Per impostazione predefinita, il merger mantiene ours e segnala un conflitto.

### Regole a livello di chiave

| Base | Ours | Theirs | Risultato |
|---|---|---|---|
| Chiave invariata | Chiave invariata | Accetta la base |
| Chiave invariata | Chiave modificata | Accetta theirs |
| Chiave modificata | Chiave invariata | Accetta ours |
| Chiave modificata (uguale) | Chiave modificata (uguale) | Accetta (concordano) |
| Chiave modificata (diversa) | Chiave modificata (diversa) | **Conflitto** (predefinito: ours) |
| Chiave presente | Chiave presente | Chiave rimossa | Accetta la rimozione |
| Chiave presente | Chiave rimossa | Chiave presente | Accetta la rimozione |
| Chiave assente | Chiave aggiunta | Chiave assente | Accetta l'aggiunta |
| Chiave assente | Chiave assente | Chiave aggiunta | Accetta l'aggiunta |

Gli oggetti annidati vengono confrontati come stringhe JSON opache, non in modo ricorsivo. Se un oggetto annidato è diverso, viene confrontato l'intero valore della chiave, non le sue sottochiavi.

### Requisiti

Tutti e tre i valori devono essere oggetti o array JSON validi. Se un valore non è JSON valido o è uno scalare JSON (stringa, numero, booleano), il merger ripiega su un conflitto.

## OpaqueCellMerger

Il merger predefinito quando non è configurata alcuna strategia a livello di cella. Tratta il valore della colonna come un blocco opaco e segnala sempre un conflitto quando entrambi i lati lo modificano.

```php
use Merql\CellMerge\OpaqueCellMerger;

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

## CellMergeResult

Tutti i merger di cella restituiscono un `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
```

Metodi factory:

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

## Merger di cella personalizzati

Implementate l'interfaccia `CellMerger` per aggiungere una strategia di merge personalizzata:

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