跪拜 Guibai
← Back to the summary

worktrunk: Five Agent Workspaces, Measured — Slower Creation, Near-Zero Dependency Copies

Five Agent Workspaces, worktrunk Tested

At 3 PM, the easiest thing to get hooked on isn't writing code, it's opening five AIs to modify one project at the same time.

Five windows, five branches, five git worktree adds, five cd ../repo.feats. What actually makes you give up on parallelism isn't that the models aren't smart enough, it's switching directories back and forth. So today I installed a small tool specifically for managing this: worktrunk, which had 10,672 stars as of November 4th; on the day I looked it was #3 on GitHub trending, but that ranking can't be verified afterward.

Let me first clarify what this piece tested and what it didn't, so the headline doesn't outrun the evidence:

So the words "fast," "slow," and "space-saving" below only apply to the actions that were measured.

Let me put the conclusion up front: It won't make worktree creation faster: creating 5 takes 0.34s more than native git, about 0.07s more per worktree (about 4x in this single measurement); but it turns copying the same 120MB from "moving 120MB in 0.20s" into "sharing disk blocks in 0.02s," with no visible additional usage in df.

Two measurements plus one control baseline:

Item Native git / cp worktrunk
Creating 5 worktrees 0.11s 0.45s
Copying 120MB test file (single file) 0.20s, df +122,916 KB 0.02s, df −32KB
Disk usage for 5 same-content directories 600MB (df measured, diff 614,716 KB) 5 reflinks not tested

Test environment: arm64 Mac, macOS 26.6.2, git 2.56.0, worktrunk v0.81.0.

1. It squeezes three commands into one, but doesn't intend to run faster

git worktree itself isn't complex: it gives different branches of the same repository their own separate directories, without interfering. When five AIs run in parallel, this is the cleanest isolation, more stable than switching back and forth with git stash.

The problem is the input cost of native commands. The repository README gives a comparison: to create and start Claude natively, you'd do:

git worktree add -b feat ../repo.feat && \
cd ../repo.feat && \
claude

worktrunk is one line:

wt switch -c -x claude feat

Cleanup is similar. Natively you'd return to the main repo, delete the worktree, delete the branch; worktrunk uses wt remove. It treats the branch name as the address of the worktree, and the path is computed automatically from a template, so you don't have to remember locations like ../repo.feat.

Project size: 10,672 stars, 388 forks (as of November 4th), MIT OR Apache-2.0, written in Rust, release package is 6.4MB, the single wt binary after extraction is 22MB.

2. Worktrees are cheap because they don't copy history

Many people's first reaction is "opening five directories is no better than cloning five times." If you actually clone five times, each copy carries a full object database; a worktree doesn't.

I took it apart. The main repo's .git is 740KB; each new worktree adds only a 55-byte .git file:

gitdir: /tmp/wt-demo/.git/worktrees/wt-demo.f1

It's a pointer; the real commits, trees, and blobs stay shared in the main repo. So my new worktree with 40 source files was only 336KB before copying dependencies.

This is also the premise that makes worktrunk worth existing: worktrees themselves are already cheap enough; what's missing is just a convenient entry point. If the underlying mechanism were clone, no amount of wrapping commands could save it.

3. How to arrange five workspaces

This section is a workflow demonstration, not a task test: I created five workspaces, each command attaches the corresponding agent to the current terminal, but I didn't actually type tasks into five windows, nor did I let them run to completion and create conflicts. The five agents were supposed to handle login, API, styling, tests, and documentation—this is a division-of-labor conception, not the result of five PRs:

wt switch -c -x claude fix-login
wt switch -c -x codex refactor-api
wt switch -c -x claude polish-css
wt switch -c -x codex add-tests
wt switch -c -x claude write-docs

-c creates a new branch, -x executes the command immediately after creation. Each agent will go into its own directory, work on a different branch, and won't overwrite each other at the file level; I didn't run the actual code modification in this test.

Note: these five commands should be executed in separate terminals. -x takes over the current terminal; once the first one runs, subsequent commands in the same window won't continue.

To check progress mid-way:

wt list

To wrap up:

wt merge -y fix-login
wt remove -y polish-css

-y skips confirmation. In this flow I only used four subcommands: switch, list, merge, remove. I didn't touch the interactive picker or have the LLM generate commit messages.

4. First, a bucket of cold water: creation takes 0.34 seconds more than native git

I didn't just read the README; I created a test repository directly: 40 source files, plus a single 120MB file placed in .gitignore to pretend to be a dependency directory, then created 5 worktrees on the same machine. This 120MB isn't a real node_modules, it's a single large file I generated with dd; real dependencies are tens of thousands of small files, and copy time and reflink behavior might differ. I'll say this again later.

Native git:

for i in 1 2 3 4 5; do git worktree add -q -b f$i ../wt-plain.f$i; done
# real 0.11

worktrunk:

for i in 1 2 3 4 5; do wt -C /tmp/wt-demo switch -c -y f$i; done
# real 0.45

5 worktrees, 0.11s vs 0.45s: the absolute difference is 0.34s, about 0.07s more per worktree. By this single measurement it's 4.1x, but at the 0.1-second scale process startup jitter has a large influence, so don't treat this multiplier as stable. My guess is the slowness comes from process startup and the 22MB binary—but that's speculation; I didn't profile or separate startup time from git operations. The only thing confirmed is the total time.

So the first counterintuitive point is: for "creating a worktree," native git wins, and wins clearly.

But what it saves isn't the 0.34 seconds of creation; it's the repeated typing you do every day. I personally switch directories about a dozen times a day, with three commands each time; worktrunk compresses that into one line, and wt switch feat can jump directly from any worktree without returning to the main repo first.

There's also an installation cost to state upfront: without shell integration, it can't cd for you. When I first ran it, I got this warning after every creation:

▲ Cannot change directory — shell integration not installed

Functionality isn't affected, but an extra warning line each time is annoying. To fix it you have to run wt config shell install, which modifies your shell configuration.

5. What it saves isn't "fast copying," it's not moving bytes at all

If worktrunk only provided command abbreviations, it wouldn't be worth an article. What it genuinely solves is the cold-start problem of parallel agents: every new worktree has no node_modules, no target/, no build cache; five agents mean five re-installs.

It provides a command: wt step copy-ignored, which copies gitignored files from the main worktree.

The test target needs explanation: a single 120MB file generated with dd, placed in a gitignored directory. So its output says Copied 1 file, because there really is only one file. Real node_modules are tens of thousands of small files; I didn't test that difference, and these conclusions only hold for "large files."

Ordinary copy:

cp -R node_modules /tmp/wt-demo.f2/node_modules
# real 0.20
# df -k difference: 122,916 KB, about 120MB

worktrunk:

wt step copy-ignored --from main
# ✓ Copied 1 file · 120.0 MiB (reflinked, no extra disk)
# real 0.02
# df -k difference: -32 KB

Same 120MB: cp -R takes 0.20s and df shows about 120MB more; copy-ignored takes 0.02s and df shows no measurable increase. The difference isn't "copying faster," it's that it doesn't copy bytes at all—on APFS it uses clonefile, and both files point at the same disk blocks. Incidentally, on macOS you can get the same effect with cp -Rc; worktrunk saves you from remembering to turn on clone.

This single measurement is 10x, but 0.02s is already close to timing precision; treat it as an order-of-magnitude difference, not a stable multiplier. On the disk side, be careful: the df difference is −32KB, well within noise, so the accurate statement is this measurement observed no obvious additional usage; you can't derive an exact "0 bytes," and even less can you derive "copying doesn't cost any disk."

What supports this is APFS's copy-on-write: copying shares the same disk blocks, and only later writes split them. worktrunk uses clonefile on macOS; Linux's btrfs / XFS also support reflink.

I remeasured the ordinary copy side, this time with df readings instead of extrapolation: copying 120MB five times each via cp -R into five directories, df -k /tmp difference was 614,716 KB, about 600MB.

dd if=/dev/zero of=base/pkg bs=1m count=120
df -k /tmp                     # before: 24,740,764 KB available
for i in 1 2 3 4 5; do cp -R base copies/c$i; done
df -k /tmp                     # after:  24,126,048 KB available
# diff 614,716 KB ≈ 600MB

For reflink I only measured one copy; I didn't measure 5 reflinks' usage, but by mechanism it should be close to 0—that's inference, not data.

There's a boundary that must be made clear: only on file systems that support CoW will blocks be shared. On ext4 it's just an ordinary copy, using whatever space it uses; and after reflink, if you write to that file, blocks split and usage grows back. Build caches get written anyway, so this benefit mainly lands on "first cold start" and "read-only dependencies."

6. Hooks aren't decorative, but I measured their cost

worktrunk's other piece is lifecycle hooks: before creation, after creation, before commit, before merge, before deletion, each with pre / post tiers. Pre blocks; if it fails, it aborts. Post runs in the background and outputs to logs.

I wrote the simplest project config .config/wt.toml:

pre-start = "echo hook-ran > hook-proof.txt && sleep 0.2"

Then I created the 6th worktree:

wt -C /tmp/wt-demo switch -c -y f6
# ✓ Created branch f6 from main and worktree @ /tmp/wt-demo.f6
# real 0.36

The new directory did contain hook-proof.txt with content hook-ran. The 0.36s included the deliberately added 0.2s sleep; the remaining 0.16s is the cost of creation plus running the hook.

The practical use of this mechanism isn't echo, it's: pre-start installs dependencies, generates .env, post-start starts the dev server, pre-merge runs tests. It even provides a hash_port template filter so each worktree's dev server automatically gets a different port, so five agents don't fight over 3000.

There's a security detail worth knowing: project hook commands need approval the first time they run, and the approval record lives in ~/.config/worktrunk/approvals.toml; if a command changes, you have to approve again. In CI you use --yes to skip.

7. wt list is the reason I ultimately stayed

What actually made me decide not to uninstall wasn't creation or reflink; it was wt list.

The state of six worktrees is compressed into a single table:

  Branch  Status      HEAD±     main↕    main…±    Remote⇅  Path           Commit    Age  Message
@ main        ^                                             .              7084621   now  init
+ f1          _                                             ../wt-demo.f1  7084621   now  init
+ f2          _                                             ../wt-demo.f2  7084621   now  init
+ f3          _                                             ../wt-demo.f3  7084621   now  init
+ f4          _                                             ../wt-demo.f4  7084621   now  init
+ f6          _                                             ../wt-demo.f6  7084621   now  init

HEAD± shows whether there are uncommitted changes, main↕ shows how far ahead/behind main, Remote⇅ shows remote sync status. Native git worktree list only gives paths and commits; the rest you'd have to git status inside each one.

For someone running five agents simultaneously, this table is a dashboard: which branch is done, which is behind, which hasn't committed yet, all at a glance, without clicking through five terminals.

Deletion is also convenient. wt remove -y f5 handles both worktree and branch in one command; natively you need three commands, and getting the order wrong causes errors.

One thing I didn't notice at first: remove puts deletion in the background by default, so the command may return before deletion actually finishes. So to compare timing you have to explicitly add --foreground, which blocks until deletion is complete. I measured this tier: removing a worktree with a 120MB dependency (43 files), wt remove -y --foreground f10 took real 0.09s, output ✓ Removed f10 worktree & branch … (43 files · 120.0 MiB), and returned only after deletion.

I didn't measure how long the native three commands take, so I only compare the number of commands, not time.

For merge I tested the conflict-free path:

wt merge -y f4
# ○ Already up to date with f4 (no new commits, no rebase needed)

This path produced no new commit, so it only verified the command runs; I didn't test conflicts or rebase scenarios.

8. Two errors I hit

First: if you create the same branch name again, it doesn't switch to it, it just refuses.

✗ Branch f1 already exists
↳ To switch to the existing branch, run without --create: wt switch f1

I agree with this design. The worst thing for parallel agents is "you think you started a new branch, but you're actually still modifying the old one." -c means strictly create; to reuse, omit -c.

Second: if a worktree has uncommitted changes, remove blocks it.

✗ Cannot remove worktree: f2 has uncommitted changes
   M src_1.txt
↳ Commit or stash changes first, or to lose uncommitted changes, run wt remove --force f2

It doesn't decide for you whether the changes should be discarded; it lists the filenames, and either you commit or explicitly use --force. For a person managing five agents, this brake is worth more than the few characters saved.

9. Cost list

I'll list them in the order I care about:

There are a few items I didn't test, so no conclusions: real node_modules copying, total 5-reflink usage, the interactive picker, LLM-generated commit messages, and merge conflict handling.

The terminal output in this article was reproduced on worktrunk v0.81.0, macOS 26.6.2 (2026-11-04). The reproducible materials are the commands and environment above: repository max-sixty/worktrunk, version v0.81.0, all tests under /tmp, df -k taken before and after. No benchmark scripts; these are one-off manual comparisons, I didn't want to dress this up as a benchmark.

10. When is it worth installing

Your situation Suggestion
Only 1 agent, occasionally switching branches Don't need it; native git is enough
3+ agents simultaneously Install; wt list and switch save hassle
Large dependencies/build caches, on APFS Worth trying: this 120MB single-file test was near-zero-copy. Real node_modules I didn't test; run it on your largest directory first
On ext4, only want to save disk Don't expect it; reflink doesn't apply
Just want to type less, don't mind 44MB extra Install; commands really are shorter
Want "faster creation" Don't install for this: creation is slower than native (five times 0.34s extra); it saves input and dependency copying, not creation itself

Finally, I'll leave one debatable judgment:

worktrunk's value isn't in worktrees; it's in pushing the cost of "opening a new workspace" close to zero. When the cost is near zero, you actually open a second, third agent; before that, most people's "parallel" is just five windows switching back and forth.

If you never run agents in parallel, this thing is a net negative for you: 44MB extra, three more commands, one more layer of configuration to remember. I accept that, and I welcome you to refute it with your own usage.

If you actually run this flow for a day, the picture looks like this: five AIs each working on their own, you watch wt list to see who's done, then wt remove to discard one, and disk doesn't pay another 120MB for each dependency. That paragraph is a scenario, not something I ran—I only verified creation, copying, and deletion; what happens when five agents run a whole day, you'll have to try yourself.