Home DevOps Git Worktrees: Work on Multiple Branches Simultaneously Without Stashing or Cloning
Intermediate 3 min · July 18, 2026

Git Worktrees: Work on Multiple Branches Simultaneously Without Stashing or Cloning

Git worktrees let you work on multiple branches at once without stashing or cloning.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.

Follow
Production
production tested
July 18, 2026
last updated
2,466
articles · all by Naren
Before you start⏱ 25 min
  • Basic Git commands (branch, checkout, commit, push)
  • Understanding of Git branches and remotes
  • Comfortable with command line
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer

Use git worktree add to create a new working directory for a branch. Work in it like a separate clone, but commits are shared. Remove with git worktree remove . Never delete worktree directories manually.

✦ Definition~90s read
What is Git Worktrees?

Git worktrees allow you to check out multiple branches of the same repository into separate working directories, each with its own index and working tree, all sharing the same Git object database.

Imagine you have a single house (the repository) with multiple rooms (branches).
Plain-English First

Imagine you have a single house (the repository) with multiple rooms (branches). Normally you can only be in one room at a time. Worktrees are like adding separate doors to each room so you can walk into any room directly without leaving the house. Each room has its own furniture (files), but the foundation (Git objects) is shared.

You're mid-sprint, three features in progress, and a production bug hits. You need to hotfix main, but you've got uncommitted changes in your feature branch. The classic move: stash, checkout main, fix, commit, checkout back, pop stash. That's friction. And friction in a fire costs time. Git worktrees eliminate that friction entirely. They let you check out multiple branches simultaneously into separate directories, each with its own working state, all sharing the same repository. No stashing, no cloning, no context switches. After this article, you'll be able to set up worktrees for parallel feature development, hotfixes, and code reviews without ever touching git stash again.

Why You Need Worktrees: The Stash Tax

Every time you stash, you pay a mental tax: 'What was I doing? Will I remember to pop? Did I stash everything?' In a hotfix scenario, that tax compounds with adrenaline. Worktrees remove the tax entirely. You keep your feature branch's working state intact in one directory, and you work on the hotfix in another. No stashing, no commit-amending, no 'oh crap I forgot to stash that one file'. The problem worktrees solve is simple: Git's single-working-tree model forces serial context switching. Worktrees give you parallel contexts. They're not new — they've been stable since Git 2.5 (2015). But most devs still don't use them because the workflow isn't obvious. Let's fix that.

worktree-basics.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Set up a demo repo
mkdir demo && cd demo
git init
echo "initial" > file.txt && git add . && git commit -m "initial"
git checkout -b feature-x
echo "feature work" >> file.txt && git add . && git commit -m "feature x commit"
git checkout main

# Now we want to work on a hotfix without losing feature-x state
# Add a worktree for the hotfix branch
git worktree add ../hotfix main
# Output: Preparing ../hotfix (identifier hotfix)
# Now ../hotfix has main checked out, while ./demo has main too (but we can switch)

# In ../hotfix, make a hotfix
cd ../hotfix
echo "hotfix" >> file.txt && git add . && git commit -m "hotfix"
cd ../demo

# Meanwhile, in ./demo, we can switch to feature-x and continue working
git checkout feature-x
echo "more feature work" >> file.txt && git add . && git commit -m "feature x commit 2"

# List worktrees
git worktree list
# Output:
# /path/to/demo          e.g. main
# /path/to/hotfix        e.g. main

# Clean up
git worktree remove ../hotfix
Output
Preparing ../hotfix (identifier hotfix)
/path/to/demo e.g. main
/path/to/hotfix e.g. main
💡Senior Shortcut:
Use git worktree add -b <new-branch> <path> <start-point> to create a new branch and worktree in one command. Saves a step.

Production Pattern: Parallel Feature Branches

In a microservices monorepo, you often need to work on two features that touch different services. With worktrees, you can have both checked out in separate directories, each with its own IDE window. No more switching branches and rebuilding the entire project. The pattern: create a worktree per branch, work independently, push from each. The key insight: worktrees share the object store, so you don't duplicate the .git directory. That means git fetch in one worktree updates refs for all. But beware: if you fetch a new branch, you need to create a worktree for it explicitly — it won't appear automatically.

parallel-features.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Assume we have a monorepo with two services: auth and billing
cd ~/monorepo

# Create worktrees for two feature branches
git worktree add -b feature/auth-upgrade ../auth-upgrade main
git worktree add -b feature/billing-fix ../billing-fix main

# In separate terminals:
# Terminal 1: cd ~/auth-upgrade; work on auth service
# Terminal 2: cd ~/billing-fix; work on billing service

# Both share the same .git in ~/monorepo
# After commits, push from each worktree:
cd ~/auth-upgrade
git push origin feature/auth-upgrade

cd ~/billing-fix
git push origin feature/billing-fix

# List worktrees to see all
git worktree list
# Output:
# /home/user/monorepo          main
# /home/user/auth-upgrade      feature/auth-upgrade
# /home/user/billing-fix       feature/billing-fix

# When done, remove worktrees:
git worktree remove ~/auth-upgrade
git worktree remove ~/billing-fix
Output
/home/user/monorepo main
/home/user/auth-upgrade feature/auth-upgrade
/home/user/billing-fix feature/billing-fix
⚠ Production Trap:
If you delete a worktree directory with rm -rf, Git still thinks it exists. You'll get errors like fatal: 'path' is not a valid path when running git worktree list. Fix: run git worktree prune to clean up stale worktree references.

Hotfix Workflow: No Stash, No Panic

Production is down. You're on a feature branch with uncommitted changes. The old way: git stash, git checkout main, fix, commit, git checkout feature, git stash pop. If stash pop conflicts, you're in merge-hell while the site burns. With worktrees: git worktree add ../hotfix main, fix in ../hotfix, commit, push, deploy. Then git worktree remove ../hotfix. Your feature branch is untouched. No stash, no conflict. This is the single most valuable use case for worktrees. I've seen teams cut deployment time from 5 minutes to 30 seconds on hotfixes using this pattern.

hotfix-workflow.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Simulate being on a feature branch with dirty state
cd ~/project
git checkout feature-xyz
echo "work in progress" >> file.txt  # uncommitted change

# Production alert! Need to fix main
# Add a worktree for main
git worktree add ../hotfix main
# Output: Preparing ../hotfix (identifier hotfix)

# In another terminal, fix the bug
cd ../hotfix
echo "critical fix" >> file.txt && git add . && git commit -m "hotfix: fix critical bug"
git push origin main

# Deploy from CI...

# Clean up: remove worktree (safe)
git worktree remove ../hotfix
# Output: Removing worktrees/hotfix...

# Back in original repo, feature branch still has uncommitted changes intact
cd ~/project
git status
# Output: Changes not staged for commit: modified: file.txt
Output
Preparing ../hotfix (identifier hotfix)
Removing worktrees/hotfix...
Changes not staged for commit: modified: file.txt
🔥Interview Gold:
Interviewer: 'How do you handle a hotfix when you have uncommitted changes?' Answer: 'I use git worktree add to create a separate working directory for the hotfix branch. No stash needed. The original branch stays untouched.' This shows you know a production-tested pattern, not just theory.

Code Review Without Context Switch

You're deep in a feature, and a colleague asks for a code review. The old way: git stash, checkout their branch, look at code, checkout back, pop stash. Or use the GitHub UI. But sometimes you need to run the code locally. Worktrees let you check out their branch in a separate directory, run it, review, and delete — all without touching your current work. The pattern: git worktree add ../review <pr-branch>. Review in ../review. Then git worktree remove ../review. Your feature branch is undisturbed.

code-review.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Assume we're on feature-xyz with dirty state
cd ~/project

# Fetch the PR branch (assuming it's from a fork or remote)
git fetch origin pull/42/head:pr-42

# Create worktree for review
git worktree add ../review pr-42
# Output: Preparing ../review (identifier review)

# In another terminal, explore the code
cd ../review
# Run tests, inspect code, etc.

# When done, remove worktree
git worktree remove ../review
# Output: Removing worktrees/review...

# Delete the temporary branch if no longer needed
git branch -D pr-42
Output
Preparing ../review (identifier review)
Removing worktrees/review...
💡Senior Shortcut:
Use git worktree add --detach ../review FETCH_HEAD to check out a detached HEAD at the PR's latest commit without creating a local branch. Saves cleanup.

Worktrees in CI: Parallel Testing Without Cloning

In CI, you often need to test multiple branches or commits in parallel. Cloning the repo N times is slow and wastes disk. Worktrees let you check out multiple branches from a single clone into separate directories, each with its own working tree. This is especially useful for monorepos where you want to run tests for each changed service in parallel. The trick: clone once, then create worktrees for each branch you need to test. Each worktree can be used by a separate CI job. Just be careful with concurrent writes to the shared object store — Git's object store is append-only, so it's safe, but avoid concurrent git gc.

ci-worktrees.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# In CI, after cloning the repo:
cd $REPO_DIR

# For each branch we want to test in parallel:
git worktree add ../branch-main main
git worktree add ../branch-feature feature-xyz

# Now run tests in each worktree in parallel (e.g., using CI matrix)
# Job 1: cd ../branch-main && npm test
# Job 2: cd ../branch-feature && npm test

# After all jobs complete, clean up worktrees
git worktree remove ../branch-main
git worktree remove ../branch-feature

# Note: The original clone's .git is shared, so no duplicate objects.
⚠ Never Do This:
Running git gc in one worktree while another worktree is actively writing can cause corruption. Git 2.6+ has guards, but avoid concurrent GC. In CI, run GC only after all worktrees are removed.

When Worktrees Break: The Gotchas

Worktrees aren't magic. They have sharp edges. First: you cannot have the same branch checked out in two worktrees. Git will refuse: fatal: 'branch' is already checked out at 'path'. If you need to work on the same branch from two places, you're better off with a clone. Second: worktrees create refs under refs/worktrees/. If you manually delete a worktree directory, those refs linger and can cause confusion. Always use git worktree remove. Third: some Git GUI tools don't understand worktrees. They'll show the main repo's state, not the worktree's. If you rely on a GUI, test first. Fourth: worktrees are local. They don't push or fetch automatically. You must push from each worktree individually. Fifth: moving or renaming a worktree directory breaks the association. Don't do it.

gotchas.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Gotcha 1: Cannot check out same branch in two worktrees
cd ~/project
git worktree add ../worktree1 main
git worktree add ../worktree2 main  # Error!
# Output: fatal: 'main' is already checked out at '../worktree1'

# Gotcha 2: Manual deletion causes stale refs
mkdir ../worktree3 && cd ../worktree3
git init --bare  # Not a real worktree, but simulates manual deletion
cd ~/project
git worktree list  # Shows worktree3 if it was added, but now broken
# Fix: git worktree prune

# Gotcha 3: Moving worktree breaks it
cd ~/project
git worktree add ../temp main
mv ../temp ../moved
git worktree list  # Still shows ../temp, not ../moved
# Fix: git worktree repair ../moved  (Git 2.7+)

# Gotcha 4: Branch deletion while worktree exists
git branch -D feature-xyz  # Error if worktree has it checked out
# Output: error: Cannot delete branch 'feature-xyz' checked out at '...'
# Must remove worktree first.
Output
fatal: 'main' is already checked out at '../worktree1'
error: Cannot delete branch 'feature-xyz' checked out at '...'
⚠ The Classic Bug:
If you see fatal: 'refs/heads/branch' is already checked out at 'path', you tried to check out a branch that's already in a worktree. Use git worktree list to find where, then either work there or remove that worktree first.

Worktrees vs. Clones: When to Use Which

Worktrees share the object store; clones don't. That means worktrees are lighter (no duplicate objects) but also more coupled. Use worktrees when: you need to work on multiple branches of the same repo, you want to avoid stash overhead, or you're in a monorepo with many services. Use clones when: you need to work on the same branch simultaneously (e.g., pair programming), you want complete isolation (different remotes, different configs), or you're on a filesystem that doesn't support hard links (worktrees use symlinks internally). Also, clones are easier to reason about for junior devs — worktrees have a learning curve. My rule: if you're the only one working on the branches, use worktrees. If multiple people need the same branch, clone.

comparison.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Worktree: share objects, separate working trees
cd ~/repo
git worktree add ../feature-x feature-x
# .git/objects is shared, ~/feature-x has its own working tree

# Clone: completely separate
cd ~
git clone ~/repo clone-feature-x --branch feature-x --single-branch
# ~/clone-feature-x has its own .git with its own objects (initially hardlinked if possible)

# Disk usage comparison:
du -sh ~/repo/.git/objects  # e.g., 100M
du -sh ~/feature-x/.git     # symlink to ~/repo/.git, negligible
du -sh ~/clone-feature-x/.git/objects  # e.g., 100M (duplicate)

# Worktree wins on disk, but clone wins on isolation.
🔥Senior Shortcut:
Use git clone --reference <path-to-existing-clone> <url> to create a clone that shares objects via alternates. This gives you isolation with less disk usage, but it's more fragile than worktrees.
● Production incidentPOST-MORTEMseverity: high

The Deleted Worktree That Lost Unpushed Commits

Symptom
Developer ran rm -rf ../feature-x then git worktree prune. Later, commits from that worktree were gone.
Assumption
Developer assumed worktree removal was like deleting a branch — safe because commits are in the repo.
Root cause
Worktrees create a separate HEAD ref (e.g., refs/worktrees/<name>/HEAD). If the worktree branch had unpushed commits and no other ref pointed to them, git worktree prune cleaned up the ref, making commits unreachable and eventually garbage-collected.
Fix
Always use git worktree remove <path> (safe, moves branch ref) or ensure branch has a remote tracking branch before manual deletion. Recover lost commits via git reflog on the main repo if not GC'd.
Key lesson
  • Never delete a worktree directory with rm -rf.
  • Always use git worktree remove to safely detach the branch.
Production debug guideSystematic recovery paths for the failure modes engineers actually hit.3 entries
Symptom · 01
fatal: 'refs/heads/branch' is already checked out at 'path' when trying to checkout a branch
Fix
1. Run git worktree list to see where the branch is checked out. 2. Either work in that worktree, or remove it with git worktree remove <path>. 3. If the worktree directory no longer exists, run git worktree prune to clean up stale references.
Symptom · 02
Worktree directory deleted manually, git worktree list shows broken path
Fix
1. Run git worktree prune to remove stale worktree entries. 2. If you need to recover commits from that worktree, check git reflog in the main repo. 3. If commits are unreachable, use git fsck --lost-found to find dangling objects.
Symptom · 03
git worktree add fails with 'fatal: could not create worktree dir' due to permissions
Fix
1. Check that the parent directory exists and is writable. 2. Use an absolute path to avoid ambiguity. 3. If using a symlink, ensure the target is accessible.
★ Git Worktrees Triage Cheat SheetFirst-response commands for when things go wrong — copy-paste ready.
`fatal: 'branch' is already checked out at 'path'`
Immediate action
Check where the branch is checked out
Commands
git worktree list
git worktree remove <path>
Fix now
git worktree remove <path> (or work in that worktree)
Worktree directory deleted manually, stale entry in list+
Immediate action
Prune stale worktree references
Commands
git worktree list
git worktree prune
Fix now
git worktree prune
Cannot delete branch because it's checked out in a worktree+
Immediate action
Find which worktree has it
Commands
git worktree list
git worktree remove <path>
Fix now
git branch -D <branch> after removing worktree
Worktree moved or renamed, Git still shows old path+
Immediate action
Repair the worktree reference
Commands
git worktree list
git worktree repair <new-path>
Fix now
git worktree repair <new-path> (Git 2.7+)
FeatureWorktreeClone
Disk usageShares objects (minimal extra)Full copy (duplicates objects)
IsolationShared refs, shared configComplete isolation
Same branch multiple timesNot allowedAllowed
Setup speedInstant (no fetch)Requires fetch (unless local)
Push/pullMust do from each worktreeIndependent per clone
Learning curveModerate (new commands)Low (standard Git)
⚙ Quick Reference
7 commands from this guide
FileCommand / CodePurpose
worktree-basics.shmkdir demo && cd demoWhy You Need Worktrees
parallel-features.shcd ~/monorepoProduction Pattern
hotfix-workflow.shcd ~/projectHotfix Workflow
code-review.shcd ~/projectCode Review Without Context Switch
ci-worktrees.shcd $REPO_DIRWorktrees in CI
gotchas.shcd ~/projectWhen Worktrees Break
comparison.shcd ~/repoWorktrees vs. Clones

Key takeaways

1
Worktrees eliminate stash overhead for hotfixes
add a worktree for main, fix, commit, remove. No context switch.
2
Always use git worktree remove to delete worktrees
never rm -rf. Manual deletion can lose commits.
3
Worktrees share the object store, making them lighter than clones but more coupled. Use clones for full isolation.
4
You cannot check out the same branch in two worktrees
Git enforces this to prevent conflicts.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
How does Git prevent the same branch from being checked out in two workt...
Q02SENIOR
When would you choose a worktree over a clone for parallel development i...
Q03SENIOR
What happens if you delete a worktree directory manually and then run `g...
Q04JUNIOR
What is a Git worktree and how do you create one?
Q05SENIOR
You have a worktree with uncommitted changes and the main repo's branch ...
Q06SENIOR
How would you design a CI pipeline that uses worktrees to test multiple ...
Q01 of 06SENIOR

How does Git prevent the same branch from being checked out in two worktrees? What's the internal mechanism?

ANSWER
Git stores a lock file in refs/worktrees/<id>/HEAD that records which branch is checked out. When you try to checkout a branch, Git checks all worktree HEAD refs for a match. If found, it rejects the operation. This prevents conflicting index and working tree states.
FAQ · 4 QUESTIONS

Frequently Asked Questions

01
How do I create a Git worktree for an existing branch?
02
What's the difference between a Git worktree and a clone?
03
How do I remove a Git worktree safely?
04
Can I push from a Git worktree?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.

Follow
Verified
production tested
July 18, 2026
last updated
2,466
articles · all by Naren
🔥

That's Git. Mark it forged?

3 min read · try the examples if you haven't

Previous
Git Stashing: Save and Restore Work-in-Progress
50 / 51 · Git
Next
Git Alias: Custom Commands for Faster Workflows