最近在折腾 AI Agent 并行开发我手头同时在跑三个 Agent 任务一个在改支付模块一个在重构用户认证还有一个在修线上崩溃的 bug。三个任务互相之间没有依赖但都对着同一个 Git 仓库下手。最开始我天真地以为开三个终端、切三个分支就能搞定结果第一天就被各种工作区污染、切换冲突、代码互相踩踏折磨到怀疑人生。直到我把 Git Worktree 用起来又迭代着写了个叫 Worktrunk 的命令行工具来管理这套流程才算是真正把并行 Agent 工作流跑顺了。这篇东西不打算讲太多虚的就把我这几周踩坑、设计、落地 Worktrunk 的过程拆开揉碎讲一遍。你会看到为什么 AI Agent 并行开发离不开 worktreeWorktrunk 到底解决了什么问题完整的使用流程是什么样以及我在实际操作中遇到的各种破事和排查思路。如果你也在用 Claude Code、Codex CLI 这类 AI 编程工具并且试图让多个 Agent 同时在同一个仓库里干活这篇文章应该能帮你少走不少弯路。1. AI Agent 并行开发为什么需要 Git Worktree1.1 多个 Agent 同时干活第一道坎是工作区冲突先说一个最原始的场景。假设你只有一个仓库目录大概长这样my-project/ ├── src/ ├── tests/ ├── package.json └── .git/现在你想让 Agent A 去实现支付模块的退款功能Agent B 去重构用户认证的中间件Agent C 去修一个日志系统的崩溃。三个 Agent 如果都在这个项目目录里工作问题马上就来了。Agent A 改src/payment/refund.ts的时候Agent B 正在跑测试测试代码刚好引用了src/auth/middleware.ts而 Agent A 的改动可能不小心触碰了公共的src/utils/目录。这些改动全部发生在同一个工作区里互相之间没有任何隔离。最要命的是AI Agent 生成代码的时候经常是大段大段地重写文件它不会像人一样小心翼翼地只改自己负责的那几行。一个 Agent 跑了十几分钟可能把你另一个 Agent 刚改好的文件整个覆盖掉。我最早试过用分支切换来隔离。Agent A 在feature/payment-refund分支Agent B 在refactor/auth-middleware分支干完一个分支就切回主干再切下一个。听起来可行但实际上有三个问题切换分支的时候工作区里未提交的改动会被 Git 拦下来报错或者被 stash 走Agent 不知道发生了什么进程直接挂掉。仓库里如果有构建产物、缓存、临时文件切分支前后这些文件状态不一致Agent 跑测试的时候会得到莫名其妙的结果。三个 Agent 同时占着同一个物理目录根本没法并行只能排队。结论很简单一个工作目录 多个分支的模型在并行 Agent 场景下完全不成立。你需要的是多个物理隔离的工作目录。1.2 Git Worktree 的核心机制和它的天然优势Git Worktree 不是什么新东西它的核心能力是同一个 Git 仓库可以拥有多个物理目录每个目录 checkout 不同的分支彼此独立工作。git worktree add ../my-project-feature-payment feature/payment-refund这条命令会在my-project的同级目录下创建一个my-project-feature-payment文件夹里面是完整的代码副本checkout 的是feature/payment-refund分支。所有 worktree 共享同一个.git对象数据库但各自拥有独立的索引、HEAD 和工作区。这对 AI Agent 工作流来说几乎是量身定做的因为每个 Agent 拥有一个完全独立的目录改什么都不会影响别的 Agent。所有 worktree 共享仓库的 commit 对象互相之间做 merge 的时候不需要 push 到远程直接本地引用对象就能合并。分支之间天然隔离Agent 跑测试、装依赖、重启服务都在自己的沙箱里进行。可以随时新增和销毁 worktreeAgent 任务结束就删掉仓库主干一直保持干净。我实测下来三个 Agent 并行跑每个都有自己的 worktree 目录互相之间零干扰。Agent A 在改支付模块的时候把src/utils/里一个公共函数重命名了Agent B 的工作区里完全感知不到它继续用旧名字跑测试一切正常。这就是物理隔离带来的确定性。1.3 原生 Git Worktree 命令的体验瓶颈既然 Git Worktree 这么强为什么还需要 Worktrunk 这个 CLI因为原生命令在 Agent 工作流场景下有几个很现实的短板。第一个短板是命令又长又碎。创建一个 worktree 要指定目录、指定分支、有时候还要先创建分支git worktree add -b feature/payment-refund ../my-project-feature-payment如果目录名和分支名不一致或者忘了加-b会各种报错。手动管理十个八个 worktree 的时候光记这些路径就能让人崩溃。第二个短板是状态管理靠人的记忆。你创建了四个 worktree分别对应四个 Agent 任务每个任务进行到哪一步、有没有未提交的改动、分支落后主干多少 commit这些信息原生 Git 命令不能给你一个清晰的概览。你只能一个个git -C path status去查效率极低。第三个短板是和 AI Agent 的会话模型不匹配。Agent 任务通常有生命周期创建任务、运行、暂停、恢复、完成、合并、清理。原生 Git Worktree 只有增删改查没有工作区会话这个概念。你需要在外面套一层自己的管理逻辑。所以我做了 Worktrunk本质上是一个把 Git Worktree 包装成Agent 工作区会话管理的 CLI 工具。它不是要取代 Git Worktree而是在上面加了一层面向并行 AI Agent 工作流的抽象。2. Worktrunk 的功能设计与核心使用场景2.1 核心命令设计让 worktree 管理像会话管理一样简单Worktrunk 的命令设计目标很简单让一个没有用过 worktree 的开发者也能在三分钟内上手。我把所有操作收敛成了五个核心命令对应 Agent 工作流的五个生命周期阶段。命令作用对应工作流阶段worktrunk create name [--base main]创建新 Agent 工作区任务启动worktrunk list查看所有工作区状态任务监控worktrunk enter name进入指定工作区目录任务执行/接管worktrunk merge name合并工作区分支到主干任务收尾worktrunk remove name销毁工作区任务清理每个工作区有一个名字这个名字就是 Agent 任务的名字比如feature/payment-refund、fix/auth-timeout。Worktrunk 会自动根据名字推导分支名和目录名你不必关心底层的路径细节。举个例子启动一个退款功能任务worktrunk create payment-refund --base main这条命令会做三件事基于main创建分支worktrunk/payment-refund在worktrees/payment-refund/目录下 checkout 该分支然后把这个工作区的元数据分支名、目录路径、创建时间、关联任务名写入 Worktrunk 自己的状态文件。之后你想进入这个工作区干活worktrunk enter payment-refund实际执行效果是切换到该 worktree 的目录同时自动设置好环境变量和 shell 提示符告诉正在使用的 Agent 或者你自己当前所处的工作区上下文是什么。这样你用同一个终端窗口在不同 Agent 任务之间来回切换不会搞混状态。list命令的输出是我比较得意的地方$ worktrunk list NAME BRANCH PATH STATUS AHEAD/BEHIND UPDATED payment-refund worktrunk/payment-refund worktrees/payment-ref/ in-progress 3/-1 12min ago auth-refactor worktrunk/auth-refactor worktrees/auth-refact/ in-progress 8/-0 45min ago hotfix-crash worktrunk/hotfix-crash worktrees/hotfix-cras/ paused 1/-2 2h ago一眼就能看出哪个任务在推进、哪个任务落后了主干、哪个任务停滞了。这在同时跑多个 Agent 的时候特别重要你不可能一个目录一个目录地切进去看状态。2.2 面向 Agent 的会话模型分支、目录、元数据三位一体Worktrunk 和单纯包一层 Git Worktree 命令的最大区别是它引入了工作区会话这个抽象。一个工作区会话由三部分组成分支、目录、元数据。分支是底层的 Git 隔离单元Worktrunk 统一用worktrunk/task-name的命名规范避免和团队的其他分支命名冲突。目录是 Agent 实际操作的文件系统空间Worktrunk 统一放在仓库根目录下的worktrees/task-name/下。元数据是 Worktrunk 自己维护的 JSON 状态文件记录这个工作区是什么时候创建的、基于哪个主干分支、对应的 Agent 任务描述、最后一次活跃时间。为什么要单独维护元数据因为 Agent 任务有上下文。比如你用 Claude Code 跑一个重构任务任务描述写了把 user service 里的所有回调改成 async/await。这个任务描述应该和工作区绑定这样工作区创建三天后你回来看还能知道当初这个 Agent 在干嘛。Git 本身不存这些信息分支名再规范也存不下完整的任务描述。元数据文件默认放在.worktrunk/state.json结构大概是这样{ workspaces: [ { name: payment-refund, branch: worktrunk/payment-refund, path: worktrees/payment-refund, baseBranch: main, createdAt: 2025-06-10T14:23:0008:00, lastActiveAt: 2025-06-10T16:02:0008:00, status: in-progress, description: 实现退款功能支持部分退款和全额退款 } ] }这个文件的妙处在于它既是人类可读的也是机器可读的。你可以直接打开看一眼也可以让脚本读它做自动化。我后面就写了一个小脚本每天下班前自动扫描lastActiveAt超过 24 小时没动过的工作区提醒我哪些 Agent 任务可能卡住了。2.3 与 AI 编程工具链的配合方式Worktrunk 本身不绑定任何特定的 AI Agent 工具但它设计上考虑了几类常见的配合方式。第一类是直接让 Agent 工具在 worktree 目录里运行。以 Claude Code 为例你只需要在 worktrunk 工作区目录下启动worktrunk enter payment-refund cd $(worktrunk path payment-refund) claudeClaude Code 会在当前目录里感知到这是一个 Git 仓库它的文件读取、修改、git commit 操作全都在这个 worktree 内完成。因为 worktree 的分支是独立的Agent 的每一次 commit 都只影响自己的分支完全不干扰主干和其他 Agent。第二类是把 Worktrunk 作为 Agent 的工具来调用。现在很多 Agent 框架支持自定义工具函数你可以把worktrunk create、worktrunk list包装成工具让 Agent 自己管理自己的工作区。比如一个编排型 Agent 收到三个任务它可以自己调用 Worktrunk 创建三个工作区然后把每个任务分别交给一个子 Agent 去执行。这样就实现了 Agent 层面的任务编排和仓库层面的工作区隔离。第三类是结合 CI/CD 的自动化流程。Worktrunk 的merge命令可以放到 CI 里执行实现Agent 完成任务后自动合并到主干并部署测试环境。我在一个内部项目里就是这样做的Agent 在 worktree 里开发完推送分支CI 检测到worktrunk/name分支有新 commit自动跑测试测试通过后调用worktrunk merge name合回主干然后销毁 worktree。整个流程不需要人手动介入。这里有一个关键的设计取舍值得展开讲。我一开始想过让 Worktrunk 直接封装 Agent 工具的启动比如worktrunk run name 任务描述这种用法内部帮你启动 Claude Code 或者 Codex CLI。但后来放弃了原因是工具链变化太快不同的 Agent 工具有不同的启动参数、交互方式、认证机制Worktrunk 如果深度绑定某一个工具很快就会过时。保持只做仓库层隔离不做 Agent 层绑定这个边界反而让工具的存活时间更长也更符合 Unix 哲学里每个工具做好一件事的原则。3. 实操全流程从零搭建一套并行 Agent 工作流3.1 安装和环境准备Worktrunk 的安装依赖两个东西Git 2.30 以上版本因为要用到git worktree的一些新特性以及你正在用的 Shell。我用的是 zsh但 bash、fish 也都能用。安装方式很简单如果你有 Node.js 环境可以通过 npm 全局安装npm install -g worktrunk或者用我打的二进制包直接解压到PATH目录下就行。安装完验证一下worktrunk version输出版本号说明安装成功。接下来在你自己的项目仓库里初始化cd my-project worktrunk init这会在项目根目录创建.worktrunk/目录和state.json文件同时把 Worktrunk 的默认 worktree 根目录配置为worktrees/。如果你不想让 worktrees 目录被 Git 跟踪记得把它加进.gitignoreecho worktrees/ .gitignore echo .worktrunk/state.json .gitignore这里我踩过一个坑.worktrunk/state.json如果被提交进仓库多个人协作的时候会互相覆盖。因为 worktree 的元数据是本地状态不是项目内容它应该只在你的本地机器上存在。除非你的团队真的需要共享工作区状态目前我没有遇到这种需求否则务必把它忽略掉。3.2 创建并配置第一个 Agent 工作区环境准备就绪后我们模拟一个真实场景假设当前仓库主干分支是main你接到两个任务——实现退款功能修复认证超时 bug。你要让两个 Agent 并行处理这两个任务。先创建第一个工作区worktrunk create payment-refund --base mainWorktrunk 会输出类似这样的信息Created workspace payment-refund branch: worktrunk/payment-refund path: worktrees/payment-refund/ base: main (a3f2c9e)此时实际的 Git 操作已经完成git worktree add -b worktrunk/payment-refund ../worktrees/payment-refund main被在内部执行了。你可以验证一下git worktree list看到的输出里应该多了一行指向worktrees/payment-refund的记录。接着创建第二个工作区worktrunk create fix-auth-timeout --base main两个工作区里各有一个目录。现在你想让 Agent A 处理退款让 Agent B 处理认证超时。你分别进入目录启动对应的 Agent 工具终端一worktrunk enter payment-refund终端二worktrunk enter fix-auth-timeout注意worktrunk enter有一个细节它在进入目录的同时会设置一个环境变量WORKTRUNK_WORKSPACE并且把 shell 提示符前缀改成工作区名。这样你在终端里一眼就能看出自己当前在哪个 Agent 工作区。3.3 并行运行多个 Agent 的实测记录两个终端分别启动了 Claude Code 之后我给它们分别下达了任务。Agent A 的任务是在 payment 模块中实现 refund 功能支持全额退款和按订单项退款要求添加单元测试。Agent B 的任务是排查 auth/token 校验中 5 分钟超时导致用户频繁掉线的问题并修复。这时候有趣的事情发生了。两个 Agent 都在自己的工作区里创建了分支、修改代码、提交 commit但它们的操作完全互不可见。Agent A 在运行测试的时候需要启动一个本地服务占用了 3000 端口。Agent B 的测试也默认用 3000 端口结果冲突了。这是工作区隔离解决不了的问题——端口是机器级的资源不是文件系统的资源。我的解决办法是在每个 worktree 里设置不同的环境变量让测试服务监听不同端口。Worktrunk 支持在创建工作区的时候注入环境变量worktrunk create payment-refund --env PORT3100 worktrunk create fix-auth-timeout --env PORT3200这些环境变量会记录在工作区元数据里worktrunk enter的时候自动加载。这样就解决了端口冲突。类似的问题还有如果 Agent 需要在本地写临时文件、缓存文件建议在 worktree 目录里就用相对路径别用绝对路径写死到/tmp或者用户目录否则多个 Agent 之间可能互相覆盖缓存。一个多小时跑下来我的worktrunk list输出是这样的NAME BRANCH STATUS AHEAD/BEHIND UPDATED payment-refund worktrunk/payment-refund in-progress 12/-1 3min ago fix-auth-timeout worktrunk/fix-auth-timeout in-progress 6/-0 8min ago两个 Agent 都在持续提交代码互相之间零干扰。这在以前用单目录切分支的方式下是完全不可能实现的。3.4 合并回主干与清理工作区Agent 任务完成之后就是收尾环节。我建议的流程是先 review 再 merge、最后清理。Review 的时候我习惯直接看 diffworktrunk diff payment-refund这本质上是在主干和该工作区分支之间做 diff。看完确认没问题执行合并worktrunk merge payment-refund内部执行的是git merge worktrunk/payment-refund --no-ff。如果合并过程有冲突Worktrunk 会停下来输出冲突文件列表并且告诉你这个工作区现在还不会被销毁等你处理完冲突再跑一次 remove 就行。合并成功后清理工作区worktrunk remove payment-refund这会同时删除分支、删除 worktree 目录、清理元数据。三步合一步避免你漏掉哪一步导致本地的 worktree 记录残留越来越多。我实测连续搞了十几个 Agent 任务之后git worktree list的输出干净利落没有被废弃的 worktree 堆积。这在用裸 Git 命令的年代是做不到的——我经常记不清哪个 worktree 是哪个任务的也不敢乱删最后越积越多。4. 常见问题与排查技巧实录4.1 高频问题速查表用 Worktrunk 跑并行 Agent 工作流我在实际使用中遇到了一堆问题整理成了一张速查表先把最常见的列出来症状可能原因解决办法创建工作区提示分支已存在上次任务没清理干净分支残留先跑worktrunk remove name清理或者手动删掉残留分支worktrunk enter后提示目录不是 Git 仓库worktree 目录被误删但元数据还在跑worktrunk repair name重新关联合并时报大量冲突主干分支在 Agent 工作期间被其他合作者推进了很多先git merge main把主干合进来解决冲突后再合并回去Agent 跑测试时访问了错误的数据多个 worktree 共享了同一个 .env 文件确认 .env 里没有写死端口和数据库连接用工作区注入的环境变量覆盖磁盘空间不足worktree 目录里装了各自的 node_modules体积巨大可以配置只读共享的依赖目录见下文git worktree list显示 expired 的 worktree之前手动删过目录用git worktree prune清理失效记录4.2 踩坑记录三个让我印象深刻的真实案例讲三个我实际踩过的坑比速查表更有参考价值。第一个坑是依赖安装的磁盘爆炸。我一个 monorepo 项目node_modules单份装完要 2GB 多。Worktrunk 默认每个 worktree 都是独立目录Agent 装依赖的时候各自 install 一份四个 worktree 就是 8GB 多磁盘直接红了。后来我采用了一个折中方案主干目录里正常安装一份依赖然后所有 worktree 不装依赖而是通过环境变量把NODE_PATH指向主干目录的node_modules。但这个方法只在某些工具下有效Webpack 和 Vite 不一定认NODE_PATH最后我换成了在 worktree 里用符号链接把node_modules指到缓存目录实测兼容性最好。具体做法是mkdir -p /path/to/shared-node-modules cd worktrees/payment-refund ln -s /path/to/shared-node-modules node_modules要注意的是如果你的项目里有原生模块比如bcrypt、sharp这类需要编译的依赖符号链接共享可能会有平台兼容问题这种情况还是老老实实每个 worktree 单独安装或者用 pnpm 这类支持硬链接的包管理器能省不少空间。第二个坑是 Agent 提交的 commit 没有关联到正确的任务。有一次 Agent A 在payment-refund工作区里跑但它的 commit message 写的是fix auth timeout bug。原因是这个 Agent 是我从上一个任务的终端里直接复制的它读取上下文的时候把任务描述搞混了。解决方案是 Worktrunk 新增了一个功能在创建工作区时把任务描述写入工作区的元数据并通过环境变量WORKTRUNK_TASK_DESCRIPTION暴露给 Agent同时我设置了 Git 的commit.template让 Agent 每次提交都能看到任务描述提醒。这个改动之后commit message 和任务对应关系基本不会再乱。第三个坑是主干分支被其他同事推进后Agent 的分支没有同步导致最后合并时出现大量重复冲突。这个问题本质上是并行分支的同步策略问题。我的做法是写了一个定时任务每三十分钟跑一次worktrunk sync --all这个命令会把所有存量工作区的分支都 rebase 到最新的主干上。如果 Agent 正在进行中的工作被 rebase 打断了它自己会感知到部分文件冲突但这总比最后一次性合并几百个冲突文件要好处理得多。后来我发现 Claude Code 这类 Agent 工具在处理 rebase 冲突方面比较弱所以我改成了每隔一段时间人工确认一下是否同步或者干脆在创建工作区的时候指定--no-sync让任务短平快不来回同步。4.3 让并行 Agent 工作流更顺滑的五个小技巧积累了这些经验之后我总结出了五个小技巧能大幅度提升并行 Agent 工作流的顺滑程度。第一命名规范要严肃。工作区名字尽量用[类型]-[模块]-[简述]的格式比如feature-payment-refund、fix-auth-timeout、refactor-user-service。名字就是任务的身份证Worktrunk 会把名字用作分支名的一部分规范的命名在worktrunk list里扫一眼就知道全局状况不需要额外去看任务管理软件。第二任务描述一定要写清楚。在创建工作区的时候用--description 实现退款功能支持部分退款和全额退款把任务描述写进元数据。这不仅是给人看的也是给 Agent 看的——我可以让 Agent 在开始干活之前先读一遍工作区描述避免它跑偏。第三尽量让一个工作区只做一件事。我见过有人让一个 Agent 在一个 worktree 里同时干两个任务理由是反正目录都是隔离的多干点省事。但这样一旦任务 A 完成、任务 B 还在进行你没法单独合并任务 A 的成果——它们的代码混在同一个分支里。遇到这种情况只能用 cherry-pick 手动挑提交非常痛苦。正确的做法是宁可在创建和合并上多花两分钟也要保持一个工作区对应一个独立任务。第四给 Agent 设置好 Git 身份。默认情况下Agent 提交用的可能是全局 Git 配置里的身份所有任务的 commit 都长一个样。我建议在不同工作区里设置不同的 user.name 和 user.email这样后续审查的时候一眼就能看出哪次提交是哪个 Agent 干的。在 Worktrunk 里可以这样配置worktrunk create payment-refund --git-user agent-payment --git-email agent-paymentexample.com内部实现是设置该 worktree 的local.gitconfig不影响全局配置。第五把 worktree 的目录放在 SSD 上。这个看起来像废话但真的很多人忽略了。多个 Agent 并行跑每个都要编译、跑测试、读写大量小文件如果 worktree 目录在机械硬盘上I/O 会变成最大瓶颈。我把 worktrees 根目录用符号链接指向了 SSD 分区之后整体任务吞吐量提升了接近一倍。5. 后续可以怎么扩展这套工作流跑了大概三周 Worktrunk 之后我自己用出了一点新的心得也给这个项目规划了下一步的方向这里分享一下。Worktrunk 目前是单机工具工作区的状态只存在于本地。我最近在琢磨怎么让它支持团队协作场景比如一个 Agent 在本地 worktree 开发完推送分支到远程之后另一个开发者在别处能一键把这个分支变成自己的 worktree并且带上原来的任务描述。这个功能其实不复杂只需要把.worktrunk/state.json里的部分元数据任务描述、创建时间、关联的 issue 编号写进分支的 commit message 或者推送到远程的一个独立分支里就能实现跨机器同步。我现在是手动把这些信息放进分支名的后缀里勉强能用但不够优雅。另一个想法是把 Worktrunk 和测试编排结合起来。现在 Agent 在不同 worktree 里开发完合并到主干之前通常要在自己目录里跑一遍测试。但有些集成测试需要两台服务同时跑单靠一个 worktree 的环境变量注入不够用。我打算设计一个worktrunk topology的概念允许定义一组工作区之间的网络关系让多个 Agent 开发的服务可以互相调用直接在本地模拟完整的分布式环境。这个方向做出来之后并行 Agent 工作流就能覆盖更复杂的场景了。最后再说一个小技巧收尾。如果你像我一样每天要开很多终端窗口建议给 Worktrunk 配一个 shell 的 aliasalias wtworktrunk alias wteworktrunk enter alias wtlworktrunk list alias wtmworktrunk merge配合 zsh 的自动补全worktrunk enter Tab能列出所有工作区名字切换起来非常顺手。我在实际使用中最依赖的就是这个补全功能它让我在管六个并行任务的时候不会记错工作区的名字。工具这种东西不管底下技术多复杂最终打动用户的永远是这些细节体验。