Correct the v12 guides from agent-skills.md through lockfile.md against pnpm/crates, and fix the same wrong text on the v11 twins where pnpm11 confirms it. Part of pnpm/tasks#191. Wrong claims (both versions): - configuring: the global auth file is auth.ini, not rc; list the npm_config_* exceptions (workspace dir, userconfig, URL-scoped auth). - config-dependencies: the hook import used an invalid ESM specifier. - docker: Example 3 ran fetch --prod and built without an offline install or pnpm-workspace.yaml. - errors: drop MISMATCHED_RELEASE_CHANNEL, describe INVALID_NODE_VERSION through nodeVersion, PEER_DEP_ISSUES needs strictPeerDependencies. - faq: no lockfile deletion after a readPackage hook, default store order, junctions depend on symlink privilege. - filtering: `!./lib` matches one project; use `!./lib/**`. - git branch lockfiles: every non [A-Za-z0-9_.-] character becomes `!`, the name is lowercased, merging deletes branch lockfiles, "or" patterns. - git-worktrees: remove the nonexistent worktree:new and shell/wt.sh. - global-packages: orphaned dirs are cleaned by add -g (and update -g in v12), not store prune; document that add -g replaces every group that contains the package. - how-peers-are-resolved: current peer directory names. - completion: built-in completion already covers --filter and scripts. - feature-comparison: broken JSR anchor. Wrong claims (v12 only): - cargo: cargo.indexUrl fails the parse, overrides apply to adding only, merge the duplicated Cargo index section into Registries. - global-virtual-store: pnx keeps its own virtual store (dlx part only; the global-install claims are left for a separate decision). - installation: the npm package installs on Node.js 18, only get-pnpm needs 22.13; trim unverified compatibility rows. Missing facts (v12): agent-skills group filtering and naming, catalogs (catalogs.default, pnx, --save-catalog, error codes), cargo recursive add, build-std, resolution keys, completion zsh/pwsh setup, config dependency location and error codes, include error codes, CI opt-outs and pnpm ci, finders on list, filtering settings and --no-fail-if-no-match, global install refusals, trust for npm-registry package managers, Rust shims, overrides link from aliases. Stale history (v12): drop v11/v10 version markers and "since v11" narration in continuous-integration, filtering, finders, git-worktrees, global-packages, global-virtual-store, how-peers-are-resolved, lockfile, completion, configuring. Review follow-up: the Node.js compatibility table keeps the pnpm 10 column; Docker Example 3 builds with all dependencies in a build stage and ships only production dependencies, with the pnpm-workspace.yaml caveat (both versions); catalogs keep the bare-path form; v11 global packages name `pnpm update -g` as cleaning orphans too; the cargo build-std note says when rust-src must be listed. Second review: build-std needs a non-empty list; the Docker fetch layer also depends on the project settings; branch lockfile names keep only ASCII characters; the conflict merge needs both sides to parse; Rust shims and package managers are default globalShims participants; pnpm self-update also cleans orphaned global groups; v12 pnpm login writes tokens to config.yaml; legacyDirFiltering keeps the subtree wording (the v12 mismatch is pnpm/tasks#196); the v12 Docker example copies the files that `include` names before pnpm fetch.
118 lines
6.4 KiB
Markdown
118 lines
6.4 KiB
Markdown
---
|
|
id: git-worktrees
|
|
title: pnpm + Git Worktrees for Multi-Agent Development
|
|
---
|
|
|
|
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](./global-virtual-store.md) 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.
|
|
|
|
## What is a 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](https://git-scm.com/docs/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.
|
|
|
|
```sh
|
|
git worktree add ../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:
|
|
|
|
```sh
|
|
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?
|
|
|
|
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
|
|
|
|
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 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](./global-virtual-store.md) 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.
|
|
|
|
:::important
|
|
|
|
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. Create a bare repository
|
|
|
|
```sh
|
|
git clone --bare https://github.com/your-org/your-monorepo.git your-monorepo
|
|
cd your-monorepo
|
|
```
|
|
|
|
### 2. Create worktrees for each branch
|
|
|
|
```sh
|
|
# 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
|
|
```
|
|
|
|
Each worktree is a full checkout with its own files, but they all share the same `.git` object store.
|
|
|
|
### 3. Enable the global virtual store
|
|
|
|
Add `virtualStoreType: global` to the `pnpm-workspace.yaml` in your repository (before v11.23.0 this setting was spelled `enableGlobalVirtualStore: true`):
|
|
|
|
```yaml title="pnpm-workspace.yaml"
|
|
packages:
|
|
- 'packages/*'
|
|
|
|
virtualStoreType: global
|
|
```
|
|
|
|
### 4. Install dependencies in each worktree
|
|
|
|
```sh
|
|
cd main && pnpm install
|
|
cd ../feature-auth && pnpm install
|
|
cd ../fix-api && 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
|
|
|
|
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 `virtualStoreType: global`, 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:
|
|
|
|
```text
|
|
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_modules` contains only symlinks to the shared global virtual store. Unlike pnpm's default behavior, which hardlinks files from the content-addressable store into a local `node_modules/.pnpm` directory, 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_modules` tree, so agents can install different dependency versions on different branches without interference.
|
|
|
|
## 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.
|