Git Worktree实战:用Worktrunk管理多AI Agent并行开发
最近这两年在折腾 AI Agent 辅助编程时我踩了一个巨坑让多个 Agent 并行干活结果它们在同一个工作目录里互相踩踏改 A 文件的时候把 B 的改动覆盖了分支切来切去最后连自己都不知道哪个代码对应哪个需求。后来我找到了 Git Worktree 这个官方机制算是解决了“一个仓库多份工作目录”的根子问题但用原生命令去维护一堆 worktree本身又是一堆琐碎活。折腾到最后我干脆写了一个叫 Worktrunk 的 CLI 小工具专门把这些管理动作封装起来。这篇文章就把这套思路和实操细节完整分享一下适合正在做多 Agent 并行开发、或者想给团队引入并行工作流的人参考。1. 为什么并行 AI Agent 工作流离不开 Git Worktree先别急着看命令得先把“为什么要用 Git Worktree”这件事掰扯清楚。很多人在单体仓库里跑多个 Agent最自然的做法是开多个终端、每个终端git checkout -b feature-xxx然后各自干活。表面上看互不干扰实际上隐患非常大。1.1 传统分支切换的并发陷阱Git 的分支本质上只是一个指针真正让工作区内容变化的是git checkout操作。它会把当前工作目录里的文件替换成目标分支对应的内容。这里有一个致命问题工作目录是共享的。如果一个 Agent 正在跑测试、生成临时文件、修改配置另一个 Agent 执行git switch切到别的分支轻则触发大量文件重编译重则直接让前一个 Agent 的进程崩溃——因为文件句柄失效、路径内容变了。更麻烦的是未提交的改动。两个 Agent 如果同时修改同一个文件后提交的那个会把前一个的改动整个覆盖掉而且 Git 不一定能及时发现——因为很多冲突只有在git add之后的 diff 阶段才会暴露。等到你发现的时候可能一个小时的上下文已经全部丢失了。我实测过最典型的场景Agent A 在重构一个模块Agent B 在修同一个模块的 bug。A 跑完测试发现自己的改动全没了因为 B 在另一个终端里git reset --hard了。这种事故在人类协作里还算少见但在 AI Agent 这种“你说什么它就干什么”的工具面前几乎必然发生。1.2 一个仓库也能拥有多个平行宇宙Git Worktree 解决的就是这个问题。它允许你在同一个仓库里创建多个工作目录每个目录对应不同的分支但它们共享同一个.git数据库。换句话说你可以在/repo-main上跑主分支同时在/repo-feature-1上跑另一个分支两个目录里的文件内容完全独立互不干扰。这相当于给每个 Agent 发了一个独立的“平行宇宙”。Agent A 在/repo-task-a改文件Agent B 在/repo-task-b改文件两者永远不会碰到对方的工作区。就算两个分支都修改了同一个文件Git 也只会在合并时提醒你解决冲突不会在中间过程里互相覆盖。它的底层原理不复杂.git/worktrees/目录下会为每个新增的工作区建立索引文件记录对应的 HEAD、分支指针、索引文件、HEAD 锁定状态等。每个 worktree 都有自己的索引所以git status、git add、git commit都只影响当前工作区不会波及别的 worktree。这里有个关键点要提醒一个分支在同一时间只能被一个 worktree 检出。如果你想在两个目录里同时跑同一个分支Git 会直接报错拒绝。这也是为什么我在 Worktrunk 的设计里强制要求“一个 worktree 对应一个独立分支”就是为了避免这个冲突。1.3 多 Agent 并行的真实痛点到底在哪看懂了 Git Worktree 的原理第二个问题来了为什么还需要一个专门的 CLI 工具来管理而不是直接用原生命令原因很简单原生命令的流程太琐碎。每创建一个 worktree你要想清楚目录放哪、分支叫什么、基于哪个基线切出来要删除时得先切走该分支、再删 worktree、再删分支三个步骤一个都不能错。更别提多个 Agent 并行工作的时候创建、查看、清理 worktree 的频率会变得非常高手敲命令不仅容易出错而且非常打断思路。另一个痛点是上下文混乱。当你同时开着三四个 worktree 时很容易搞混“当前这个终端在哪个 worktree 里”。原生命令里没有一个好用的“快速查看所有 worktree 的状态”面板。Worktrunk 设计之初最核心的诉求就是把这些重复、易错、难以追踪的操作变成几个语义清晰的命令。2. Worktrunk 的设计思路与核心功能拆解既然是面向 AI Agent 工作流的管理工具Worktrunk 的功能设计就不能停留在“封装 git worktree 命令”这个层面。我一开始也确实只做了简单的封装但用了一周后发现真正有价值的不是把git worktree add变成wt add而是围绕“Agent 并行工作”这个场景做完整的流程编排。2.1 从任务到工作区的自动映射Agent 干活通常是从一个任务描述开始的比如“修复登录页面的样式问题”。如果你手动创建分支写什么名字全靠经验。Worktrunk 做了一件事把任务描述自动转换成合法的分支名和工作区目录名。比如你输入worktrunk create fix login page style它会自动生成分支fix-login-page-style目录../repo-fix-login-page-style并基于当前主分支的最新代码切出新的 worktree。这样你就不用再思考“目录放哪、分支叫什么”这些与编码无关的问题Agent 拿到的是一个完全干净、语义清晰的工作环境。这个转换虽然不复杂本质上是字符串的 slugify 操作但如果不用工具而是靠人肉处理十个任务里有三个会冒出奇怪的分支名有带空格的有带括号的还有中文的。原因很简单Agent 生成的任务名称千奇百怪你不会希望分支名里出现feat: 修复(UI) - 第2版这种内容。2.2 会话绑定让 Agent 永远知道自己在哪里AI Agent 和人类开发者有一个巨大差异人类会自己看当前目录的路径、会执行git branch确认分支但 Agent 不会总是主动做这些事。很多时候它拿到一个任务就直接在当前目录里开始改代码了完全不管自己在哪个分支上。Worktrunk 对此的设计是“会话绑定”。每创建一个 worktreeWorktrunk 会生成一个会话 ID并把“目录路径、分支名、创建时间、绑定任务、上次活跃时间”这些信息记录在一个状态文件里。当你用worktrunk attach session-id进入这个环境时它会自动设置一个环境变量WORKTRUNK_SESSION并且在终端提示符上显示当前绑定的任务名。这样 Agent 在启动时就可以读取环境变量明确感知自己在哪个任务环境里。如果你用的是 Codex CLI、Claude Code 这类支持自定义提示词的 Agent 工具完全可以把WORKTRUNK_SESSION拼接到系统提示词里让它每执行一条命令之前都自动确认一次当前工作区。这样做的好处非常明显Agent 不会再出现“我以为自己在修 bug 分支结果改了 main 分支上的代码”这种低级的、但一旦发生就很致命的问题。2.3 生命周期管理自动回收闲置工作区Agent 并行工作还有一个随之而来的问题工作区会越建越多。你开了 10 个 Agent 跑任务仓库目录下就会多出 10 个 worktree 目录。如果每个人都不清理磁盘里塞满重则几百 MB、动不动上 GB 的完整项目文件很快你的磁盘就不够用了。Worktrunk 的解决方案是给每个 worktree 增加“活跃度”概念。它会在每次 attach、执行命令时更新时间戳然后提供一个worktrunk prune --stale 30命令意思是清理 30 天以上没有活跃过的 worktree。这个功能的实现思路不复杂遍历状态文件里的所有 session过滤出last_active_at超过阈值的然后按“先切走分支、再删 worktree、最后删分支”的顺序执行清理。但它解决了一个实际问题你不用再手工判断哪个 worktree 可以删、哪个不能删因为时间戳是最客观的依据。你可能会问那如果 Agent 还没跑完但 30 天没活跃怎么办这种情况 Worktrunk 会打印一个警告列表列出每个即将被清理的 session 对应的任务描述和最后活跃时间执行前还会要求你输入--force确认。我的实践原则是超过 60 天没活跃的任务基本可以判定为废掉了。2.4 为什么不做成 GUI 而是坚持 CLI之前有朋友问我为什么不给 Worktrunk 加个 Web 界面看起来更直观。我的回答是这个工具的目标用户是 AI Agent而 Agent 最擅长的交互方式就是命令行接口。你把一个 GUI 工具丢给 Agent它反而不知道该怎么用——它没有办法移动鼠标、点击按钮但它可以轻松读取--help输出、执行命令、解析 stdout。CLI 是 Agent 与工具之间的天然接口。作为开发者我自己的使用习惯也是全部在终端里完成切到浏览器去操作网页只会打断思路。另一个原因是 CLI 的脚本化能力。Worktrunk 的输出全部设计为“人类可读 机器可解析”双格式默认输出是彩色表格加上--json参数后可以输出结构化 JSON。这样不管是人看还是 Agent 自动处理都能直接消费。3. 实操安装配置与典型并行工作流落地理论讲得再多不如上手跑一遍。下面这部分是完整的实操记录包含安装方式、命令用法以及我最常用的一套并行 Agent 工作流。3.1 环境准备与命令行安装Worktrunk 是用 Go 写的主要原因是编译产物是单一二进制部署到任何 Linux 服务器、Mac 或者 Windows 的 WSL 环境都很方便。安装方式有两种# 方式一通过 Go 直接安装 go install github.com/mojie2023/worktrunklatest # 方式二下载编译好的二进制 wget https://github.com/mojie2023/worktrunk/releases/latest/download/worktrunk-linux-amd64.tar.gz tar -xzf worktrunk-linux-amd64.tar.gz sudo mv worktrunk /usr/local/bin/安装完成后先用worktrunk version验证是否成功。如果一切正常你会看到版本号和构建信息。接下来需要做一步初始化操作告诉 Worktrunk 你的仓库根目录在哪里。它支持两种方式一是直接在仓库目录里执行它会自动识别二是通过环境变量WORKTRUNK_ROOT指定。cd /home/user/my-project worktrunk init这条命令会在仓库下创建.worktrunk/目录用来存放 session 状态文件。它不会改动任何 Git 原生配置所以即使你之后不想用了直接删掉这个目录即可对仓库本身没有任何影响。3.2 核心命令速查与参数说明我把每天都会用到的命令整理成了一张表方便大家对照查阅命令作用典型用法关键参数worktrunk init初始化状态目录worktrunk init在仓库根目录执行worktrunk create创建新任务工作区worktrunk create 任务描述--base指定基线分支默认当前 HEADworktrunk list列出所有工作区worktrunk list--json输出结构化格式worktrunk attach进入指定工作区worktrunk attach session-id自动设置环境变量worktrunk switch快速切换工作区worktrunk switch session-id等价于 attach 并退出当前 shellworktrunk sync将主分支最新代码合并进当前工作区worktrunk sync可选--rebase用变基代替合并worktrunk prune清理过期工作区worktrunk prune --stale 30--force跳过确认worktrunk clean清空所有历史状态worktrunk clean --all谨慎使用创建任务是最常用的操作我详细说明一下它的执行逻辑。当你运行worktrunk create add user profile page时它内部会依次做这几件事将任务描述转换成 slug 格式得到分支名和目录名用git worktree add创建新工作区切换到一个新分支基于--base指定的基线分支生成唯一的 session ID并把任务描述、目录路径、分支名、创建时间写入.worktrunk/sessions/目录下的 JSON 文件输出 session ID、路径等信息方便你立即attach。整个过程大约 1 到 3 秒取决于仓库大小。创建完成后执行worktrunk list可以看到类似下面的输出ID Branch Dir Task Active s1 add-user-profile-page ../my-project-add-user-profile add user profile page 2m ago s2 fix-login-style ../my-project-fix-login-style fix login page style 1h ago表格里每一列都很有用ID 用来 attach、Branch 用来确认分支、Dir 告诉你目录在哪、Active 帮你判断这个会话是否还在活跃。3.3 让多个 AI Agent 在隔离空间中并行工作准备好工具之后下面是一套我每天在用的 3-Agent 并行工作流。假设场景是我要同时完成“添加用户资料页”、“修复登录样式”、“实现密码重置接口”三个任务。第 1 步为每个任务创建一个独立工作区worktrunk create add user profile page worktrunk create fix login style worktrunk create implement password reset API执行完后仓库目录下会出现一个my-project-add-user-profile、my-project-fix-login-style、my-project-implement-password-reset-api这样的平行目录。第 2 步分别启动三个 Agent 实例。以 Codex CLI 为例启动命令长这样# 终端 1 cd ../my-project-add-user-profile worktrunk attach s1 codex # 终端 2 cd ../my-project-fix-login-style worktrunk attach s2 codex # 终端 3 cd ../my-project-implement-password-reset-api worktrunk attach s3 codex每个 Agent 的当前工作目录是不同的 worktree它们共享同一个.git数据库但工作区文件完全隔离。Agent A 改了src/user/profile.tsxAgent B 改的是src/login/style.css两个文件的落盘位置完全不同不会有任何写冲突。第 3 步每个 Agent 完成自己的工作后提交代码并推送分支git add . git commit -m feat: add user profile page git push origin add-user-profile-page三个分支互不干扰。等所有任务都完成你可以在主分支上逐个合并也可以开一个集成分支把所有功能分支合到一起做联调。这套流程我跑了差不多一个月稳定性比之前“多终端共享一个工作目录”的方案高了不止一个量级。最重要的一点几乎再也没出现过“Agent 改错文件”导致的事故。3.4 与 Codex CLI、Claude Code 等工具的无缝集成很多人在用 AI 编程工具时会遇到一个问题不知道怎么让工具感知 worktree 之间的区别。Worktrunk 的思路是不要试图在其他工具内部做文章而是在系统提示词层面解决。当worktrunk attach执行时它除了切换工作目录之外还会生成一个 shell 提示符的变更并设置WORKTRUNK_SESSION环境变量。你在启动 Agent 的时候可以顺便把这个环境变量作为上下文传给 Agent。以 Codex CLI 为例可以在配置文件里加入类似这样的提示词codex -p 当前任务环境: $WORKTRUNK_SESSION。请先确认你在正确的 worktree 中然后开始执行任务。或者在更精细的场景里把worktrunk list --json的输出塞给 Agent让它自己判断当前环境状态。实测下来大部分 Agent 都能精准理解这些信息并且会在工作期间不断核对分支和路径。如果你用的是 n8n 这类工作流自动化工具也可以把 Worktrunk 封装成一个命令行节点接收任务描述输出 worktree 路径作为后续流程的环境上下文。这样整个“创建任务 → 分配 Agent → 执行编码 → 提交代码”的链路就能串起来。4. 常见问题与排查技巧实录用 Worktrunk 的过程中难免碰到各种坑。我把这些问题按频率从高到低排个序每个都附上排查思路和解决办法。看过之后你大概率能避开我踩过的那些雷。4.1 原生命令与 Worktrunk 状态不一致现象你用原生的git worktree remove删了一个工作区但worktrunk list里还显示这个 session 存在。原因很简单Worktrunk 的状态文件是自己维护的它不会监听原生命中命令的变化。解决办法是加一个状态同步命令。我在项目里设计了worktrunk reconcile它会重新扫描所有已经存在的 worktree然后对比.worktrunk/sessions/里的记录自动清理已经不存在的工作区对应 session。worktrunk reconcile建议每周跑一次保持状态文件干净。如果你经常手痒直接敲原生 Git 命令这个命令是救命的。4.2 无法删除 worktree分支正被另一个进程占用这个是最常见的原生 Git 报错之一。删除 worktree 前Git 会检查该工作区是否有未提交的改动、是否有正在运行的进程占用文件。如果有直接worktrunk prune会失败报错信息类似fatal: cannot remove worktree: /path/to/worktree is dirty正确做法分两步先确认该 worktree 里的改动是否还有价值有的话先 commit 或 stash没有的话执行worktrunk prune --stale 0 --force这里的--stale 0意思是“0 天未活跃即视为过期”配合--force跳过确认。它内部会先执行git worktree remove --force强制删除工作区再清理分支最后更新 session 文件。4.3 Agent 在错误的目录中启动了使用 Agent 时最常见的人为失误在/repo-main下启动了 Agent但你想让它干活的任务对应的是另一个 worktree。Agent 看到当前目录是主仓库就直接在 main 分支上开始改代码了。排查方法很简单启动 Agent 前务必确认两件事。第一pwd是否指向正确的 worktree 目录第二git branch显示的分支名是否和任务对应。如果这两条都正确再启动 Agent。Worktrunk 为此做了一个防护设计worktrunk attach之后如果你的 shell 里定义了WORKTRUNK_SESSION但当前目录并不是这个 session 对应的路径终端提示符会显示红色警告。这个视觉反馈能非常有效地防止“启动错目录”这种低级错误。4.4 并行 Agent 之间出现合并冲突即便工作区完全隔离合并时依然可能冲突。比如两个 Agent 一个改了登录页面的样式另一个改了同一个组件的逻辑合并时 Git 会要求人工解决。这里我的经验是与其最后一次性把所有分支合到 main不如每完成一个任务就合并一个保持主分支始终处于“最新可用”状态。这样冲突范围很小解决起来也容易。Worktrunk 的sync命令就是为这个场景设计的。它会把主分支的最新代码合并到你当前的工作区保证你的分支不会长期脱离主线。我给自己定了个规则一个任务如果预计要干超过两天每天早上第一件事就是执行worktrunk sync避免攒出大量冲突。4.5 磁盘空间被大量 worktree 耗尽这个是并行工作流的通病。每个 worktree 都是一份完整的项目快照Node 项目带个node_modules几个 G 就没了。如果同时开五六个任务磁盘很容易告急。我的建议是给 Worktrunk 的工作区目录做个软链接指向一块独立的大容量磁盘或分区。比如mkdir -p /data/worktrees ln -s /data/worktrees /home/user/my-project-worktrees这样就算某个仓库创建了 20 个 worktree也不会影响系统盘空间。配合定期执行worktrunk prune --stale 14基本可以把磁盘占用控制在一个健康的水平。5. 我踩过坑之后的几点实战心得最后聊几个用 Worktrunk 过程中的个人体会不一定写在官方文档里但实际真的很管用。第一给 Agent 的任务描述越具体worktree 分支名越有意义。如果你输入worktrunk create fix bug生成的分支名就是fix-bug过两天你就认不出这个分支到底在干嘛。我的习惯是用“动词 对象 细节”的结构比如worktrunk create fix empty state on dashboard chart这样列表里扫一眼就知道任务内容。第二强烈建议用--base参数控制基线分支。默认情况是基于当前 HEAD 创建但如果当前工作区已经有一堆没提交的改动创建出来的分支会把这个脏状态带过去。所以我每次跑新任务之前会先切换到主分支然后执行worktrunk create task --base main确保新工作区是干干净净的。第三Agent 的上下文窗口有限频繁切换目录会导致它们“失忆”。这是并行工作流里最容易被忽略的点。我的做法是一个 Agent 只负责一个 worktree从头到尾不换环境。如果任务完成要开新任务新建一个 worktree再启动一个新 Agent 实例而不是让同一个 Agent 切来切去。第四尽量把 Worktrunk 封装成团队统一的入口而不是每个成员自己手敲。项目里可以放一个Makefile或者 shell 脚本定义好任务创建、合并、清理的标准流程所有人都走同一条路径团队协作的摩擦会少很多。目前 Worktrunk 在我的日常开发里已经是基础设施级的工具了。每次起新任务第一个动作就是worktrunk create直接给任务分配一个独立的平行宇宙。如果你也在用多个 Agent 并行开发或者正在为团队的工作区管理头疼这个工具应该能帮你省下不少心力——至少能让你彻底告别“多个 Agent 互相踩踏”的那种崩溃体验。