Ga naar de inhoud

Samenvoeging op celniveau

Een samenvoeging op kolomniveau detecteert wanneer beide kanten dezelfde kolom hebben gewijzigd. Een samenvoeging op celniveau gaat dieper en voegt de inhoud binnen die kolom samen. Twee mensen die verschillende regels in een TEXT-veld of verschillende sleutels in een JSON-object bewerken, kunnen hun wijzigingen probleemloos samenvoegen.

PHP
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;

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

Samenvoeging op celniveau inschakelen

Samenvoeging op celniveau is optioneel. Deze wordt alleen uitgevoerd in een ThreeWayMerge die is samengesteld met een CellMergeConfig. Zonder zo'n configuratie levert elke kolom die beide kanten in verschillende waarden hebben gewijzigd een update_update-conflict op.

De Merql-gevel accepteert geen configuratie: Merql::merge(), Merql::patch() en Merql::plan() maken een new ThreeWayMerge() zonder configuratie, en de opdracht merge van de CLI loopt via Merql::merge(). Ze voegen altijd samen op kolomniveau (#7).

Om snapshots die zijn opgeslagen via de gevel of de CLI samen te voegen met samenvoeging op celniveau, laad je ze en roep je zelf ThreeWayMerge aan. Het resultaat is een gewone MergeResult, dus de gevel kan deze nog steeds toepassen, en MergePlanBuilder zet deze om in een samenvoegplan:

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

Samenvoegers voor cellen worden alleen uitgevoerd voor kolommen die beide kanten hebben gewijzigd. Als slechts één kant een kolom heeft gewijzigd, wordt de waarde ervan ongewijzigd overgenomen.

CellMergeConfig

CellMergeConfig koppelt kolommen of kolomtypen aan implementaties van samenvoegers voor cellen. De auto()-factory biedt redelijke standaardwaarden:

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

Prioriteit bij het kiezen van een samenvoeger:

  1. Volledig gekwalificeerde kolomnaam (table.column).
  2. Niet-gekwalificeerde kolomnaam (column).
  3. Kolomtype op basis van een patroon (niet hoofdlettergevoelig str_contains).
  4. Standaard: OpaqueCellMerger (meldt altijd een conflict wanneer waarden verschillen).

TextCellMerger

Voegt tekst van meerdere regels samen met Myers diff, hetzelfde algoritme dat git gebruikt voor bestandsinhoud. Elke regel wordt als een eenheid behandeld en wijzigingen aan verschillende regels worden probleemloos samengevoegd.

PHP
use Merql\CellMerge\TextCellMerger;

$merger = new TextCellMerger();

Voorbeeld: tekst probleemloos samenvoegen

Code
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

Er zijn verschillende regels bewerkt. De samenvoeging levert "LINE ONE\nline two\nLINE THREE" op, zonder conflict.

Voorbeeld: tekstconflict

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

Beide kanten hebben dezelfde regel gewijzigd in verschillende waarden. De samenvoeger retourneert een conflicteresultaat. De samengevoegde inhoud bevat conflictmarkeringen die lijken op die van git.

Werking

TextCellMerger gebruikt de ThreeWayMerge::merge() van pitmaster, die een Myers diff berekent tussen base en elke kant en vervolgens beide bewerkingsscripts combineert. Als bewerkingen elkaar overlappen in hetzelfde regelgebied, wordt een conflict gemeld.

JsonCellMerger

Voegt JSON-objecten samen door de sleutels op het hoogste niveau afzonderlijk te vergelijken. Elke sleutel wordt samengevoegd volgens de standaardregels voor drieweg-samenvoeging.

PHP
use Merql\CellMerge\JsonCellMerger;

$merger = new JsonCellMerger();

Voorbeeld: JSON probleemloos samenvoegen

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}

Er zijn verschillende sleutels gewijzigd. Het samengevoegde resultaat is:

JSON
{"name": "Bob", "role": "admin", "active": true}

Voorbeeld: JSON-conflict

JSON
// Base
{"color": "red"}

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

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

Beide kanten hebben de color-sleutel gewijzigd in verschillende waarden. Dit is een conflict. De samenvoeger kiest standaard ours en meldt één conflict.

Regels op sleutelniveau

Basis Ours Theirs Resultaat
Sleutel ongewijzigd Sleutel ongewijzigd Basis accepteren
Sleutel ongewijzigd Sleutel gewijzigd Theirs accepteren
Sleutel gewijzigd Sleutel ongewijzigd Ours accepteren
Sleutel gewijzigd (zelfde) Sleutel gewijzigd (zelfde) Accepteren (komen overeen)
Sleutel gewijzigd (anders) Sleutel gewijzigd (anders) Conflict (standaard: ours)
Sleutel bestaat Sleutel bestaat Sleutel verwijderd Verwijdering accepteren
Sleutel bestaat Sleutel verwijderd Sleutel bestaat Verwijdering accepteren
Sleutel ontbreekt Sleutel toegevoegd Sleutel ontbreekt Toevoeging accepteren
Sleutel ontbreekt Sleutel ontbreekt Sleutel toegevoegd Toevoeging accepteren

Geneste objecten worden vergeleken als ondoorzichtige JSON-strings, niet recursief. Als een genest object verschilt, wordt de volledige waarde van die sleutel vergeleken, niet de subsleutels.

Vereisten

Alle drie de waarden moeten geldige JSON-objecten of arrays zijn. Als een waarde geen geldige JSON is, of een scalaire JSON-waarde is (string, getal, boolean), valt de samenvoeger terug op een conflict.

OpaqueCellMerger

De standaardmerger wanneer er geen strategie op celniveau is ingesteld. Deze behandelt de kolomwaarde als een ondoorzichtige blob en meldt altijd een conflict wanneer beide kanten verschillen.

PHP
use Merql\CellMerge\OpaqueCellMerger;

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

CellMergeResult

Alle celmergers retourneren een 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

Factorymethoden:

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

Aangepaste celmergers

Implementeer de interface CellMerger om je eigen samenvoegstrategie toe te voegen:

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