Слияние на уровне столбцов
Слияние на уровне столбцов — главное преимущество 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" }
При слиянии на уровне строк обнаруживается, что обе стороны изменили строку, и объявляется конфликт. Но изменения никак не пересекаются. Мы изменили content, а они — 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)
Для каждого столбца действуют те же правила:
| База | Наша сторона | Их сторона | Результат |
|---|---|---|---|
| A | A | A | Оставить базовое значение (без изменений) |
| A | A | B | Принять значение их стороны |
| A | B | A | Принять наше значение |
| 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 для этой строки содержит объединенные значения для столбцов без конфликтов и наше значение (по умолчанию) для столбцов с конфликтами. О конфликте по-прежнему сообщается отдельно.
Base: { title: "Hello", content: "Body", status: "draft" }
Ours: { title: "Welcome", content: "Body v2", status: "draft" }
Theirs: { title: "Greetings", content: "Body", status: "publish" }
Здесь возникает конфликт для title (обе стороны изменили его по-разному), content объединяется без конфликта (изменено только нами), а status — без конфликта (изменено только ими). Операция содержит:
// 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 (сравнение по ключам) и позволяет разрешать случаи, когда обе стороны изменили разные части значения одного и того же столбца.