Skip to content
Dev Tools Beginner Tutorial

Work on Multiple Branches at Once with Git Worktrees

Stop stashing and switching. Check out two (or ten) branches into separate directories from the same repo, and run them side by side.

Lenn Voss
Lenn Voss
Cloud & Infrastructure Writer · Jul 11, 2026 · 8 min read
Work on Multiple Branches at Once with Git Worktrees

What you'll build

You'll take a single Git repo and check out two branches into two separate directories at once: your main working branch stays untouched while a second directory holds a feature branch (or a PR you're reviewing). Both are fully independent working copies, no stashing, no git checkout juggling.

Prerequisites

  • Git 2.20 or later. Worktrees have existed since Git 2.5, but stick to something recent. Check yours with git --version.
  • An existing Git repo with at least one commit. Any repo you already have cloned works fine.
  • Basic comfort with git branch and git checkout/git switch.
  • Works the same on macOS, Linux, and Windows (Git Bash or PowerShell). No OS-specific flags here.

1. See the problem worktrees solve

Normally, one repo directory = one checked-out branch. If you're mid-feature and need to check out main to review a hotfix, you either commit unfinished work, stash it, or lose track of what you were doing. git worktree gives you a second (or third, or tenth) working directory linked to the same repo and .git history, each one checked out to a different branch simultaneously.

2. Check your setup

From inside your existing repo:

cd ~/projects/my-app
git --version
git worktree list

On a repo with no extra worktrees yet, git worktree list shows just one entry: your current directory and its branch.

3. Create a worktree for a new branch

Say you're on main and need to start a hotfix without disturbing your current work:

git worktree add -b hotfix/login-bug ../my-app-hotfix main

Breaking that down:

  • -b hotfix/login-bug creates a new branch named hotfix/login-bug.
  • ../my-app-hotfix is the path for the new working directory. Convention is to put it as a sibling of your main repo folder, not nested inside it.
  • main is the starting point (commit or branch) for the new branch.

cd ../my-app-hotfix and you've got a completely separate directory with its own file state, checked out to hotfix/login-bug. Your original my-app directory is untouched, still on main.

4. Create a worktree for an existing branch (e.g. a PR)

If a teammate pushed a branch and you just want to review or run it locally, fetch first so your local repo knows about it:

git fetch origin

Here's the part people get wrong. You might expect this to just work:

git worktree add ../my-app-review feature/new-checkout

It doesn't. feature/new-checkout isn't a local branch yet, so Git fails with something like fatal: 'feature/new-checkout' is not a local branch and is not a valid ref. Git's "figure out the remote branch for me" behavior (its docs call this DWIM) only kicks in when you omit the branch argument entirely and let Git infer the branch name from the destination directory's name. It does not kick in just because you typed a branch name that happens to not exist locally yet.

So you've got two real options, depending on whether you care about the directory name.

Option A: explicit, works with any branch name (including ones with slashes). Use -b and point the third argument at the actual remote ref:

git worktree add -b feature/new-checkout ../my-app-review origin/feature/new-checkout

This creates a local branch feature/new-checkout tracking origin/feature/new-checkout, checked out into ../my-app-review. This is the version you want for PR reviews, since you usually want a custom, readable directory name rather than one that mirrors the branch.

Option B: let Git guess, but the directory name has to match the branch exactly. If you drop the branch argument completely, Git derives the branch name from the last path component of the directory you're creating. If there's exactly one remote with a tracking branch of that same name, Git creates a local branch and wires up tracking automatically:

git worktree add ../feature-new-checkout

This only works cleanly if the remote branch is named feature-new-checkout (no slash) and matches the directory name. If the real branch name contains a slash, like our feature/new-checkout example, Git only looks at the final path segment (new-checkout) when guessing, which won't match, so the DWIM shortcut effectively doesn't apply to slash-containing branch names unless your directory path mirrors the branch path exactly, segment for segment. In practice, for anything with a / in it, just use Option A. It's one extra argument and it's unambiguous.

5. Run both simultaneously

Open two terminal tabs (or panes):

# Terminal 1
cd ~/projects/my-app
npm install
npm run dev
# Terminal 2
cd ~/projects/my-app-hotfix
npm install
npm run dev -- --port 3001

Each directory has its own node_modules, build output, and uncommitted changes. Commits in one worktree show up instantly in git log from the other, since they share the same .git object database. That's the whole point: independent working trees, shared history.

6. Clean up when you're done

Once the hotfix is merged, don't just rm -rf the folder, use Git so it also removes the internal worktree metadata.

First, make sure you're not currently inside the worktree you're removing. git worktree remove will refuse to run if your shell's current directory is inside it:

cd ../my-app
git worktree remove ../my-app-hotfix

If the worktree has uncommitted changes, untracked files, or leftover build artifacts (a dist/ folder or node_modules that isn't gitignored, for example), Git will also refuse to remove it, to avoid silently destroying work. If you're sure you don't need any of it:

git worktree remove --force ../my-app-hotfix

Once the branch itself is no longer needed:

git branch -d hotfix/login-bug

Verify it works

Run git worktree list from any of the linked directories:

git worktree list

Expected output, something like:

/Users/you/projects/my-app          a1b2c3d [main]
/Users/you/projects/my-app-hotfix   e4f5g6h [hotfix/login-bug]

Each line shows a directory, its current commit, and the branch checked out there. After running git worktree remove, that entry should disappear from the list.

Troubleshooting

  • fatal: 'hotfix/login-bug' is already checked out at ...: Git won't let the same branch be checked out in two worktrees at once. Use a different branch name, or cd to the existing worktree instead of creating a new one.
  • fatal: '../my-app-hotfix' already exists: pick a different path, or delete the leftover directory first if it's not a registered worktree.
  • Worktree removal fails with "contains modified or untracked files": either commit/clean the changes inside it first, or use git worktree remove --force if you're certain you don't need them.
  • Stale entries in git worktree list after manually deleting a folder: run git worktree prune to clean up Git's internal references to worktrees that no longer exist on disk.

Next steps

  • Use git worktree lock <path> to prevent a worktree (say, one on a removable drive) from being pruned or auto-removed.
  • Git 2.42.0 added git worktree add --orphan <path>, which creates a worktree on a brand-new branch with no history yet, useful for things like starting a gh-pages branch from scratch.
  • Pair worktrees with a bare repository (git clone --bare) if you never want a default checkout at all, just a folder of worktrees. Worth reading the git-worktree man page (man git-worktree) once you're comfortable with the basics above.
Lenn Voss
Written by
Lenn Voss · Cloud & Infrastructure Writer

Lenn writes about cloud platforms, Kubernetes internals, and the infrastructure decisions that quietly make or break engineering organizations. Based in Berlin's vibrant tech scene, they have a talent for turning dense platform-engineering topics into prose that people actually finish reading.

Discussion 0

Join the discussion

Sign in or create an account to comment and vote.

No comments yet

Be the first to weigh in.

Related Reading