Skip to content

Git Worktree — Parallel CI Debug & Hotfix Builds

Use case: Debug a failing CI pipeline, build a backport release, or verify a hotfix—all without disturbing your current branch, without stashing work, and without cloning a fresh copy.

git worktree lets you checkout multiple branches simultaneously in separate working directories, each sharing the same .git object store. Each worktree is fully isolated: separate HEAD, index, and working tree.


Core Concept

# Typical setup: main worktree (bare repo clone)
~/projects/myapp $ git worktree list
/home/hermes/projects/myapp/.worktrees/main  3a91f2d [main]

Each worktree is a lightweight, independent workspace rooted at a subdirectory of .worktrees/. They share all Git objects and refs—creating a new worktree does not duplicate .git/objects.


DevOps Scenario Walk-Through

Scenario 1: Debug CI Failure on a Feature Branch

Your main is clean. A PR is failing CI on feat/payment-refactor. Rather than git stash or git switch, spin up a dedicated debug worktree:

# Ensure the PR branch exists locally
git fetch origin feat/payment-refactor

# Create a worktree at a known path
git worktree add .worktrees/debug-payment origin/feat/payment-refactor

# Navigate in, run the failing pipeline step directly
cd .worktrees/debug-payment
git log --oneline -5           # confirm HEAD is feat/payment-refactor
npm ci
npm run test -- --grep "payment"

# Fix, commit, push—all without leaving your main branch context
git commit -m "fix: resolve flaky payment unit test"
git push origin feat/payment-refactor

# When done, remove the worktree
cd ..
git worktree remove .worktrees/debug-payment

Benefit: Your main branch's working tree is untouched. No stash, no context switch in your IDE.


Scenario 2: Build a Hotfix Against a Tag Without Disturbing main

A production incident on v2.1.3. You need to build a hotfix and cut a release tag—all while main is mid-sprint.

# Create a worktree locked to the production tag
git worktree add ../repo-hotfix v2.1.3 -b hotfix/v2.1.4

# In the hotfix worktree, apply the fix
cd ../repo-hotfix
vim src/buggy_module.py
git add -A && git commit -m "hotfix: correct race condition in module"
git tag -a v2.1.4 -m "production hotfix"
git push origin hotfix/v2.1.4 --tags

# Meanwhile, your main worktree is completely unaffected
cd ../projects/myapp
git log --oneline -3  # still on latest main, no detaching needed

Benefit: Tag-based worktree gives you a pristine production snapshot. main continues uninterrupted.


Scenario 3: Multi-Branch Release Validation Matrix

Validate that a change compiles cleanly against v2.0.x, v2.1.x, and main before merging.

for branch in v2.0.8 v2.1.3 main; do
  tag_or_branch="origin/$branch"
  wtdir=".worktrees/validate-$branch"
  echo "=== Building against $branch ==="
  git worktree add "$wtdir" "$tag_or_branch" 2>/dev/null || {
    echo "Worktree $wtdir already exists, skipping"
    continue
  }
  (cd "$wtdir" && npm ci && npm run build)
  result=$?
  echo "=== $branch build exit code: $result ==="
  git worktree remove "$wtdir" --force
done

Benefit: Parallel matrix validation without any git switch or git stash.


Scenario 4: Locked Worktree for Long-Running Analysis

You have a branch that requires running a full static analysis suite (30+ minutes). Lock the worktree so it cannot be accidentally removed while the process runs.

# Create worktree with an explicit prune window
git worktree add .worktrees/static-analysis origin/feat/large-refactor

# Lock it (creates .git/worktrees/<name>.lock)
git worktree lock .worktrees/static-analysis -m "static analysis in progress"

# When done
cd .worktrees/static-analysis
# ... analysis runs ...
git worktree unlock .worktrees/static-analysis
git worktree remove .worktrees/static-analysis

Advanced: Worktree Management Commands

# List all worktrees (including bare repo's main working tree)
git worktree list
# Output:
# /home/hermes/projects/myapp/.worktrees/main   3a91f2d [main]
# /home/hermes/projects/myapp/.worktrees/debug-payment  a1b2c3d [feat/payment-refactor]

# Prune stale worktree references (automatic, but can be manual)
git worktree prune

# Move a worktree
git worktree move .worktrees/debug-payment ../payment-debug

# Forcibly remove a worktree (discard uncommitted changes)
git worktree remove .worktree-dir --force

Shared Hooks Across Worktrees

Each worktree has its own .git directory (symlinked to the main .git/worktrees/<name>). Hooks in .git/hooks/ are shared across all worktrees automatically—no need to install hooks per worktree.

To have per-worktree hooks:

# In a specific worktree
git config core.hooksPath .worktrees/my-worktree/.githooks

CI/CD Integration Notes

Consideration Detail
Shared objects All worktrees share .git/objects — no extra disk for objects
Separate refs Each worktree has its own HEAD, index, refs
Locked worktrees CI pipelines can git worktree lock to prevent accidental removal
Parallel jobs CI runner can git worktree add per job in a single clone
Cleanup git worktree prune cleans stale entries after runner completes
Bare repos git worktree list works on bare repos too

Common Pitfalls

  • Nested worktrees: Do not create a worktree inside another worktree's working tree—use paths outside the original repo root.
  • Locked worktrees: A locked worktree cannot be removed until unlocked. CI pipelines should always unlock before exiting.
  • Push conflicts: If two worktrees both have feat/X checked out and you push from one, the other may need a git fetch to update its remote-tracking refs.
  • Worktree on network filesystem: Performance degrades; prefer local disk for CI runners.