Злиття на рівні стовпців
Головна перевага merql над наївним порівнянням на рівні рядків — злиття на рівні стовпців. Коли обидві сторони змінюють той самий рядок, merql порівнює кожен стовпець окремо, а не позначає весь рядок як конфлікт.
Проблема злиття на рівні рядків
Уявімо таблицю posts, у якій двоє людей редагують той самий рядок:
Base: post #42 { title: "Hello", content: "Body", status: "draft" }
Ours: post #42 { title: "Hello", content: "Body v2", status: "draft" }
Theirs: post #42 { title: "New Title", content: "Body", status: "publish" }
Злиття на рівні рядків виявляє, що обидві сторони змінили рядок, і оголошує конфлікт. Але зміни зовсім не перетинаються: ours змінила content, а theirs — title і status.
Як злиття на рівні стовпців розв’язує цю проблему
merql застосовує однакові правила тристороннього злиття до кожного стовпця окремо:
title: base="Hello", ours="Hello", theirs="New Title" -> accept theirs
content: base="Body", ours="Body v2", theirs="Body" -> accept ours
status: base="draft", ours="draft", theirs="publish" -> accept theirs
Результат: { title: "New Title", content: "Body v2", status: "publish" }. Чисте злиття без конфлікту.
Як це працює
Коли ThreeWayMerge виявляє, що обидві сторони оновили той самий рядок, він передає завдання ColumnMerge::merge():
use Merql\Merge\ColumnMerge;
$result = ColumnMerge::merge(
table: 'posts',
rowKey: '42',
base: ['title' => 'Hello', 'content' => 'Body', 'status' => 'draft'],
ours: ['title' => 'Hello', 'content' => 'Body v2', 'status' => 'draft'],
theirs: ['title' => 'New Title', 'content' => 'Body', 'status' => 'publish'],
);
// $result['values'] -> merged column values
// $result['conflicts'] -> list of Conflict objects (empty if clean)
Для кожного стовпця застосовуються ті самі правила:
| Base | Ours | Theirs | Результат |
|---|---|---|---|
| A | A | A | Залишити base (без змін) |
| A | A | B | Прийняти theirs |
| A | B | A | Прийняти ours |
| A | B | B | Прийняти (обидві сторони згодні) |
| A | B | C | Конфлікт (різні значення) |
Коли конфлікти все ж виникають
Конфлікт на рівні стовпця виникає, коли обидві сторони змінюють той самий стовпець на різні значення:
Base: post #42 { title: "Hello" }
Ours: post #42 { title: "Welcome" }
Theirs: post #42 { title: "Greetings" }
Обидві сторони змінили title на різні значення. Це справжній конфлікт, який потрібно розв’язати.
$result = ColumnMerge::merge(
'posts', '42',
['title' => 'Hello'],
['title' => 'Welcome'],
['title' => 'Greetings'],
);
// $result['conflicts'] contains one Conflict:
// table: "posts", column: "title"
// oursValue: "Welcome", theirsValue: "Greetings", baseValue: "Hello"
Часткові конфлікти
Один рядок може містити як безконфліктні злиття, так і конфлікти. Якщо один стовпець об’єднується без конфлікту, а в іншому виникає конфлікт, MergeOperation цього рядка містить об’єднані значення безконфліктних стовпців і значення ours (за замовчуванням) для конфліктних стовпців. Про конфлікт повідомляється окремо.
Base: { title: "Hello", content: "Body", status: "draft" }
Ours: { title: "Welcome", content: "Body v2", status: "draft" }
Theirs: { title: "Greetings", content: "Body", status: "publish" }
Тут конфліктує title (обидві сторони змінили його по-різному), content об’єднується без конфлікту (змінила лише ours), а status — також без конфлікту (змінила лише theirs). Операція містить:
// values: { title: "Welcome", content: "Body v2", status: "publish" }
// One conflict on the "title" column.
Порівняння значень
Значення стовпців порівнюються як рядки після приведення типів. NULL — це окреме значення:
NULLдоNULL= без змін.NULLдо"hello"= оновлення."hello"доNULL= оновлення."1"до1= без змін (обидва приводяться до рядка"1").
Це відповідає семантиці отримання даних адаптером підключення MerQL: усі значення бази даних повертаються як рядки (або NULL).
Злиття на рівні комірок
Якщо злиття на рівні стовпців усе ще призводить до конфлікту (обидві сторони змінили той самий стовпець на різні значення), merql може перейти ще на рівень нижче за допомогою злиття на рівні комірок. Це застосовується до стовпців TEXT (порівняння рядок за рядком) і JSON (порівняння ключ за ключем), усуваючи випадки, коли обидві сторони змінили різні частини значення того самого стовпця.