Git Worktrees

Marek Kwasecki · March 25, 2026

What Are Git Worktrees?

Git worktrees let you check out multiple branches of the same repository into separate directories — simultaneously. Instead of stashing, committing, or shelving your work to switch branches, you simply cd into another directory.

Each worktree shares the same .git object store, so there is no duplication of history and no need to re-clone.

Why did the developer plant a git repository in the garden? Because they wanted more branches — and ended up with a whole worktree.

Question: why I didn’t know this before?! I was today years old to discover that. Shame!

Why Use Worktrees?

No more context switching friction

Switching branches with git checkout or git switch rewrites your working directory. That means:

  • Rebuilds get triggered (especially painful in C/C++ projects)
  • IDE indexes refresh
  • Running processes (servers, watchers) break
  • Unsaved or uncommitted work must be stashed or committed

With worktrees, each branch lives in its own directory. You can keep a server running on develop while writing a feature in another directory.

Parallel work on multiple features

Need to fix a bug on main while in the middle of a feature? Open a new terminal, cd to the main worktree, fix it, push, and go back. No interruption.

Safer code reviews

Check out a colleague’s PR branch in a fresh worktree. Build it, run tests, poke around — all without disturbing your own branch.

Shared git history

All worktrees share one object database. Fetches, gc, and rerere data are shared. Disk usage is minimal compared to multiple clones.

Quick Reference

List existing worktrees

git worktree list

Example output:

/home/user/projects/my-app           a1b2c3d [feature/auth]
/home/user/projects/my-app-hotfix    d4e5f6g [hotfix/login-bug]
/home/user/projects/my-app-refactor  h7i8j9k [refactor/db-layer]

Create a worktree from an existing branch

# Fetch the branch if it's remote-only
git fetch origin <branch-name>

# Create the worktree
git worktree add ../<directory-name> <branch-name>

Create a worktree with a new branch

git worktree add -b <new-branch> ../<directory-name> <base-branch>

Initialize submodules in the new worktree

Submodules are not automatically initialized in new worktrees. After creating one:

cd ../<directory-name>
git submodule update --init --recursive

Remove a worktree

# Delete the directory
rm -rf ../<directory-name>

# Clean up git's internal tracking
git worktree prune

Or in one step:

git worktree remove ../<directory-name>

Practical Example

Suppose you’re working on feature/auth in my-app/ and need to start work on feature/notifications:

# From within any existing worktree of the repo
cd my-app/

# Fetch and create the worktree
git fetch origin feature/notifications
git worktree add ../my-app-notifications feature/notifications

# Initialize submodules (if your project uses them)
cd ../my-app-notifications
git submodule update --init --recursive

Now you have two fully independent working directories:

my-app/                  → feature/auth
my-app-notifications/    → feature/notifications

Each can have its own IDE window, running server, and build artifacts without interfering with the other.

Example Project

To make this concrete, here is a minimal C++ CMake project you can use to try worktrees yourself. It uses Conan 2 for dependency management and Catch2 for tests.

Project structure

my-cmake-app/
├── CMakeLists.txt
├── conanfile.txt
├── build.sh
├── .gitignore
├── src/
│   └── main.cpp
└── tests/
    └── test_example.cpp

Files

CMakeLists.txt

cmake_minimum_required(VERSION 3.20)

project(CMakeConanBoilerplate CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Catch2 3 REQUIRED)

add_executable(sample src/main.cpp)

add_executable(tests tests/test_example.cpp)
  target_link_libraries(tests PRIVATE Catch2::Catch2WithMain)

include(CTest)
add_test(NAME ExampleTest COMMAND tests)

conanfile.txt

[requires]
catch2/3.5.0

[generators]
CMakeDeps

build.sh

#!/usr/bin/env bash
set -euo pipefail

BUILD_DIR="cmake-build-debug"
BUILD_TYPE="Debug"

while [[ $# -gt 0 ]]; do
  case $1 in
    --debug)   BUILD_DIR="cmake-build-debug";   BUILD_TYPE="Debug";   shift ;;
    --release) BUILD_DIR="cmake-build-release";  BUILD_TYPE="Release"; shift ;;
    *)         echo "Usage: $0 [--debug | --release]"; exit 1 ;;
  esac
done

mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"

conan install .. -g CMakeDeps -s build_type=$BUILD_TYPE --output-folder=. --build=missing
cmake .. \
  -DCMAKE_BUILD_TYPE=$BUILD_TYPE \
  -DCMAKE_C_COMPILER=clang \
  -DCMAKE_CXX_COMPILER=clang++

cmake --build . -- -j$(nproc)
echo "Build completed in $(pwd)"

src/main.cpp

#include <iostream>

int main(int ac, const char** av) {
    std::cout << "Hello!\n";
    return 0;
}

tests/test_example.cpp

#include <catch2/catch_all.hpp>

TEST_CASE("Simple addition", "[math]") {
    REQUIRE(1 + 1 == 2);
}

Try it with worktrees

# Clone and build
git clone <your-repo-url> my-cmake-app
cd my-cmake-app
./build.sh --debug

# Now create a worktree for a new feature
git worktree add ../my-cmake-app-feature feature/new-parser
cd ../my-cmake-app-feature
./build.sh --debug

# Both directories build independently
# Edit code in either one without affecting the other

Notice how each worktree gets its own cmake-build-debug/ directory — builds are completely isolated.

Worktrees vs. Multiple Clones

  Worktrees Multiple Clones
Disk usage Shared object store — minimal overhead Full copy of history per clone
Fetching Fetch once, all worktrees see it Must fetch in each clone separately
Branch locking A branch can only be checked out in one worktree No restriction
Setup One command Clone + configure remotes + submodules each time

Tips

  • Naming convention — Use a short, recognizable prefix: <project>-<feature> or <project>-<ticket-id>. This keeps the parent directory tidy and git worktree list output readable.
  • IDE support — Most IDEs (CLion, VS Code, GoLand, IntelliJ) can open each worktree directory as a separate project with no issues.
  • Don’t check out the same branch twice — Git prevents this by default. If you need to, use git worktree add --detach.
  • Pruning — If you delete a worktree directory manually, run git worktree prune to clean up stale references.
  • Submodules — Always remember git submodule update --init --recursive after creating a new worktree. This is easy to forget and will cause build failures.
  • Build artifacts — Each worktree gets its own working directory, so build caches and output are isolated. This is a feature, not a bug — it means builds in one worktree won’t corrupt another.

This article was generated with the assistance of AI and reviewed by a human.

Written on March 25, 2026