
去年我做了个小工具叫 Worktrunk定位是面向并行 AI Agent 工作流的 Git Worktree 管理 CLI。起因非常直接我同时用 Codex CLI、Claude Code 这类编程代理并行做几个任务结果被 Git 分支切来切去折腾到崩溃。一个目录开三个任务每次切换分支都要先 stash、再 checkout、再恢复上下文Agent 还经常在当前目录下找不到它想要的文件导致生成一堆乱七八糟的建议。做这个工具到现在大半年几个项目组都在用今天把设计思路、完整实操和踩过的坑一次性聊透。Worktrunk 能做什么简单说你把一个任务名字丢给它它替你创建独立的 Git Worktree、设定好对应分支、记录任务状态任务收尾时替你合并、删除、清理。适合谁用如果你每天开着多个 AI 编程 Agent 并行开发不同功能或者你一个人想同时维护几条开发线这个工具都能帮你省掉大量无意义的 Git 操作。下面我会从问题拆解、核心设计、安装使用、内部实现、问题排查几个维度展开尽量把每个决策背后的原因也讲清楚方便你理解它能解决什么问题、不能解决什么问题。1. Worktrunk 要解决的问题并行 AI Agent 的分支管理混乱1.1 传统单目录开发模式在 Agent 并行场景下为什么行不通先说一个最常见的场景。你有一个 Web 项目同时开着三个编程 Agent一个负责做推荐算法接入一个修登录鉴权相关问题一个做首页性能优化。如果只有一个工作目录那这三个 Agent 就必须轮流使用这个目录。问题在于Agent 不像人那样有完整的任务上下文记忆它的“工作记忆”几乎全部来自当前目录下的文件、终端输出和对话历史。当你把分支从feature/rec-engine切到fix-auth时目录里的文件会瞬间变成另一套代码。前一个 Agent 的对话上下文还在但文件已经变了它如果继续往下写很容易把登录模块的逻辑套在推荐算法代码上或者干脆开始生成一些“幻觉”修改。我也试过用 stash 保存进度但 stash 列表一多谁对应谁根本理不清而且 Agent 自动执行的git stash pop经常因为冲突直接卡住。另一个问题是依赖目录和构建产物的冲突。前端项目的node_modules、Python 项目的.venv、Java 项目的target这些东西在一个目录下是共享的。Agent A 为了验证修改跑了npm install把依赖版本升级了Agent B 在另一个分支上运行测试发现环境全变了报错一大堆。它们不是在协作是在互相拆台。1.2 Git Worktree 能解决目录隔离但裸用命令太啰嗦Git Worktree 这个功能其实很早就有了核心思想就是允许同一个仓库存在多个工作目录每个目录对应不同分支。这样一来每个 Agent 都能拥有完全独立的工作区文件互不干扰依赖目录也能各装各的。这个方向是完全正确的。但纯手工使用git worktree命令体验非常原始。你每次都要记路径比如git worktree add ../rec-engine -b worktrunk/rec-engine develop分支名、目录路径、基础分支一个都不能错。任务开得多了你还得靠脑子记住哪条路径对应哪个分支、哪个任务。任务结束后的合并和清理更麻烦git merge一个分支、git worktree remove一个目录、再删除分支三步操作没有任何关联性漏一步就留下垃圾。我自己用了一段时间后意识到这个场景需要的不是又一层 Git 命令封装而是一个真正的“任务管理模型”。用户关心的是“我有哪些任务在跑、每个任务在哪个目录、Agent 跑到什么进展、怎么收尾”而不是 Worktree 内部那些路径和分支名。1.3 Worktrunk 的核心定位把 Worktree 变成任务生命周期管理Worktrunk 的设计出发点就是把“任务”作为第一公民。它维护了任务名、分支、路径、基础分支、所属 Agent、状态这组映射关系。你只需要说“创建任务 rec-engine”它负责在.worktrunk/rec-engine目录下开出分支worktrunk/rec-engine你说“关闭任务 rec-engine”它负责把分支合并回 base、删除 worktree、清理元数据一气呵成。这个定位让 Worktrunk 既不是git worktree的简单包装也不是项目管理工具它就是专门为“多个 AI Agent 并行干活、人来做收尾”这个场景服务的。我后面所有设计决策都是围绕“减少心智负担、保证操作可逆、避免状态不一致”这三个目标来的。2. 核心设计思路任务名、分支、路径与 Agent 的映射关系2.1 任务如何映射到 Git 分支和目录任务必须要有稳定、可读的命名规范同时不能依赖用户手动管理。Worktrunk 采用了一套固定映射规则任务名会被规范化成 slug然后在.worktrunk/目录下创建工作区分支固定为worktrunk/task-name。例如任务名rec-engine目录就是.worktrunk/rec-engine分支就是worktrunk/rec-engine。这里有几个刻意的设计决定。第一目录统一放在仓库根目录的.worktrunk/下而不是散落在仓库外部的../rec-engine。这样做的好处是仓库整体相对路径简单删除时一目了然也不容易误删其他项目目录。第二分支名带worktrunk/前缀能在git branch列表里一眼识别出哪些分支是由任务系统管理出来的方便后期清理和审计。第三.worktrunk这个名字刻意以点开头隐含“这是工作状态目录”的语义它和.git一样默认不参与提交。2.2 任务元数据为什么存 JSON 文件而不是解析分支名所有任务的状态不能靠解析分支名或目录列表来推断那样太脆弱。Worktrunk 在.worktrunk/state.json文件里维护完整元数据包括任务 ID、名称、分支名、基础分支、路径、状态、绑定的 Agent、创建时间和更新时间。JSON 文件的好处是直观、可读、易于调试出了问题打开看一眼就知道什么情况。为什么不存进git config因为我实验过Git 的 config 结构对复杂列表数据支持得并不顺手把任务列表塞进去会让.git/config变得非常难读而且全局 config 和局部 config 混在一起容易出问题。为什么不直接扫描.worktrunk/*/目录加上git worktree list反推任务状态因为目录存在不等于任务活跃可能已经关闭了但目录没删干净分支状态也不等同于任务状态。有个显式的状态文件才可能处理“任务关闭中”“合并冲突中”“已暂停”这类中间态。2.3 Agent 绑定一条命令进入正确的工作上下文并行 Agent 工作流里还有一个隐形痛点哪个任务用了哪个 Agent、在哪个目录跑的很容易记混。Worktrunk 在创建任务时支持--agent参数把任务和 Agent 名称比如codex、claude、gemini绑定起来。之后通过worktrunk enter task命令它会自动进入对应目录并启动指定 Agent 的交互会话。这个功能最开始我觉得是锦上添花后来发现它才是提高效率的关键。绑定关系写进状态文件后你随时可以用worktrunk ls看到每个任务对应的 Agent不会出现“诶这个任务刚才是不是用错了 CLI”这种疑问。enter命令也解决了“使用哪个命令启动 Agent、要不要传额外参数”的问题这些配置可以统一放在 Worktrunk 的配置文件里不用每个任务都手敲一遍。3. 安装与命令速查上手只需要十分钟3.1 环境要求和安装方式Worktrunk 的核心依赖是 Git 2.5 以上版本因为git worktree在那个版本才正式可用。但我建议至少用 Git 2.30 以上的版本因为早期版本在处理 worktree 元数据和prune时有一些边界问题新版稳定得多。安装方式有两种。最省事的是下载编译好的二进制放到 PATH 目录下源码安装则依赖 Rust 工具链执行cargo install worktrunk。我选 Rust 而不是 Go 或 Python主要考虑三点编译产物是单个静态二进制部署简单处理文件系统、进程和并发锁时类型系统能挡住不少低级错误生态里clap和serde非常成熟CLI 解析和配置管理省了很多事。如果你更熟 Go用 Go 重写也不是不行但并发安全和状态文件事务写入这两个点上 Rust 写起来更顺手。安装完先验证一下版本worktrunk --version正常会输出类似worktrunk 0.4.0的内容。接下来在一个已有 Git 仓库里初始化cd /path/to/your/project worktrunk initinit会检查当前目录是否是合法 Git 仓库、Git 版本是否达标然后创建.worktrunk/目录和初始状态文件。3.2 核心命令速览我整理了一份常用命令表方便你快速对照命令作用典型参数worktrunk init初始化工具状态目录无worktrunk create name创建新任务工作区--base branch、--agent nameworktrunk ls查看所有任务及状态--json输出结构化数据worktrunk show task查看单个任务详情无worktrunk enter task进入任务目录并启动 Agent--path-only只打印目录worktrunk close task关闭任务可合并分支并清理--merge、--keep-dirworktrunk reopen task恢复已关闭任务无worktrunk prune清理失效的 worktree 引用无这里最常用的是create、ls、enter、close。reopen是我后来加的功能因为实际操作中发现任务关了之后又想继续改是很常见的事。关闭时我保留了分支和元数据快照只要目录没被删就能恢复回来。3.3 命令的可恢复性设计几乎所有写操作我都做了防御。close默认不会直接删除东西而是先检查任务里有没有未提交的改动和未推送的提交prune每次执行前会先输出将要清理的列表确认后才动手。如果因为合并冲突导致close中断任务状态会停在closing你可以手动解决冲突后再次执行close --merge从断点继续。这种“操作默认保守、可恢复”的设计思路避免了最糟糕的情况一个命令把用户几个小时的 Agent 工作成果全部删掉。命令行工具面向自动化场景破坏性操作必须能明确预期后果。4. 一套完整的并行 Agent 工作流实操4.1 场景设定三个任务并行启动假设项目是shop-web默认开发分支叫develop。现在需要同时做三个任务推荐算法接入、登录鉴权修复、首页性能优化。按传统模式我会来回切分支用 Worktrunk流程是一次性把三个工作区都建好。先把三个任务创建工作区建出来git checkout develop worktrunk create rec-engine --base develop --agent codex worktrunk create fix-auth --base develop --agent claude worktrunk create perf-home --base develop --agent gemini每次执行create工具内部依次做这几件事校验 base 分支存在、从 base 创建worktrunk/name分支、调用git worktree add创建目录、更新状态文件。执行完之后仓库目录会多出三个彼此隔离的工作树shop-web/ ├── .worktrunk/ │ ├── rec-engine/ │ ├── fix-auth/ │ └── perf-home/ ├── src/ ├── package.json └── ...4.2 查看任务状态并进入工作区用worktrunk ls能看到整个并行任务的概览ID NAME BRANCH BASE STATUS AGENT PATH 1 rec-engine worktrunk/rec-engine develop active codex .worktrunk/rec-engine 2 fix-auth worktrunk/fix-auth develop active claude .worktrunk/fix-auth 3 perf-home worktrunk/perf-home develop active gemini .worktrunk/perf-home启动 Agent 我不用手动cd .worktrunk/rec-engine codex直接worktrunk enter rec-engine它会进入工作目录并启动codex交互会话。三个 Agent 分别在各自的目录下跑互不干扰。由于每个目录都是独立工作区它们各自的node_modules、.env、临时构建产物都互不影响。唯一的问题是依赖需要各装一遍这既是缺点也是优点缺的依赖不会串测试环境更干净。4.3 任务收尾合并分支并清理工作区Agent 完成一个任务后收尾是最容易出错的一步。Worktrunk 把合并、删除目录、删除分支、更新状态串成了一个原子流程worktrunk close rec-engine --merge命令执行过程大致如下task rec-engine found merging branch worktrunk/rec-engine into develop merge ok deleting linked worktree at .worktrunk/rec-engine branch worktrunk/rec-engine deleted task rec-engine closed如果合并时产生冲突输出会变成merge conflict detected, task left in closing state resolve conflicts manually, then run: worktrunk close rec-engine --merge这种做法很好用因为我不需要在多个终端里分别跑 merge、remove、branch -d也不必担心遗漏操作导致仓库里残留一堆 worktree 和分支。4.4 让并行任务保持同步的小技巧实际使用中我会在每个任务刚开始时先git fetch origin确保 base 分支是最新的。如果某个任务开发周期较长为了避免和主分支偏离太多我会每个小时在对应工作目录里执行一次git fetch origin git merge origin/develop。因为是在独立 worktree 里合并完全不影响其他任务。Agent 的上下文我也有一个习惯把任务相关的设计文档、接口说明单独放到对应 worktree 的一个docs/或context/目录里。这样 Agent 在目录里天然能看到这些文件不需要在对话里反复粘贴内容它能自己检索并保持上下文一致效果比靠对话记忆好很多。5. 内部实现关键细节状态文件、锁机制与 Git 操作次序5.1 状态文件的设计与事务写入state.json是 Worktrunk 的“数据库”结构大致如下{ version: 1, base: develop, tasks: [ { id: 1, name: rec-engine, branch: worktrunk/rec-engine, base: develop, path: .worktrunk/rec-engine, status: active, agent: codex, created_at: 2025-06-01T10:12:00Z, updated_at: 2025-06-01T10:12:00Z } ] }这里最需要注意的点是写入的原子性。如果直接对state.json做覆盖写中途进程崩溃可能留下半截文件之后所有命令都解析失败。我的做法是先写临时文件.worktrunk/state.json.tmp写完后用fs::rename原子替换原文件。这样无论任何时候崩溃原文件都保持完整可读。5.2 并发安全多终端同时操作同一任务开发中遇到最严重的 bug是两个终端同时执行worktrunk close。两者都读到任务状态是active都去执行git worktree remove结果一个成功、另一个报错状态文件还可能被覆盖成互相矛盾的内容。解决方案是给状态文件加文件锁。执行写操作之前先获取.worktrunk/state.lock的排他锁拿到锁之后才读取和修改状态。锁文件不用长期存在但它在运行期间能阻止另一个 Worktrunk 进程同时修改状态。Rust 里我用的是flock库Linux 和 macOS 都支持Windows 上虽然略有差异但我在 WSL 下测试没有问题。加锁后的伪代码大概是这样let _lock flock::Lock::new(path.join(.worktrunk/state.lock))?; let state: WorktrunkState serde_json::from_str(fs::read_to_string(state_path)?)?; // ...执行 git worktree 操作、合并分支... fs::write(tmp_path, serde_json::to_string_pretty(state)?)?; fs::rename(tmp_path, state_path)?;5.3 与 Git worktree 底层命令的协作边界Worktrunk 没有直接操作.git/worktrees/目录下的内部文件而是尽量通过git worktree官方命令完成创建和删除。直接改内部文件虽然也能实现但 Git 本身有自己的元数据缓存绕过命令很容易导致git worktree list显示不一致严重时还会让仓库处于“半损坏”状态。删除 worktree 时如果目标目录里存在未跟踪文件git worktree remove会拒绝删除这是 Git 自带的安全机制。Worktrunk 会把这个错误捕获下来提示你目录里有未跟踪文件要么手动清理要么用--forceWorktrunk 也会二次提示。这个设计保护了很多次误操作比如 Agent 在目录里生成了未提交的配置文件直接删目录等于永久丢失。5.4 命令解析和配置管理的实现选型CLI 参数解析我用的是clapRust 生态里最主流的库它支持子命令、参数校验、自动补全脚本生成省了不少功夫。配置管理则相对简单一个worktrunk.toml可选文件放在项目根目录用于定义默认 base 分支、Agent 启动命令模板、任务创建后的初始化 hook 等。配置示例default_base develop default_agent codex [agents] codex { command codex, entry_args [] } claude { command claude, entry_args [--dangerously-skip-permissions] } gemini { command gemini, entry_args [--autostart] } [hooks] after_create npm installafter_create这个 hook 是很多用户提的需求。每次创建 worktree 后自动执行装依赖省得每个任务手动装一遍非常实用。6. 常见问题与排查经验实录6.1 故障速查表在实际使用中我整理了几类高频问题做成一个速查表现象原因处理方法git worktree add报 branch already checked out分支被其他 worktree 占用worktrunk ls查看对应任务目录不要手工切分支worktrunk close提示 worktree 存在未提交改动任务目录里有未提交的修改进入目录提交或 stash再重新执行 close状态文件损坏无法解析 JSON手动编辑或进程异常写入从.worktrunk/state.json.bak恢复或根据git worktree list重建任务worktree 目录被外部程序占用无法删除Windows 下常见或 IDE 打开了目录文件关掉占用程序执行worktrunk prune --forceworktrunk ls显示的 worktree 和git worktree list不一致手工执行过 worktree 命令以git worktree list为准执行worktrunk prune清理合并冲突后任务卡在 closing命令中断或冲突未解决手动解决后再次执行close --merge6.2 最容易踩的坑手工切分支导致 worktree 状态错乱我第一个要提醒的是不要在main工作区手工git switch worktrunk/xxx。凡是带worktrunk/前缀的分支都有对应的工作树git checkout切换过去会直接报错或导致 Git 内部状态混乱。你需要进入对应的.worktrunk/xxx目录去操作。我在早期版本里遇到过用户把.worktrunk/rec-engine目录删了但分支还留着导致git worktree显示目录缺失。恢复方式也比较麻烦需要手动git worktree prune。所以现在我加了一个建议一切对任务工作区的操作尽量走 Worktrunk 命令不要手动裸操作 Git。6.3 关于依赖目录和 Agent 缓存的细节因为 worktree 是独立目录所以node_modules和 Python 的.venv不会自动出现。如果一个项目依赖非常多每次 create 都从零装依赖确实痛苦。我的解法是在配置里设置after_createhook 执行安装命令如果你觉得还是太慢可以考虑把依赖目录做符号链接到外部共享目录。但要注意如果 Agent 需要改依赖版本共享符号链接会让不同 worktree 互相影响权衡下来我更推荐干净安装配合包管理器的缓存实际耗时可接受。另一个坑是 Agent 本身的会话缓存。Codex CLI、Claude Code 这类工具默认会在用户目录存放会话历史跨任务可能会串上下文。我实际使用时会把 Agent 的会话目录通过环境变量重定向到 worktree 内部比如.worktrunk/rec-engine/.codex-sessions这样任务和会话完全绑定删任务时就能顺带清理掉会话数据。6.4 多 Agent 同时运行时的资源注意点三个 Agent 同时跑CPU、内存和终端进程数都不会太高但要注意 API 调用量。如果三个 Agent 都在高频请求大模型接口账单会涨得非常快。我的使用经验是按任务复杂度分配小改动用参数少的模型大模块重构才上强模型。Worktrunk 本身不限制这些但是你知道每个任务用它做了多少事回收任务时心里会有数。7. 后续扩展方向与个人心得这个工具目前已经覆盖了我日常 90% 的并行 Agent 开发需求。再往远处想有几个方向值得做一是close时自动 push 到远端并创建 Merge Request把流程接到代码托管平台上二是支持任务模板比如创建任务时自动从仓库里的templates/目录拷入一组上下文文件方便 Agent 快速了解项目规范三是和 terminal multiplexer 集成一个命令把多个任务工作区全部挂到后台会话这样就能真正实现“一键并行启动三个 Agent”的效果。从做这个工具到在团队里推广我最大的体会是并行 AI Agent 开发爆发之后我们缺的不只是能写代码的模型更缺一套能管理多路产出的工程化基础设施。Git Worktree 本身是极好的机制但它是以“仓库”为单位设计的而 AI Agent 工作流是以“任务”为单位运转的中间这层映射正是像 Worktrunk 这样的工具能发挥价值的地方。如果你也在手动维护多条任务分支强烈建议试试这类任务化的 worktree 管理方式它会让你从 Git 操作的泥潭里腾出精力真正专注在任务本身。