A git worktree is a second working directory attached to the same repository: its own files, its own checked-out branch, one shared history. git worktree add ../myrepo-hotfix hotfix gives you a complete checkout of hotfix next door in about a second — no second clone, no stash, no re-downloading the object database.
That used to be a niche trick for the one afternoon a quarter when a hotfix landed mid-refactor. It stopped being niche the moment people started running more than one coding agent at a time, because two agents editing one working directory is a merge conflict with extra steps.
This guide covers the manual method first, honestly, including the parts that are tedious enough to make people give up. Then what changes when the things using your worktrees are agents rather than you.
What is a git worktree?
A git worktree is an additional working directory linked to a single repository. Each one has its own files, its own HEAD, and its own checked-out branch, but they all share one object database, one set of refs, and one remote. Git has shipped the command since version 2.5, released in July 2015.
The checkout you cloned into is the main worktree. Everything you add afterwards is a linked worktree. The difference is mostly bookkeeping: inside a linked worktree, .git is a file rather than a directory, and it holds one line pointing back at a private directory under the main repository — /path/main/.git/worktrees/<name>. Git sets $GIT_DIR to that private directory and $GIT_COMMON_DIR to the main repository, and everything expensive lives in the common one.
That split is the whole trick. Commits, tags, blobs, packfiles, remotes — all shared. A new worktree costs you a checkout of the working files and nothing else, which is why adding one is instant and adding a clone is a coffee break.
Worktree vs. branch vs. clone
A branch is a pointer to a commit. A worktree is a place to check one out. A clone is an entire second copy of the repository. If you want two branches on disk at the same time without duplicating history or fetching twice, the worktree is the middle option, and for day-to-day work it is usually the right one.
| What you get | Extra branch | Extra worktree | Extra clone |
|---|---|---|---|
| Files on disk | One tree, one branch at a time | One tree per branch | A full second copy |
| History | Shared | Shared | Duplicated |
| Cost to create | Instant | A checkout | A full clone, over the network |
| Cost to switch | git switch, then rebuild and restart | None — both are already open | None — both are already open |
git fetch reaches | Everything | Every worktree | Only that clone |
| Best for | One thing at a time | Several things at once | A genuinely separate copy |
The row that decides it in practice is the fourth. Switching branches is cheap for git and expensive for everything else: the dev server restarts, the build cache invalidates, the editor reindexes, and half-finished work goes into a stash you forget. A worktree makes that cost zero by never asking you to switch.
The four commands you actually need
add, list, remove, prune. Everything else in the command — lock, unlock, move, repair — you will meet once and then forget. add creates a checkout, list shows what exists, remove deletes one cleanly, and prune clears the bookkeeping left behind when a worktree is deleted with rm -rf instead.
Creating one:
# New branch and new checkout in one step — the common case
git worktree add ../fredrin-search -b feat/search
# Check out a branch that already exists
git worktree add ../fredrin-hotfix hotfix/login-loop
# No branch given: git names one after the directory ("fredrin-spike")
git worktree add ../fredrin-spike
# Just a commit, no branch
git worktree add --detach ../fredrin-bisect v1.4.0One rule surprises everybody exactly once: a branch can only be checked out in one worktree at a time. add refuses when the branch is already checked out somewhere else. --force overrides it and you almost never want that; the refusal is protecting you from two directories writing to one branch.
Seeing what you have:
git worktree list
# Machine-readable, for scripts
git worktree list --porcelainCleaning up:
# Removes the directory and its bookkeeping
git worktree remove ../fredrin-search
# ...but only clean worktrees. Uncommitted or untracked work needs --force
git worktree remove --force ../fredrin-search
# After deleting a worktree directory by hand
git worktree pruneTwo things to know about removal. First, remove only takes clean worktrees — no untracked files, no modifications to tracked ones — which is a feature, not an obstacle. Second, it removes the worktree, not the branch: git branch -d feat/search is a separate step, and forgetting it is how you end up with a pile of merged branches nobody deleted.
Where to put them on disk
Siblings of the main checkout, named after the branch: ~/code/fredrin and ~/code/fredrin-search. Nesting worktrees inside the repository works, but it puts an untracked directory into every git status and every file watcher, so if you do it, gitignore the path. Keep the main worktree on your default branch and stop working in it.
~/code/
fredrin/ # main worktree — stays on main, used for nothing else
fredrin-search/ # feat/search
fredrin-hotfix/ # hotfix/login-loop
fredrin-bisect/ # detached at v1.4.0Treating the main checkout as read-only is the single habit that makes this workflow stick. It gives you one directory that is always clean, always on main, and always safe to git pull in — somewhere to answer "what does this look like on main?" without disturbing anything.
Tools that create worktrees for you pick their own convention. Claude Code puts them under .claude/worktrees/<name>/ on a branch named worktree-<name>; Fredrin puts each ticket's worktree outside the repository entirely. Neither is more correct than the sibling layout — just know where yours are before you go looking for them.
The gotchas nobody mentions
A worktree is a fresh checkout of tracked files only. Everything your project needs that git does not track — .env, node_modules, a seeded database, a free port — is simply absent, and putting it back is your job. This is the real tax on the workflow, and it is why worktree setups get abandoned in week two.
Gitignored files do not come along
Your .env, your .env.local, the service-account JSON, the local SQLite file: none of them are tracked, so none of them are there. The dev server in your shiny new worktree starts, fails to find a database URL, and exits.
The fix is a one-line copy in whatever script creates the worktree. Claude Code formalizes this as a .worktreeinclude file in the project root: it uses gitignore syntax, and only files that match a pattern and are gitignored get copied, so tracked files are never duplicated. Even without that tooling, write the cp down somewhere. You will not remember it at 11pm.
Two servers, one port
Every worktree wants port 3000. The second one to start gets EADDRINUSE, and if your stack binds several ports you get to play that game once per service.
Assign a port per worktree and put it in that worktree's env file. The same problem exists one layer down and hurts more: if all your worktrees share one local Postgres, a migration run in one changes the schema underneath the others. Either give each worktree its own database, or accept that only one worktree at a time is allowed to touch migrations.
Hooks are shared; config mostly is
Hooks live in the shared repository directory, so a pre-commit hook installed once fires in every worktree. That is almost always what you want, and it is worth knowing before you go hunting for why a hook you never installed here just ran.
Config is shared too, which is less convenient when you want a per-worktree value. Git's answer is opt-in: run git config extensions.worktreeConfig true, then set per-worktree values with git config --worktree. One caveat from the manual — older git versions refuse to access a repository with that extension enabled, so it is not free to turn on if other people or other tools share the checkout.
node_modules, disk, and the ones you forget
Each worktree installs its own dependencies. How much that costs depends on your package manager: pnpm hard-links packages from one content-addressable store, so the marginal worktree is cheap, while npm and yarn write a full copy per checkout. Multiply by however many worktrees you are carrying.
And you will carry more than you think. The repository this post was written in has twenty-nine worktrees on disk right now, most of them finished work nobody deleted. Git will eventually tidy up after you — git gc calls git worktree prune --expire 3.months.ago, configurable via gc.worktreePruneExpire — but that only clears the bookkeeping for directories that are already gone. The directories themselves, and their node_modules, sit there until you remove them.
Why AI coding agents made worktrees mainstream
Because two agents cannot share one working directory. An agent edits files, checks out branches, and runs your test suite — and two of them doing that at once in one tree will corrupt each other's work within minutes. A worktree is the cheapest isolation boundary that still shares history, so every parallel-agent workflow converged on it independently.
You can watch the convergence in the vendor docs. Anthropic's answer to "how do I run several sessions at once" is a page about worktrees: claude --worktree feature-auth creates one under .claude/worktrees/, starts a session in it, and offers to clean it up when you exit. Where that flag sits in the rest of the workflow — the project instructions, the permission rules, the session commands — is our full Claude Code walkthrough. The terminal crowd got there first and built it by hand — tmux panes, cmux tabs, a four-line shell function everybody rewrote slightly differently. We wrote about that crowd in cmux solved spawning, nobody solved shipping.
Worth being precise, because the isolation gets oversold: a worktree keeps agents from overwriting each other's files. It does nothing about a shared database, ports, rate limits, or four agents producing four incompatible refactors of one module, each perfectly correct on its own branch. Isolation on disk is necessary, not sufficient. It is one piece of the harness around the model — the context, the permissions, and the check that decides when a run is finished are the others.
Running several agents, one worktree each
One task, one branch, one worktree, one agent session. Create the checkouts up front, copy the env file into each, give each a port, then start an agent in every directory. The pattern is genuinely simple and you can write it in a shell loop this afternoon. What is not simple is everything that happens after the agents stop typing.
for task in search-filters login-loop empty-state; do
dir="../fredrin-$task"
git worktree add "$dir" -b "feat/$task"
cp .env "$dir/.env"
doneAdd a per-worktree port to each .env, run your installer in each directory, and you have three isolated lanes — three tasks in parallel for about ten lines of shell.
Then the work comes back, and the shell loop has nothing to say about any of it. Which worktree held which task. Which agent finished and which is sitting on a question you never saw. Which of the three diffs to read first. Which branches are merged and which directories are now dead node_modules. Every one of those you track in your head, and your head is a poor database at three lanes and a hopeless one at eight. Reading agent-written diffs at volume is a discipline of its own, and it is the one the isolation does nothing for.
When to stop doing this by hand
When you are spending more time on worktree bookkeeping than on the diffs the agents produced. The tell is always teardown: worktrees you meant to delete, branches you cannot place, an agent sitting blocked on a question nobody read. At that point the missing piece is not a better command — it is a lifecycle.
That lifecycle is what Fredrin is. Every ticket gets its own branch, worktree, and agent session, created when the ticket starts and torn down when its pull request merges. The board is what the shell loop cannot give you: columns ordered by stage rather than recency, so a glance tells you what is building, what is blocked, and what is waiting on review. Merging closes the loop. The features page is the full tour; the docs define the vocabulary.
Being straight about the trade: at one or two agents the loop above is genuinely enough and a desktop app is overkill. Nothing here is hard until it is concurrent. Fredrin earns its keep somewhere north of three lanes, when the question stops being "can I start another agent" and becomes "what is reviewable right now". If you never hit that, keep your shell function. It is a good one.
FAQ
Can I check out the same branch in two worktrees?
No. git worktree add refuses when the branch is already checked out in another worktree. --force overrides it, and you almost never want to — two directories writing one branch is exactly the corruption the check prevents. Use a second branch, or --detach for a read-only look at a commit.
Does a worktree copy the repository history?
No. Every worktree shares one object database and one set of refs. Adding one costs a checkout of the working files and nothing more, which is why it is near-instant while a second clone is a full network fetch. A git fetch in any worktree is visible from all of them.
What happens if I just delete the directory?
Git keeps the worktree's administrative files under .git/worktrees/ until something prunes them. git worktree prune clears them immediately. Otherwise they expire on their own: git gc runs git worktree prune --expire 3.months.ago unless you change gc.worktreePruneExpire.
Do my hooks and git config carry over?
Hooks do — they live in the shared repository directory, so a hook installed once fires in every worktree. Config is shared by default. For a per-worktree value, enable extensions.worktreeConfig and set it with git config --worktree, keeping in mind that older git versions refuse to read a repository with that extension.
Is a branch per agent enough, or do I need a worktree? A branch is not enough. Two agents on two branches in one directory still fight over the same files on disk, the same build output, and the same dev server. The isolation you need is a separate working directory, and that is precisely what a worktree is.
How many worktrees is too many? Git does not care; your disk and your attention do. The practical ceiling is the number of parallel diffs you can actually review, which for most people is smaller than the number of agents they can start.
Fredrin is in closed alpha on macOS. BYOS — Claude Code, Codex, Cursor, and Copilot runtimes, one branch and one worktree per ticket, created and cleaned up for you: fredrin.com.