Git worktrees let you check out multiple branches simultaneously without the usual context-switching pain.

Imagine you’re deep in feature development on main, but a critical bug pops up on release-v1.2. Normally, you’d stash your main work, switch to release-v1.2, fix it, commit, switch back, and unstash. With worktrees, you can have both branches checked out in separate directories, instantly accessible.

Let’s see it in action. Suppose we have a repository with two branches: main and feature/new-login.

First, create a worktree for the feature/new-login branch. This command creates a new directory named feature-login in the current directory and checks out the feature/new-login branch into it.

git worktree add ../feature-login feature/new-login

Now, you have two separate directories:

  • ./ (your original directory, checked out to main)
  • ../feature-login/ (the new directory, checked out to feature/new-login)

You can cd into ../feature-login and start coding on your feature. Meanwhile, your original directory still has main checked out, ready for you to switch contexts. If a bug report comes in for main, you can cd back to your original directory and start working on it immediately, without any stashing or messy state.

The core problem worktrees solve is the friction of context switching. When you’re working on multiple tasks, each requiring a different branch, the Git workflow can become a series of git stash, git checkout, git stash pop, and git checkout commands. This is not only time-consuming but also error-prone. A forgotten stash or an accidental commit to the wrong branch can lead to significant headaches. Worktrees eliminate this by giving each branch its own clean working directory, completely isolated from the others.

Internally, Git worktrees are surprisingly simple. When you create a worktree, Git creates a new directory and then uses a mechanism called "hard linking" (or symbolic links on some systems) to share the .git directory and the object database with your main repository. This means you’re not duplicating your entire repository history; you’re just creating separate working copies of the files for each branch. Each worktree gets its own .git directory, but this directory primarily contains configuration and links back to the main repository’s object store. This makes worktrees very space-efficient.

The key levers you control are the path to the new worktree directory and the branch you want to check out into it. You can also specify a specific commit instead of a branch name.

To see all your active worktrees, you can use:

git worktree list

This will show you something like:

/path/to/your/repo      main               [main]
/path/to/your/repo/../feature-login  feature/new-login  [feature/new-login]

When you’re finished with a worktree, you can remove it. This command removes the worktree directory and cleans up its associated Git metadata.

git worktree remove ../feature-login

If you have uncommitted changes in a worktree you’re removing, Git will prevent you from doing so by default. You can force the removal, but it’s usually better to commit or stash your changes first.

The most surprising thing about worktrees is how seamlessly they integrate with your existing Git workflow. You can run git status, git diff, git commit, git pull, git push – all the usual commands – within each worktree, and they operate entirely independently. It feels like you have multiple, entirely separate Git repositories, but they all share the same underlying history and object database, making them incredibly efficient.

Once you’re comfortable with basic worktree management, you’ll want to explore how they interact with git bisect.

Want structured learning?

Take the full Git course →