
# Συγχώνευση σε επίπεδο κελιού

Η συγχώνευση σε επίπεδο στήλης εντοπίζει πότε άλλαξαν και οι δύο πλευρές την ίδια στήλη. Η συγχώνευση σε επίπεδο κελιού προχωρά βαθύτερα, συγχωνεύοντας το περιεχόμενο μέσα σε εκείνη τη στήλη. Δύο άτομα που επεξεργάζονται διαφορετικές γραμμές ενός πεδίου TEXT ή διαφορετικά κλειδιά ενός αντικειμένου JSON μπορούν να συγχωνεύσουν τις αλλαγές ομαλά.

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

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

## Ενεργοποίηση συγχώνευσης σε επίπεδο κελιού

Η συγχώνευση σε επίπεδο κελιού είναι προαιρετική. Εκτελείται μόνο σε ένα `ThreeWayMerge` που έχει κατασκευαστεί με ένα `CellMergeConfig`. Χωρίς αυτό, κάθε στήλη που άλλαξαν και οι δύο πλευρές σε διαφορετικές τιμές αποτελεί σύγκρουση `update_update`.

Το facade `Merql` δεν δέχεται διαμόρφωση: τα `Merql::merge()`, `Merql::patch()` και `Merql::plan()` κατασκευάζουν `new ThreeWayMerge()` χωρίς διαμόρφωση, ενώ η εντολή `merge` του CLI περνά από το `Merql::merge()`. Πάντα συγχωνεύουν σε επίπεδο στήλης ([#7](https://github.com/inline0/merql/issues/7)).

Για να συγχωνεύσετε στιγμιότυπα που αποθηκεύτηκαν από το facade ή το CLI με συγχώνευση σε επίπεδο κελιού, φορτώστε τα και καλέστε μόνοι σας το `ThreeWayMerge`. Το αποτέλεσμα είναι ένα συνηθισμένο `MergeResult`, επομένως το facade μπορεί και πάλι να το εφαρμόσει, ενώ το `MergePlanBuilder` το μετατρέπει σε [σχέδιο συγχώνευσης](https://merql.inline0.dev/el/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);
```

Οι συγχωνευτές κελιών εκτελούνται μόνο για στήλες που άλλαξαν και οι δύο πλευρές. Όταν άλλαξε τη στήλη μόνο η μία πλευρά, η τιμή της χρησιμοποιείται ως έχει.

## CellMergeConfig

Το `CellMergeConfig` αντιστοιχίζει στήλες ή τύπους στηλών σε υλοποιήσεις συγχωνευτών κελιών. Το εργοστάσιο `auto()` παρέχει εύλογες προεπιλογές:

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

Προτεραιότητα επιλογής συγχωνευτή:

1. Πλήρως προσδιορισμένο όνομα στήλης (`table.column`).
2. Μη προσδιορισμένο όνομα στήλης (`column`).
3. Αντιστοίχιση μοτίβου τύπου στήλης (χωρίς διάκριση πεζών-κεφαλαίων `str_contains`).
4. Προεπιλογή: `OpaqueCellMerger` (δηλώνει πάντα σύγκρουση όταν οι τιμές διαφέρουν).

## TextCellMerger

Συγχωνεύει κείμενο πολλών γραμμών με τη διαφορά Myers, τον ίδιο αλγόριθμο που χρησιμοποιεί το git για το περιεχόμενο αρχείων. Κάθε γραμμή αντιμετωπίζεται ως μονάδα και οι αλλαγές σε διαφορετικές γραμμές συγχωνεύονται ομαλά.

```php
use Merql\CellMerge\TextCellMerger;

$merger = new TextCellMerger();
```

### Παράδειγμα: καθαρή συγχώνευση κειμένου

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

Έγιναν αλλαγές σε διαφορετικές γραμμές. Η συγχώνευση παράγει `"LINE ONE\nline two\nLINE THREE"` χωρίς σύγκρουση.

### Παράδειγμα: σύγκρουση κειμένου

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

Και οι δύο πλευρές άλλαξαν την ίδια γραμμή σε διαφορετικές τιμές. Ο συγχωνευτής επιστρέφει αποτέλεσμα με σύγκρουση. Το συγχωνευμένο περιεχόμενο περιλαμβάνει δείκτες σύγκρουσης παρόμοιους με του git.

### Πώς λειτουργεί

Το `TextCellMerger` χρησιμοποιεί το `ThreeWayMerge::merge()` του pitmaster, το οποίο υπολογίζει διαφορά Myers μεταξύ της βάσης και κάθε πλευράς και, στη συνέχεια, συνδυάζει τα δύο σενάρια επεξεργασίας. Όταν οι αλλαγές επικαλύπτονται στην ίδια περιοχή γραμμών, αναφέρει σύγκρουση.

## JsonCellMerger

Συγχωνεύει αντικείμενα JSON συγκρίνοντας ανεξάρτητα τα κλειδιά ανώτατου επιπέδου. Κάθε κλειδί συγχωνεύεται σύμφωνα με τους συνήθεις κανόνες τριών κατευθύνσεων.

```php
use Merql\CellMerge\JsonCellMerger;

$merger = new JsonCellMerger();
```

### Παράδειγμα: καθαρή συγχώνευση 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}
```

Άλλαξαν διαφορετικά κλειδιά. Το αποτέλεσμα της συγχώνευσης είναι:

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

### Παράδειγμα: σύγκρουση JSON

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

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

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

Και οι δύο πλευρές άλλαξαν το κλειδί `color` σε διαφορετικές τιμές. Πρόκειται για σύγκρουση. Από προεπιλογή, ο συγχωνευτής επιλέγει την τιμή ours και αναφέρει μία σύγκρουση.

### Κανόνες σε επίπεδο κλειδιού

| Βάση | Ours | Theirs | Αποτέλεσμα |
|---|---|---|---|
| Το κλειδί δεν άλλαξε | Το κλειδί δεν άλλαξε | Αποδοχή της βάσης |
| Το κλειδί δεν άλλαξε | Το κλειδί άλλαξε | Αποδοχή του theirs |
| Το κλειδί άλλαξε | Το κλειδί δεν άλλαξε | Αποδοχή του ours |
| Το κλειδί άλλαξε (ίδια τιμή) | Το κλειδί άλλαξε (ίδια τιμή) | Αποδοχή (συμφωνία) |
| Το κλειδί άλλαξε (διαφορετική τιμή) | Το κλειδί άλλαξε (διαφορετική τιμή) | **Σύγκρουση** (προεπιλογή: ours) |
| Το κλειδί υπάρχει | Το κλειδί υπάρχει | Το κλειδί αφαιρέθηκε | Αποδοχή αφαίρεσης |
| Το κλειδί υπάρχει | Το κλειδί αφαιρέθηκε | Το κλειδί υπάρχει | Αποδοχή αφαίρεσης |
| Το κλειδί λείπει | Το κλειδί προστέθηκε | Το κλειδί λείπει | Αποδοχή προσθήκης |
| Το κλειδί λείπει | Το κλειδί λείπει | Το κλειδί προστέθηκε | Αποδοχή προσθήκης |

Τα ένθετα αντικείμενα συγκρίνονται ως αδιαφανείς συμβολοσειρές JSON, όχι αναδρομικά. Αν ένα ένθετο αντικείμενο διαφέρει, συγκρίνεται ολόκληρη η τιμή αυτού του κλειδιού και όχι τα υποκλειδιά του.

### Απαιτήσεις

Και οι τρεις τιμές πρέπει να είναι έγκυρα αντικείμενα ή πίνακες JSON. Αν οποιαδήποτε τιμή δεν είναι έγκυρο JSON ή είναι βαθμωτή τιμή JSON (συμβολοσειρά, αριθμός, λογική τιμή), ο συγχωνευτής καταφεύγει σε σύγκρουση.

## OpaqueCellMerger

Ο προεπιλεγμένος συγχωνευτής όταν δεν έχει διαμορφωθεί στρατηγική σε επίπεδο κελιού. Αντιμετωπίζει την τιμή της στήλης ως αδιαφανές blob και αναφέρει πάντα σύγκρουση όταν διαφέρουν οι τιμές και των δύο πλευρών.

```php
use Merql\CellMerge\OpaqueCellMerger;

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

## CellMergeResult

Όλοι οι συγχωνευτές κελιών επιστρέφουν ένα `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
```

Μέθοδοι εργοστασίου:

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

## Προσαρμοσμένοι συγχωνευτές κελιών

Υλοποιήστε τη διεπαφή `CellMerger` για να προσθέσετε τη δική σας στρατηγική συγχώνευσης:

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