如何用 Git 工作树并行运行 AI 编程智能体
为每个智能体分配一个独立的 Git 工作树:一个位于自己分支上、与仓库共享历史的独立工作目录。用 git worktree add 创建它,在里面启动智能体,工作完成后合并分支,再删除工作树。MAO 的图层在画布上完成同样的步骤,并在合并前先做检查。
- 工作树是同一个仓库在自己分支上的第二个检出目录
- Git 不允许在两个工作树中检出同一个分支
- 新工作树中没有 .env、node_modules 等被忽略的文件
要并行运行 AI 编程智能体而不让它们互相覆盖,就为每个智能体分配一个独立的 Git 工作树:一个位于自己分支上、共享同一个仓库的独立工作目录。用 git worktree add -b 创建它,在里面启动智能体(Claude Code、Codex、Gemini CLI 或其他任何智能体),工作完成后合并分支,再删除工作树。在 MAO 中,图层就是这样一个工作树:从导航栏创建,合入前先经过检查。
什么是 Git 工作树?
工作树是挂在同一个仓库上的额外工作目录,有自己检出的分支。
Git 自己的文档是这样描述的:一个仓库可以支持多个工作树,因此你可以同时检出多个分支。git clone 或 git init 生成的检出目录是主工作树;之后添加的每一个都是链接工作树。链接工作树与仓库共享一切,只有 HEAD 和索引这类每个工作树各自的文件除外。
实际效果是:每个工作树都有自己的文件、自己的分支和自己暂存的改动,而提交、分支和对象库是共享的。在一个工作树中做的提交,所有其他工作树立刻就能看到,不需要 push 或 fetch。
为什么并行的编程智能体需要各自的工作树?
因为在同一个检出目录中的智能体会编辑同一批文件,并共用一个分支。
两个智能体在同一个目录中工作时,一个智能体改到一半的内容就是另一个智能体的起点;一次测试运行会混进两个智能体都没打算合在一起的改动;而在某个终端里执行一次 git switch,会改变所有智能体脚下的文件。每个智能体一个工作树时,每个智能体在自己的分支上编辑自己的那份文件,由你通过合并分支来决定合并哪些内容。
如何为每个智能体设置一个工作树?
在主检出目录中为每个智能体添加一个工作树和一个新分支,然后在各自的文件夹中启动每个智能体。
- 在主检出目录中,为每个智能体创建一个工作树和一个新分支。路径就是文件夹所在的位置;放在仓库的同级目录,可以让它不混进项目文件。
- 把项目运行所需的被忽略文件(例如
.env)复制进去,并在每个工作树中安装依赖。 - 在每个工作树中各启动一个智能体,每个使用自己的终端。
- 随时用
git worktree list查看现有的工作树。
git worktree add ../myapp-api -b agent/api
git worktree add ../myapp-tests -b agent/tests
cp .env ../myapp-api/
cp .env ../myapp-tests/
cd ../myapp-api && claude # agent 1
cd ../myapp-tests && claude # agent 2
git worktree listgit worktree list 先列出主工作树,再列出每个链接工作树,以及它检出的提交和分支:
/home/me/myapp d3d2dd1 [main]
/home/me/myapp-api d3d2dd1 [agent/api]
/home/me/myapp-tests d3d2dd1 [agent/tests]在脚本中,git worktree list --porcelain 输出的格式在各个 Git 版本之间保持稳定。要让智能体使用一个已经存在的分支,去掉 -b 即可:git worktree add ../myapp-fix fix/login。
如何合并智能体的工作并清理?
在主检出目录中合并智能体的分支,然后删除工作树和分支。
- 让智能体把工作提交到它的分支上。
- 在主检出目录中,像合并其他分支一样合并它。
- 删除工作树。Git 只删除干净的工作树:没有未跟踪的文件,已跟踪的文件也没有改动。
- 删除已合并的分支。只要还有工作树检出着这个分支,Git 就拒绝删除它,所以要先删除工作树。
cd ../myapp
git merge agent/api
git merge agent/tests
git worktree remove ../myapp-api
git worktree remove ../myapp-tests
git branch -d agent/api agent/tests如果 git worktree remove 因为工作树中有改动或未跟踪的文件而拒绝执行,就提交或移走你想保留的内容,或者加上 --force 丢弃它们。如果工作树文件夹是被手动删除的,git worktree list 会把它标记为 prunable,git worktree prune 会清除残留的记录(git worktree prune -n 只显示将要清除的内容)。
使用工作树时会遇到哪些问题?
大多数问题来自已经被检出的分支、Git 不跟踪的文件,以及各个工作树仍然共享的东西。
- 同一个分支不能检出两次。
git worktree add拒绝使用另一个工作树已经检出的分支,在工作树中执行git switch也一样。主检出目录所在的main也包括在内,所以要在主检出目录中合并,而不是把某个工作树切换到main。用-b为每个智能体创建新分支。 - 被忽略和未跟踪的文件不会被复制。 新工作树只包含 Git 跟踪的文件。
.env、node_modules、构建产物和本地配置都留在它们被创建的那个检出目录中,所以要在每个工作树中复制或重新安装。 - 被忽略的文件会被直接删除,不会询问。 只有改动过或未跟踪的文件会阻止
git worktree remove。一个工作树里如果只有你复制进去的被忽略的.env,它会和工作树一起被删除(在 Git 2.56.0 上测试)。 - 端口冲突。 每个工作树都是项目的完整副本,所以在其中两个里启动开发服务器,就会启动两个服务器。如果两者监听同一个固定端口,第二个会启动失败。为每个工作树设置自己的端口,例如写在它自己的
.env中。 - 直接删除文件夹会留下记录。 用
rm删除工作树目录后,它的记录会留在仓库中,直到运行git worktree prune或被 Git 自动过期清除。请改用git worktree remove。 - 对子模块的支持不完整。 Git 的文档称多个检出目录仍属实验性功能,对子模块的支持不完整,并且不建议对超级项目做多个检出。包含子模块的工作树需要加
--force才能删除。
Claude Code 会自己创建工作树吗?
会。它的工作树参数会创建工作树和分支,并在其中启动会话。
根据 Claude Code 的文档,claude --worktree feature-auth(或 -w)会在仓库根目录的 .claude/worktrees/feature-auth/ 下创建一个工作树,位于名为 worktree-feature-auth 的新分支上。在第二个终端中换一个名字再运行一次,就得到第二个隔离的会话。同一页面上的几点细节:
- 新工作树从仓库的默认分支创建,除非
worktree.baseRef设置为"head"。 - 项目根目录下的
.worktreeinclude文件使用.gitignore语法,会把匹配的被 Git 忽略的文件(例如.env)复制到每个新工作树中。 - 退出时,未命名会话的干净工作树会连同分支一起被删除;有改动或提交的工作树会询问你保留还是删除。
- 在 frontmatter 中设置了
isolation: worktree的自定义子智能体,总是在自己的临时工作树中运行。
文档还建议把 .claude/worktrees/ 加入 .gitignore,这样这些工作树就不会在主检出目录中显示为未跟踪的文件。
一个检出目录、工作树、克隆还是 MAO 图层,哪种更好?
工作树和图层让每个智能体拥有自己的文件,同时共享一个仓库;克隆隔离得更彻底,但需要 push 和 fetch 才能共享工作。
MAO 的图层如何使用工作树?
MAO 中的图层是一个位于自己分支上、有自己画布的 Git 工作树,在导航栏中创建、切换和合入。
- 创建。 在导航栏的图层栏中选择 New layer,输入图层名和分支名。你可以复制 Ground 的布局作为起点;复制的节点以草稿开始,绕过权限的启动选项不会被复制。
- 工作。 在图层栏中切换到该图层,并在上面放置智能体。切换会改变当前的画布和工作树,但不会合并任何内容。图层的画布保存在 MAO 自己的状态中,而不是工作树中的文件,所以不会出现在
git status中。 - 汇报。 图层上的智能体可以用
mao layer status发布工作进展。标记为blocked或review的图层会被标记为需要关注。 - 合入。 切换到 Ground,在图层的菜单中选择 Land…。MAO 会检查冲突和未提交的改动,并在合并或快进之前显示目标分支。它从不重置分支、强制更改或暂存你的工作,如果检查之后有分支发生了移动,它会拒绝执行。
- 归档。 合入后的图层会被归档,除非你打开 Keep the layer。归档可以撤回;没有删除图层的命令。
智能体在自己的终端中发布所在图层的状态。被标记为编排者的智能体还可以创建、合入、归档和恢复图层,并把新的智能体放到某个图层上:
mao layer status --state review --progress 3/5
mao layer land "api"
mao layer archive "api"
mao layer restore "api"常见问题
不可以。git worktree add 拒绝使用另一个工作树已经检出的分支,在工作树中执行 git switch 也会拒绝。用 -b 为每个工作树创建一个新分支。
不会。新工作树只包含 Git 跟踪的文件,所以 .env、node_modules 这类被忽略的文件需要在每个工作树中复制或重新安装。Claude Code 的 .worktreeinclude 文件可以把被 Git 忽略的文件复制到 Claude Code 创建的工作树中。
不一样。克隆是一个独立的仓库,有自己的分支和对象,克隆之间通过 push 和 fetch 传递工作。工作树共享一个仓库,所以在一个工作树中的提交会立刻在所有工作树中可见。
Git 会保留这个工作树的记录,git worktree list 会把它标记为可清理。运行 git worktree prune 清除这些记录,下次请使用 git worktree remove。
会。MAO 中的每个图层都是位于自己分支上的 Git 工作树,在你选择 New layer 并输入分支名时创建。MAO 检查冲突和未提交的改动之后,你就可以在画布上把它合入 Ground。
资料来源
- git-worktree 文档:主工作树和链接工作树以及它们共享的内容,
add -b,拒绝两次检出同一个分支,list和--porcelain,remove和--force,prune,以及关于子模块的说明。 - git-clone 文档:克隆是一个新的仓库,带有指向原仓库的远程跟踪分支。
- Claude Code:用工作树运行并行会话:
--worktree、.claude/worktrees/、worktree-分支名、worktree.baseRef、.worktreeinclude、退出时的清理,以及子智能体的isolation: worktree。 - 创建工作树图层:New layer、图层名和分支名、复制 Ground 布局。
- 将图层合入 Ground:合入前的检查、Keep the layer、合入后归档。
- 设置图层状态、在图层之间切换和归档和恢复图层:状态类型、切换而不合并、归档和恢复。
- MAO 0.8.0 更新说明:合入前的试运行,不重置、不强制、不暂存,以及编排者管理图层。
- MAO 0.5.20 更新说明:图层的画布保存在 MAO 的状态中,而不是工作树中。
- 本页的命令在一个测试仓库中用 Git 2.56.0 在 Linux 上运行过。
检查于 2026-10-28。
有问题?可以在 Discord 提问,或发邮件到 hello@miguer.com。