让多个 AI Agent 同时改一个代码库,最大的坑是它们会互相踩踏文件。Git Worktree 给每个 Agent 一块物理隔离的工作区,再由主 Agent 协调合并——这篇讲清楚它的底层原理、为什么有效,以及这样做到底带来哪些收益。
当你想让多个 AI Agent 同时给一个代码库干活,第一个撞上的墙是:它们共用一个工作目录,会互相覆盖彼此正在改的文件。Git Worktree + 主从协调这套模式就是为解决这个问题而生的。这篇不讲一行行怎么敲命令,而是讲清楚它的原理、为什么有效、以及收益在哪。
一、先看问题:为什么多 Agent 不能直接「一起改」
单个 Agent 在一个工作目录里串行地改代码,没问题。但一旦想让多个 Agent 真正并行,麻烦就来了:
- 它们指向同一个工作目录、同一个分支——A 刚改的文件 B 又覆盖了;
- Git 的暂存区(index)和
HEAD是全局共享的——两个 Agent 同时git add/commit,提交历史会乱成一团; - 谁也不知道别人正在动哪些文件,没有隔离,就没有真并行。
最朴素的解法是「给每个 Agent 克隆一份仓库(git clone)」。能隔离,但代价大:每份都是完整副本,磁盘和时间成本高,而且改完散落在多个独立仓库里,合并回来很别扭。Git Worktree 正是这个问题的轻量答案。
二、核心原理:Git Worktree 是什么
一个 Git 仓库,平时只有一个工作目录绑在某个分支上。worktree 允许你从同一个仓库里,签出多个工作目录,每个挂在不同分支上:
flowchart TD
G[".git 对象库
历史 / 对象 / refs(全部共享)"]
W1["worktree A
分支 feature/backend
独立工作区 + index + HEAD"]
W2["worktree B
分支 feature/frontend
独立工作区 + index + HEAD"]
W3["worktree C
分支 feature/ros2
独立工作区 + index + HEAD"]
G --- W1
G --- W2
G --- W3
style G fill:#dbe9ff,stroke:#2563eb,stroke-width:2px
关键在于这两点的组合:
- 对象库共享:所有 worktree 共用同一个
.git对象库(提交历史、对象、引用都是一份)。所以它不是克隆——几乎不额外占空间,创建一瞬间完成。 - 工作区 + 暂存区 + HEAD 各自独立:每个 worktree 有自己的文件目录、自己的暂存区、自己的当前分支。一个 worktree 里改文件、
add、commit,完全不影响另一个。
一句话:Worktree 给了你「文件系统级的物理隔离」,却又「共享同一份历史」。前者让并行安全,后者让合并简单——这正是多 Agent 并行所需要的两个性质。
现实里很多 Agent 框架已经原生支持这一点。比如 Claude Code 的子 Agent 就支持
isolation: "worktree"——派发并行任务时自动给每个 Agent 开一个独立 worktree,结束后自动回收。原理就是上面这套。
三、整体模式:分发 → 并行执行 → 协调合并
这套模式是**主从(协调者 + 执行者)**结构,跑三个阶段:
flowchart LR
A["① 分发
主 Agent 拆任务
为每个任务建隔离 worktree"] --> B["② 并行执行
子 Agent 各自在
独立 worktree 里开发并提交"]
B --> C["③ 协调合并
主 Agent 检查冲突
顺序合并 → 清理环境"]
style A fill:#dbe9ff,stroke:#2563eb
style C fill:#ffe9d5,stroke:#b71d18
阶段一:主 Agent 分发
主 Agent 是协调者:它把大任务拆成互不重叠的子任务,为每个子任务开一个独立 worktree + 独立分支,然后并行派发子 Agent。
阶段二:子 Agent 并行执行(隔离的精髓)
每个子 Agent 只被告知自己的工作目录和分支,任务范围被限定在某个文件域内,并被明确禁止去碰别人的目录。它不感知其他 Agent 的存在——这点至关重要:
flowchart TD
M["主 Agent(协调者)"]
M -->|建环境 + 派发| S1["子 Agent 1 · backend"]
M -->|建环境 + 派发| S2["子 Agent 2 · frontend"]
M -->|建环境 + 派发| S3["子 Agent 3 · ros2"]
S1 -->|commit + 变更摘要| M
S2 -->|commit + 变更摘要| M
S3 -->|commit + 变更摘要| M
M --> R["收集摘要 → 检查冲突 → 顺序合并 → 清理"]
style M fill:#dbe9ff,stroke:#2563eb,stroke-width:2px
「子 Agent 互相看不见」是特性,不是缺陷:正因为它们之间没有共享状态,才能像无锁并发一样真正并行,不需要彼此协调、不会互相阻塞。协调的复杂度被收敛到主 Agent 一处。
阶段三:主 Agent 协调合并
子 Agent 全部完成后,主 Agent 回到主目录,先看各分支的变更摘要、评估冲突风险,再逐个顺序合并(而不是一把梭),最后回收 worktree。
为什么强调「顺序、逐个」合并:并行产出的多个分支若一次性合,冲突会糊成一团、难以定位。逐个合并时,每次冲突都只来自「当前分支 vs 已合并结果」,范围小、好处理——这是把并行产生的不确定性,在合并阶段重新串行化、可控化。
四、冲突处理的原理
冲突不是靠运气避免的,而是靠结构:
| 情况 | 背后的原理 / 策略 |
|---|---|
| 两个 Agent 改了同一文件 | 说明任务拆分时文件域没隔离干净——由主 Agent 统一裁决,或专门派一个「合并 Agent」处理 |
| 改到了接口 / 协议文件 | 先合接口层,再合实现层——让实现去适配已确定的契约,而不是反过来 |
| 合并失败、纠缠不清 | 放弃这次合并,改用「逐提交挑拣(cherry-pick)」——提交粒度越小,越容易定位是哪一笔引入的冲突 |
底层逻辑只有一句:冲突的概率正比于文件域的重叠度。把任务拆得「文件域天然不重叠」,冲突就趋近于零。
五、收益:这样做到底好在哪
这是这套模式真正的价值所在:
| 收益 | 为什么能得到 |
|---|---|
| 真并行,墙钟时间大幅缩短 | 三个模块本可串行做完,现在三个 Agent 同时做,总耗时约等于最慢的那一个 |
| 物理隔离,杜绝互相踩踏 | 各自独立工作区,A 的改动在文件系统层面碰不到 B 的文件 |
| 轻量,远胜克隆 | 共享 .git 对象库,创建 worktree 几乎零成本,不像 git clone 那样复制整个仓库 |
| 失败隔离 | 某个子 Agent 跑偏或崩溃,只影响它自己的分支,其它分支照常推进,直接丢弃重来即可 |
| 责任边界清晰、易审查 | 每个功能 = 一条独立分支 + 独立 diff,review 时一目了然,出问题能精确回滚到某分支 |
| 关注点分离 | 「协调」(主 Agent)和「执行」(子 Agent)解耦——子 Agent 只需埋头做好一件事,全局复杂度集中在主 Agent |
| 可扩展 | 加一个模块就加一个 worktree + 子 Agent,模式不变,水平扩展 |
六、什么时候适合用(以及不适合)
这套模式不是万能的,它的收益高度依赖一个前提:任务能被拆成文件域不重叠的子任务。
非常适合:
- 项目本身按模块解耦(如
backend/、frontend/、ros2/这类天然分目录)——子 Agent 各管一块,几乎不冲突; - 子任务之间依赖弱、可独立完成;
- 单个任务体量够大,值得为它付出「建环境 + 协调」的开销。
不太适合:
- 任务高度耦合、反复要改同一批核心文件——并行只会制造一堆冲突,不如串行;
- 任务很小很碎——协调开销超过并行收益,得不偿失;
- 子任务间有强顺序依赖(B 必须等 A 的产出)——那本质上是流水线,不是并行。
判断准则:先问「这些子任务改的文件域重叠吗」。重叠少 → 用这套模式收益大;重叠多 → 先重构解耦,或干脆串行。
七、几条原理性的最佳实践
这些不是操作步骤,而是让模式成立的约束条件:
- 拆分原则:文件域不重叠——这是整套模式的地基。拆任务时就要保证各子 Agent 的「势力范围」不交叉。
- 共享文件由主 Agent 独占——
docker-compose.yml、公共接口定义等「谁都可能动」的文件,统一交给主 Agent 改,别让子 Agent 碰。 - 接口契约要前置——并行开工前先把 API 契约定下来,否则各自定义出不兼容的接口,合并时是灾难。
- 提交粒度要小——每个功能点单独提交。合并冲突时,小提交让你能精准定位、甚至逐笔挑拣。
一句话总结
多 Agent 并行开发的难点是「共享工作区导致互相踩踏」;Git Worktree 用「共享历史 + 隔离工作区」这一个巧妙结构同时解决了隔离与合并两件事。再套上「主 Agent 协调、子 Agent 隔离执行」的主从结构,就得到一套真并行、低冲突、易审查、可扩展的开发范式。它的威力,完全取决于你能否把任务拆成文件域不重叠的子任务——这一步想清楚了,后面的并行与合并都是水到渠成。
