Sloučení na úrovni buněk
Sloučení na úrovni sloupců rozpozná, když obě strany změnily stejný sloupec. Sloučení na úrovni buněk jde ještě dál a slučuje obsah uvnitř daného sloupce. Dva lidé mohou bez problémů sloučit změny, když upravují různé řádky pole TEXT nebo různé klíče objektu JSON.
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;
$config = CellMergeConfig::auto();
$merge = new ThreeWayMerge($config);
$result = $merge->merge($base, $ours, $theirs);
Zapnutí slučování na úrovni buněk
Sloučení na úrovni buněk je volitelné. Probíhá pouze v instanci ThreeWayMerge vytvořené s CellMergeConfig. Bez něj každá změna téhož sloupce na různé hodnoty na obou stranách představuje konflikt update_update.
Rozhraní Merql nepřijímá konfiguraci: Merql::merge(), Merql::patch() a Merql::plan() vytvářejí new ThreeWayMerge() bez ní a příkaz merge v CLI prochází přes Merql::merge(). Vždy slučují pouze na úrovni sloupců (#7).
Chcete-li snímky uložené pomocí rozhraní nebo CLI sloučit na úrovni buněk, načtěte je a sami zavolejte ThreeWayMerge. Výsledkem je běžné MergeResult, které může rozhraní stále použít, a MergePlanBuilder z něj vytvoří plán sloučení:
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);
Slučování buněk probíhá pouze u sloupců změněných na obou stranách. Pokud sloupec změnila jen jedna strana, jeho hodnota se převezme beze změny.
CellMergeConfig
CellMergeConfig mapuje sloupce nebo typy sloupců na implementace slučovačů buněk. Továrna auto() poskytuje vhodné výchozí hodnoty:
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());
Pořadí priorit při výběru slučovače:
- Plně kvalifikovaný název sloupce (
table.column). - Nekvalifikovaný název sloupce (
column). - Shoda se vzorem typu sloupce (bez rozlišení velikosti písmen
str_contains). - Výchozí:
OpaqueCellMerger(při rozdílných hodnotách vždy hlásí konflikt).
TextCellMerger
Sloučí víceřádkový text pomocí Myersova diffu, stejného algoritmu, jaký git používá pro obsah souborů. Každý řádek se považuje za jednotku a změny v různých řádcích se sloučí bez problémů.
use Merql\CellMerge\TextCellMerger;
$merger = new TextCellMerger();
Příklad: čisté sloučení textu
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
Byly upraveny různé řádky. Sloučení vytvoří "LINE ONE\nline two\nLINE THREE" bez konfliktu.
Příklad: konflikt v textu
Base: "line one\nline two"
Ours: "changed by us\nline two" // changed line 1
Theirs: "changed by them\nline two" // also changed line 1
Obě strany změnily stejný řádek na různé hodnoty. Slučovací nástroj vrátí výsledek s konfliktem. Sloučený obsah zahrnuje značky konfliktu podobné těm v git.
Jak to funguje
TextCellMerger deleguje na ThreeWayMerge::merge() z pitmaster, který vypočítá Myersův diff mezi základem a každou stranou a poté zkombinuje oba skripty úprav. Pokud se úpravy překrývají ve stejné oblasti řádků, nahlásí konflikt.
JsonCellMerger
Sloučí objekty JSON porovnáním klíčů nejvyšší úrovně zvlášť. Každý klíč se sloučí pomocí standardních pravidel třícestného sloučení.
use Merql\CellMerge\JsonCellMerger;
$merger = new JsonCellMerger();
Příklad: čisté sloučení 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}
Byly změněny různé klíče. Výsledek sloučení je:
{"name": "Bob", "role": "admin", "active": true}
Příklad: konflikt JSON
// Base
{"color": "red"}
// Ours
{"color": "blue"}
// Theirs
{"color": "green"}
Obě strany změnily klíč color na různé hodnoty. Jde o konflikt. Slučovací nástroj ve výchozím nastavení upřednostní naši verzi a nahlásí jeden konflikt.
Pravidla na úrovni klíčů
| Základ | Naše | Jejich | Výsledek |
|---|---|---|---|
| Klíč beze změny | Klíč beze změny | Přijmout základ | |
| Klíč beze změny | Klíč změněn | Přijmout jejich | |
| Klíč změněn | Klíč beze změny | Přijmout naši verzi | |
| Klíč změněn (stejně) | Klíč změněn (stejně) | Přijmout (shoda) | |
| Klíč změněn (jinak) | Klíč změněn (jinak) | Konflikt (výchozí: naše verze) | |
| Klíč existuje | Klíč existuje | Klíč odstraněn | Přijmout odstranění |
| Klíč existuje | Klíč odstraněn | Klíč existuje | Přijmout odstranění |
| Klíč chybí | Klíč přidán | Klíč chybí | Přijmout přidání |
| Klíč chybí | Klíč chybí | Klíč přidán | Přijmout přidání |
Vnořené objekty se porovnávají jako neprůhledné řetězce JSON, nikoli rekurzivně. Pokud se vnořený objekt liší, porovnává se celá hodnota daného klíče, nikoli jeho podklíče.
Požadavky
Všechny tři hodnoty musí být platné objekty nebo pole JSON. Pokud některá hodnota není platné JSON nebo jde o skalární hodnotu JSON (řetězec, číslo, logická hodnota), slučovací nástroj přejde na konflikt.
OpaqueCellMerger
Výchozí slučovací nástroj, pokud není nakonfigurována strategie na úrovni buněk. S hodnotou sloupce zachází jako s neprůhledným blokem a při rozdílu mezi oběma stranami vždy hlásí konflikt.
use Merql\CellMerge\OpaqueCellMerger;
$merger = new OpaqueCellMerger();
$result = $merger->merge($base, $ours, $theirs);
// $result->clean is always false when $ours !== $theirs
CellMergeResult
Všechny slučovací nástroje buněk vracejí CellMergeResult:
$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
Metody továrny:
CellMergeResult::resolved($value); // clean merge
CellMergeResult::conflict($oursValue, $n); // n conflicts, defaults to ours
Vlastní slučovací nástroje buněk
Implementujte rozhraní CellMerger a přidejte vlastní strategii sloučení:
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());