Pereiti prie turinio

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.

PHP
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.

Kodas
         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ų.

PHP
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:

PHP
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():

PHP
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:

PHP
$result = $merge->patch($base, $changes);
// Always clean: ours is identical to base.

Naudojant fasadą:

PHP
$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.

PHP
$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:

PHP
$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() (arba Applier) 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, ir merged atnaujinimus. Pasirinkite jas naudodami sujungimo planą, o įrašykite pasitelkę apsaugotą pritaikymą su veikiančių eilučių išankstinėmis sąlygomis.