AB
AiBoss站
教程

用 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 版本间可能有细微差异,具体行为以所用版本的官方文档为准。