
# 列级合并

与简单的行级比较相比，列级合并是 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 可以进一步执行[单元格级合并](https://merql.inline0.dev/zh/docs/merge/cell-level)。它适用于 TEXT 列（按行比较差异）和 JSON 列（按键比较差异），从而解决双方编辑同一列值中不同部分的情况。
