Git Worktrees

Check out several branches at the same time in separate folders with git worktree, so you can fix a bug or review a pull request without stashing or disturbing your current work.

Lesson 19 of 30Advanced17 min read

You're halfway through a feature, with files changed everywhere, when an urgent bug report arrives. The usual answer is to stash or commit your half-finished work, switch branches, fix the bug, switch back and unstash. It works, but it's disruptive: your editor reloads files, development servers restart, build folders get rebuilt, and it's easy to lose track of what was stashed where.

Worktrees offer a calmer way. A repository can have several working trees, each in its own folder and each with a different branch checked out, all sharing the same history and .git data. You leave your feature exactly as it is in one folder, and fix the bug in another.

In this lesson you'll create worktrees for a hotfix and a review, list and remove them, and learn the one rule that trips people up.

Adding a worktree

Terminal
git worktree add ../shop-hotfix -b hotfix/login main
  • ../shop-hotfix is the new folder, usually placed next to your main project folder rather than inside it.
  • -b hotfix/login creates a new branch for the work.
  • main is where the new branch starts.

To check out an existing branch instead, leave out -b: git worktree add ../shop-review feature/search. Open the new folder in your editor, or a second editor window, and work there normally: edit, commit, push. Commits made in any worktree go into the shared repository, so every worktree sees them immediately.

Listing and the one-branch rule

git worktree list shows every working tree, its latest commit and its branch. git branch marks branches checked out in other worktrees with a +.

There's one important rule: a branch can be checked out in only one worktree at a time. If feature/search is open in your main folder, git worktree add ../x feature/search fails. This prevents two folders changing the same branch underneath each other. If you need a second copy of the same code, create a new branch from it, or use --detach for a read-only look at a commit.

Removing and cleaning up

When you've finished with a worktree, remove it from another worktree:

Terminal
git worktree remove ../shop-hotfix

Git refuses if the folder has uncommitted or untracked changes, to protect your work; add --force only if you're sure. If you deleted a worktree folder by hand, run git worktree prune so Git forgets about it. Removing a worktree doesn't delete its branch: merge it or delete it with git branch -d as usual.

When worktrees shine

SituationWhy a worktree helps
Urgent hotfix during feature workNo stashing, no rebuild of your feature setup
Reviewing a teammate's pull requestRun their branch side by side with yours
Long tests or buildsKeep coding while they run in another folder
Comparing two versionsOpen old and new in separate windows
AI coding assistants or scriptsLet them work on a branch without touching your files

Each worktree has its own files, so things like installed dependencies (node_modules, virtual environments) need setting up in each one.

Git Worktrees Syntax

Worktree commands

git worktree add ../project-task -b new-branch main
git worktree add ../project-review existing-branch
git worktree add --detach ../project-v1 v1.0.0
git worktree list
git worktree remove ../project-task
git worktree prune

Git Worktrees Summary

  • A worktree is an extra folder with its own checked-out branch, sharing one repository.
  • git worktree add <folder> -b <new-branch> <start> creates one; omit -b to use an existing branch.
  • A branch can only be checked out in one worktree at a time.
  • git worktree list shows them all; git worktree remove and prune clean up.
  • Worktrees beat stashing for interruptions, reviews and long-running tasks.

Git Worktrees Examples

Each example shows the code, the output it produces and what each part does.

Example 1

A hotfix without touching your feature

Terminal
git branch --show-current
git worktree add ../shop-hotfix -b hotfix/login main
git worktree list
cd ../shop-hotfix
git branch --show-current
Output
feature/search
Preparing worktree (new branch 'hotfix/login')
HEAD is now at b0ad9b9 Start
/home/sam/shop         e72a719 [feature/search]
/home/sam/shop-hotfix  b0ad9b9 [hotfix/login]
hotfix/login
Line-by-line explanation
  1. Line 1You're in the middle of feature/search, with uncommitted changes you don't want to disturb.
  2. Line 2A new folder with a new branch started from main.
  3. Line 3Two working trees, each on its own branch, sharing one repository.
  4. Lines 4 to 5In the new folder, you're on hotfix/login. Fix, commit and push from here as normal.
Example 2

Finishing and tidying up

Terminal
git commit -am "fix: correct login redirect"
git push -u origin hotfix/login
cd ../shop
git worktree remove ../shop-hotfix
git worktree list
git branch
Output
[hotfix/login 4c8e2a1] fix: correct login redirect
 1 file changed, 1 insertion(+)
To https://github.com/samlee/shop.git
 * [new branch]      hotfix/login -> hotfix/login
branch 'hotfix/login' set up to track 'origin/hotfix/login'.
/home/sam/shop  e72a719 [feature/search]
* feature/search
  hotfix/login
  main
Line-by-line explanation
  1. Lines 1 to 2Commit and push the fix from the hotfix worktree, then open a pull request.
  2. Lines 3 to 4Back in the main folder, remove the finished worktree.
  3. Line 6The hotfix/login branch still exists; delete it after it's merged.
Example 3

The one-branch rule and protected changes

Terminal
git worktree add ../shop-copy feature/search
git worktree add ../shop-review main
echo "notes" > ../shop-review/todo.txt
git worktree remove ../shop-review
Output
Preparing worktree (checking out 'feature/search')
fatal: 'feature/search' is already used by worktree at '/home/sam/shop'
Preparing worktree (checking out 'main')
HEAD is now at b0ad9b9 Start
fatal: '../shop-review' contains modified or untracked files, use --force to delete it
Line-by-line explanation
  1. Line 1feature/search is already checked out in the main folder, so Git refuses.
  2. Line 2Checking out main, which isn't open anywhere else, works.
  3. Lines 3 to 4An untracked file in the review folder makes Git refuse to remove it, so nothing is lost by accident.

Common Git Worktrees Mistakes

Deleting the folder by hand

Don't
rm -rf ../shop-hotfix         # Git still thinks the worktree exists
Do
git worktree remove ../shop-hotfix
# or, if it's already deleted:
git worktree prune

Creating a worktree inside the project folder

Don't
git worktree add ./hotfix -b hotfix/login    # shows up as untracked files
Do
git worktree add ../shop-hotfix -b hotfix/login

Forgetting each worktree needs its own setup

Don't
cd ../shop-hotfix && npm test    # fails: node_modules isn't there
Do
cd ../shop-hotfix && npm install && npm test

Git Worktrees in Real Projects

Worktrees in practice

Developers keep a permanent worktree for main and short-lived ones for hotfixes and reviews. AI coding assistants and scripts are often run in their own worktrees, so their changes stay on a separate branch until someone reviews them.

Git Worktrees FAQ

Do worktrees share stashes and settings?

Yes. All worktrees share one repository, so branches, tags, stashes and the repository config are the same everywhere. Each worktree has its own checked-out files, staging area and HEAD, which is what lets you work on two branches at once.

How do I create a worktree for a branch that only exists on the remote?

Run git fetch, then git worktree add ../project-feature feature. If there's no local branch called feature but one remote has it, Git creates a local branch tracking it, just as git switch does.

Can I move a worktree to a different folder?

Yes, with git worktree move ../old-path ../new-path. Moving the folder by hand breaks the link Git keeps to it, so use the command. If you've already moved it manually, git worktree repair can fix the links.

What does git worktree lock do?

It stops Git from pruning a worktree's records when its folder can't be found, which matters for worktrees on removable drives or network shares. Run git worktree lock ../path --reason "on USB drive" and git worktree unlock when you're done. git worktree list marks locked ones.

Is a worktree better than cloning the repository twice?

Usually. A worktree shares the existing history, so it's created quickly and uses no extra space for old commits, and a commit made in one worktree is immediately visible from the others. Two clones are fully independent and must push and fetch to exchange work.

Git Worktrees Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the command that creates a worktree with a new branch based on main.

git worktree  ../shop-hotfix -b hotfix/login main
Multiple choiceeasy

What is a Git worktree?

Multiple choicemedium

Why does git worktree add ../x feature/search fail if feature/search is open in your main folder?

Multiple choicemedium

You deleted a worktree folder with your file manager. What should you run?

Multiple choicehard

Why can a worktree be better than stashing for an urgent fix?

Git Worktrees Quiz

5 questions to check what you learned.

  1. Which command shows all worktrees?

    • git worktree show
    • git branch --worktrees
    • git worktree list
    • git status --all
  2. Does removing a worktree delete its branch?

    • No, the branch stays until you delete it
    • Yes
    • Only if it was merged
    • Only with --force
  3. In git branch output, what does a + next to a branch mean?

    • It's ahead of the remote
    • It has uncommitted changes
    • It's checked out in another worktree
    • It's protected
  4. Where is a good place to create worktree folders?

    • Inside the .git folder
    • Next to the project folder, such as ../shop-hotfix
    • Inside the project folder
    • On the desktop only
  5. What happens if you try to remove a worktree that has untracked files?

    • They're deleted silently
    • They're moved to a stash
    • The branch is deleted
    • Git refuses unless you add --force