Háromutas egyesítés
A háromutas egyesítés két változáshalmazt hasonlít össze egy közös elődhöz képest, majd egyetlen eredménnyé egyesíti őket. A merql ezt az algoritmust fájlsorok helyett adatbázissorokra alkalmazza.
use Merql\Merge\ThreeWayMerge;
$merge = new ThreeWayMerge();
$result = $merge->merge($base, $ours, $theirs);
echo $result->operationCount(); // resolved operations
echo $result->conflictCount(); // unresolved conflicts
A három állapot
Minden egyesítéshez pontosan három pillanatkép szükséges:
- Alap -- az adatbázis állapota azon a ponton, ahol a két ág szétvált.
- Miénk -- az egyik ág az azon végrehajtott változtatások után, például egy szerkesztett sandbox-másolat.
- Övék -- a másik ág az azon végrehajtott változtatások után, például a tovább működő éles adatbázis.
A miénk és az övék pillanatképét egyaránt arról az adatbázisról kell készíteni, amelyik az adott ágat tartalmazza. Az egyesítési szabályok mindkét oldalt egyformán kezelik; az elnevezések határozzák meg az egyes műveletek source, a konfliktustípusok nevét (a update_delete azt jelenti, hogy a miénk frissített, az övék pedig törölt), valamint azt, melyik oldalt részesíti előnyben a ConflictPolicy::OursWins és a TheirsWins.
Base (snapshot at fork time)
/ \
Ours (our changes) Theirs (their changes)
\ /
--------- MERGE ----------
|
Merged result
+ conflicts (if any)
Közös alap nélkül nem lehet megkülönböztetni a „hozzáadott” és a „változatlan” állapotot. A kétirányú összehasonlítás azt látja, hogy az A oldalon van érték, a B oldalon pedig nincs, de nem tudja eldönteni, hogy A adta-e hozzá, vagy B törölte. Erre ad választ az alap.
A változáskészletek kiszámítása
A Differ az alaphoz hasonlítja mindkét pillanatképet, így két változáskészletet hoz létre. Mindegyik változáskészlet háromféle műveletet tartalmaz:
| Alap | Aktuális | Művelet |
|---|---|---|
| A sor létezik | A sor létezik, az ujjlenyomat azonos | Nincs változás |
| A sor létezik | A sor létezik, az ujjlenyomat eltér | UPDATE (oszloponkénti diff) |
| A sor létezik | A sor hiányzik | DELETE |
| A sor hiányzik | A sor létezik | INSERT |
Az ujjlenyomatok (a normalizált sor tartalmának SHA-256 hash-ei) gyorsítják a feldolgozást. Ha két ujjlenyomat egyezik, a sor változatlan, ezért nincs szükség oszlopszintű összehasonlításra.
use Merql\Diff\Differ;
$differ = new Differ();
$oursChangeset = $differ->diff($base, $ours);
$theirsChangeset = $differ->diff($base, $theirs);
Egyesítési szabályok
Ezután a két változáskészletet műveletenként egyesíti a rendszer. Ez a táblázat minden kombináció eredményét meghatározza:
| Miénk | Övék | Eredmény |
|---|---|---|
| Nincs változás | Nincs változás | Alap megtartása |
| Nincs változás | Frissítve | Övék elfogadása |
| Frissítve | Nincs változás | Miénk elfogadása |
| Frissítve (azonos értékre) | Frissítve (azonos értékre) | Elfogadás (mindkét oldal egyetért) |
| Frissítve (eltérő értékre) | Frissítve (eltérő értékre) | Konfliktus |
| Nincs változás | Törölve | Törlés elfogadása |
| Törölve | Nincs változás | Törlés elfogadása |
| Törölve | Törölve | Törlés elfogadása (mindkét oldal egyetért) |
| Frissítve | Törölve | Konfliktus |
| Törölve | Frissítve | Konfliktus |
| Beszúrva (csak a miénk) | -- | Beszúrás elfogadása |
| -- | Beszúrva (csak az övék) | Beszúrás elfogadása |
| Beszúrva (azonos PK-val) | Beszúrva (azonos PK-val) | Konfliktus |
Ha mindkét oldal frissíti ugyanazt a sort, a merql nem jelez azonnal konfliktust. Oszlopszintű egyesítésre vált, és külön-külön hasonlítja össze az oszlopokat. Részletekért lásd: Oszlopszintű egyesítés.
A facade használata
A Merql facade lemezre mentett, névvel ellátott pillanatképekkel dolgozik. A pillanatkép készítése előtt hívd meg a Merql::init() függvényt azzal az adatbázissal, amelyik az adott állapotot tartalmazza:
use Merql\Connection;
use Merql\Merql;
$live = Connection::sqlite('/data/live.sqlite');
$sandbox = Connection::sqlite('/data/sandbox.sqlite');
Merql::init($live);
Merql::snapshot('base'); // before the sandbox is copied from live
// ... copy live.sqlite to sandbox.sqlite, then both databases change ...
Merql::init($sandbox);
Merql::snapshot('ours');
Merql::init($live);
Merql::snapshot('theirs');
$result = Merql::merge('base', 'ours', 'theirs');
Ne ugyanarról az adatbázisról készítsd el egymás után mindhárom pillanatképet. Ekkor az övék már a miénk változtatásait is tartalmazná, így minden, általunk beszúrt sor mindkét oldalon beszúrtként jelenne meg, és insert_insert konfliktusként lenne jelentve.
A Merql::merge(), a Merql::patch() és a Merql::plan() egy ThreeWayMerge hoz létre CellMergeConfig nélkül, ezért csak oszlopszinten egyesítenek. Cellaszintű egyesítéshez töltsd be a pillanatképeket, majd használd közvetlenül az alábbi osztályt.
Az osztályok közvetlen használata
Nagyobb szabadságért példányosítsd magad a komponenseket. A Snapshotter::capture() nem ment semmit, ezért tartsd a pillanatképeket memóriában, vagy mentsd el őket a SnapshotStore::save() segítségével:
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;
use Merql\Snapshot\Snapshotter;
use Merql\Snapshot\SnapshotStore;
$base = (new Snapshotter($live))->capture('base');
// ... copy live to the sandbox, then both databases change ...
$ours = (new Snapshotter($sandbox))->capture('ours');
$theirs = (new Snapshotter($live))->capture('theirs');
$merge = new ThreeWayMerge();
$result = $merge->merge($base, $ours, $theirs);
// Snapshots saved earlier, merged with cell-level merge for TEXT and JSON columns.
$merge = new ThreeWayMerge(CellMergeConfig::auto());
$result = $merge->merge(
SnapshotStore::load('base'),
SnapshotStore::load('ours'),
SnapshotStore::load('theirs'),
);
A patch parancsikon
Ha csak az egyik oldalon vannak változások (nincs párhuzamos módosítás), használd a patch() parancsot. Ez kétutas egyesítés, amelyben a miénk megegyezik az alappal, így konfliktus nem keletkezhet:
$result = $merge->patch($base, $changes);
// Always clean: ours is identical to base.
A facade-on keresztül:
$result = Merql::patch('base', 'changes');
A patch() egyenértékű a merge($base, $base, $changes) használatával. Mivel a miénk soha nem tért el az alaptól, az övék minden változása konfliktus nélkül elfogadható.
Sémaeltérések észlelése
Ha a pillanatképek között oszlopokat adtak hozzá egy táblához vagy távolítottak el belőle, az egyesítés folytatódik, de rögzíti az eltéréseket. A megváltozott oszloptípusokat nem hasonlítja össze, az átnevezett oszlop pedig egy eltávolított és egy hozzáadott oszlopként jelenik meg; lásd: Sémaellenőrzés.
$result = $merge->merge($base, $ours, $theirs);
if ($result->hasSchemaMismatches()) {
foreach ($result->schemaMismatches() as $mismatch) {
echo $mismatch->getMessage() . "\n";
}
}
A merql nem migrálja automatikusan a sémákat. A séma módosítása a hívó felelőssége. Az egyesítés jelenti ezeket, így eldöntheted, hogyan folytasd.
A MergeResult
Az eredményobjektum mindent tartalmaz, amire szükséged van:
$result->isClean(); // true if no conflicts
$result->operations(); // list of MergeOperation objects
$result->conflicts(); // list of Conflict objects
$result->operationCount(); // number of resolved operations
$result->conflictCount(); // number of unresolved conflicts
$result->hasSchemaMismatches(); // true if schemas differ
$result->baseSnapshot(); // the base snapshot used for the merge
Minden MergeOperation típushoz tartozik (insert, update vagy delete), táblanév, sorkulcs, oszlopértékek és forrás (ours, theirs vagy merged). A merged jelöli, ha mindkét oldal frissített egy sort (az oszlopok egyesítésével), vagy mindkét oldal törölte azt.
Mire alkalmazható az egyesítés eredménye
Az egyesítés eredménye azt írja le, hogyan alakítható az alap egyesített állapottá. A műveletei mindkét oldal változásait tartalmazzák: a miénk és az övék által beszúrt sor egyaránt beszúrásként jelenik meg.
- A
Merql::apply()(vagy aApplier) minden műveletet végrehajt a csatlakoztatott adatbázison. Tisztán alkalmazható olyan adatbázisra, amely még az alapállapotot tartalmazza, például az alapállapot friss másolatára. Ha olyan adatbázison fut, amely már tartalmazza az egyik oldal változtatásait, egy már létező sor beszúrása hibát okoz, és a tranzakció visszagörgetésre kerül. - Ahhoz, hogy a miénk változásait olyan adatbázisba vidd át, amely már az övék állapotát tartalmazza (például sandbox-módosítások éles környezetbe emeléséhez), csak az adatbázisból hiányzó műveleteket alkalmazd: a
oursforrásúakat és amergedfrissítéseket. A kiválasztásukhoz használj egyesítési tervet, az éles sorokra vonatkozó előfeltételekkel történő íráshoz pedig Védett alkalmazást.