pnpm + Git 工作树用于多代理开发
当多个 AI 代理需要同时在同一个 monorepo 上工作时,它们每个都需要一个隔离的工作副本,并且具有完全功能的 node_modules。Git 工作树结合 pnpm 的 全局虚拟存储 使这成为可能:每个工作树都有自己的检出和自己的 node_modules,但依赖通过磁盘上的单个内容寻址存储在它们之间共享。
🌐 When multiple AI agents need to work on the same monorepo simultaneously, they each need an isolated working copy with fully functional node_modules. Git worktrees combined with pnpm's global virtual store make this practical: each worktree gets its own checkout and its own node_modules, but dependencies are shared across all of them through a single content-addressable store on disk.
什么是 git worktree?
🌐 What is a git worktree?
通常,一个 git 仓库一次只有一个工作目录绑定到一个分支。如果你想查看另一个分支,你必须先将更改存储(stash)或提交(commit)后再切换。git worktree 允许你同时检出多个分支,每个分支在其自己的目录中。所有工作树共享相同的仓库历史和对象——它们只是同一仓库的不同视图。
🌐 Normally, a git repository has a single working directory tied to one branch at a time. If you want to look at another branch, you have to stash or commit your changes and switch. A git worktree lets you check out multiple branches simultaneously, each in its own directory. All worktrees share the same repository history and objects — they're just different views into the same repo.
git worktree add ../feature-branch feat/my-feature
这会创建一个名为 ../feature-branch 的新目录,并检出 feat/my-feature,同时你原来的工作目录保持在当前分支。你可以在两个目录中独立工作。
🌐 This creates a new directory ../feature-branch with feat/my-feature checked out, while your original working directory stays on its current branch. You can work in both directories independently.
一个常见的模式是使用一个裸仓库(没有自己的工作目录)作为中心,并将所有工作目录创建为工作树:
🌐 A common pattern is to use a bare repository (one with no working directory of its own) as the hub, and create all working directories as worktrees:
git clone --bare https://github.com/your-org/your-repo.git your-repo
cd your-repo
git worktree add ./main main
git worktree add ./feature feat/something
为什么使用工作树?
🌐 Why worktrees?
即使在有 AI 代理之前,worktrees 对于维护项目的多个主要版本也很有用。在我的开发机器上,我使用一个 pnpm 仓库,至少有两个 worktrees:一个在 main 分支上用于 pnpm v11,另一个在 v10 分支上用于回溯移植和维护版本。通过这种方式,我可以在不存放正在进行的 v11 工作的情况下修复 v10 的错误——两个版本始终都是已检出并可以使用的。过去,在 pnpm 仓库中 2 到 3 个 worktrees 通常就足够了。然而,自从我开始广泛使用 AI 代理之后,我需要更多的 worktrees,让我的代理可以并行处理许多任务。
🌐 Even before AI agents, worktrees are useful for maintaining multiple major versions of a project. On my dev machine, I use a pnpm repository with at least two worktrees: one on main for pnpm v11, and another on the v10 branch for backports and maintenance releases. This way, I can fix a bug on v10 without stashing my in-progress v11 work — both versions are always checked out and ready to go. In the past, 2 or 3 worktrees were usually enough for me in the pnpm repository. However, since I started using AI agents extensively, I need a lot more worktrees to let my agents work on many tasks in parallel.
为什么这在人工智能代理中尤为重要
🌐 Why this matters even more with AI agents
有了人工智能编码代理,工作树从方便变得必不可少。每个代理都需要自己的工作目录来编辑文件、运行构建以及执行测试,而不会干扰其他代理。如果没有工作树,这意味着需要多次克隆仓库,为每个副本复制 git 历史记录。
🌐 With AI coding agents, worktrees go from convenient to essential. Each agent needs its own working directory to edit files, run builds, and execute tests without interfering with other agents. Without worktrees, this means cloning the repository multiple times, duplicating git history for each copy.
Worktrees 解决了 git 这一部分问题——每个代理都有自己的独立检出,同时共享底层的 git 对象。但每个 worktree 仍然需要它自己的 node_modules,这可能有数百兆字节。这就是 pnpm 的 全局虚拟存储 发挥作用的地方:启用它后,每个 worktree 的 node_modules 只包含指向磁盘上单个内容寻址存储的符号链接。这意味着添加新代理既快又几乎不占用额外磁盘空间。
🌐 Worktrees solve the git side — every agent gets its own isolated checkout while sharing the underlying git objects. But each worktree still needs its own node_modules, which can be hundreds of megabytes. That's where pnpm's global virtual store comes in: with it enabled, each worktree's node_modules contains only symlinks into a single content-addressable store on disk. This means adding a new agent is fast and costs almost no extra disk space.
此设置假设工作树和代理共享相同的信任边界。不要为相互不信任的代理或用户使用同一个可写的 pnpm 存储。
🌐 This setup assumes the worktrees and agents share the same trust boundary. Do not use one writable pnpm store for mutually untrusted agents or users.
设置它
🌐 Setting it up
1. 创建一个裸仓库
🌐 1. Create a bare repository
git clone --bare https://github.com/your-org/your-monorepo.git your-monorepo
cd your-monorepo
2. 为每个分支创建工作树
🌐 2. Create worktrees for each branch
# Main development worktree
git worktree add ./main main
# A feature branch for agent A
git worktree add ./feature-auth feat/auth
# A bugfix branch for agent B
git worktree add ./fix-api fix/api-error
每个工作树都是一个完整的检出,拥有自己的文件,但它们都共享相同的 .git 对象存储。
🌐 Each worktree is a full checkout with its own files, but they all share the same .git object store.
3. 启用全球虚拟商店
🌐 3. Enable the global virtual store
将 enableGlobalVirtualStore: true 添加到你仓库中的 pnpm-workspace.yaml:
🌐 Add enableGlobalVirtualStore: true to the pnpm-workspace.yaml in your repository:
packages:
- 'packages/*'
enableGlobalVirtualStore: true
4. 在每个工作树中安装依赖
🌐 4. Install dependencies in each worktree
cd main && pnpm install
cd ../feature-auth && pnpm install
cd ../fix-api && pnpm install
第一个 pnpm install 会将软件包下载到全局存储中。在其他工作树中的后续安装几乎是即时的,因为它们只需创建指向同一存储的符号链接。
🌐 The first pnpm install downloads packages into the global store. Subsequent installs in other worktrees are nearly instant because they only create symlinks to the same store.
它是如何运作的
🌐 How it works
如果没有全局虚拟存储,每个工作树都会在 node_modules 内拥有自己的 .pnpm 虚拟存储,其中包含每个包的硬链接或副本。使用 enableGlobalVirtualStore: true 时,pnpm 会将所有包内容保存在一个共享目录(全局存储,你可以通过运行 pnpm store path 找到它)中,每个工作树的 node_modules 包含指向那里 的符号链接:
🌐 Without the global virtual store, each worktree would have its own .pnpm virtual store inside node_modules, with hardlinks or copies of every package. With enableGlobalVirtualStore: true, pnpm keeps all package contents in a single shared directory (the global store, which you can find by running pnpm store path), and each worktree's node_modules contains symlinks pointing there:
your-monorepo/ (bare git repo)
├── main/ (worktree: main branch)
│ ├── packages/
│ └── node_modules/
│ ├── lodash → <global-store>/links/@/lodash/...
│ └── express → <global-store>/links/@/express/...
├── feature-auth/ (worktree: feat/auth branch)
│ └── node_modules/
│ ├── lodash → <global-store>/links/@/lodash/... ← same target
│ └── express → <global-store>/links/@/express/...
└── fix-api/ (worktree: fix/api-error branch)
└── node_modules/
├── lodash → <global-store>/links/@/lodash/... ← same target
└── express → <global-store>/links/@/express/...
这意味着:
🌐 This means:
-
Near-zero per-worktree overhead — the local
node_modulescontains only symlinks to the shared global virtual store. Unlike pnpm's default behavior, which hardlinks files from the content-addressable store into a localnode_modules/.pnpmdirectory, the global virtual store means no files are copied or hardlinked into the worktree at all. -
Instant installs for new worktrees — packages are already in the global store.
-
No conflicts — each worktree has its own
node_modulestree, so agents can install different dependency versions on different branches without interference.
例如:pnpm 单仓库本身
🌐 Example: the pnpm monorepo itself
pnpm 仓库 使用了一个裸 git 仓库和 enableGlobalVirtualStore: true 的完全相同的设置。它包含辅助脚本以简化工作树的管理:
🌐 The pnpm repository uses this exact setup with a bare git repo and enableGlobalVirtualStore: true. It includes helper scripts to make worktree management easier:
pnpm worktree:new <branch-name|pr-number> — 创建一个新的工作树并进行设置:
# Create a worktree for a branch (creates it from main if it doesn't exist)
pnpm worktree:new feat/my-feature
# Create a worktree for a GitHub PR (fetches the PR ref automatically)
pnpm worktree:new 10834
该脚本处理一些超出普通 git worktree add 的内容:
🌐 The script handles a few things beyond plain git worktree add:
-
PR numbers are fetched via
git fetch origin pull/<number>/headso they work for forks too. -
Branch names with slashes (e.g.
feat/my-feature) are converted to dashes for the directory name (e.g.feat-my-feature). -
The
.claudedirectory is symlinked from the bare repo's git common directory into the new worktree, so all worktrees share the same Claude Code settings and approved commands.
还有一个 shell 辅助工具 shell/wt.sh,它将脚本和 cd 封装到新的工作树中:
🌐 There's also a shell helper shell/wt.sh that wraps the script and cds into the new worktree:
# Source it in your shell config, then:
wt feat/my-feature
wt 10834
小贴士
🌐 Tips
-
Creating worktrees for agents: When launching an AI agent, create a dedicated worktree for it. The agent gets full isolation to edit files, run tests, and install packages without affecting other agents.
-
Cleanup: Remove a worktree when it's no longer needed with
git worktree remove ./feature-auth. Leftover worktrees are cheap but can accumulate.