列级合并
与简单的行级比较相比,列级合并是 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():
PHP
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,且值不同。这是真正的冲突,需要解决。
PHP
$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 顺利合并(仅对方修改)。该操作包含:
PHP
// 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 列(按键比较差异),从而解决双方编辑同一列值中不同部分的情况。