Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXXIII · WorktreesWorktrees

Worktrees and branches — same branch in two worktrees; detached HEAD

Advanced⏱ ~21 mingit

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

Not yet marked complete on this device.

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. --detach creates 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 .git directory 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

  1. 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.
  2. Detached HEAD worktrees are for investigation, not for long-running work. If the work will last more than a day, create a branch.
  3. Promote detached-HEAD commits to a branch before removing the worktree. The reflog is the only thing keeping them reachable, and reflog entries expire.
  4. 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.
  5. Use git worktree list as 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 --detach worktrees in CI runners for read-only build contexts.

Quiz

Knowledge check · 4 questions

  1. 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?

  2. Q2. Two detached-HEAD worktrees created with `git worktree add --detach` can both point at the same commit simultaneously without Git refusing.

  3. Q3. Explain why Git refuses to check out the same branch in two worktrees, and what error the user sees when they try.

  4. 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.