Beads 合并冲突完全指南:基于 Dolt 存储的检测、解决与验证实战
导读:Beads 以 Dolt 作为存储后端,把 issue 数据保存在一个类似 Git 的 SQL 数据库中,因此多端同步(
bd dolt pull/bd dolt push、bd sync)天然会面临三路合并(three-way merge)产生的行级冲突。本文以仓库中的 .agent/workflows/resolve-beads-conflict.md 为核心骨架,结合cmd/bd下的真实实现,系统讲解冲突的检测入口、SQL 级解决方案、仓库内置的bd conflicts操作面、验证与收尾流程,以及遗留 JSONL 设置的迁移路径。读完你将掌握一套可复制、可自动化、有源码依据的 Beads 冲突处理工作流。
Beads 使用 Dolt 作为存储后端。Dolt 像 Git 一样原生支持三路合并(three-way merge):它把每次写入提交成带哈希的历史,pull 时以本地、远端与共同祖先(merge base)三方比对,自动合并无冲突的改动;只有同一行的同一字段被双方分别修改时,才会产生真正的冲突。理解这一点是后续所有排查工作的前提——绝大多数 pull 并不会冲突,冲突恰恰说明两端在同步间隔内对同一批 issue 做了互相矛盾的修改。
1. 冲突检测:两条互补的检查入口
原文档给出的第一步是两条命令:
bd doctor
bd dolt pull
1.1 bd doctor:结构化健康扫描
bd doctor 是 Beads 的体检工具,会从多个维度检查数据库健康状态,其中专门包含对 Dolt 合并冲突的检查。在 cmd/bd/doctor/validation.go 中可以看到两条路径:
- Dolt 后端:
checkDoltConflicts直接查询 Dolt 的系统表dolt_conflicts,按表汇总冲突数量(SELECT \table`, num_conflicts FROM dolt_conflicts),一旦发现未解决的冲突,会给出修复建议Resolve conflicts with 'bd dolt conflicts resolve' or 'dolt conflicts resolve --ours/--theirs'`(见 cmd/bd/doctor/validation.go)。 - 遗留 JSONL 后端:
CheckGitConflicts扫描.beads/目录下的 JSONL 文件,检测 Git 冲突标记(<<<<<<<、=======、>>>>>>>),发现后提示“Resolve merge conflicts in .beads/ files, then commit”(见 cmd/bd/doctor/validation.go)。
建议先跑 bd doctor 而不是直接 pull:它能把你尚未意识到的存量冲突一次性列出来,避免 pull 在冲突基础上叠加更多合并动作。
1.2 bd dolt pull:拉取即探测
bd dolt pull
bd dolt pull 从配置的 Dolt remote 拉取提交并执行合并(实现位于 cmd/bd/dolt.go)。如果远端没有新提交,命令输出 Pull complete.;如果拉取的改动与本地改动发生碰撞,Dolt 会在返回信息中列出发生冲突的表与行。
需要区分两种行为模式(源码注释明确说明,见 cmd/bd/dolt.go):
- 嵌入式存储(embedded,进程内默认模式):
bd dolt pull可以带--strategy ours|theirs让 Dolt 的自动解析器直接按策略解决它不敢自动处理的冲突(例如两端同时修改了同一个 issue),而不是中止 pull 等待人工处理(功能编号 #4992)。 - 服务端模式(sql-server / server-mode):
--strategy不被支持,pull 报告冲突后应使用bd conflicts resolve解决。
另外,pull 在尝试合并前会先检查 dolt.local-only 配置(bd config unset dolt.local-only 可重新启用远端同步),并且当 rig 完全没有配置 remote 时,bd dolt pull 会打印提示并安全退出 0,而不是报错——这是“没有 remote 是合法配置”的刻意设计(见 cmd/bd/dolt.go)。
2. 解决冲突:从 SQL 原语到 bd conflicts 操作面
2.1 SQL 级解决(Dolt 原生方案)
Dolt 为冲突提供了 SQL 化的解决接口。原文档给出的是最直接的方案:
# 查看冲突
bd sql "SELECT * FROM dolt_conflicts"
# 按“保留己方”或“采用对方”解决
bd sql "CALL dolt_conflicts_resolve('--ours')"
# 或者
bd sql "CALL dolt_conflicts_resolve('--theirs')"
dolt_conflicts 是 Dolt 的系统表,pull 合并后任何未解决的行冲突都会出现在其中;CALL dolt_conflicts_resolve('--ours') 与 CALL dolt_conflicts_resolve('--theirs') 则是表级(table-level)的批量解决过程——前者保留本地一侧的值,后者采用远端一侧的值。这是 Dolt 引擎自带的能力,因此无论通过 bd sql 还是裸 dolt CLI 执行都有效。
注意语义差异:
--ours/--theirs的方向取决于合并发生的方向。pull 合并时,“ours”指你本地工作区所在的分支(通常是 master),"theirs" 指刚拉下来的远端分支;而在 push 失败后的同步里方向可能相反。批量解决前先用dolt_conflicts看清每个表冲突了哪些行,再决定策略。
2.2 面向 issue 的现代方案:bd conflicts 子命令
裸 Dolt CLI 的冲突命令(dolt conflicts cat issues、dolt conflicts resolve --ours issues、dolt add -A && dolt commit)旗标面与 Git 略有差异,容易踩坑。为此 Beads 在 CLI 中内置了面向 issue 的冲突操作面(源码注释见 cmd/bd/conflicts.go),它读取的是 live working set,并且可以把解决粒度从“整张表”细化到“单个 issue 的单个字段”:
# 列出哪些表、哪些 issue 处于冲突状态
bd conflicts list
# 逐字段查看所有冲突行(base/ours/theirs 三方对照)
bd conflicts show
# 只看某一个 issue
bd conflicts show bd-1234
# 只看有分歧的字段(默认),或显示全部列
bd conflicts show --all-fields
# 逐行解决:保留己方 / 采用对方
bd conflicts resolve bd-1234 --ours
bd conflicts resolve bd-1234 bd-5678 --theirs
# 整表解决(Dolt 的表级解析)
bd conflicts resolve --all --ours
bd conflicts resolve --all --table config --theirs
# 收尾:提交一个冲突已全部解决的合并
bd conflicts resolve --conclude
关键行为(均有 cmd/bd/conflicts.go 源码背书):
- 默认只显示分歧字段:
bd conflicts show只展示 base/ours/theirs 三方不一致的字段,--all-fields才展开全部列,便于聚焦真正的分叉点。 - 冲突类别提示:
conflictKind会区分“both sides deleted(双方删除)”“we deleted / they modified(我方删除、对方修改)”“we modified / they deleted(我方修改、对方删除)”“both sides added(双方新增)”“both sides modified(双方修改)”五类,帮助判断该选--ours还是--theirs(见 cmd/bd/conflicts.go)。 - 策略互斥校验:
--ours与--theirs不能同时传;--strategy ours|theirs与旗标冲突会直接报错(见 cmd/bd/conflicts.go)。 - 部分解决不提交:resolve 后只有当
dolt_conflicts中不再有任何冲突行、且本次确实解决了内容时,才会自动提交合并(shouldCommitResolution的判定逻辑见 cmd/bd/conflicts.go)。残留冲突时输出N conflict(s) remain; the merge is not committed yet.,下一次bd conflicts list继续排查。 - schema 冲突与约束违反不会被 dolt_conflicts 列出:
bd conflicts list/resolve会额外检查 schema 冲突和约束违反(mergeBlockers),并给出人工处理指引——schema 冲突需要中止合并(dolt merge --abort)后在本地应用对方ALTER TABLE再重新合并,约束违反需要清理dolt_constraint_violations_<table>中的违规行(见 cmd/bd/conflicts.go)。 - 并发保护:
bd conflicts在 proxied-server 模式下不被支持(requireConflictSupport),因为它由代理服务器独立持有 working set。
2.3 自动化场景:bd sync 的冲突语义
如果你的工作流使用 bd sync(定时同步循环),需要注意它定义的三类退出语义(见 cmd/bd/sync.go):正常同步、合并冲突导致同步中止且未推送(syncStatusConflict,不会被自动解决)、以及推送竞争(push race)类瞬时问题(下一轮会自动重试)。bd sync 采用正向冲突检测——从结构化冲突数据(合并捕获的冲突 + dolt_conflicts 存活行)判断,而不是从 pull 的退出码猜测,避免把网络错误误报成冲突、或漏掉“pull 成功但留下冲突行”的情况。对 CI 而言,收到冲突语义的退出码后,应当路由到人工或 bd conflicts resolve 流程,而不是盲目重试。
3. 验证与收尾:确认解决结果并推送
冲突解决后,必须验证结果并推送,才算完成一轮同步:
# 验证解决结果:列出所有 issue,确认数据符合预期
bd list --json | head
# 推送解决后的状态到远端
bd dolt push
bd list --json 输出结构化 JSON,便于 grep 或管道到 jq 校验关键 issue 的字段值。bd dolt push 的实现细节(见 cmd/bd/dolt.go):
- 支持
--force覆盖远端工作集(当远端存在未提交改动时使用),以及--remote <name>指定推送到某个命名 remote。 - 若 rig 配置了
no-push: true或dolt.local-only=true,push 会明确提示“skipping push / Remote sync is disabled”并跳过,而不是报错。 - 没有配置任何 remote 时,Beads 会在征得同意(交互确认或
--yes)后尝试从 git origin 派生并采用一个 Dolt remote(adoptGitOriginRemoteForPush),用--no-adopt或环境变量BD_NO_REMOTE_ADOPT=1可完全禁用自动采用。 - 若本地与远端历史分叉(无共同祖先,常见于多个 agent 各自
bd init后推同一 remote),会打印三种恢复选项:bd bootstrap重克隆(保留远端)、bd dolt push --force(以本地为准)、或删除.beads/dolt后bd bootstrap手动重建(见 cmd/bd/dolt.go)。
Hosted Dolt 场景需要为 push/pull 设置认证环境变量:DOLT_REMOTE_USER 与 DOLT_REMOTE_PASSWORD(见 cmd/bd/dolt.go)。
4. 冲突后的状态一致性:is_blocked 重算与合并收尾
一个容易被忽略的细节:合并进来的写入会绕过常规的 is_blocked 计算。因此在 bd conflicts resolve 提交合并后,Beads 会调用 RecomputeBlockedAfterMerge 按解决前的 HEAD 重新计算 is_blocked 状态(见 cmd/bd/conflicts.go),保证 bd ready 等依赖该字段的查询不会在合并后返回过期结果。若存储后端不支持重算,会打印警告提示稍后运行 bd recompute-blocked。这意味着:解决冲突 ≠ 任务完成,还需要确认 bd ready 队列恢复正常,才不会把已解决的 issue 误判为仍被阻塞。
5. 遗留设置:JSONL 合并冲突的处理
如果项目还停留在旧版基于 Git 的 JSONL 设置(.beads/issues.jsonl),冲突形态完全不同——它不是 Dolt 行级冲突,而是 Git 文本冲突标记。原文档给出的流程是:
# 先在编辑器中手动解决 .beads/issues.jsonl 里的 Git 冲突标记,然后:
bd import -i .beads/issues.jsonl
git add .beads/issues.jsonl
git merge --continue
要点:
- 手动解决:Git 会把两端修改以
<<<<<<< HEAD/=======/>>>>>>>标记包裹在同一文件里,需要人工决定保留哪一端的 issue 行(必要时合并字段)。 - 重新导入:
bd import -i .beads/issues.jsonl将解决后的文件重新导入 Beads 数据库,确保内存中的存储与文件一致。 - 收尾 Git 合并:
git add标记已解决,git merge --continue完成合并提交。
这也正是 bd doctor 中 CheckGitConflicts 扫描 JSONL 冲突标记的原因——旧项目升级后,doctor 会同时覆盖 Dolt 与 JSONL 两种冲突形态,避免“Dolt 没冲突但 Git 合并卡住”的夹生状态。
6. 完整实战流程图
bd doctor / bd dolt pull
│
▼
有冲突? ──否──► 直接同步:bd dolt push / bd sync
│是
▼
bd conflicts list # 哪些表、哪些 issue 冲突
bd conflicts show [issue-id] # 逐字段看 base/ours/theirs
│
├─ 行冲突 ──► bd conflicts resolve <id> --ours|--theirs
│ (或 --all 整表解决;schema 冲突需
│ dolt merge --abort 后手动 ALTER TABLE)
▼
bd conflicts list # 确认 remaining == 0
│
▼
bd list --json | head # 验证解决结果
bd dolt push # 推送(必要时 --force)
7. 预防冲突的工程建议
从源码可以提炼出几条预防性结论(均出自 cmd/bd/dolt.go 与 cmd/bd/sync.go 的注释与行为):
- 多 agent 场景用
bd bootstrap而非各自bd init:历史分叉(“no common ancestor”)正是多个 agent 独立初始化后推同一 remote 造成的,bd bootstrap从既有 remote 克隆可根治(见 cmd/bd/dolt.go)。 - 避免两端在同步间隔内同时编辑同一 issue:冲突本质是“同一行同一字段双写”。agent 职责分区、编辑后尽快
bd sync能显著降低冲突概率。 - schema 变更(升级 bd 触发迁移)时先收敛再升级:两端分别升级 bd 并各自跑迁移,可能造成主键集不同的 schema 分叉(
isAncestorPKMismatchErr,见 cmd/bd/dolt.go),这类冲突无法自动解决、重试无效,只能选一个权威 clone 用bd dolt push --force确立远端,其余 clone 导出本地改动后重新bd bootstrap(完整恢复指引见 cmd/bd/dolt.go)。
相关资源索引
- 工作流文档:.agent/workflows/resolve-beads-conflict.md
- 冲突命令实现:cmd/bd/conflicts.go
- Dolt 同步命令(pull/push/commit/remote):cmd/bd/dolt.go
- 同步循环与冲突语义:cmd/bd/sync.go
- doctor 冲突检查:cmd/bd/doctor/validation.go
- 历史分叉 / 主键分叉恢复指引:cmd/bd/dolt.go、cmd/bd/dolt.go
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust4.21 K637- DDeepSeek-V4.1-FlashDeepSeek-V4.1-Flash 是一个多模态混合专家(MoE)模型,拥有 5520 亿骨干参数,并支持最多一百万 token 的上下文长度。该模型原生支持图像和文本输入,并以自回归方式生成文本Python290
cherry-studio🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端TypeScript2 K146
hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程Python46267
new-apiAI模型聚合管理中转分发系统,一个应用管理您的所有AI模型,支持将多种大模型转为统一格式调用,支持OpenAI、Claude、Gemini等格式,可供个人或者企业内部管理与分发渠道使用。🍥 A Unified AI Model Management & Distribution System. Aggregate all your LLMs into one app and access them via an OpenAI-compatible API, with native support for Claude (Messages) and Gemini formats.Go20043
JeecgBoot🔥企业级低代码平台集成了AI应用平台,帮助企业快速实现低代码开发和构建AI应用!前后端分离架构 SpringBoot,SpringCloud、Mybatis,Ant Design4、 Vue3.0、TS+vite!强大的代码生成器让前后端代码一键生成,无需写任何代码! 引领AI低代码开发模式: AI生成->OnlineCoding-> 代码生成-> 手工MERGE,显著的提高效率,又不失灵活~Java33951