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:
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/Xchecked out and you push from one, the other may need agit fetchto update its remote-tracking refs. - Worktree on network filesystem: Performance degrades; prefer local disk for CI runners.