Git, CI/CD & GitOpsXXIII · WorktreesWorktrees
Worktrees and branches — same branch in two worktrees; detached HEAD
What you'll learn
- Explain why Git refuses to check out the same branch in two worktrees and what the error protects against
- Describe the detached HEAD state in a worktree and how its commits are recovered
- Recognise the rule that a worktree gives every branch its own working copy without duplicating history
- Map worktree behaviour to branch-protection, ref-update, and CI scenarios
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
Lessons 01 through 03 covered the mechanics of worktrees: what
they are, how to create and remove them, and how they share the
.git directory. This lesson covers the relationship between
worktrees and branches — the rule that Git enforces, the
detached-HEAD escape hatch, and the operational picture for an
infrastructure team that has many branches and many worktrees
running concurrently.
One branch per worktree, by design
The rule: a branch can be checked out in at most one worktree at a time. The error, when violated, looks like this:
git worktree add ~/work/iac-feature-iam-copy feature/iam-rotation
# fatal: 'feature/iam-rotation' is already checked out at '/home/alice/work/iac-feature-iam'
The check happens before the add does any on-disk work. Git
reads the HEAD file in each existing worktree’s metadata
directory (.git/worktrees/*/HEAD), determines that
feature/iam-rotation is already checked out in
~/work/iac-feature-iam, and refuses the new add.
The reason is safety. Two worktrees with the same branch checked
out would each have their own index and working tree, both
labelled “on feature/iam-rotation”, both producing commits that
advance the branch. Without the check, a git commit in either
worktree would silently move the shared branch ref under the
other worktree’s feet; the other worktree’s “current commit”
would no longer match what its index and working tree describe.
flowchart LR
subgraph A["worktree A on feature/x"]
A_IDX["index"]
A_FS["working tree"]
A_HEAD["HEAD -> feature/x"]
end
subgraph B["worktree B (forbidden)"]
B_IDX["index"]
B_FS["working tree"]
B_HEAD["HEAD -> feature/x"]
end
REF["refs/heads/feature/x"]
A -. commit moves ref .-> REF
B -. commit moves ref .-> REF
REF -. silent divergence .-> A
REF -. silent divergence .-> B
The check is what prevents the picture on the right. Two worktrees pointing at the same branch ref is a divergence hazard: each can commit, push, and pull against the same ref, and the two working trees will not know what the other has done. Git’s refusal is the equivalent of “two processes cannot both hold the write lock on the same file”.
The legitimate escape: —detach and clones
The two legitimate ways to have two working copies that look at the same content are:
- A detached HEAD worktree.
--detachcreates a worktree without checking out any branch; the worktree sits on a commit. New commits made on top of the detached HEAD update the worktree’s HEAD but do not move any branch ref (because no branch is checked out). Two detached worktrees can both be at the same commit; neither advances a branch.
# Detached worktree at v1.4.0 for read-only inspection
git worktree add --detach ~/work/iac-v1.4 v1.4.0
# Another detached worktree at the same commit is allowed
git worktree add --detach ~/work/iac-v1.4-inspect v1.4.0
- A clone. A clone has its own
.gitdirectory with its own refs. Two clones of the same branch are not two worktrees of the same.git; they are two independent repositories that happen to share an origin.
The worktree primitive’s “one branch per worktree” rule is
intentionally not lifted by --detach and intentionally not
workaroundable for branches. The clone exists precisely for the
“second copy that needs independent refs” case.
Detached HEAD in a worktree
A detached HEAD worktree behaves the same way detached HEAD behaves in the main worktree: HEAD points directly at a commit, not at a ref, and any new commits are reachable only from the worktree’s reflog:
# Create a detached worktree at the release tag
git worktree add --detach ~/work/iac-release-1.4 v1.4.0
# Make an exploratory commit on top
cd ~/work/iac-release-1.4
echo "investigating bug #4711" > NOTES.txt
git add NOTES.txt
git commit -m "investigation notes"
# HEAD now points at the new commit, no branch ref points at it
git rev-parse HEAD
# e5f6g7h...
git branch --contains e5f6g7h
# (empty — no branch contains it)
# The worktree's reflog is the only thing keeping the commit reachable
git reflog
# e5f6g7h HEAD@{0}: commit: investigation notes
# 8a3f9d2 HEAD@{1}: checkout: moving from ... to 8a3f9d2
If the worktree is removed without preserving the commit, the reflog entry expires (after the configured reflog expiry, default 90 days) and the commit becomes unreachable. The recovery discipline is the same as for detached HEAD in the main worktree: if the commit matters, create a branch before removing the worktree:
# Promote the detached commit to a named branch before removing the worktree
git branch rescue/investigation-4711 e5f6g7h
git worktree remove ~/work/iac-release-1.4
# The commit is now reachable from refs/heads/rescue/investigation-4711
Worktrees and branch protection
Worktrees interact with branch protection in one important way: a worktree’s local commits and pushes are subject to the same remote-side rules as commits and pushes from the main worktree. Branch protection is a property of the remote, not of the worktree. An engineer can have ten worktrees, each on a different feature branch, each pushing to its own PR — the remote-side branch-protection rules apply to every push from every worktree.
The implication for production discipline: per-worktree isolation does not give an engineer a way around branch protection. A force-push from a worktree is still a force-push on the remote; a direct commit to a protected branch from a worktree is still blocked by the remote; a required-review PR is still required. The worktree primitive is a working-copy mechanism, not a privilege-escalation mechanism.
The operational picture
For a team running multiple IaC branches in parallel, the worktree layout typically looks like:
git worktree list
# /home/alice/work/iac a1b2c3d [main]
# /home/alice/work/iac-feature-iam d4e5f6a [feature/iam-rotation]
# /home/alice/work/iac-feature-network e5f6g7h [feature/vpc-refactor]
# /home/alice/work/iac-hotfix-vpn f6g7h8i [hotfix/vpn-cert]
# /home/alice/work/iac-incident-4711-detached 8a3f9d2 (detached HEAD)
Five worktrees, five branches (or four branches plus one detached
investigation), one shared .git. Each worktree has its own
working copy, its own index, its own HEAD. Edits in one do not
touch the others. Push from any worktree to its own branch,
merge through PRs, repeat.
Production discipline
- One branch per worktree; one worktree per active branch. If a branch needs active work, it gets its own worktree; if a worktree is not actively used, remove it.
- Detached HEAD worktrees are for investigation, not for long-running work. If the work will last more than a day, create a branch.
- Promote detached-HEAD commits to a branch before removing the worktree. The reflog is the only thing keeping them reachable, and reflog entries expire.
- Worktrees do not bypass branch protection. A force-push from a worktree is still a force-push on the remote; the remote-side rules apply uniformly.
- Use
git worktree listas the inventory. When a worktree’s branch is merged, remove the worktree. Stale worktrees accumulate and produce future conflicts.
Cross-course references
- Git, CI/CD & GitOps - Part V (HEAD) lesson 03 covers detached HEAD at the main-worktree level; this lesson applies it to worktrees.
- Git, CI/CD & GitOps - Part XVII (Reflog) covers the reflog mechanism that keeps detached-HEAD commits reachable inside a worktree.
- CI/CD Pipeline Patterns - Part III (CheckoutStrategies)
uses
--detachworktrees in CI runners for read-only build contexts.
Quiz
Knowledge check · 4 questions
Q1. Two worktrees on the same machine share a .git directory. Worktree A has `feature/iam-rotation` checked out. The engineer tries `git worktree add ~/work/iac-copy feature/iam-rotation` from the main worktree. What is the correct outcome?
Q2. Two detached-HEAD worktrees created with `git worktree add --detach` can both point at the same commit simultaneously without Git refusing.
Q3. Explain why Git refuses to check out the same branch in two worktrees, and what error the user sees when they try.
Q4. Recover useful commits from a detached-HEAD worktree that is about to be removed, and recommend the discipline for future investigations.
An engineer investigating a production incident has made five exploratory commits in a detached-HEAD worktree at v1.4.0. The investigation produced a confirmed root cause and a candidate fix, but the engineer is about to remove the worktree to free the path for a hotfix worktree. The commits are not on any branch.
Passing score: 75%. Answers are checked in this browser.