
This blog lives in a single repository: the site content on one side, SEO and analytics tooling on the other.
On a typical day, one Claude Code session drafts a post while another works on the analytics side.
Both sessions run from the same repository on different branches, and their changes never touch.
Git worktrees are what make that clean.
A **git worktree** is a built-in Git feature that lets you check out multiple branches of one repository simultaneously.
Each checkout lives in its own directory; no stashing, no branch switching, no second clone.

Worktrees have been in Git since 2015, but it took coding agents to make them popular.
The original use case was something like fixing an urgent bug when you have uncommitted changes in your working
directory.
With coding agents, people commonly run multiple sessions that would otherwise write to the same directory and race each
other.

This guide covers how I use worktrees day to day, including the two gotchas that bite everyone the first time.
The second half walks through the parallel Claude Code workflow.

## What Is a Git Worktree?

A git worktree is an additional working directory linked to the same repository, with its own checked-out branch and its
own files on disk.
Every worktree shares one `.git` object database, so a commit made in one is immediately visible from all of them.
You get multiple checkouts without cloning the repo twice.

There is one constraint to know up front.
A branch can only be checked out in one worktree at a time.
Git enforces this so two directories don't silently fight over the same ref.

## Why Use Git Worktrees?

Use git worktrees whenever you need two branches checked out at once.
This avoids the conflicts that can be introduced with stashing.
A worktree shares the object database with your main checkout and keeps each directory isolated on the filesystem.

The most common reasons developers reach for worktrees:

| Situation | Why a worktree wins |
|---------------------------------|-------------------------------------------------------------------------------------|
| Urgent hotfix mid-feature | Fix ships from its own directory; your dirty feature tree never moves |
| Long build or test run | The build keeps running in one worktree while you code in another |
| Reviewing a teammate's PR | Check out their branch without disturbing your work in progress |
| Comparing branches side by side | Two full checkouts, two editor windows, real diffs of running code |
| Parallel AI coding agents | Each Claude Code session gets its own files, branch, and dev server - no collisions |

That last row is the reason worktrees went from a niche Git feature to a daily tool for agentic development, and it gets
its [own section below](#running-parallel-claude-code-sessions-with-worktrees).

## Prerequisites

- Git 2.5 or newer (`git --version` to check; anything from the last decade qualifies). The `git worktree remove`
  subcommand used later in this guide needs 2.17.
- An existing repository to work in

Worktrees are built into Git core, so there is nothing to install or configure.

## Setting Up Your First Worktree

I keep worktrees inside the repository in a dedicated directory, `.claude/worktrees/`.
The directory has to be gitignored, otherwise every worktree shows up as a pile of untracked files in every other checkout.
So the first step is a one-time addition to `.gitignore`:

```bash
echo '.claude/worktrees/' >> .gitignore
```

Then add a worktree for an existing branch:

```bash
git worktree add .claude/worktrees/hotfix hotfix/login-bug
```

This creates the directory `.claude/worktrees/hotfix` with `hotfix/login-bug` checked out inside it.
If you want to create a new branch at the same time, use `-b`:

```bash
git worktree add -b feature/billing .claude/worktrees/billing main
```

That single command creates the branch `feature/billing` from `main` and checks it out in `.claude/worktrees/billing`.
Now list what you have:

```bash
git worktree list
```

```
/Users/paul/code/myapp                            a1b2c3d [main]
/Users/paul/code/myapp/.claude/worktrees/hotfix   b4c5d6e [hotfix/login-bug]
/Users/paul/code/myapp/.claude/worktrees/billing  c7d8e9f [feature/billing]
```

Each directory is a fully functional checkout.
You commit and push from it exactly as you would anywhere else.

A note on where to put git worktrees: they can live anywhere on disk, and older guides usually put them next to the main checkout (`../myapp-hotfix`) or in a home directory like `~/worktrees/`.
The gitignored in-repo directory has become the common pattern for agentic development, and `.claude/worktrees/` is also where Claude Code puts the worktrees it creates itself.
Everything stays inside the project, which matters when coding agents are sandboxed to the repository directory, and one glance at `.claude/worktrees/` shows what's active.
Any of these locations works as long as you're consistent.

## How to Use Git Worktrees

The core loop is four commands:

1. **Add**: Run `git worktree add -b <branch> <path> <base>` to create a new branch checked out in its own directory.
1. **Work**: Change into the directory and work normally. Commit, run tests, and push exactly as you would in any
   checkout.
1. **Remove**: When the branch is merged or abandoned, run `git worktree remove <path>` to delete the directory and its
   metadata together.
1. **Prune**: If a worktree directory got deleted manually, run `git worktree prune` to clean up the stale metadata Git
   left behind.

## Removing Worktrees the Right Way

The mistake I see most often is deleting a worktree directory with `rm -rf`.
It won't corrupt your repository, but it leaves stale administrative files in `.git/worktrees/`, and Git will still
think the worktree exists.
The dead worktree keeps showing up in `git worktree list`, and its branch stays reserved, so you can't check that
branch out anywhere else.

Use the dedicated command instead:

```bash
git worktree remove .claude/worktrees/hotfix
```

If you already nuked the directory manually, fix the bookkeeping with:

```bash
git worktree prune
```

Git refuses to remove a worktree with uncommitted changes. Pass `--force` if you're sure you want to discard them.

One related error is worth knowing.
`fatal: '<path>' is not a working tree` means Git has no record of a worktree at the path you gave it, usually because
of a typo or because the worktree was already pruned.
Run `git worktree list` and copy the exact path from there.

## The Two Gotchas: node_modules and .env Files

Worktrees don't copy anything in your `.gitignore`. We need a strategy to manage this reality.

**Dependencies don't follow you.**
If you have build dependencies, like `node_modules`, these need to be re-generated in each worktree.
For a large JavaScript project this also means paying the disk cost per worktree.

**.env files don't follow you either.**
Since they're gitignored, each worktree starts without secrets or local config.
Tests fail and dev servers won't boot.
My fix is a tracked `.env.example` with placeholder values, plus a one-line copy when I create a worktree:

```bash
cp .env .claude/worktrees/billing/.env
```

Extend this to whatever other files need to be included.
Teams with heavier secret requirements pull from a central store instead (1Password CLI, Doppler, dotenv-vault), which
removes the copying step entirely.

## Running Parallel Claude Code Sessions with Worktrees

If you run AI coding agents, worktrees solve the problem of agents stepping on each other's files.

Two Claude Code sessions in one directory is a race condition.
Both agents edit the same files, and their changes interleave in ways you can't untangle.
Builds run against half-modified code.
Each agent gets its own worktree, so it only ever sees its own files and its own branch, with no awareness of the other sessions.

The manual setup:

```bash
git worktree add -b feature/auth .claude/worktrees/auth main
git worktree add -b feature/search .claude/worktrees/search main

# Terminal 1
cd .claude/worktrees/auth && claude

# Terminal 2
cd .claude/worktrees/search && claude
```

Each session gets a focused prompt for its own feature.
The repository's [CLAUDE.md file](/posts/how-to-write-claude-md/) is tracked, so every worktree carries the same
project instructions automatically; it's the gitignored local config that gets left behind.
While one agent grinds through a refactor, you review the other's diff.
When a branch is done, merge it and remove the worktree.
I've found two or three parallel sessions is the practical ceiling.
For me, using worktrees is most helpful when managing different types of tasks; feature coding in one worktree, and documentation in another, for example.

Claude Code also has native support for this pattern, so you often don't need the manual setup at all.
Starting a session with `claude --worktree feature-auth` creates the worktree under `.claude/worktrees/` and drops you
into it in one step.
When you exit, Claude checks for unfinished work before removing the worktree.
Sub-agents can be given their own worktrees so parallel edits never conflict.
Ask Claude directly to "use worktrees for your agents," or make it permanent for a custom sub-agent by adding
`isolation: worktree` to its frontmatter.
In the desktop app, every new session gets its own worktree automatically.

Claude-created worktrees hit the same `.env` gotcha as manual ones, and here Claude Code has a cleaner fix than the manual copy.
Add a `.worktreeinclude` file at the repository root listing the gitignored files you want carried over, and every
worktree Claude creates gets a copy automatically.
There are also `WorktreeCreate` and `WorktreeRemove` hook events, which I mentioned in
my [Claude Code hooks tutorial](/posts/claude-code-hooks-pretooluse-posttooluse/), though those replace the worktree
creation logic entirely, mainly for repositories on version control systems other than git.
When a `WorktreeCreate` hook is active, `.worktreeinclude` is not processed, so the hook script has to copy any local config files itself.

The one thing worktrees don't isolate is shared external state.
Two agents pointed at the same database or the same dev server port will still collide.

## When Not to Use Worktrees

Worktrees earn their keep when you actually have parallel work, like an agent running in one directory while you code in another.
When you don't, they're just overhead.

Quick branch hops don't need a worktree, since `git switch` is faster than creating and removing a directory.
Repos where installs are expensive and branches share dependencies may be happier with plain stashing.
Run `git worktree list` once in a while and clean up.

## Frequently Asked Questions

**What is a git worktree?**

A git worktree is an additional working directory attached to the same repository, with its own checked-out branch and
its own files on disk.
All worktrees share one object database, so a commit made in one is immediately visible from the others.
You get multiple simultaneous checkouts without a second clone.

**How does a git worktree work?**

Every worktree shares the repository's object database and refs while keeping its own HEAD, index, and files on disk.
Git stores the bookkeeping as lightweight metadata under `.git/worktrees/`.
Checking out a branch in one worktree reserves that branch there, so two directories never write to the same ref.

**Why use git worktrees instead of a second clone?**

A second clone duplicates the full history on disk, and the two copies drift apart until you fetch.
A worktree shares the object database with your main checkout, so it is faster to create and lighter on disk.
A commit made in one checkout is immediately visible in all the others.

**Can two worktrees check out the same branch?**

Not by default.
Git allows a branch to be checked out in only one worktree at a time, so two directories cannot silently fight over the
same ref.
`git worktree add --force` overrides the check, but then they really do fight.
If you need a second copy of the same code, check out a detached HEAD at that commit instead.

**How do I delete a git worktree?**

Run `git worktree remove <path>`, which deletes the directory and its metadata together.
If you already deleted the directory manually, run `git worktree prune` to clean up the stale metadata.
Git refuses to remove a worktree with uncommitted changes unless you pass `--force`.

**Do git worktrees copy node_modules and .env files?**

No.
A new worktree contains only tracked files, and anything in `.gitignore` stays behind, including `node_modules` and
`.env` files.
Reinstall dependencies in each new worktree and copy your `.env` file in by hand, or pull secrets from a central store.

**Can I use git worktrees with Claude Code?**

Yes.
Claude Code supports worktrees natively.
The `--worktree` flag starts a session in a fresh worktree.
Ask Claude to use worktrees for its agents, or add `isolation: worktree` to a sub-agent's frontmatter to make the
isolation permanent.
In the desktop app, every new session gets its own worktree automatically.

## Wrapping Up

Git worktrees replace stashing and branch switching with cheap, disposable checkouts.
Create them with `git worktree add` and clean them up with `git worktree remove` rather than `rm -rf`.
Remember to copy your `.env` file and reinstall dependencies in each new one.
If you're running parallel Claude Code sessions, worktrees are what keep them from stepping on each other.

For the rest of my day-to-day git toolkit, including the submodule and squash workflows I reach for constantly, see
my [git cheat sheet](/posts/git-cheat-sheet/).

## Further Reading

- [git-worktree official documentation](https://git-scm.com/docs/git-worktree)
- [Run parallel sessions with worktrees, Claude Code docs](https://code.claude.com/docs/en/worktrees)
- [Git Worktree: add, list, remove (GitKraken)](https://www.gitkraken.com/learn/git/git-worktree)

