Hoppa till innehåll

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

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:

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

Kod
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

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