We have all been there. You are halfway through a complex feature branch. You have ten modified files, three failing TypeScript tests you are actively debugging, and a half-configured local database state. The slack notification sounds: a critical bug is live in production. You need to fix it immediately.
Your options look grim. You can run git stash, switch to main, write the patch, push it, switch back, and run git stash pop. This is one of the common Git mistakes that breaks your flow. Stashing clears your working directory, which forces your IDE to re-index the project. Your build cache gets invalidated. If you are working on a large codebase, you might spend ten minutes waiting for compilation pipelines to run again just to change a single line of code. Even worse, if you had untracked files or schema migrations in progress, stashing can leave your local environment in a broken state.
Another option is cloning the repository a second time into another folder. This works, but it wastes disk space. You end up with multiple copies of the entire project history. If you run git fetch in one folder, the other folder knows nothing about it until you pull again.
Git worktree solves this problem. It lets you run multiple working directories from a single local repository database. You can keep your feature branch open in one terminal pane, open a new directory for the hotfix in another, and write the patch without touching your active workspace.
How Git Worktree Works
Let's look at the mechanics. When you run a standard git clone, Git creates a directory containing two main components: the working tree (your project files) and the .git folder (the repository database).
A worktree is simply another checked-out copy of your repository, placed in a separate directory. Instead of creating a new .git database, the new directory contains a small .git file. This file acts as a pointer. It points directly back to the main repository database in your original folder.
Because both directories share the same database, they share the same history, branches, and stashes. If you run git fetch in your feature directory, the hotfix directory immediately sees the new commits. You do not need to push to GitHub or use local remotes to share code between them.
Setting Up Your First Worktree
Let's walk through the basic commands. Suppose you are working in a repository located at ~/projects/my-app. You are on a branch called feature-login.
To create a new workspace for a hotfix without disturbing your current files, run:
git worktree add ../my-app-hotfix mainThis command does three things:
- Creates a new directory named
my-app-hotfixone level up from your current folder. - Checks out the
mainbranch inside that directory. - Links the directory to your main Git database.
Now you can open a new terminal tab, change directory to ~/projects/my-app-hotfix, and start working. Your IDE can open this folder separately. Your editor state in my-app remains untouched. Your modified files are still there, your tests are still in progress, and your build caches are safe.
If you need to create a new branch for the hotfix instead of checking out an existing one, use the -b flag:
git worktree add -b hotfix-security ../my-app-hotfix origin/mainThis creates the hotfix-security branch based on origin/main and checks it out in the new directory.
Managing Worktrees
As you start using worktrees, you will need to keep track of them. To see all active directories linked to your database, run:
git worktree listThe output shows the absolute path of each worktree, the commit hash it is currently on, and the active branch name:
/Users/username/projects/my-app a1b2c3d [feature-login]
/Users/username/projects/my-app-hotfix e5f6g7h [hotfix-security]Once you finish the hotfix, commit the changes, push them to the remote repository, and open your pull request. After that, you no longer need the temporary directory. You can remove it using the remove command:
git worktree remove ../my-app-hotfixThis command deletes the directory from your disk and cleans up the references inside the main .git folder.
If you manually delete the directory using the command line (rm -rf ../my-app-hotfix) or your file manager, Git will still think the worktree exists. It will show up when you run git worktree list. To clean up these dead references, run:
git worktree pruneThis scans the database for worktrees whose directories no longer exist on disk and deletes their metadata.
The Bare Repository Workflow
Using sibling folders like ../my-app-hotfix works, but it creates an uneven structure. You have a "parent" directory (my-app) that contains the actual .git database, and several "child" directories scattered around it. If you accidentally delete my-app, you lose the Git database for all your worktrees.
To avoid this, you can use the bare repository pattern. A bare repository contains only the Git database and no working files. By cloning your project as a bare repository, you can treat all your active branches as equal worktrees.
Here is how to set up this workflow from scratch:
# Create a parent directory for the project
mkdir my-project
cd my-project
# Clone the repository as a bare repo into a hidden folder
git clone -bare git@github.com:username/repo.git .bare
# Tell Git where to find the database for commands run in the root
echo "gitdir: .bare" > .gitNow, your root directory my-project contains only the .bare database folder and a .git pointer file. You do not write code in this root directory. Instead, you create worktrees inside it for each branch you want to work on:
# Add a worktree for the main branch
git worktree add main
# Add a worktree for your current feature
git worktree add feature-billingYour directory structure will look like this:
my-project/
├── .bare/ # The shared Git database
├── .git # Pointer file
├── main/ # Worktree for the main branch
└── feature-billing/ # Worktree for feature developmentThis setup is clean. If you need a temporary hotfix, you create a folder inside my-project/hotfix. When you are done, you delete it. The main database remains untouched inside .bare.
Handling Dependency and Build Cache Overhead
One challenge of running multiple worktrees is managing dependencies. If you are working on a Node.js project, running npm install in three different worktrees will copy gigabytes of duplicate files to your disk.
To handle this, look at your package manager.
If you use pnpm or Bun, this problem disappears. Both tools use global content-addressable storage. When you run pnpm install in a new worktree, it creates hard links to the global store instead of copying files. The installation takes seconds and uses virtually no extra disk space.
If you are stuck with npm or Yarn, you can use workspaces or configure your tools to share cache directories.
For compiled languages like Rust or Go, build artifacts can consume massive amounts of disk space. For example, a Rust target directory can easily grow to several gigabytes. If you compile the same project in three different worktrees, you will run out of disk space quickly.
You can solve this by configuring your build tools to share a single target directory. In Rust, you can set the CARGO_TARGET_DIR environment variable in your shell configuration:
export CARGO_TARGET_DIR="$HOME/.cache/cargo-target"Alternatively, you can create a shared configuration file in your home directory or in each worktree. This forces Cargo to compile all dependencies to a central location, saving disk space and speeding up build times across different branches.
Working with IDEs
Modern editors handle worktrees well, but you need to adjust your workflow slightly.
When using VS Code, open each worktree as a separate window. If you use the bare repository layout, you can open the root folder my-project as a workspace, or open individual directories like main and feature-billing depending on what you are doing.
JetBrains IDEs (IntelliJ, WebStorm, PyCharm) automatically detect Git worktrees. When you open a linked worktree folder, the IDE reads the .git file pointer and configures the version control integration automatically.
If you use terminal-based editors like Neovim, you can use plugins designed for worktrees, or simply rely on standard shell commands to jump between directories.
Important Limitations and Edge Cases
While Git worktree is powerful, it has a few rules you must follow.
First, you cannot check out the same branch in two different worktrees at the same time. If you try to run git worktree add ../another-dir main while main is already checked out in your main/ directory, Git will block the action with an error:
fatal: 'main' is already checked out at '/path/to/my-project/main'This restriction exists to prevent data corruption. If you were allowed to modify files on the same branch in two places simultaneously, committing changes in one directory would invalidate the index of the other directory, leading to conflicts and lost work. If you need to work on the same logical branch, create a temporary branch pointing to the same commit:
git checkout -b feature-login-experiment origin/feature-loginSecond, local configuration options are shared by default. If you run git config user.email "work@company.com" inside a worktree, that change applies to the shared .git database and affects all other worktrees.
If you need worktree-specific configurations-for example, using a different commit email for a specific branch-you must enable the worktree configuration extension:
git config extensions.worktreeConfig trueOnce enabled, you can write configuration options that apply only to the current worktree by using the -worktree flag:
git config -worktree user.email "personal@email.com"This writes the settings to a special configuration file located at .git/worktrees/<worktree-name>/config.worktree, keeping it separate from the main repository configuration.
Transitioning to Git Worktree
You do not need to switch your entire workflow overnight. Start by using worktrees for quick code reviews or testing pull requests. The next time you need to review a coworker's branch, instead of switching your current workspace, run git worktree add ../review-branch branch-name. Run the tests, check the code, delete the directory, and run git worktree prune.
Once you get used to the speed of keeping your primary workspace clean, you can experiment with the bare repository layout to organize your daily development.



