Kolme osapoole liitmine
Kolme osapoole liitmisel võrreldakse kahte muudatuste kogumit ühise eelkäijaga ja ühendatakse need üheks tulemuseks. merql rakendab seda algoritmi failiridade asemel andmebaasiridadele.
use Merql\Merge\ThreeWayMerge;
$merge = new ThreeWayMerge();
$result = $merge->merge($base, $ours, $theirs);
echo $result->operationCount(); // resolved operations
echo $result->conflictCount(); // unresolved conflicts
Kolm olekut
Iga liitmine nõuab täpselt kolme hetktõmmist:
- Alus -- andmebaasi olek hetkel, mil mõlemad harud lahknesid.
- Meie -- üks haru pärast muudatusi, näiteks muudetud liivakastikoopia.
- Nende -- teine haru pärast muudatusi, näiteks tööd jätkanud aktiivne andmebaas.
Meie ja nende hetktõmmis tuleb jäädvustada vastava haru andmebaasist. Liitmisreeglid käsitlevad mõlemat poolt võrdselt; nimetused määravad iga tehte source, konfliktitüüpide nimed (update_delete tähendab, et meie uuendasime ja nemad kustutasid) ning selle, kumba poolt eelistavad ConflictPolicy::OursWins ja TheirsWins.
Base (snapshot at fork time)
/ \
Ours (our changes) Theirs (their changes)
\ /
--------- MERGE ----------
|
Merged result
+ conflicts (if any)
Ilma ühise aluseta ei saa eristada „lisatud” ja „muutmata” väärtust. Kahesuunaline võrdlus näitab, et poolel A on väärtus ja poolel B seda pole, kuid ei tuvasta, kas A lisas selle või B kustutas selle. Alus annab sellele küsimusele vastuse.
Kuidas muudatuskogumid arvutatakse
Differ võrdleb iga hetktõmmist alusega, et luua kaks muudatuskogumit. Iga muudatuskogum sisaldab kolme tüüpi tehteid:
| Alus | Praegune | Tehe |
|---|---|---|
| Rida on olemas | Rida on olemas, sama sõrmejälg | Muutusi pole |
| Rida on olemas | Rida on olemas, erinev sõrmejälg | UPDATE (veeruvahe) |
| Rida on olemas | Rida puudub | DELETE |
| Rida puudub | Rida on olemas | INSERT |
Sõrmejäljed (SHA-256 normaliseeritud rea sisu räsi) pakuvad kiiret teed. Kui kahe sõrmejälje väärtus on sama, pole rida muutunud ja veerutaseme võrdlust pole vaja.
use Merql\Diff\Differ;
$differ = new Differ();
$oursChangeset = $differ->diff($base, $ours);
$theirsChangeset = $differ->diff($base, $theirs);
Liitmisreeglid
Seejärel liidetakse kaks muudatuskogumit tehe tehte haaval. Järgmine tabel määrab tulemuse iga kombinatsiooni korral:
| Meie | Nemad | Tulemus |
|---|---|---|
| Muutusi pole | Muutusi pole | Jäta alus alles |
| Muutusi pole | Uuendatud | Võta nende muudatus vastu |
| Uuendatud | Muutusi pole | Võta meie muudatus vastu |
| Uuendatud (sama väärtus) | Uuendatud (sama väärtus) | Võta vastu (mõlemad nõustuvad) |
| Uuendatud (erinev väärtus) | Uuendatud (erinev väärtus) | Konflikt |
| Muutusi pole | Kustutatud | Võta kustutamine vastu |
| Kustutatud | Muutusi pole | Võta kustutamine vastu |
| Kustutatud | Kustutatud | Võta kustutamine vastu (mõlemad nõustuvad) |
| Uuendatud | Kustutatud | Konflikt |
| Kustutatud | Uuendatud | Konflikt |
| Lisatud (ainult meie) | -- | Võta lisamine vastu |
| -- | Lisatud (ainult nemad) | Võta lisamine vastu |
| Lisatud (sama PK) | Lisatud (sama PK) | Konflikt |
Kui mõlemad pooled uuendavad sama rida, ei kuuluta merql seda kohe konfliktseks. See liigub veerutaseme liitmise juurde, võrreldes iga veergu eraldi. Üksikasju vaadake jaotisest Veerutaseme liitmine.
Fassadi kasutamine
Merql-fassad töötab nimega hetktõmmistega, mis on kettale salvestatud. Enne hetktõmmise loomist kutsuge Merql::init() välja igas olekus olevat andmebaasi kasutades:
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');
Ärge jäädvustage kõiki kolme hetktõmmist järjest ühest andmebaasist. Nende hetktõmmis sisaldaks siis ka meie muudatusi, mistõttu paistaks iga meie lisatud rida olevat lisatud mõlemal poolel ja tekitaks konflikti insert_insert.
Merql::merge(), Merql::patch() ja Merql::plan() loovad ThreeWayMerge ilma objektita CellMergeConfig, seega toimub liitmine ainult veerutasemel. Lahtritaseme liitmiseks laadige hetktõmmised ja kasutage otse allpool näidatud klassi.
Klasside otsekasutus
Suurema kontrolli saamiseks looge komponendid ise. Snapshotter::capture() ei salvesta midagi, seega hoidke hetktõmmiseid mälus või salvestage need SnapshotStore::save() abil:
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'),
);
Kiirtee paiga rakendamiseks
Kui muudatusi on ainult ühel poolel (samaaegseid muudatusi pole), kasutage patch(). See on kahesuunaline liitmine, kus meie võrdub alusega, seega konflikte tekkida ei saa:
$result = $merge->patch($base, $changes);
// Always clean: ours is identical to base.
Fassaadi kaudu:
$result = Merql::patch('base', 'changes');
patch() on samaväärne objektiga merge($base, $base, $changes). Kuna meie haru ei lahknenud kunagi alusest, võetakse kõik nende muudatused konfliktita vastu.
Skeemierinevuste tuvastamine
Kui hetktõmmiste vahel tabelisse veerge lisati või sealt eemaldati, jätkub liitmine, kuid erinevused salvestatakse. Muudetud veerutüüpe ei võrrelda ja ümbernimetatud veerg kuvatakse ühe eemaldatud ning ühe lisatud veeruna; vaadake jaotist Skeemi valideerimine.
$result = $merge->merge($base, $ours, $theirs);
if ($result->hasSchemaMismatches()) {
foreach ($result->schemaMismatches() as $mismatch) {
echo $mismatch->getMessage() . "\n";
}
}
merql ei migreeri skeeme automaatselt. Skeemimuudatuste eest vastutab kutsuja. Liitmine annab neist teada, et saaksite otsustada, kuidas edasi toimida.
MergeResult
Tulemusobjekt sisaldab kõike vajalikku:
$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
Igal MergeOperation-il on tüüp (insert, update või delete), tabeli nimi, reavõti, veeruväärtused ja allikas (ours, theirs või merged). merged märgib rea, mida mõlemad pooled uuendasid (veerud liideti eraldi) või mille mõlemad kustutasid.
Kus liitmise tulemust rakendada
Liitmise tulemus kirjeldab, kuidas muuta alus liidetud olekuks. Selle tehted sisaldavad mõlema poole muudatusi: nii meie kui ka nende lisatud read ilmuvad lisamistena.
Merql::apply()(võiApplier) käivitab kõik tehted ühendatud andmebaasis. Seda saab puhtalt rakendada andmebaasile, kus on endiselt alus, näiteks aluse värskele koopiale. Kui andmebaasis on juba ühe poole muudatused, nurjub olemasoleva rea lisamine ja tehing pööratakse tagasi.- Meie muudatuste toomiseks andmebaasi, kus on juba nende muudatused, näiteks liivakasti muudatuste aktiivsesse keskkonda viimiseks, rakendage ainult need tehted, mis andmebaasis veel puuduvad: allikaga
ourstehted jamergeduuendused. Kasutage nende valimiseks liitmisplaani ja nende kirjutamiseks koos aktiivse rea eeltingimustega jaotist Kaitstud rakendamine.