
用 git worktree 让多个 AI 编码代理并行改同一个仓库
用 git worktree 让多个 AI 编码代理并行改同一个仓库
同一个仓库里同时跑多个 AI 编码代理时,它们会互相覆盖对方的改动。git worktree 给每个任务一个独立的物理目录和独立分支,同时共享提交历史与远程配置。本文讲清 worktree 的原理、创建与清理命令、与 git clone 的差别,以及用两个终端并行跑代理、最后合并回收的完整流程。
在同一个仓库里同时开多个 AI 编码代理会话,最容易出的事故是:两个代理编辑同一批文件,后写入的改动把先写入的覆盖掉,而且往往要到合并或运行测试时才发现。git worktree 解决的就是这个问题——它让同一个仓库拥有多个物理上独立的作业目录,每个目录检出各自的分支,文件互不干扰,但提交历史、分支列表和远程配置仍然共用一份。这篇教程按「先理解原理、再记命令、最后跑一遍完整流程」的顺序,讲清怎么用它把并行任务安全地隔离开。适合已经在用命令行 git、并且想让多个代理会话同时推进不同任务的人。
准备工作
开始之前确认下面几件事,能省掉后面大部分返工。
- 一个已经初始化好的 git 仓库,并且当前在某个分支上(通常是 main)。worktree 是从当前检出的提交上分出去的,所以先确认工作区状态符合预期。
- 可用的 git 命令行。本文所有操作都在终端里完成,不依赖图形界面。
- 规划好目录布局。worktree 一般放在主仓库的同级目录,例如主仓库在
~/dev/myapp,就建~/dev/myapp-agent-a、~/dev/myapp-agent-b。放在仓库内部会导致新目录被主仓库当成未跟踪文件,尽量避开。 - 想清楚哪些文件不在 git 管理范围内。被 .gitignore 忽略的文件(典型是
.env)不会出现在新 worktree 里,需要手动准备。 - 确认端口和数据库的分配。两个 worktree 里同时起开发服务器会抢同一个端口,跑测试也可能连同一个数据库。开工前先决定谁用哪个端口、哪个库。
如果打算让 AI 代理在 worktree 里工作,代理本身不需要任何特殊配置——从它的视角看,每个 worktree 就是一个普通仓库。
操作步骤
第一步:理解 worktree 到底隔离了什么
普通仓库只有一个工作目录,切换分支时目录里的文件会整体换成那个分支的内容,所以没法一边在 main 上工作、一边把另一个分支的文件同时打开。worktree 则是在同一份 git 数据之上再挂一个工作目录。假设仓库叫 myapp,加一个 worktree 之后目录结构大致是这样:
~/dev/
├── myapp/ ← 原目录(main 分支)
│ ├── README.md
│ └── src/app.js
└── myapp-agent-a/ ← worktree 新增的目录(agent-a 分支)
├── README.md
└── src/app.js
两个目录是真正独立的文件夹,在其中一个里改文件,另一个完全不受影响。可以验证一下:
cd ~/dev/myapp
# 创建 worktree
git worktree add ../myapp-agent-a -b agent-a
# 只改 agent-a 那一侧的文件
echo 'console.log("feature A")' > ../myapp-agent-a/src/app.js
cat ~/dev/myapp/src/app.js
# console.log("hello") ← 原目录没变
cat ~/dev/myapp-agent-a/src/app.js
# console.log("feature A") ← 只有这一侧变了
关键点在于:新增的目录里没有完整的 .git 目录,只有一个指向原仓库的指针文件。
cat ~/dev/myapp-agent-a/.git
# gitdir: /Users/me/dev/myapp/.git/worktrees/myapp-agent-a
也就是说,文件放在不同地方,git 的元数据仍然集中在一处。由此带来三个共享行为,理解它们才能预判代理操作的影响范围:
- 提交立即可见。在 agent-a 里提交后,不用 push,回到主目录就能看到这条提交:
git log --oneline agent-a会列出它。所以代理干完活,直接在主目录git merge agent-a即可。 - 远程配置共用。在一侧改 origin 的 URL,另一侧同步变化;
git fetch在任意一侧执行,结果对两边都生效。 - 分支列表共用。在主目录执行
git branch会看到 worktree 里的分支,前面带+号表示该分支正被另一个 worktree 检出。
反过来,同一个分支不能在两个目录里同时检出。硬要这么做会报错:fatal: 'main' is already used by worktree at ...。每个 worktree 必须对应各自的分支。
第二步:和再 clone 一份仓库做对比
隔离工作区还有另一种做法:把仓库再 clone 一份。两者的差别值得先弄清楚,再决定用哪种。
| 对比项 | 再 clone 一份 | git worktree |
|---|---|---|
| 提交历史(.git) | 各自独立一份 | 共享同一份 |
| 新提交的可见性 | 必须 push/pull 才能互相看到 | 不用 push,其他工作目录直接可见 |
| 创建耗时 | 需要从远程重新下载 | 只是展开文件 |
并行跑代理的场景里,worktree 的优势主要是创建快、合并路径短。代价是共享历史也意味着共享风险:代理如果在某个 worktree 里用 git 命令操作了别的分支,影响会波及整个仓库。
第三步:掌握创建、查看、删除的命令
用新分支创建 worktree。第一个参数是目录路径,-b 后面是新分支名。新分支从当前检出的提交上创建:
git worktree add ../myapp-agent-a -b agent-a
用已有分支创建 worktree。把已经存在的分支检出到新目录:
git worktree add ../myapp-bugfix fix-issue-456
列出所有 worktree。输出里能看到路径、当前提交和对应分支:
git worktree list
# /Users/me/dev/myapp e989fa3 [main]
# /Users/me/dev/myapp-agent-a e989fa3 [agent-a]
删除 worktree。如果目录里还有未提交的改动或未跟踪文件,删除会失败:
git worktree remove ../myapp-agent-a
# fatal: '../myapp-agent-a' contains modified or untracked files, use --force to delete it
确认内容可以丢弃后加 --force:
git worktree remove --force ../myapp-agent-a
注意,像 .env 这种被 git 忽略的文件不会被算作「未跟踪文件」,--force 删除时会一并清掉,不会额外提示。
删除分支。删掉 worktree 不会删掉分支,需要单独处理:
git branch -d agent-a # 只删已合并的分支
git branch -D agent-a # 未合并也强制删除
用 -d 删未合并的分支会被拦下:error: the branch 'agent-a' is not fully merged。如果分支已经 push 到远程,还要删远程分支:
git push origin --delete agent-a
清理手工删掉的 worktree 记录。如果直接用 rm -rf 删了 worktree 目录,git 的管理信息会残留,git worktree list 里显示为 prunable。用下面的命令清掉:
git worktree prune
一个完整示例
下面用两个并行会话为例,把整个流程走一遍。所有命令默认在主目录 ~/dev/myapp 执行,除非特别说明。
1. 为每个任务建一个 worktree
git worktree add ../myapp-agent-a -b agent-a
git worktree add ../myapp-agent-b -b agent-b
如果仓库里有 .env 之类不在 git 管理范围内的文件,新目录里不会有,需要手动复制过去。
2. 各自准备运行环境
worktree 只是把文件展开,依赖包不会跟着过来。需要的话在每个目录里单独安装:
cd ~/dev/myapp-agent-a
npm install
agent-b 那一侧做同样的操作。这一步也可以交给代理去执行。
3. 在两个终端里分别启动代理
开两个终端窗口,各自进入对应目录再启动:
# 终端 1
cd ~/dev/myapp-agent-a
claude
# 终端 2
cd ~/dev/myapp-agent-b
claude
然后分别下达任务,例如终端 1 做功能新增,终端 2 修另一个页面的 bug。代理看到的就是一个普通仓库,不需要额外设置。
如果用的是支持子代理的会话,也可以让子代理跑在独立 worktree 里:在子代理定义中写 isolation: worktree,该子代理就会在专属 worktree 中工作。这个 worktree 默认从远程的默认分支创建。
4. 提交并汇总结果
任务完成后,让每个 worktree 里的代理把改动提交。之后按平常的方式处理即可,两条路选一条:
- 把分支 push 上去,开 pull request 走评审合并;
- 回到主目录直接合并,例如
git merge agent-a。
需要提前有心理准备:如果两个代理改到了同一批文件,合并时照样会冲突,处理方式和普通 git 合并没有区别——在后合并的那个分支上解决冲突。
5. 清理现场
合并完成后按顺序回收:
git worktree remove ../myapp-agent-a
git branch -d agent-a
git push origin --delete agent-a # 只有 push 过才需要
清理时有几个容易卡住的地方:
- 先停掉该 worktree 里正在运行的代理进程和开发服务器,否则删除可能失败。
remove报错时,先用git -C ../myapp-agent-a status --short看清残留了什么,再决定要不要加--force。- 如果是通过 pull request 合并的,先在主目录
git pull,这样git branch -d才能识别为已合并。 - squash 合并的情况下
-d删不掉,确认已经合并后再用-D。 - 如果代码托管平台在合并 PR 时自动删除了远程分支,就不必再执行
git push origin --delete。
注意事项
worktree 只隔离两样东西:工作目录里的文件和各自检出的分支。其余全部共享,下面这些坑都源于这一点。
- 被 git 忽略的文件不会复制。
.env这类文件在新 worktree 里不存在,必须手动准备,否则代理一启动就会因为缺配置而报错。 - 端口、数据库、Docker 容器都不隔离。两个 worktree 同时起监听同一端口的开发服务器,其中一个必然启动失败;两边跑测试连同一个数据库,数据会互相污染。开工前分配好资源。
- 历史共享意味着影响面共享。代理如果在某个 worktree 里用 git 命令改动了其他分支,整个仓库都会受影响。给代理的指令里最好明确它只应操作自己所在的分支。
- 一个分支只能在一个 worktree 里检出。重复检出会直接报错,规划任务时就要把分支分配好。
- 删除是不可逆的。
--force会连同被忽略的文件一起删掉,执行前先确认没有需要保留的本地配置。 - 手工删目录会留下脏记录。用
rm -rf删掉 worktree 后记得跑git worktree prune,否则git worktree list里会一直挂着失效条目。
把流程记成五步就不会乱:建 worktree → 补齐不会被自动复制的文件与依赖 → 在各目录里启动代理并行干活 → 提交并合并(必要时解决冲突)→ 删除 worktree 与分支。工具本身的命令和参数在不同 git 版本间可能有细微差异,具体行为以所用版本的官方文档为准。