上个月我接了个有点狼狈的活儿:一会儿让 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 files,Agent 生成的临时文件经常是 untracked 状态,你 stash 了也没用,它们还是留在工作区里碍事。
Git Worktree 解决的就是这个“既要物理隔离、又要共享仓库”的矛盾。它的原理是在同一个仓库下创建多个工作目录,每个工作目录对应一个独立分支,但共享同一个 .git 目录。你可以同时 checkout 到不同分支,各自改各自的,互相之间没有任何干扰。提交之后,这些 commit 都在同一个对象库里,合并只是时间问题。
我一直觉得 Git Worktree 是 Git 里最被低估的功能之一,它明明能把“单仓库多任务并行”这件事做得非常优雅,但大多数人不了解它,或者只在极少数场景下用一下。Worktrunk 干的事,就是把这个能力做成一整套面向 AI Agent 的工作流。
2. Worktrunk 的设计:围绕 Agent 习惯打造的 CLI
2.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 是.codex,Claude 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. 实操全过程:从安装到并行跑三个 Agent
3.1 安装与环境准备
Worktrunk 是 Go 写的,分发上只依赖一个二进制文件。macOS 上你可以直接用 Homebrew 安装,Linux 和 Windows 可以从 release 页面下载对应平台的可执行文件,或者用 Go 直接构建:
brew install worktrunk # 或者 go install github.com/worktrunk/worktrunk@latest装完先初始化配置:
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 跑完一天的活儿之后,各自都提交在各自的分支上。在合并之前,我习惯先做一轮验证。合并前先跑一下任务目录里的测试和 lint:
worktrunk 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 时会自动加载:
PORT=3100 API_BASE_URL=http://localhost:3200 DATABASE_URL=postgres://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 --prune=now --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 自己报的测试结果,它往往会只看最后一次运行的输出,而那个输出可能早就过期了。