十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

多Agent并行开发实践:用Worktrunk管理Git Worktree

多Agent并行开发实践:用Worktrunk管理Git Worktree 最近我一直在折腾一件事让多个 AI Agent 同时在一个代码仓库上干活而且互相不踩脚。试了几种方案最后发现问题比预想的复杂而答案远比想象中更该落在 Git 的工作树机制上。真正让我敢把三四个 Agent 同时放出去、还能收得回来的是一个叫 Worktrunk 的命令行工具——它把 Git Worktree 原生命令重新包装成面向任务、面向 Agent 会话的工作流管理 CLI。这篇就把这几周的实践和踩坑完整梳理一遍尤其适合正在用 Codex CLI、Claude Code、Gemini CLI 这类编码代理又想在同一仓库里并行开展多个分支任务的朋友。1. 为什么多 Agent 并行最缺的是一层 Worktree 管理1.1 多个 Agent 一起动手头一个问题就是工作区打架现在 AI 编程 Agent 越来越能打很多人已经不是“让一个 Agent 干一件事”而是“一个需求拆成几个子任务同时分给两三个 Agent”。听起来很美真正做起来第一个坎就不是模型能力而是 Git 工作区。举个例子。我本地有一个 FastAPI 服务仓库某天接到三个改动重构认证中间件、新增一个 OCR 上传接口、把核心接口的测试补到八成覆盖。如果这三件事在同一个目录里同时动会发生什么第一个 Agent 在app/auth.py里改了一半第二个 Agent 正好也在读这个文件打算在同一个文件里加装饰器逻辑。第三个 Agent 跑测试的时候目录里既有新的中间件代码又有还没写完的接口代码测试结果一塌糊涂还会反过来干扰前两个 Agent 的上下文。最后大家看到的都是“当前工作区很乱”却分不清到底是谁弄乱的。可如果你把三个任务拆到三个互不相干的目录里每个目录对应一条独立分支三条流水线同时跑改完再依次合回主分支这个问题就变成了普通的代码冲突问题而不是工作区互相污染的问题。这正是 Git Worktree 的设计用途同一个仓库可以同时存在多个工作目录每个目录检出一个不同的分支共享同一个.git对象库。1.2 Worktree 的隔离模型为什么适合 Agent这里补一点原理。普通情况下一个仓库就是一个.git目录加一个工作区目录你通常只能在一条分支上做修改。git worktree会把.git拆成两部分一套共享的 Git 对象库外加挂在/.git/worktrees/name下的多条“工作树元数据”。你可以为一条分支建一棵新的工作树放到任意路径比如../myrepo-feat-ocr。新工作树里的文件是完完整整的代码副本但它和主工作区共享提交历史、共享对象存储commit、merge、push 都不需要在两个目录之间做任何手工搬运。这个模型和 AI Agent 的工作方式天然合拍。Agent 最强的能力是基于自然语言生成代码最弱的地方恰恰是“全局状态管理”。你把一个 Agent 放进一个干净、独立、对应明确分支的工作树它完全不需要了解另外两个 Agent 在干嘛也不需要在同一个目录里规避别人的半成品。它唯一要关心的是“我自己的分支、我自己的测试、我自己的提交”。理论上这种隔离早就存在但实操里很少有人好好用起来核心原因是原生 Git 命令太“机械”。Agent 不像人它有很强的“完成指令”倾向你如果只给它一句“去你自己的工作区干活”它大概率会困惑工作区在哪我该用哪个路径我的分支是什么它需要的是一个更直接、更语义化的操作界面。1.3 原生git worktree用起来到底哪里不爽用原生命令建一棵工作树是这样git worktree add ../myrepo-feat-ocr -b feat/ocr-upload就这一条看着还行但任务一多就露馅了。首先是记忆负担你必须在脑子里维护“任务A对应哪个路径、对应哪个分支、基于哪条 base 分支、现在走到哪一步了”。Agent 可不会帮你记它们自己甚至会忘。其次是状态不直观git worktree list输出的只有路径、提交和分支没有“这个任务做到一半了”“测试还没跑”“上次同步是两小时前”这类语义信息。再有就是没有面向 Agent 的原子操作你想“把这个任务基于最新 main 更新一下”“把这个 Agent 的修改合回主分支”“任务做完把这棵树清掉”这些组合操作用原生命令要三四条甚至五六条步骤越多Agent 出错的概率就越大。所以问题的关键不是“worktree 够不够用”而是“缺一个能把 worktree 封装成任务状态机的 CLI”。这时候 Worktrunk 就冒出来了。我试过之后的感觉是它本质上不是替代 Git而是把 Git Worktree 的“物理隔离能力”翻译成了“任务管理语言”。2. Worktrunk 的核心模型与命令设计2.1 一切从“任务”开始Worktrunk 的抽象非常简单你不会直接和“工作树”打交道你打交道的是“任务”。一个任务包含五个要素要素含义任务名给 Agent 看的语义化名字比如refactor-auth分支实际 Git 分支比如refactor/auth-middleware路径独立工作树所在目录默认放在项目根目录的兄弟目录Base任务基于哪条分支创建通常是main或develop状态任务的生命周期created/running/merged/cleaned等这样设计的好处是对 Agent 说“去 refactor-auth 里干活”比说“去 ../myrepo-2 目录干活”要符合直觉得多。Agent 不需要理解路径结构只需要认任务名。它的核心理念是“工作树作为任务的物理载体任务作为工作树的语义身份”。比如我需要给一个 Agent 一个新任务命令非常短worktrunk create refactor-auth --base main --branch refactor/auth-middleware底层它会做三件事基于main创建分支、在约定路径建一棵工作树、把这条工作树登记进任务状态里。对用户来说一条命令到位。2.2 核心命令速查我把这段时间用得最多的命令列在这里你可以感受一下这个 CLI 的“任务视角”有多大差别worktrunk create task-name [--base main] [--branch feat/xxx] [--path ...] worktrunk list # 看所有任务的状态 worktrunk show task-name # 看单个任务的详细信息 worktrunk start task-name # 进入任务工作树等价于 cd git switch worktrunk sync task-name # 把任务分支 rebase 到最新 base 上 worktrunk commit task-name -m ... # 在任务工作树里直接提交 worktrunk merge task-name # 把任务分支合并回 base 分支 worktrunk cleanup task-name # 任务结束后删除工作树回收分支 worktrunk prune # 清理所有已合并或丢失的任务记录初看你会觉得这些命令就是在“换皮”真正用起来才发现关键是它把这些动作的语义对齐到了任务周期创建、启动、同步、合并、清理。每一步你都能在worktrunk list里看到状态变化不会出现“工作树建好了但忘了在哪”或者“分支合并了但工作树还堆在那里”的情况。实际执行时每个任务的工作树默认放在../project-name--task-name也就是主项目目录的兄弟目录。这个默认值很关键因为把工作树放在仓库内部比如.worktrees/task1很容易被 IDE、构建工具误扫进去也会被 Agent 在搜索文件时当成业务目录。放在仓库外面一劳永逸唯一要注意的就是后续移动项目目录时工作树对应的绝对路径会失效需要git worktree repair修复。2.3 配置格式与可编程性Worktrunk 在项目根目录维护了一个配置文件默认是worktrunk.toml大致长这样[project] name my-api default_base main worktree_root ../worktrees [agents] enabled true allowed_commands [commit, sync, create, list, show] [rules] sync_before_merge true require_clean_tree_before_switch true这个文件既是配置也是给 Agent 看的“使用说明书”。我后来实际体会到一个小细节允许列表allowed_commands非常重要。如果你同时把多个 Agent 放进同一套 Worktrunk 环境最好限制某类 Agent 只能 commit 和 sync不能 merge不能 cleanup否则一个 Agent 在上下文混乱时可能把别人的任务清理掉。权限边界越早设定后期事故越少。Worktrunk 还支持以 JSON 输出状态方便被脚本或 Agent 程序化消费。例如worktrunk list --json会输出每个任务的完整信息Codex CLI、Claude Code 这类工具通过解析 JSON 去“理解当前项目里有几个分支任务”比人看表格文本要稳定得多。这是它在 Agent 场景里很关键的一个设计。3. 从零开始的实操流程一个项目跑三个并行 Agent3.1 环境准备与初始化先说一下我这次的实操背景。机器是 macOSPython 3.12仓库是一个中等规模的 FastAPI 项目约两百个文件依赖用 uv 管理。安装 Worktrunk 我用的是 Homebrewbrew install worktrunk如果你机器上已经有 Node.js也可以用npm i -g worktrunk安装底层没有特殊依赖只要 Git 版本在 2.30 以上就行。装完之后在项目目录里初始化cd ~/projects/my-api worktrunk init --default-base main初始化会在项目根目录生成worktrunk.toml同时检查当前 Git 仓库状态、确认 main 分支存在。我当时犯了一个小错误仓库默认分支其实是master没改配置就直接 init导致后面所有任务 base 都指向了一个不存在的分支。所以初始化前最好先git branch --show-current看一眼实际默认分支再决定--default-base传什么。3.2 创建三个任务三棵树环境就绪后我把需求拆成三个并行任务worktrunk create refactor-auth --branch refactor/auth-middleware worktrunk create feat-ocr --branch feat/ocr-upload worktrunk create test-upload --branch test/coverage-upload三条命令跑完项目目录的兄弟目录下就多出了三个独立工作树。这时worktrunk list输出类似TASK BRANCH PATH BASE STATUS refactor-auth refactor/auth-middleware ../worktrees/my-api--r... main created feat-ocr feat/ocr-upload ../worktrees/my-api--f... main created test-upload test/coverage-upload ../worktrees/my-api--t... main created这里有一个非常关键的动作三个任务目录都是完整的代码副本所以各自都要安装依赖。如果每个工作树都跑一遍uv sync会有三份 site-packages 或者三份 node_modules磁盘占用会翻倍安装时间也要翻三倍。我的处理方法是用符号链接把依赖目录指到主项目目录的.venv或者node_modules只读共享。比如ln -s ~/projects/my-api/.venv ../worktrees/my-api--refactor-auth/.venv依赖共享能省大量时间但要清楚这是一个“只读共享”Agent 在任务过程中如果执行uv add xxx或者npm install会往共享目录里写进而影响其他任务。所以我给所有 Agent 下的指令都带了一条硬约束“不要安装依赖如果需要新依赖告诉我由我统一在主目录安装然后你 sync base。”这个约束在实操里比预想的重要。3.3 把三个 Agent 放进去干活任务树建好后就可以分别启动 Agent 了。我用的是 Codex CLI 和 Claude Code给每个 Agent 的工作目录分别是三个 worktree 路径然后在每条会话的初始提示里做了统一约定你现在在任务 refactor-auth 的工作树里项目目录是 ../worktrees/my-api--refactor-auth。 操作原则 - 只编辑你自己任务相关的文件不要搜索或修改主项目目录和其他 worktree。 - 在任务工作树里用 worktrunk commit 提交不要在主项目里提交。 - 提交信息用英文包含任务名前缀例如 refactor-auth: move auth dependency to middleware。 - 工作过程中如需看任务状态运行 worktrunk show refactor-auth。这段提示看起来很朴素但作用很大。AI Agent 的工作方式是基于上下文猜操作如果你不明确告诉它“你处在独立工作树、不要碰主项目”它完全可能试图去cd ~/projects/my-api干活或者在错误的目录里执行git status。把任务边界写进系统提示等于给 Agent 画了一条行为护栏。三个 Agent 同时跑起来之后神奇的是主项目目录始终保持干净。我在主目录里可以继续做 review、看文档、改 CI完全不担心被 Agent 的中间产物打扰。这是 worktree 模式最直接的收益你的“主工作区”变成了一块只读中立的调度台。3.4 任务完成同步、合并、清理过了一段时间三个 Agent 陆续说“做完了”。这时候不要直接让它 merge先逐个处理。比如处理feat-ocr我习惯的完整流程是# 1. 先看任务当前状态确认没有堆积的改动 worktrunk show feat-ocr # 2. 基于最新 main 做一次 rebase确认没有冲突 worktrunk sync feat-ocr # 3. 在该工作树里跑测试Agent 如果已经跑过我会再手动确认一次 cd ../worktrees/my-api--feat-ocr pytest tests/ -x -q # 4. 合回主分支 cd ~/projects/my-api worktrunk merge feat-ocr # 5. 清理任务工作树 worktrunk cleanup feat-ocrworktrunk merge干的事情本质上就是回到 base 分支、合并任务分支、删除任务分支但它是当作“一个任务生命周期动作”来做的所以会在 merge 前强制检查sync_before_merge规则如果任务工作树上有未提交改动它会拒绝执行并提示你先提交或放弃改动。这种保护非常实用不然很容易出现“Agent 自认为提交了其实改动还躺在工作区里然后 merge 把脏东西带进主分支”的脏乱局面。三个任务我按优先级依次 merge如果中间出现冲突就手动解决。幸运的是因为任务之间文件交集很少这次实操只有test-upload和refactor-auth在app/auth.py上出现了轻微冲突处理成本大约是五分钟。4. 与主流 AI Agent CLI 的集成方案4.1 Codex CLI 场景直接作为外部命令调用Codex CLI 允许 Agent 在授权下执行任意 shell 命令所以接入 Worktrunk 是最直接的。我只需要在系统提示或者 AGENTS.md 里写一句## Git 工作流 本项目使用 Worktrunk 管理并行任务工作树。请使用 worktrunk 命令进行分支与任务操作 - 完成任务的一部分后运行 worktrunk commit 任务名 -m 描述 - 运行 worktrunk show 任务名 查看任务状态 - 永远不要在主项目目录直接执行 git checkout -b 或 git commitCodex CLI 在执行worktrunk commit时本质上就是在该任务对应的 worktree 路径下执行git add -A git commit。因为它是个纯 CLI 工具Agent 不用理解 task 到 path 的映射命令本身就封装好了所有细节。4.2 Claude Code 场景利用 hooks 补心智Claude Code 有一个比较特殊的点它有自己的 CLAUDE.md 作为长期记忆还有 hooks 机制。我通常会做两件事。第一在 CLAUDE.md 里加一节“并行任务工作流”明确告诉 Claude当前多个 agent 在不同 worktree 里工作你的代码只存在于你自己的任务工作树主项目目录是共享的、不要动它。这段文字对 Claude 保持“空间意识”非常有效。第二我写一个非常简单的 PostToolUse hook在 Claude 每次执行git status或者git diff的时候自动附加一句提示{ hooks: [ { matcher: Git|Worktrunk, hooks: [ { type: command, command: worktrunk status-hint } ] } ] }worktrunk status-hint会输出当前任务工作树的简要状态比如“你正在 refactor-auth 任务中文件 3 处改动未提交”。这样每次 Claude 想检查 git 状态时都会先看到一个更贴合任务的上下文不容易跑偏。这个思路本质上是“给 Agent 的每次 git 操作加一个上下文标记”效果很稳强烈建议试试。4.3 通过 MCP 把 Worktrunk 暴露给 Agent如果你的 Agent 支持 MCPModel Context Protocol更优雅的方案是运行 Worktrunk 的 MCP serverworktrunk mcp启动后它会暴露一组工具比如create_task、list_tasks、sync_task、commit_task、show_task。我在 Claude Desktop 和最新的 Codex 里都试过效果比纯 Shell 指令稳定得多。原因在于MCP 工具带结构化参数和返回值Agent 不需要“猜”命令语法。比如create_task的入参就是name、base、branch三个字段返回值是一份 JSONAgent 直接就能读。这里有一个实操提醒同一个 Agent 如果既可以通过 MCP 调用 Worktrunk又能在 Shell 里执行worktrunk要选择一种并用到底不要两种混着来否则 Agent 会困惑于“工具状态”到底以哪个为准。我自己现在的做法是Codex CLI 用 Shell 方式Claude Code 用 MCP 方式各自固定。5. 踩坑记录并行 Worktree 的常见问题5.1 工作树里堆着未提交改动导致 sync 失败这是我最开始碰到最多的问题。Agent 改到一半被打断重新开一个会话时任务工作树里还堆着大量未提交的改动。这时跑worktrunk sync会失败因为 rebase 要求工作区干净。很多 Agent 遇到这种报错会“自行发挥”比如自己git checkout -- .把改动全丢了或者git stash完之后忘了恢复。我的对策有两个。第一在 Agent 的提示里写死一条命令习惯每次开始一次新会话先跑worktrunk show 任务名让它知道当前遗留了哪些未提交内容。第二加了一个保护规则让 Worktrunk 在检测到脏工作区时拒绝执行sync而不是自动丢弃改动。Agent 被迫先 commit 或明确处理未提交内容之后再 sync。这个“拒绝比自作主张安全”的原则对 Agent 场景尤其重要。5.2 分支名冲突和路径冲突Git worktree 有一个硬限制同一个仓库里同一时刻一棵工作树只能对应一个分支而且每个工作树目录必须唯一。如果你在worktrunk create test-upload时不小心传了--branch feat/ocr-upload和已有任务的分支重了Git 会直接报错。这类报错本身不复杂坑的是 Agent 不理解“我明明创建任务成功了”的错位感。所以我后来强制约定任务名和分支名必须严格对应任务名用中划线短横分支名用同一套命名比如feat-ocr对应feat/ocr-upload这种映射只存在于 Worktrunk 的配置里Agent 不要自行修改。5.3 Windows 路径和长路径问题如果你在 Windows 上跑这套方案要额外小心。Git for Windows 默认可能不支持超过 260 字符的路径而 worktree 默认路径是你项目根目录的兄弟目录一旦项目本身路径很深叠加 worktree 路径后很容易超长。解决办法有两个一是把core.longpaths打开git config --global core.longpaths true二是在worktrunk.toml里把worktree_root显式指到一个浅层目录比如C:\worktrees。我在 Windows 上实测过浅层目录配合 longpaths能省掉 90% 的诡异报错。还有一个 Windows 特有的坑有些项目里有符号链接比如 Windows 下用git config core.symlinks控制的链接在多个工作树之间行为不一致。如果你发现某个 Agent 报“文件找不到”先检查是不是符号链接在检出过程中没有正确建立跑一下git worktree repair往往能解决一部分问题。5.4 依赖目录共享带来的“幽灵问题”我在前面建议用符号链接共享.venv/node_modules。这个做法确实省时间和磁盘但也引入了一个很隐蔽的问题如果某个 Agent 在共享的依赖目录里执行了uv sync或者npm install它会把另外两个正在运行的 Agent 的环境弄坏。表现很诡异比如 Agent A 上午还能跑通测试下午突然报缺包。我现在的方案是更保守的三个任务如果依赖完全一致我可以共享如果有一个任务可能需要新增依赖我不共享它的依赖目录让它单独装。说到底隔离的目的就是为了减少相互影响为了省一点磁盘而破坏隔离就本末倒置了。6. 落地到团队里的几点建议6.1 把 Worktrunk 写进开发规范而不是口头约定如果你想让团队里多个开发者都参与同一个仓库的并行 Agent 工作流最好把 Worktrunk 的用法写进仓库根目录的AGENTS.md或 README 对应章节里。里面至少要有三块任务命名规范、谁可以 merge、依赖目录是否可以共享。不然每个开发者带着自己的 Agent 习惯来用着用着就变成各写各的脚本最后管理状态又开始混乱。命名规范我自己推荐这样的格式类型-功能-序号比如feat-ocr-02、fix-login-01。类型前缀和 Git 分支名对齐序号保证唯一。平时跑worktrunk list时整列任务一眼扫过去哪些是 feature、哪些是 fix、哪些已经跑了两轮非常清楚。6.2 Agent 的测试应该在独立环境里执行并行工作流最容易出现的低级事故是Agent A 说“我测试全绿”但它是基于一个没有 Agent B 新代码的旧 base 上跑过的等把所有任务 merge 到一起集成测试挂了。所以我在团队里定了一条硬规矩每个 Agent 在收尾前必须worktrunk sync一次把 base rebase 到最新 main再跑测试。不 sync 不许报告完成。这条规则能让冲突在合入前暴露而不是等集成阶段才爆雷。6.3 定期清理和状态归档并行任务多了以后worktrunk list会越来越长。已合并的任务如果不清理工作树目录就会一直占着磁盘。我习惯每完成两个任务就跑一次worktrunk prune把所有已经 merge 掉的任务记录和工作树一次清空。清理动作本身很快但它带来的心理收益很大——你随时看到的是一个干净的任务列表而不是一堆没人管的目录残骸。除了清理我还建议把worktrunk list --json的输出接到一个简单的脚本里每天下班前生成一份任务状态快照。这样第二天早上回来不管是自己还是 Agent都能快速恢复“昨天做到哪了”的上下文。这个快照文件我放在项目目录的.worktrunk-state.json不提交到 Git但留着本地做断点续传很有用。最后说一点个人体会。我一开始推行这套工作流的时候最担心的是“多一层 CLI 会不会让 Agent 更乱”。实际用了三周之后我的结论恰恰相反Agent 最缺的从来不是“更聪明的模型”而是“更清楚的环境边界”。Worktrunk 提供的不是魔法它只是把 Git Worktree 原生的物理隔离翻译成了 Agent 能理解的任务生命周期语言。你依然要自己判断任务怎么拆、冲突怎么解、合并顺序怎么排但至少那个“工作区打架”的问题终于不用再操心了。如果你现在正被多 Agent 并行的工作区混乱折磨建议从一个小仓库开始先建两个任务跑通一遍创建、同步、合并、清理的完整循环你会立刻感受到这套工作流带来的稳定感。
返回列表