Worktrunk:并行AI Agent工作流的Git Worktree管理方案

Worktrunk:并行AI Agent工作流的Git Worktree管理方案 最近在给团队的 AI 编码流程做升级核心目标就一句话让多个 AI Agent 能并行干活互不干扰。折腾一圈下来最大的瓶颈反而不是模型本身而是 Git 的分支管理工作流。我现在的日常是这样的早上拿到三个任务分别交给三个不同的编码 Agent比如一个用 codex cli一个用 claude code cli还有一个本地脚本驱动的 Agent它们要同时修改同一个仓库。如果让它们全挤在一个工作目录里或者让它们在同一个 clone 里来回切分支基本就是灾难现场——文件互相覆盖、构建产物冲突、未提交的修改被带得到处都是。Git Worktree 是官方给出的解法但原生命令又太啰嗦创建、关联、清理全靠人肉记三个以上就乱套。所以我自己写了 Worktrunk 这个命令行工具专门管理面向并行 AI Agent 工作流的 Git Worktree。这篇文章会把完整的设计思路、核心命令、真实用法和踩过的坑都写清楚给同样在折腾 Agent 并行开发的工程师一个可以直接参考的方案。1. 为什么并行 AI Agent 工作流需要 Worktree 管理1.1 Git Worktree 到底解决了什么问题Git Worktree 不是新东西它允许一个仓库同时存在多个工作目录每个目录可以独立检出不同的分支而且这些目录共享同一个.git对象库。听起来很美好那它相比直接复制多个仓库或者开多个分支手动切换优势在哪儿先说复制仓库的方案。你cp -r十个目录出来每个里面都有一个完整的.git后面合并时就要手动处理 remote、fetch、merge 的关系而且一份大仓库要占十份磁盘空间对象库还无法复用。Git Worktree 共享对象库分支之间的关系天然就是同一仓库内的分支关系合并成本低得多。再说手动切分支的方案同一时间你只能在一个目录里干一件事Agent A 切到 feature-aAgent B 就得等着完全谈不上并行。Worktree 让每个 Agent 一个工作区、一个分支、互不打扰成为可能。我实际使用中最大的感受是工作区隔离带来的不只是代码层面的干净还有心态上的安全感。以前多个 Agent 共用一个目录跑完一个任务我总得人工检查有没有残留文件、有没有被改坏的东西。有了 Worktree每个 Agent 的产出边界清清楚楚坏也只坏在它自己的工作区里主分支始终是安全的。1.2 多 Agent 并行时真正混乱的是什么很多人以为并行就是把 Agent 丢到不同分支就完了其实分支只是第一层。真正复杂的是三层东西叠加在一起分支、工作目录、任务状态。第一层是分支命名和分工。三个 Agent 都从 main 拉分支谁叫 feature-aaa谁叫 fix-bbb如果不做约定后面 review 的时候根本分不清哪个分支对应哪个任务、哪个目录对应哪个 Agent。第二层是工作目录。Agent 是终端里的程序它需要在某个具体的路径下运行如果这个路径和分支对应不上Agent 可能在错误的分支里改了代码提交到了错误的地方。第三层是任务状态。任务做到一半Agent 停了工作区里留着未提交的改动这时你需要快速知道哪个工作区是干净的、哪个有中间产物、哪个已经合并可以回收。原生git worktree list只能告诉你路径、分支和 commit它不关心你的任务是什么、这个工作区是谁在管、生命周期走到哪一步了。短期跑一两个任务还好一旦任务多了你就要在脑袋里维护一张表这恰恰是机器最擅长做的事情。1.3 手敲命令和临时脚本为什么撑不住刚开始我也没有直接写工具先用 Git 原生命令加几个 shell 别名顶了一阵子。痛点非常具体每次创建要敲git worktree add ../path -b branch还要记住 main 仓库路径、约定好的目录前缀创建完再手动记录任务和分支的对应关系清理时得先git worktree list看一遍确认分支是否已合并再git worktree remove最后还得git worktree prune清掉失效引用。动作本身不难难的是流程的一致性。中间我也写过一段临时脚本把创建和清理流程固化下来。但脚本维护到后面就变味了今天要支持不同的基础分支明天要给 Agent 传入上下文目录后天要对接 CI 状态脚本里全是if分支和临时变量最后变成只有自己能读懂的shitty.sh。所以我下定决心把它做成一个正经的 CLI 工具也就是 Worktrunk。2. Worktrunk 的核心理念与设计取舍2.1 核心模型任务即分支任务即目录Worktrunk 把并行开发的最小单元定义为任务task。一个任务对应一个工作区目录、一个专用分支、一份状态元数据。用户不需要关心底层 Git Worktree 怎么建、怎么删只需要告诉工具我要开一个叫 payment-refactor 的任务给 codex 这个 Agent 用从 main 拉分支。模型上我做了两个关键映射。一是任务到分支的映射任务名默认就是分支名但会做合法性处理比如把不合法的字符转成短横线避免分支名里出现斜杠导致层级混乱。二是任务到目录的映射所有工作区统一放在主仓库下的.worktrunk/目录中目录名由任务名规范化得到方便批量操作和一眼识别。这个模型带来的直接收益是任何时刻你只要拿到一个任务名就能推导出它的分支名、目录路径和状态记录位置完全不需要额外的记忆。Agent 的上下文、任务的描述、 review 的备注都可以挂在任务元数据上让任务成为整个工作流的唯一入口。2.2 状态数据库 vs 全量扫描设计元数据的时候我纠结过两种方案一种是每次动态扫描git worktree list解析输出另一种是自己维护一份状态文件记录任务和 worktree 的对应关系。最终我选择了自维护状态文件放在主仓库.worktrunk/metadata.json理由是git worktree list的输出格式虽然稳定但它只反映 Git 眼中的事实不包含业务信息。我需要记录哪个 Agent 在用这个工作区任务的创建时间对应的 PR 号是否允许强制清理这类 Git 不管的事。而且当 worktree 存在但目录被手动删除时Git 的 list 输出会有歧义自己维护一份元数据可以保留更多现场。当然动态扫描作为兜底同步手段仍然保留。每次执行 Worktrunk 命令时工具会把 Git 的实际状态和元数据做一次对齐如果发现某个 worktree 已经被手动删掉就标记为 stale 并提示用户执行prune。这种元数据为主、扫描兜底的方式兼顾了业务信息的完整性和 Git 事实的一致性。2.3 为什么要做成 CLI 而不是 GUI 或普通脚本选择 CLI 形态有几个很实际的考虑。第一Agent 本身就是跑在终端里的CLI 和 Agent 是天然同构的。我可以直接在 task 的元数据里记录每个工作区对应的 Agent然后在命令行里把 Agent 跑起来形成创建工作区 - 启动 Agent - 收集结果的流水线。GUI 在这个场景下反而多了一层摩擦。第二CLI 便于组合。我可以把 Worktrunk 嵌进 shell 脚本、CI 任务和 Makefile比如在分支合并前自动跑一遍所有工作区的测试这些用命令行都很好接。第三CLI 对用户的心智负担最低。Git 用户本来就在终端里工作多一个类似git worktree风格的命令学习的坡度很缓。我不需要教用户去一个陌生的界面上点按钮你只要记住worktrunk create、worktrunk list、worktrunk cleanup三件事就够了。3. 安装配置与核心命令实操3.1 环境要求与安装方式Worktrunk 目前要求 Git 版本不低于 2.30因为低版本的 worktree 功能有一些边界情况处理得不好尤其是git worktree remove --force的行为差异比较大。运行环境方面我提供了编译好的静态二进制也支持从源码构建源码放在 GitHub 上依赖极少基本是标准库加一个 JSON 解析库。安装很简单# 通过包管理器安装 brew install worktrunk # 或者直接下载二进制放到 PATH 下 wget https://github.com/yourname/worktrunk/releases/latest/download/worktrunk-linux-amd64 chmod x worktrunk-linux-amd64 sudo mv worktrunk-linux-amd64 /usr/local/bin/worktrunk # 验证 worktrunk --version初次使用建议先初始化配置文件默认会去读~/.config/worktrunk/config.toml。如果没找到工具会用内置默认值你可以在任意仓库目录执行worktrunk init它会自动生成一份带注释的配置模板。# ~/.config/worktrunk/config.toml [defaults] base_branch main worktrees_dir .worktrunk run_after_create npm ci gitignore_worktrees true [agent] # 预置的 agent 名称列表和启动命令模板 codex { command codex, args [] } claude { command claude, args [-p] }这里run_after_create是很多人在意的一个选项它会在每次创建 worktree 后自动执行一条命令比如装依赖。原因我稍后在踩坑部分详细说简单提一句Git Worktree 的工作区之间不共享被 ignore 的依赖目录每个新工作区都需要重新安装依赖这一步自动掉能省很多事。3.2 核心命令速览create、list、switch、cleanup、pruneWorktrunk 的常用命令不多我建议从这张表开始看命令作用底层对应的 Git 操作worktrunk create task [--agent name] [--base branch]创建任务工作区git worktree add 建分支worktrunk list查看所有工作区及运行状态git worktree list的增强版worktrunk switch task快速定位某个任务的工作区路径无纯元数据导航worktrunk cleanup [--force]清理已合并或标记完成的工作区git worktree removegit branch -dworktrunk prune清理失效元数据和幽灵 worktreegit worktree prune实际创建的时候是这样cd /path/to/your/repo worktrunk create pay-refactor --agent codex --base main # 输出示例 # [ok] created task pay-refactor # branch : feature/pay-refactor # path : .worktrunk/pay-refactor # agent : codex跑完之后目录.worktrunk/pay-refactor里就是一个检出了feature/pay-refactor分支的完整工作区。注意分支名会自动带上feature/前缀这是配置里的branch_prefix决定的你可以改但统一前缀在管理大量任务时非常有用。worktrunk list的输出比原生命令多出不少信息TASK STATUS BRANCH AGENT PATH pay-refactor active feature/pay-refactor codex .worktrunk/pay-refactor obs-logging active feature/obs-logging claude .worktrunk/obs-logging deps-upgrade cleaned chore/deps-upgrade local -一眼能看出哪个任务还活着、对应哪个 Agent、需不需要处理。我就是靠这个列表来驱动每天的并行开发节奏的。3.3 与 AI Agent CLI 的联动方式Worktrunk 本身不做 Agent 调度它负责提供干净的工作区剩下的联动交给 shell 或者用户自己的编排脚本。我常用的模式是这样taskpay-refactor worktrunk create $task --agent codex workdir$(worktrunk switch $task) cd $workdir codex 重构支付模块保持对外接口不变完成后运行测试并提交worktrunk switch其实是个很朴素的命令它做的事就是把对应工作区的路径打印到标准输出方便你用cd $(...)的方式切过去。但就是这个小函数把任务 - 目录 - Agent的链条串起来了。你还可以把它做成 shell 函数直接wtcd pay-refactor就能进到对应目录。对于 claude code cli 这类交互型工具同样适用。先在 Worktrunk 的配置里注册 agent 名称然后在对应目录里启动它给它一个清晰的任务描述即可。每个 Agent 只会在自己的工作区里操作它是一个独立的分支最后合不合并、怎么合并控制权在你手里而不是在 Agent 手里。4. 实战同一个仓库三个 Agent 并行开发4.1 场景设定一个 Web 服务仓库的三个任务用一个具体的例子来演示整套流程。假设有一个 Web 服务仓库主分支是main。今天要并行做三件事任务一重构支付模块代码路径internal/payment交给 codex 处理任务二增加日志链路追踪涉及中间件和请求上下文交给 claude 处理任务三升级核心依赖并修复 API 变更交给一个本地脚本驱动的 Agent 处理这三个任务改动的文件区域基本不重叠但都依赖main作为基准分支。如果串行来做光等 Agent 跑完就得几个小时并行做的话关键是确保它们互不干扰、基准一致、合并可控。4.2 创建工作区并分派 Agent先用 Worktrunk 一次性建好三个工作区worktrunk create pay-refactor --agent codex --base main worktrunk create obs-logging --agent claude --base main worktrunk create deps-upgrade --agent local --base main每条命令执行完.worktrunk/下就多了一个独立的 checkout。这里有一点必须提醒.worktrunk/目录要提前写进主仓库的.gitignore否则主仓库的git status永远会显示一堆未跟踪的目录非常干扰。然后分别启动 Agentcd $(worktrunk switch pay-refactor) codex 重构 internal/payment 模块保持对外接口不变补充单元测试 cd $(worktrunk switch obs-logging) claude 为请求链路增加 trace id 日志覆盖中间件和请求上下文 cd $(worktrunk switch deps-upgrade) upgrade-agent 升级 go.mod 里的依赖到最新稳定版修复 API 变更三个目录并行跑互不打扰。你在主仓库里git status、git log看到的始终是稳定的main分支状态和 Agent 的中间产物完全隔离。4.3 收集结果、review 与合并Agent 跑完后先在各自的工作区里检查产出。我会用一个简单的脚本批量看每个任务的改动规模和测试结果for task in $(worktrunk list --formatname --statusactive); do dir$(worktrunk switch $task) echo $task git -C $dir status --short | head -20 git -C $dir log --oneline -3 done确认没问题后按风险从低到高逐个合并。我通常的顺序是先合依赖升级再合日志链路最后合支付重构因为支付重构改动最大放最后能减少前面的变动影响它。合并前每个工作区要先同步最新的 maincd $(worktrunk switch deps-upgrade) git fetch origin main git merge origin/main git checkout main git merge --no-ff chore/deps-upgrade这三步依次在三个任务上执行。如果中间出现冲突别慌Git 会告诉你是哪些文件。这时候我的做法是把冲突文件的上下文重新抛给对应的 Agent让它在自己的工作区里修复再拉回来继续合并。因为每个 Agent 的上下文只和它自己的任务相关处理自己任务引发的冲突时它比通用的冲突解决工具更清楚业务意图。全部合并完最后一个动作是清理worktrunk cleanup它会自动判断哪些任务的分支已经合并进 main然后把对应 worktree 和分支删掉更新元数据。这一套走下来整个并行流程的收尾不会超过两分钟。5. 常见问题与踩坑记录5.1 工作区删不掉not removing 报错最常遇见的问题就是清理时报错类似fatal: not removing path。原因基本是工作区里存在未提交的改动或者未跟踪的文件。Git Worktree 出于保护考虑不允许直接删除带有未保存内容的工作区这其实是个好设计防止你误删 Agent 的中间产物。我的处理方式是先在工作区里查看遗留内容确认是 Agent 的半成品就保留确认是无用缓存就手动删掉再重新执行清理。Worktrunk 的cleanup --force可以绕过检查但我严格限制它的使用场景必须是在先备份或者确认完全不需要的情况下才用。如果你想在命令行里快速看工作区里有什么可以执行worktrunk list --show-dirty它会逐个检查各个工作区的git status并列出有改动的任务。5.2 分支被别的 worktree 占用导致切换失败如果你在一个 worktree 里手动切换分支而那个分支恰好被另一个 worktree 占用了Git 会直接拒绝报错的内容是fatal: branch is already checked out at path。这条报错很吓人但其实保护了你的数据同一个分支同时在两个目录被检出最后提交的东西会串得没法看。正确的理解是一个分支同时只能被一个 worktree 检出这是 Git Worktree 的铁律不要试图绕过它。真要同时用同一个分支就用两个分支然后互相合并。我在 Worktrunk 里也做了对应的校验创建任务时如果发现任务名对应的分支已经存在于其他 worktree会直接报错并提示你先处理旧任务。5.3 依赖安装的重复消耗这个问题新手最容易忽略。Worktrunk 的多个工作区共享.git但 pytest、node_modules、target 这类被.gitignore忽略的目录每个工作区都是独立的。也就是说你建了三个 worktree就要装三遍依赖。如果项目依赖特别多光安装依赖的时间就够受的了。我的解决方案分三层。第一层在配置里设置run_after_create让 Worktrunk 创建完工作区后自动跑安装命令把成本摊到创建时不会出现任务做到一半发现忘记装依赖的情况。第二层尽量用带全局缓存的包管理器比如 pnpm、cargo 这类共享依赖存储的实际磁盘占用和安装时间都会大幅下降。第三层如果某些任务只是改文档或改配置根本不需要装依赖我会手动在创建时指定--no-install跳过自动安装。5.4 路径过长和 Windows 兼容性问题这件事是我在一个同事的 Windows 机器上调试时踩到的。Git Worktree 默认会在仓库路径下生成新的目录如果仓库本身路径很深加上.worktrunk/和任务名很容易撞上 Windows 的路径长度上限。表现就是 Git 命令突然报一些看不懂的文件系统错误。解决思路是把 worktrees 目录从仓库里挪出来放到一个短路径下比如在 Worktrunk 的配置里设置worktrees_dir c:/wt或者直接放在仓库外部的同级目录。这样既绕开了路径长度问题还不容易误删仓库里的东西。如果你是 macOS 或 Linux 用户路径长度问题相对少见但任务名也别起得太离谱短而清晰的任务名在后续维护里省心得多。6. 一些心得与扩展方向6.1 把 Worktree 的生命周期交给工具而不是人肉记忆用了 Worktrunk 一个多月后我最大的体会是工具替我们管住了当前手里到底有几个并行任务这件事。以前开三个线程干活我脑子里一直绷着一根弦反复确认每个目录是不是干净的、哪个分支已经合了。现在worktrunk list一刷所有状态一目了然我只需要关注任务本身的进度而不是 Git 的机械状态。另外一个被低估的好处是它改变了团队的协作方式。以前让团队成员各自用一套脚本管理 worktree每个人习惯不同交接时总要解释半天。现在所有人都用同一套命令任务、目录、分支的命名规则一致即使某个人中途要接手别人的任务看一眼列表就知道怎么回事。这种隐性的规范化比任何文档都管用。6.2 后续可以扩展的方向目前 Worktrunk 还只是一个称职的工作区管家我接下来想做的几个方向也顺便列出来。第一个是把 PR 关联做进来。任务创建时可以指定目标分支合并时自动基于 main 刷新分支对应的 PR 链接也作为元数据挂在任务上这样worktrunk list里就能直接点开看 PR 状态。第二个是更智能的清理策略比如根据任务的更新时间、是否有未提交改动、PR 是否合入来自动判断哪些工作区可以回收减少人工决策。第三个是做一个 Agent 侧的插件让 Agent 在完成任务后主动调用 Worktrunk 上报状态这样并行任务的进展会自动汇总而不是靠人去逐个轮询。从更广阔的视角看我觉得并行 AI Agent 开发的核心其实不是模型而是围绕代码仓库的工作流设计。谁先把任务 - 工作区 - Agent - 合并这条链路的摩擦降到最低谁就能真正享受并行带来的效率提升。Worktrunk 是我在这个方向上的一次实践代码本身还很年轻但设计思路是经过真实场景打磨的希望能给正在做类似尝试的人一些启发。