git-ai:几十个 agent 抢同一个分支,把 merge queue 换成 compare-and-swap
Anthropic 在 Redesigning source control 里描述了 Scion:一个以服务端为中心、为「成百上千个 agent 同时读、写、落地代码」设计的版本控制系统。Scion 是 Anthropic 内部系统,不对外提供。于是有人照公开描述写了一个独立实现:git-ai(仓库内也叫 gitai),Go 写的 Gitaly fork,MIT。它和 Anthropic 没有关联,也没有背书;服务端从不调用语言模型,所有 AI 相关能力对接任意 provider,或者完全不用。
项目地址:https://github.com/feder-cr/git-ai
设计来源:https://www.anthropic.com/engineering/redesigning-source-control
为什么经典 Git 托管会卡
经典 Git 托管假设用户是人:clone 一次,改几小时,push 一次。coding agent 的工作方式完全不同——几千个短命任务,每个只碰几个文件,每个都想在几秒内落地,而且全压在同一个分支上。全量 clone、merge queue、客户端遍历历史,三样都会在这里变成瓶颈。
Scion 的答案是把手上的活搬去服务端:客户端保持在线、只取自己需要的对象;历史类问题由服务端回答并缓存;变更通过 compare-and-swap 落地而不是排队;每次更新都写进 changeset log;仓库状态放在一致的数据库加对象存储里,服务端节点可以水平扩。git-ai 在普通 Git 仓库之上实现了这些想法。
Scion 的设计在 gitai 里对应什么
| Scion 的想法 | gitai 里的实现 |
|---|---|
| 客户端常在线,只取需要的对象 | ObjectService 提供单对象和稀疏 tree;graft clone 和 graft sparse 只物化你要求的路径 |
| 源码管理即 API,历史在服务端跑 | blame、路径历史、ancestry、代码搜索都在服务端执行,并作为 derived data 记忆化,每个 commit 只算一次 |
| 不用 merge queue 的落地 | UserLand 把变更 rebase 到当前 tip,用 compare-and-swap 落地;同一分支上的并发落地合并成一次 reference 更新 |
| 自动冲突解决 | 冲突的 land 交给你选的 resolver(任意 LLM provider、确定性工具,或者不解决),走版本化、vendor neutral 的协议。模型的解法只是 proposal:只有你配置的 verify.command 在即将落地的那个 tree 上通过才允许自动落地,否则把冲突包连同 proposal 一起退回 agent。确定性规则没有这个前置条件,打开 verify.enabled 后同样要在那个 tree 上验证 |
| Changeset log | 每次被接受的 reference 更新都连操作和 actor 一起记进稠密、有序的 log,客户端可以列出或实时 watch |
| 存储核心:数据库 + 对象存储,无状态服务端 | 每个变更是一条 changeset,写进 SQLite 或 PostgreSQL 的 WAL,每个 reference 一次 CAS;pack 放对象存储(file、S3、GCS、Azure)。服务端节点是缓存,任何节点都能重建,加节点即扩容 |
| 落地后再测,回滚肇事者 | graft-verify 测 tip,二分定位失败,回滚引入问题的那个变更 |
| 监控 agent(“superfans”) | agent 跟随 changeset 流,按组件汇报重要信息,也能发到 Slack |
| 面向 agent 的 review | 语义化审批规则:纯注释变更可以比逻辑变更要求更少的审批 |
| agent 是一等用户 | per-agent token、RPC allowlist 和 branch policy 决定每个 agent 能做什么 |
| 严格的缓存层 | graft-cache 在 daemon 和区域两层缓存不可变对象,每个对象都校验 |
其中最关键的一行是落地。没有 queue,就没有「排队等着轮到我」这一层:变更直接 rebase 到当前 tip 上,用 CAS 提交,同一分支上的并发落地被合并成一次 reference 更新。冲突不是靠重试次数磨过去的——这也是下面 benchmark 里 attempts 从 26 降到 1 的原因。
服务端结构
链路是:agents / CI / 稀疏工作区 → graft CLI 或 Go SDK → gRPC → graft-cache(可选的 daemon 与缓存层)→ gitai server(任意节点数),后者承担 land、objects、changesets、derived data、search、review。
写入路径是 changeset 加每个 reference 一次 CAS,落到 SQLite / PostgreSQL(changeset WAL、refs、changelog、reviews)和对象存储(content-addressed packs;file、S3、GCS、Azure)。本地 Git 仓库在这里只是缓存,任何节点都能重建。细节见 doc/graft/ARCHITECTURE.md 和 doc/graft/STORAGE.md。
冲突解决:模型只提 proposal,验证命令说了算
这是整套设计里最值得抄的一条约束。冲突的 land 交给一个可插拔的 resolver:任意 LLM provider 的模型、一个确定性工具,或者干脆不解决。协议是版本化、vendor neutral 的,服务端自己不调用 LLM。
模型的输出不算数。它给出的解法只是一个 proposal,只有你配置的 verify.command 在即将落地的那个 tree 上跑通,才允许自动落地;跑不通就把冲突包连同 proposal 一起退回给 agent,让 agent 自己处理。确定性规则没有这个前置条件,但打开 verify.enabled 之后同样要在那个确切 tree 上验证。
把「模型说了什么」和「什么能落地」切开,是这套机制能用在生产路径上的前提:LLM 可以是 resolver,但不成为信任边界。
构建与运行
依赖:Linux 或 macOS、Go 1.26、C toolchain、Meson、Ninja(构建过程会编译一份内置的 Git)。
git clone https://github.com/feder-cr/git-ai.git
cd git-ai
make build # 服务端、CLI 和工具输出到 _build/bin
examples/graft/quickstart.sh # 临时目录里起一个服务端、一个 cache daemon、两个 agent 并落地一次
./_build/bin/graft # CLI 及其子命令
跑自己的服务端:改 examples/graft/config.toml,用 _build/bin/gitaly serve config.toml 启动。想把仓库状态放进数据库和对象存储,就加一个 [graft.storage] 段:先在 shadow 模式下导入已有仓库,再切到 authoritative(见 STORAGE.md)。
代码里这些扩展统一叫 Graft:[graft] 配置段、graft CLI、internal/graft、doc/graft。
Benchmark
场景:50 个 agent 在同一个分支上各落地 5 个变更,其中 20% 故意制造冲突。“classic” 是同一个服务端关掉 batching:每个 agent 自己 rebase、自己重试 CAS,最多 20 次。用 acceptance-phase4.sh 在单台 16 核机器上跑了两轮。
| 50 agents, 250 changes, 无冲突 resolver | classic | gitai |
|---|---|---|
| changes landed | 100–106 | 200 |
| attempts per landed change | 26.5–28.7 | 1.0 |
| lands per minute | 264–385 | 1,487–1,944 |
| p99 latency | 4.4–5.2 s | 1.9–2.7 s |
gitai 没落地的 50 个变更就是那些计划内的冲突,原样退回给 agent——这组没挂 resolver。换成 resolve.toml 里的确定性 resolver 后,冲突全部解决,250 个变更全部落地,1,245–1,806 lands/min,p99 2.0–3.4 s。细节在 BENCH.md。
这些数字来自项目自己的脚本、单台 16 核机器、两轮,不是第三方复现,看的时候按这个前提折算。
现状与取舍
版本 0.4.0。上面列的功能都已实现,有单元、端到端、多节点、崩溃和 fuzz 测试覆盖。1.0 之前接口还会变,接入前要接受这一点。已知缺口写在 LIMITATIONS.md,比如存储核心里的 pack GC。
它不是托管产品:没有 Web UI、没有 issue、没有 MR review、没有 CI。别把它当 GitLab 的替代品用。访问策略是本地配置的,per-agent token 加 RPC allowlist 加 branch policy;共享 token 属于管理凭证,不该随手发给 agent。
适用边界因此比较清楚:多个 agent 抢同一个仓库、落地速度是真瓶颈的团队,值得拿它做基础设施;需要的是完整的代码托管平台,现在还不是它。
文档与社区
文档覆盖 quickstart 与配置参考、架构与存储核心、landing 与 batching、冲突解决、derived data、review / search / verification / superfans 与 Slack chat、agent 安全、benchmark、fuzzing、limitations 与 roadmap,另有 PLAN.md 到 PLAN6.md 和 PLAN8.md(更快的 landing)等设计文档。
社区模式是自己维护自己:issue 报 bug 或提想法,大改前先开 discussion,PR 欢迎。
Credits 与许可
git-ai 是 MIT。它的起点是 Gitaly——GitLab 的 Git RPC server(MIT,Copyright GitLab Inc.)——的 fork,底层 Git plumbing 由 Gitaly 提供,上游每个 Gitaly RPC 都还能用。上游 Gitaly 的非 MIT ee/ 目录不在这个仓库里。