
# Fusion au niveau des colonnes

La fusion au niveau des colonnes est le principal avantage de merql par rapport à la comparaison naïve au niveau des lignes. Lorsque les deux côtés modifient la même ligne, merql compare chaque colonne séparément au lieu de signaler la ligne entière comme conflit.

## Le problème de la fusion au niveau des lignes

Considérez une table `posts` dans laquelle deux personnes modifient la même ligne :

```
Base:    post #42  { title: "Hello",     content: "Body",    status: "draft"   }
Ours:    post #42  { title: "Hello",     content: "Body v2", status: "draft"   }
Theirs:  post #42  { title: "New Title", content: "Body",    status: "publish" }
```

Une fusion au niveau des lignes constate que les deux côtés ont modifié la ligne et déclare un conflit. Pourtant, leurs modifications ne se chevauchent pas. Nous avons modifié `content` ; eux ont modifié `title` et `status`.

## Résolution par la fusion au niveau des colonnes

merql applique les mêmes règles de fusion à trois voies à chaque colonne séparément :

```
title:   base="Hello", ours="Hello",  theirs="New Title"  -> accept theirs
content: base="Body",  ours="Body v2", theirs="Body"      -> accept ours
status:  base="draft", ours="draft",  theirs="publish"    -> accept theirs
```

Résultat : `{ title: "New Title", content: "Body v2", status: "publish" }`. Fusion sans conflit.

## Fonctionnement

Lorsque `ThreeWayMerge` détecte que les deux côtés ont mis à jour la même ligne, il délègue à `ColumnMerge::merge()` :

```php
use Merql\Merge\ColumnMerge;

$result = ColumnMerge::merge(
    table: 'posts',
    rowKey: '42',
    base: ['title' => 'Hello', 'content' => 'Body', 'status' => 'draft'],
    ours: ['title' => 'Hello', 'content' => 'Body v2', 'status' => 'draft'],
    theirs: ['title' => 'New Title', 'content' => 'Body', 'status' => 'publish'],
);

// $result['values']    -> merged column values
// $result['conflicts'] -> list of Conflict objects (empty if clean)
```

Les mêmes règles s'appliquent à chaque colonne :

| Base | Nous | Eux | Résultat |
|---|---|---|---|
| A | A | A | Conserver la base (aucune modification) |
| A | A | B | Accepter leur valeur |
| A | B | A | Accepter notre valeur |
| A | B | B | Accepter (accord) |
| A | B | C | **Conflit** (valeurs différentes) |

## Quand des conflits surviennent malgré tout

Un conflit au niveau des colonnes survient lorsque les deux côtés modifient la même colonne avec des valeurs différentes :

```
Base:    post #42  { title: "Hello" }
Ours:    post #42  { title: "Welcome" }
Theirs:  post #42  { title: "Greetings" }
```

Les deux côtés ont modifié `title` avec des valeurs différentes. C'est un conflit réel qui nécessite une résolution.

```php
$result = ColumnMerge::merge(
    'posts', '42',
    ['title' => 'Hello'],
    ['title' => 'Welcome'],
    ['title' => 'Greetings'],
);

// $result['conflicts'] contains one Conflict:
//   table: "posts", column: "title"
//   oursValue: "Welcome", theirsValue: "Greetings", baseValue: "Hello"
```

## Conflits partiels

Une même ligne peut contenir des fusions sans conflit et des conflits. Si une colonne fusionne sans conflit et une autre est en conflit, le `MergeOperation` de la ligne contient les valeurs fusionnées pour les colonnes sans conflit et, par défaut, notre valeur pour les colonnes en conflit. Le conflit est tout de même signalé séparément.

```
Base:    { title: "Hello", content: "Body",    status: "draft" }
Ours:    { title: "Welcome", content: "Body v2", status: "draft" }
Theirs:  { title: "Greetings", content: "Body",    status: "publish" }
```

Ici, `title` est en conflit (les deux côtés l'ont modifié différemment), `content` fusionne sans conflit (seul notre côté l'a modifié) et `status` fusionne sans conflit (seul leur côté l'a modifié). L'opération contient :

```php
// values: { title: "Welcome", content: "Body v2", status: "publish" }
// One conflict on the "title" column.
```

## Comparaison des valeurs

Les valeurs des colonnes sont converties en chaînes avant d'être comparées. `NULL` est une valeur distincte :

- `NULL` vers `NULL` = aucun changement.
- `NULL` vers `"hello"` = mise à jour.
- `"hello"` vers `NULL` = mise à jour.
- `"1"` vers `1` = aucun changement (les deux sont convertis en chaîne `"1"`).

Cela correspond à la sémantique de récupération des valeurs de l'adaptateur de connexion MerQL : toutes les valeurs de la base de données sont renvoyées sous forme de chaînes (ou de NULL).

## Fusion au niveau des cellules

Lorsque la fusion au niveau des colonnes produit malgré tout un conflit (les deux côtés ont modifié la même colonne avec des valeurs différentes), merql peut aller plus loin avec la [fusion au niveau des cellules](https://merql.inline0.dev/fr/docs/merge/cell-level). Cette fonction s'applique aux colonnes TEXT (diff ligne par ligne) et aux colonnes JSON (diff clé par clé), pour résoudre les cas où les deux côtés ont modifié différentes parties de la même valeur de colonne.
