
上个月我接了个有点狼狈的活儿一会儿让 Codex CLI 去重构用户中心的鉴权逻辑一会儿让 Claude Code 去处理支付回调的异常还顺手开了个任务让 Trae CLI 补接口测试。听起来是并行提效对吧实际操作起来完全不是那么回事。只要其中一个 Agent 说“我要动一下公共库”接下来就是无休止的 “You have uncommitted changes” 和分支切换冲突。切来切去最严重的一次是三个 Agent 的会话上下文全部乱套其中一个把没写完的半成品直接 commit 到了 main 上。后来我干脆写了个小工具去管这件事就是标题里的 Worktrunk。它是一个建立在 Git Worktree 原生机制之上的命令行工具专门面向并行 AI Agent 工作流做分支管理、任务编排和上下文隔离。这篇文章把它的设计思路、核心机制、完整实操和踩坑记录都捋一遍适合正在重度使用 Codex CLI、Claude Code、Trae CLI 这类 Agent 工具又苦于多任务并行时分支混乱的开发者。先说明白我这里讲的是真实项目经验工具本身的原理不复杂核心是把 Git Worktree 这个被低估的能力挖出来再围绕 Agent 的工作习惯做了一层编排。1. AI Agent 并行工作流的痛点到底在哪1.1 单分支切换带来的三连烂摊子如果你只用一个工作目录同时塞给 AI Agent 多个任务最常见的场景是这样的第一个 Agent 改到一半第二个 Agent 提了一个新需求。你切到新分支Git 当场拒绝因为工作区有未提交的修改。这时候你有几个选择commit、stash或者带着脏文件切分支。带着脏文件切到别的分支是所有灾难的源头——Agent 会看到一堆不属于这个任务的改动然后自作主张去“修”它们或者更糟把这些改动当成已有代码去分析最后产出一堆莫名其妙的建议。commit 也不安全。多个任务混在同一个分支里commit 历史会变成一锅粥。我见过有人让 Agent 连续处理五个小需求最后 main 分支上出现了十几个“fix bug”的提交鬼知道每个提交改了什么。stash 是另一个坑它的本质是把工作区改动暂存起来但多个任务的 stash 堆在一起恢复的时候你根本分不清哪个是哪个人干的。更麻烦的是 Agent 的上下文。Codex CLI 会在项目目录下维护会话记录Claude Code 也有自己的状态文件。你切换分支、切换任务这些状态文件跟着工作区走Agent 的记忆就被切断了。它刚分析了“用户中心的重构方案”你一告诉它“现在处理支付回调”它可能还记着上一件事的思路紧接着输出一段前言不搭后语的东西。这不是模型能力的问题是工作流的设计有缺陷。1.2 为什么 stash、裸 clone 都救不了场面有人说既然单目录切换麻烦那我用裸 clone 总可以吧每个任务一个完整 repo物理隔离总不会有冲突了。这个方案看起来没问题实际操作起来也是一堆事每个 clone 都是一个独立的 Git 仓库主仓库新增的分支和提交不会自动同步过去你得不厌其烦地 fetch配置、密钥、环境变量每个 clone 都要单独设置一遍最要命的是如果你在某个 clone 里改了依赖版本另外一个 clone 不会知道最终合并的时候冲突能让你怀疑人生。stash 的问题前面说了它本质上是在同一个工作目录上做“暂存”多个任务同时存在就会互相污染。还有一个容易被忽略的问题stash 不影响 untracked filesAgent 生成的临时文件经常是 untracked 状态你 stash 了也没用它们还是留在工作区里碍事。Git Worktree 解决的就是这个“既要物理隔离、又要共享仓库”的矛盾。它的原理是在同一个仓库下创建多个工作目录每个工作目录对应一个独立分支但共享同一个 .git 目录。你可以同时 checkout 到不同分支各自改各自的互相之间没有任何干扰。提交之后这些 commit 都在同一个对象库里合并只是时间问题。我一直觉得 Git Worktree 是 Git 里最被低估的功能之一它明明能把“单仓库多任务并行”这件事做得非常优雅但大多数人不了解它或者只在极少数场景下用一下。Worktrunk 干的事就是把这个能力做成一整套面向 AI Agent 的工作流。2. Worktrunk 的设计围绕 Agent 习惯打造的 CLI2.1 任务即分支粒度控制是第一原则Worktrunk 的第一个设计原则落地很直白一个任务对应一个 worktree一个 worktree 对应一个分支。任务粒度完全由开发者自己控制。你可以用worktrunk task new创建一个任务它内部会在指定基线上创建分支并建立对应 worktree。典型的分支命名规范是“类型/模块-描述”比如 feat/backend-user-auth、fix/payment-timeout、test/api-coverage。我不建议用 Agent 的名字去命名分支比如 codex-fix-bug 这种因为一个 Agent 可能连续处理多个任务分支对应任务而不是对应 Agent以后合并、回溯、code review 都更清晰。为什么要强调粒度控制因为 Agent 的处理能力有限任务太大会导致它“丢失注意力”反而产出质量下降。我实测下来的经验是一个任务最好控制在“改动 3-5 个文件、涉及一个业务模块”的范围内。任务太长中间插入需求变更时一个分支上的改动会严重漂移最后合并时你根本不知道这个分支到底改了什么。Worktrunk 在创建分支时还会记录基线 commit。这个设计是有用的后续做 diff 对比、代码走查、CI 集成你总是需要知道“这个任务是从哪里开始的”这样才不会把主分支上的新改动误判成任务代码。2.2 会话状态缓存让 Agent 记得住这是 Worktrunk 比较核心的设计。AI Agent 工具会在项目目录下生成会话状态目录Codex CLI 是.codexClaude Code 是.claude里面保存了对话记录、计划、配置等。问题是这些状态目录是在工作目录内的如果你为每个任务新建了一个 worktree每个 worktree 默认都不会有这些目录Agent 一开始是“失忆”的。Worktrunk 的做法是引入一个全局的会话缓存层。它默认在~/.worktrunk/runtime/下按任务保存一份 Agent 运行时状态并通过符号链接的方式挂载到对应 worktree 的.codex和.claude目录上。这样一来即使你删掉了 worktree或者换了一台机器重新拉取任务Agent 的上下文都还在。有人会问所有任务都共享同一个.codex不行吗我试过不行。多个 Agent 同时跑的时候它们会互相覆盖对方的会话状态最后输出的内容变成“四不像”。每个任务独立保存状态才能保证上下文真正隔离。这一点在多 Agent 并行的时候尤其重要。需要注意的是并不是所有状态都适合隔离。比如 Git 凭证、npm token、API key 这类全局配置Worktrunk 全部放在用户级目录不跟随任务走。这样既保证了安全又避免了重复配置的麻烦。2.3 快照恢复机制崩溃了也有后悔药AI Agent 不是每次都能正确完成任务。我见过 Agent 把一整个文件删掉重写结果语法错误一堆也见过它改了配置文件的格式导致服务起不来。人写代码会犯错Agent 写代码同样会犯错而且很多时候你还没注意到它错了。Worktrunk 的快照机制本质是在任务的关键节点保存一份引用级的备份。执行worktrunk snapshot save时它会基于当前 worktree 的 HEAD 创建一个临时 commit然后记录这个 commit 的 hash。这个操作非常轻量因为 Git 对象存储本身就有去重机制不会占用太多空间。恢复的时候worktrunk snapshot restore会直接重置工作区到快照状态。相比手动复制文件这种方式保证所有改动都能被回滚干净包括新增文件和删除的文件。关键节点建议在任务开始前、Agent 提交大改动前以及一天工作结束前都打一个快照反正是瞬时操作不心疼。2.4 收尾清理让合并不再头疼任务完成的收尾阶段Worktrunk 做了一套默认的合并策略。执行worktrunk task merge时它会基于你创建任务时的基线 commit 做 diff然后以 squash 的方式合并到主分支。squash 会把这个任务的所有 sub-commit 压成一个主分支历史保持线性这个问题在多人协作时尤其重要。消息默认是“任务名 任务描述”你也可以用--message覆盖。如果主分支在你工作期间有新的提交Worktrunk 会先做 rebase 再合并避免出现拉扯式冲突。干净的单 commit 还有一个好处reviewer 只需要看一个 diff也不需要把一段“改了加加了又改回去”的历史翻来覆去地看。合并完成之后worktrunk task cleanup会把 worktree 和临时分支一起清理掉。注意这个操作不会删除已经合并的代码只是把工作目录和分支引用清掉。这样你的工作区始终保持干净不会有几十个 worktree 挂在那边“吃灰”。3. 实操全过程从安装到并行跑三个 Agent3.1 安装与环境准备Worktrunk 是 Go 写的分发上只依赖一个二进制文件。macOS 上你可以直接用 Homebrew 安装Linux 和 Windows 可以从 release 页面下载对应平台的可执行文件或者用 Go 直接构建brew install worktrunk # 或者 go install github.com/worktrunk/worktrunklatest装完先初始化配置worktrunk init --platform codex--platform参数指定你常用哪一类 Agent 工具可选codex、claude、trae也支持--multi同时配置多个。init 之后会在~/.worktrunk/下生成配置文件config.yaml里面包含了任务目录的位置、会话缓存的路径以及 branch 命名的规范。默认配置不需要改就能用但如果你有自己的仓库组织方式建议花两分钟看一眼。环境要求上Git 版本最好在 2.30 以上。2.15 之前 Git 的 worktree 功能有不少坑早期版本在并发访问 git index 时容易出问题。我自己的开发机是 Git 2.39跑得很稳。3.2 创建三个任务分支我实际项目里的一个场景是主分支叫main我需要同时做三件事用户中心的重构、支付超时问题的修复、接口测试的补充。用 Worktrunk 创建三个任务worktrunk task new feat/user-center-refactor --base main worktrunk task new fix/payment-timeout --base main worktrunk task new test/api-coverage --base main执行完之后worktrunk task list的输出大概是这样的ID NAME BRANCH DIR 1 feat/user-center-refactor feat/user-center-refactor .worktrunk/tasks/feat/user-center-refactor 2 fix/payment-timeout fix/payment-timeout .worktrunk/tasks/fix/payment-timeout 3 test/api-coverage test/api-coverage .worktrunk/tasks/test/api-coverage这些 worktree 都在仓库根目录的.worktrunk/tasks/下面不会污染主工作目录。每个 worktree 都是一个完整可开发的项目目录依赖安装可以在各自目录里独立执行互不影响。3.3 并行启动 Agent 并监控状态接下来要做的是在三个目录里分别启动不同的 Agent 工具这就是真正意义上并行了。打开三个终端分别进入对应目录然后启动 Codex CLI、Claude Code、Trae CLI。Worktrunk 也提供了一个入口命令可以辅助启动worktrunk agent spawn --task feat/user-center-refactor --cmd codex worktrunk agent spawn --task fix/payment-timeout --cmd claude worktrunk agent spawn --task test/api-coverage --cmd trae这条命令会先进入对应的 worktree 目录再把该任务缓存好的会话状态挂载到.codex或.claude目录上最后执行你指定的命令。这样省去了手动 cd 和检查目录的手续。跑起来之后用worktrunk task status可以实时查看每个任务的改动情况worktrunk task status输出会显示每个 worktree 当前有哪些被修改的文件、有没有未提交的改动、工作区是否干净。我在实际使用中一般是开着这个命令的 watch 模式随时掌握三个 Agent 的进展。哪个任务卡住了、哪个任务乱改文件都是从这里第一时间发现的。3.4 验证、合并与清理三个 Agent 跑完一天的活儿之后各自都提交在各自的分支上。在合并之前我习惯先做一轮验证。合并前先跑一下任务目录里的测试和 lintworktrunk task verify --task feat/user-center-refactor -- npm run lint npm test这个命令会在指定 worktree 里执行你传给它的命令。只有验证全部通过我才会进到合并。合并的入口是worktrunk task merge --task feat/user-center-refactor --message refactor: 重构用户中心鉴权流程它会把改动 squash 成一个 commit 提交到main上并保留任务信息在 commit message 里。合并完之后执行清理worktrunk task cleanup --task feat/user-center-refactor我实测的感受是这套流程把“多 Agent 协作”从不可能变成了有章可循。以前三个任务轮流切换一天下来光处理冲突和找回上下文就要两三个小时现在规划好任务之后基本解放了我可以专心做 code review合并之前每个独立分支都是干净的review 成本也降了不少。4. 常见问题与排查技巧实录4.1 端口冲突与工作区隔离怎么避免 Agent 互相干扰并行跑多个 Agent 最容易踩的第一个坑是端口冲突。两个 Agent 都启动 dev server默认端口都是 3000第二个直接报 Address already in use。这个其实不是 Worktrunk 本身的问题而是工作流层面需要用环境变量隔离。我的做法是给每个任务的 worktree 设置独立的端口和资源配置。Worktrunk 支持在每个任务目录下放一个worktrunk.env文件启动 Agent 时会自动加载PORT3100 API_BASE_URLhttp://localhost:3200 DATABASE_URLpostgres://localhost:5432/project_task1三个任务各用各的端口谁都碰不到谁。如果你在做一个微服务相关的项目还可以通过这种方式给每个任务注入不同的服务发现配置。另外要说的是Agent 修改配置文件的时候经常自作主张跳上这些“环境档位”特别是会去改全局的配置文件。我的原则是所有跟本地运行环境相关的配置都不放在代码库里全部走环境变量或者本地忽略文件这样 Agent 再怎么折腾都不会影响其他并行任务。4.2 “worktree 目录被占用”的经典报错git worktree remove报错“not empty”或者提示“directory is busy”是高频出现的。这个报错的本质其实不复杂有进程还在这个目录里或者目录里有 untracked files 没有清理掉。排查方法先看有没有进程占用lsof D .worktrunk/tasks/fix/payment-timeout有结果就说明还有进程没有退出最常见的是 dev server 没关干净。终端里的 Agent 会话也要确认彻底退出它们可能在后台挂着监听进程。确认没有进程之后再检查有没有 untracked 文件用git status看一下。Worktrunk 的 cleanup 命令默认会提示你哪些文件会保留但如果你已经确认这些文件不需要了可以加--force强制清理。还有一个小坑是 Windows 下文件被锁定的问题某些编辑器会在后台锁定目录里的文件。遇到这种情况关掉编辑器再重试通常就解决了。4.3 主仓库 .git 目录膨胀的问题多个 worktree 共享同一个 .git 目录用久了你会发现 .git 变得特别大。原因是每个任务分支的提交、重建、再提交都会在对象库里留下大量可回收对象。Agent 写代码还有一个特点经常会把整个文件重写一遍这样产生的对象数量比人写的代码多不少。应对手段是定期执行 Git 的垃圾回收。我一般两周跑一次git gc --prunenow --aggressive跑之前确认所有工作都已经提交否则 gc 可能会把未引用的对象清理掉。Worktrunk 也在规划一个自动 gc 的策略但目前我建议你手动维护。实际操作中跑一次 aggressive gc 能把 .git 目录瘦身 40%-60%效果明显。4.4 与 Git 钩子和 CI 的兼容性这个坑比较隐蔽但影响很大。每个 worktree 都是一个独立工作目录如果你想在 .git 上配置一个统一的钩子比如 pre-commit、prepare-commit-msg它会作用到所有 worktree 上。这意味着并行任务里某个 Agent 提交时钩子也会执行有可能因为依赖没有安装而报错。我的建议是钩子脚本自己做好环境检测如果发现依赖不满足就跳过而不是直接失败。CI 方面也有一个容易踩的坑多个任务分支同时推进时如果你在 CI 上跑全量测试每个分支都会触发一次完整的构建和测试。短期没什么任务多了会造成资源浪费也容易让 CI 队列拥堵。我的做法是CI 上只构建 main 分支和 release 分支task 分支的验证全部在本地通过worktrunk task verify完成。等任务合并到 main 之后再统一跑全量流水线。这样既保证了质量又不会让 CI 爆炸。还有几个小问题比如 worktree 里新建的分支默认关联的是该 worktree 的 HEAD而不是整个仓库的分支列表这个符合预期再比如有些 IDE 对 worktree 的支持不友好数据库、缓存文件路径是写死的那就需要配环境变量。有人会问用 worktree 管理 Agent 工作流会不会多此一举我的体会是如果你只是偶尔让 Agent 改个脚本确实没必要上这套工具但如果你像我一样每天让 Agent 并行推进多个需求还参与一些周期较长的重构那么一个任务一个 worktree 的工作方式能帮你省掉大量处理冲突和切换上下文的成本。最后再分享一个小技巧我习惯在 Agent 开工前先手动把分支的初始版本跑一遍测试留个“基线”。这样在 Agent 改完之后用worktrunk task verify一对比哪些行为被意外改变了立刻就能看出来。别太信任 Agent 自己报的测试结果它往往会只看最后一次运行的输出而那个输出可能早就过期了。