
# Úvod

merql je knihovna v čistém PHP, která provádí třícestná sloučení stavů databáze. Pořizuje snímky tabulek, vypočítává sady změn mezi snímky, slučuje dvě sady změn vůči společnému základu a zjišťuje konflikty na úrovni sloupců. Používá stejný algoritmus jako git u zdrojových souborů, ale pracuje s řádky a sloupci místo řádků textu.

Bez externích nástrojů. Podporuje MySQL a SQLite prostřednictvím připojitelných adaptérů připojení a ovladačů dialektů.

## Rychlý příklad

Sloučení vyžaduje tři stavy stejných dat: základní stav, ze kterého vycházely obě kopie, a obě kopie po samostatných změnách. Zde byla izolovaná kopie vytvořena z ostré databáze a od té doby byly upraveny obě.

```php
use Merql\Connection;
use Merql\Merql;

$live    = Connection::sqlite('/data/live.sqlite');
$sandbox = Connection::sqlite('/data/sandbox.sqlite');

// Base: live at the moment the sandbox was copied from it.
Merql::init($live);
Merql::snapshot('base');

// ... copy live.sqlite to sandbox.sqlite, then both databases change ...

// Ours: the sandbox with our edits. Theirs: live with its own changes.
Merql::init($sandbox);
Merql::snapshot('ours');
Merql::init($live);
Merql::snapshot('theirs');

$result = Merql::merge('base', 'ours', 'theirs');

if ($result->isClean()) {
    foreach ($result->operations() as $operation) {
        echo "{$operation->type} {$operation->table} #{$operation->rowKey} ({$operation->source})\n";
    }
} else {
    echo "Conflicts: {$result->conflictCount()}\n";
}
```

Každý snímek se pořizuje z databáze, která obsahuje příslušný stav. Pořízení všech tří snímků postupně z jedné databáze nepopisuje dvě odlišné kopie: theirs by už obsahoval změny z ours a každý řádek vložený v ours by byl označen jako konflikt `insert_insert`.

Výsledek uvádí operace, které převedou základní stav na sloučený, včetně změn z obou stran. Každá operace je označena zdrojem (`ours`, `theirs` nebo `merged`). Chcete-li zapsat změny izolované kopie do ostré databáze, použijte [Podmíněné použití změn](https://merql.inline0.dev/cs/docs/apply/guarded) pouze pro operace, které v ostré databázi ještě nejsou.

Nebo z CLI, který se pro každý `snapshot` připojí k databázi uvedené v `MERQL_DB_DSN`:

```bash
MERQL_DB_DSN="sqlite:/data/live.sqlite" vendor/bin/merql snapshot base
# ... copy live.sqlite to sandbox.sqlite, then both databases change ...
MERQL_DB_DSN="sqlite:/data/sandbox.sqlite" vendor/bin/merql snapshot ours
MERQL_DB_DSN="sqlite:/data/live.sqlite" vendor/bin/merql snapshot theirs

vendor/bin/merql merge base ours theirs --dry-run
```

## Proč merql

Většina nástrojů pro porovnávání stavu databází pracuje na úrovni řádků: pokud se mezi dvěma verzemi liší kterýkoli sloupec řádku, celý řádek se označí jako konflikt. Tím vznikají falešné konflikty, když dva lidé upraví různé sloupce téhož řádku.

merql slučuje na úrovni sloupců. Pokud jedna strana změnila `title` a druhá `status`, změny se sloučí bez konfliktu. Konflikt vzniká pouze tehdy, když obě strany změní stejný sloupec na různé hodnoty.

U sloupců TEXT a JSON může merql jít ještě o úroveň níž a slučovat obsah buněk. Dvě úpravy různých řádků textového sloupce nebo různých klíčů sloupce JSON lze sloučit stejným třícestným algoritmem. Slučování buněk je volitelné: předejte `CellMergeConfig` do `ThreeWayMerge`. Rozhraní `Merql` a CLI slučují pouze na úrovni sloupců. Viz [Sloučení na úrovni buněk](https://merql.inline0.dev/cs/docs/merge/cell-level).

## Model sloučení

```
         Base (snapshot at fork time)
        /                              \
   Ours (one copy's changes)            Theirs (the other copy's changes)
        \                              /
         --------- MERGE ----------
                     |
              Merged result
              + conflicts (if any)
```

Tři stavy, stejně jako v git. Base je společný předek. Ours a theirs jsou dvě kopie po rozdělení, každá zachycená z vlastní databáze. Pravidla sloučení zacházejí s oběma stranami stejně. Tyto názvy se objevují v poli `source` každé operace, v typech konfliktů (`update_delete` znamená, že ours aktualizovalo a theirs odstranilo) i v zásadách řešení `OursWins` a `TheirsWins`. Bez společného základu nelze rozlišit mezi „přidáno“ a „nezměněno“.

Samotné sloučení nic nezapisuje. Jeho operace převedou základní stav na sloučený, takže `Merql::apply()` je bezpečně provede pouze v databázi, která stále obsahuje základní stav. Chcete-li přenést změny jedné strany do databáze, která už obsahuje změny druhé strany, například úpravy izolované kopie do ostré databáze, použijte jen operace, které v ní chybí; viz [Podmíněné použití změn](https://merql.inline0.dev/cs/docs/apply/guarded).

## Požadavky

- PHP 8.2 nebo novější.
- `ext-mbstring` a `ext-zlib`, které vyžaduje závislost `pitmaster/pitmaster` poskytující rozdíly textu.
- Rozšíření databáze pro použitý adaptér: `ext-pdo` s `ext-pdo_sqlite` nebo `ext-pdo_mysql`, případně `ext-mysqli`. `composer.json` v merql pouze doporučuje `ext-pdo` a `ext-mysqli`, proto Composer jejich přítomnost nekontroluje.
- Databázi MySQL nebo SQLite. Ostatní databázové stroje vyžadují vlastní [ovladač](https://merql.inline0.dev/cs/docs/apply/drivers).

## Co merql není

merql není nástroj pro migraci schématu. Nereplikuje databáze ani je nesynchronizuje v reálném čase. Řeší jeden konkrétní problém: ze tří stavů databáze vytvoří čtvrtý, sloučený stav a označí konflikty.

## Dokumentace

- [Začínáme](https://merql.inline0.dev/cs/docs/getting-started) -- instalace, první snímek a první sloučení.
- [CLI Reference](https://merql.inline0.dev/cs/docs/cli) -- úplná reference příkazu `vendor/bin/merql`.
- [Reference PHP API](https://merql.inline0.dev/cs/docs/api) -- kompletní dokumentace API všech tříd.
- [Plány sloučení](https://merql.inline0.dev/cs/docs/merge/plans) -- stabilní ID operací, skupiny změn a výběry.
- [Podmíněné použití změn](https://merql.inline0.dev/cs/docs/apply/guarded) -- vybrané zápisy s podmínkami pro aktuální řádky.
- [Vrácení změn](https://merql.inline0.dev/cs/docs/apply/rollback) -- artefakty pro vrácení změn a kontroly odchylek.
