Trismerno združevanje
Trismerno združevanje primerja dva nabora sprememb glede na skupnega prednika in ju združi v en rezultat. merql ta algoritem uporablja za vrstice zbirke podatkov namesto za vrstice datotek.
use Merql\Merge\ThreeWayMerge;
$merge = new ThreeWayMerge();
$result = $merge->merge($base, $ours, $theirs);
echo $result->operationCount(); // resolved operations
echo $result->conflictCount(); // unresolved conflicts
Tri stanja
Vsako združevanje zahteva natanko tri posnetke:
- Base -- stanje zbirke podatkov, ko sta se veji razšli.
- Ours -- ena veja po svojih spremembah, na primer urejena peskovniška kopija.
- Theirs -- druga veja po svojih spremembah, na primer živa zbirka podatkov, ki je še naprej delovala.
Ours in theirs morata biti vsak zajeta iz zbirke podatkov, ki hrani ustrezno vejo. Pravila združevanja obravnavajo obe strani enako; imeni določata source posamezne operacije, poimenovanje vrst konfliktov (update_delete pomeni, da je ours posodobil, theirs pa izbrisal) ter strani, ki jima ConflictPolicy::OursWins in TheirsWins dajeta prednost.
Base (snapshot at fork time)
/ \
Ours (our changes) Theirs (their changes)
\ /
--------- MERGE ----------
|
Merged result
+ conflicts (if any)
Brez skupne osnove ni mogoče razlikovati med »dodano« in »nespremenjeno«. Dvosmerna primerjava pokaže, da ima stran A vrednost, stran B pa je nima, ne more pa ugotoviti, ali jo je dodala A ali izbrisala B. Na to vprašanje odgovori base.
Kako se izračunajo nabori sprememb
Differ primerja vsak posnetek z osnovo in ustvari dva nabora sprememb. Vsak nabor vsebuje tri vrste operacij:
| Osnova | Trenutno | Operacija |
|---|---|---|
| Vrstica obstaja | Vrstica obstaja, enak prstni odtis | Brez spremembe |
| Vrstica obstaja | Vrstica obstaja, drugačen prstni odtis | UPDATE (razlika po stolpcih) |
| Vrstica obstaja | Vrstica manjka | DELETE |
| Vrstica manjka | Vrstica obstaja | INSERT |
Prstni odtisi (SHA-256 zgoščene vrednosti normalizirane vsebine vrstice) omogočajo hitrejšo obdelavo. Če se dva prstna odtisa ujemata, vrstica ni spremenjena in primerjava po posameznih stolpcih ni potrebna.
use Merql\Diff\Differ;
$differ = new Differ();
$oursChangeset = $differ->diff($base, $ours);
$theirsChangeset = $differ->diff($base, $theirs);
Pravila združevanja
Nabora sprememb se nato združita operacijo za operacijo. Ta tabela določa rezultat za vsako kombinacijo:
| Ours | Theirs | Rezultat |
|---|---|---|
| Brez spremembe | Brez spremembe | Ohranite base |
| Brez spremembe | Posodobljeno | Sprejmite theirs |
| Posodobljeno | Brez spremembe | Sprejmite ours |
| Posodobljeno (enaka vrednost) | Posodobljeno (enaka vrednost) | Sprejmite (obe se ujemata) |
| Posodobljeno (drugačna vrednost) | Posodobljeno (drugačna vrednost) | Konflikt |
| Brez spremembe | Izbrisano | Sprejmite izbris |
| Izbrisano | Brez spremembe | Sprejmite izbris |
| Izbrisano | Izbrisano | Sprejmite izbris (obe strani se strinjata) |
| Posodobljeno | Izbrisano | Konflikt |
| Izbrisano | Posodobljeno | Konflikt |
| Vstavljeno (samo ours) | -- | Sprejmite vstavitev |
| -- | Vstavljeno (samo theirs) | Sprejmite vstavitev |
| Vstavljeno (enak PK) | Vstavljeno (enak PK) | Konflikt |
Ko obe strani posodobita isto vrstico, merql takoj ne razglasi konflikta. Preide na združevanje na ravni stolpcev in vsak stolpec primerja posebej. Podrobnosti so v razdelku Združevanje na ravni stolpcev.
Uporaba vmesnika facade
Vmesnik facade Merql deluje z imenovanimi posnetki, shranjenimi na disku. Pred ustvarjanjem posnetka pokličite Merql::init() za zbirko podatkov, ki hrani posamezno stanje:
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 ustvarite vseh treh posnetkov zaporedoma iz ene zbirke podatkov. Theirs bi potem vseboval tudi spremembe ours, zato bi bila vsaka vrstica, ki jo je vstavil ours, videti kot vstavljena na obeh straneh in bi bila prijavljena kot konflikt insert_insert.
Merql::merge(), Merql::patch() in Merql::plan() ustvarijo ThreeWayMerge brez CellMergeConfig, zato združujejo samo na ravni stolpcev. Za združevanje na ravni celic naložite posnetke in uporabite razred neposredno, kot je prikazano spodaj.
Neposredna uporaba razredov
Za več nadzora sami ustvarite primerke komponent. Snapshotter::capture() ničesar ne shranjuje, zato posnetke ohranite v pomnilniku ali jih shranite z 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'),
);
Bližnjica za popravek
Kadar so spremembe samo na eni strani (brez sočasnih sprememb), uporabite patch(). Gre za dvosmerno združevanje, pri katerem je ours enak base, zato so konflikti nemogoči:
$result = $merge->patch($base, $changes);
// Always clean: ours is identical to base.
Prek vmesnika facade:
$result = Merql::patch('base', 'changes');
patch() je enakovreden merge($base, $base, $changes). Ker se ours nikoli ni razšel od base, so vse spremembe s strani theirs sprejete brez konflikta.
Zaznavanje neskladij shem
Če so bili med posnetki tabeli dodani ali iz nje odstranjeni stolpci, se združevanje kljub temu izvede, neskladja pa se zabeležijo. Spremenjene vrste stolpcev se ne primerjajo, preimenovan stolpec pa se prikaže kot odstranjen in dodan; glejte Preverjanje sheme.
$result = $merge->merge($base, $ours, $theirs);
if ($result->hasSchemaMismatches()) {
foreach ($result->schemaMismatches() as $mismatch) {
echo $mismatch->getMessage() . "\n";
}
}
merql shem ne seli samodejno. Za spremembe sheme je odgovoren klicatelj. Združevanje jih zabeleži, da se lahko odločite, kako nadaljevati.
MergeResult
Rezultatni objekt vsebuje vse, kar potrebujete:
$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
Vsak MergeOperation ima vrsto (insert, update ali delete), ime tabele, ključ vrstice, vrednosti stolpcev in vir (ours, theirs ali merged). merged označuje vrstico, ki sta jo posodobili obe strani (stolpec za stolpcem), ali vrstico, ki sta jo obe izbrisali.
Kje uporabiti rezultat združevanja
Rezultat združevanja opisuje, kako pretvoriti base v združeno stanje. Njegove operacije vključujejo spremembe obeh strani: vstavljanje, ki ga je izvedel ours, in vstavljanje, ki ga je izvedel theirs, sta prikazana kot vstavljanji.
Merql::apply()(aliApplier) izvede vsako operacijo v povezani zbirki podatkov. Brez težav se uporabi v zbirki podatkov, ki še vedno vsebuje base, na primer v sveži kopiji osnovnega stanja. V zbirki podatkov, ki že vsebuje spremembe ene strani, vstavljanje obstoječe vrstice ne uspe in transakcija se povrne.- Če želite prenesti spremembe ours v zbirko podatkov, ki že vsebuje theirs, na primer uveljaviti peskovniške popravke v živi zbirki, uporabite samo operacije, ki tej zbirki podatkov manjkajo: tiste z virom
ourster posodobitvemerged. Uporabite načrt združevanja, da jih izberete, in Zaščiteno uporabo, da jih zapišete s predpogoji za žive vrstice.