Trijų krypčių sujungimas
Trijų krypčių sujungimas palygina du pakeitimų rinkinius su bendra pradine versija ir sujungia juos į vieną rezultatą. merql šį algoritmą taiko duomenų bazės eilutėms, o ne failų eilutėms.
use Merql\Merge\ThreeWayMerge;
$merge = new ThreeWayMerge();
$result = $merge->merge($base, $ours, $theirs);
echo $result->operationCount(); // resolved operations
echo $result->conflictCount(); // unresolved conflicts
Trys būsenos
Kiekvienam sujungimui reikia tiksliai trijų momentinių vaizdų:
- Bazinė versija -- duomenų bazės būsena tuo metu, kai šakos išsiskyrė.
- Mūsų pusė -- viena šaka po pakeitimų, pavyzdžiui, redaguota smėliadėžės kopija.
- Jų pusė -- kita šaka po pakeitimų, pavyzdžiui, veikusi tiesioginė duomenų bazė.
Mūsų ir jų pusės turi būti užfiksuotos iš duomenų bazės, kurioje saugoma atitinkama šaka. Sujungimo taisyklės abi puses vertina vienodai; pavadinimai nurodo kiekvienos operacijos source, nustato konfliktų tipų pavadinimus (update_delete reiškia, kad mes atnaujinome, o jie ištrynė) ir tai, kurios pusės naudai sprendžiami ConflictPolicy::OursWins ir TheirsWins.
Base (snapshot at fork time)
/ \
Ours (our changes) Theirs (their changes)
\ /
--------- MERGE ----------
|
Merged result
+ conflicts (if any)
Neturint bendros bazinės versijos, neįmanoma atskirti „pridėta“ nuo „nepakeista“. Palyginus dvi versijas matyti, kad A pusė turi reikšmę, o B pusė jos neturi, bet neįmanoma nustatyti, ar A ją pridėjo, ar B ištrynė. Į šį klausimą atsako bazinė versija.
Kaip apskaičiuojami pakeitimų rinkiniai
Differ palygina kiekvieną momentinį vaizdą su bazine versija ir sudaro du pakeitimų rinkinius. Kiekviename pakeitimų rinkinyje yra trijų tipų operacijos:
| Bazė | Dabartinė | Operacija |
|---|---|---|
| Eilutė yra | Eilutė yra, kontrolinės sumos sutampa | Nėra pakeitimų |
| Eilutė yra | Eilutė yra, kontrolinės sumos skiriasi | ATNAUJINTI (skirtumai pagal stulpelius) |
| Eilutė yra | Eilutės nėra | IŠTRINTI |
| Eilutės nėra | Eilutė yra | ĮTERPTI |
Kontrolinės sumos (SHA-256 normalizuotos eilutės turinio maišos) leidžia greitai patikrinti eilutes. Jei dvi kontrolinės sumos sutampa, eilutė nepakito ir nereikia lyginti atskirų stulpelių.
use Merql\Diff\Differ;
$differ = new Differ();
$oursChangeset = $differ->diff($base, $ours);
$theirsChangeset = $differ->diff($base, $theirs);
Sujungimo taisyklės
Tada abu pakeitimų rinkiniai sujungiami operacija po operacijos. Šioje lentelėje apibrėžtas rezultatas kiekvienam deriniui:
| Mūsų pakeitimai | Jų pakeitimai | Rezultatas |
|---|---|---|
| Nėra pakeitimų | Nėra pakeitimų | Palikti bazinę versiją |
| Nėra pakeitimų | Atnaujinta | Priimti jų pakeitimą |
| Atnaujinta | Nėra pakeitimų | Priimti mūsų pakeitimą |
| Atnaujinta (ta pati reikšmė) | Atnaujinta (ta pati reikšmė) | Priimti (abi pusės sutaria) |
| Atnaujinta (skirtinga reikšmė) | Atnaujinta (skirtinga reikšmė) | Konfliktas |
| Nėra pakeitimų | Ištrinta | Priimti ištrynimą |
| Ištrinta | Nėra pakeitimų | Priimti ištrynimą |
| Ištrinta | Ištrinta | Priimti ištrynimą (abi pusės sutaria) |
| Atnaujinta | Ištrinta | Konfliktas |
| Ištrinta | Atnaujinta | Konfliktas |
| Įterpta (tik mūsų pusėje) | -- | Priimti įterpimą |
| -- | Įterpta (tik jų pusėje) | Priimti įterpimą |
| Įterpta (tas pats PK) | Įterpta (tas pats PK) | Konfliktas |
Kai abi pusės atnaujina tą pačią eilutę, merql iškart nepaskelbia konflikto. Jis pereina prie stulpelių lygmens sujungimo ir lygina kiekvieną stulpelį atskirai. Daugiau informacijos rasite skyriuje Sujungimas stulpelių lygmeniu.
Fasado naudojimas
Merql fasadas veikia su pavadintomis momentinėmis kopijomis, įrašytomis diske. Prieš darydami momentinę kopiją, iškvieskite Merql::init() su duomenų baze, kurioje yra kiekviena būsena:
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');
Nedarykite visų trijų momentinių kopijų iš vienos duomenų bazės paeiliui. Tada jų kopijoje taip pat būtų mūsų pakeitimai, todėl kiekviena mūsų įterpta eilutė atrodytų kaip įterpta abiejose pusėse ir būtų pranešta apie insert_insert konfliktą.
Merql::merge(), Merql::patch() ir Merql::plan() sukuria ThreeWayMerge be CellMergeConfig, todėl sujungia tik stulpelių lygmeniu. Jei reikia sujungti langelių lygmeniu, įkelkite momentines kopijas ir naudokite pačią klasę, kaip parodyta toliau.
Tiesioginis klasių naudojimas
Jei reikia daugiau kontrolės, sukurkite komponentų egzempliorius patys. Snapshotter::capture() nieko neišsaugo, todėl laikykite momentines kopijas atmintyje arba išsaugokite jas naudodami SnapshotStore::save():
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'),
);
Greitasis pataisos būdas
Kai pakeitimų yra tik vienoje pusėje (nėra lygiagrečių pakeitimų), naudokite patch(). Tai dvipusis sujungimas, kuriame mūsų pusė sutampa su bazine versija, todėl konfliktų negali kilti:
$result = $merge->patch($base, $changes);
// Always clean: ours is identical to base.
Naudojant fasadą:
$result = Merql::patch('base', 'changes');
patch() atitinka merge($base, $base, $changes). Kadangi mūsų pusė niekada nenukrypo nuo bazinės versijos, visi jų pakeitimai priimami be konfliktų.
Schemos neatitikimų aptikimas
Jei tarp momentinių kopijų į lentelę buvo įtraukti arba iš jos pašalinti stulpeliai, sujungimas vis tiek vykdomas, tačiau užregistruojami neatitikimai. Pakeistų stulpelių tipai nelyginami, o pervadintas stulpelis laikomas vienu pašalintu ir vienu pridėtu stulpeliu; žr. Schemos tikrinimas.
$result = $merge->merge($base, $ours, $theirs);
if ($result->hasSchemaMismatches()) {
foreach ($result->schemaMismatches() as $mismatch) {
echo $mismatch->getMessage() . "\n";
}
}
merql automatiškai nemigruoja schemų. Už schemos pakeitimus atsako iškvietėjas. Sujungimo ataskaitoje jie nurodomi, kad galėtumėte nuspręsti, kaip elgtis toliau.
MergeResult
Rezultato objekte pateikiama visa, ko reikia:
$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
Kiekvienas MergeOperation turi tipą (insert, update arba delete), lentelės pavadinimą, eilutės raktą, stulpelių reikšmes ir šaltinį (ours, theirs arba merged). merged žymi eilutę, kurią atnaujino abi pusės (sujungus stulpelius atskirai), arba kurią abi pusės ištrynė.
Kur galima pritaikyti sujungimo rezultatą
Sujungimo rezultatas aprašo, kaip bazinę versiją paversti sujungta būsena. Jo operacijose pateikiami abiejų pusių pakeitimai: mūsų pusėje atliktas įterpimas ir jų pusėje atliktas įterpimas abu rodomi kaip įterpimai.
Merql::apply()(arbaApplier) vykdo kiekvieną operaciją prijungtoje duomenų bazėje. Jį galima taikyti duomenų bazei, kurioje tebėra bazinė versija, pavyzdžiui, naujai bazinės būsenos kopijai. Jei taikysite duomenų bazei, kurioje jau yra vienos pusės pakeitimų, nepavyks įterpti jau esančios eilutės, o operacija bus atšaukta.- Norėdami perkelti mūsų pakeitimus į duomenų bazę, kurioje jau yra jų pakeitimai, pavyzdžiui, perkelti smėliadėžės pakeitimus į veikiančią aplinką, pritaikykite tik trūkstamas operacijas: tas, kurių šaltinis yra
ours, irmergedatnaujinimus. Pasirinkite jas naudodami sujungimo planą, o įrašykite pasitelkę apsaugotą pritaikymą su veikiančių eilučių išankstinėmis sąlygomis.