Skip to content

Add: discipline rule on shared branch refs across worktrees - #1554

Merged
ChaoWao merged 1 commit into
hw-native-sys:mainfrom
ChaoWao:docs/worktree-shared-ref-rule
Jul 29, 2026
Merged

Add: discipline rule on shared branch refs across worktrees#1554
ChaoWao merged 1 commit into
hw-native-sys:mainfrom
ChaoWao:docs/worktree-shared-ref-rule

Conversation

@ChaoWao

@ChaoWao ChaoWao commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

This repo is worked through many concurrent git worktree checkouts, several driven by agent sessions. A branch ref is shared by all of them — only the index and working tree are per worktree. So git checkout -B <name> does not merely move "your" branch: if another worktree has that name checked out, it is taken out from under it.

Rule 6 states the general form: only ever create a new branch, or reset one you exclusively own. This is not specific to main — every branch another worktree holds is equally off-limits.

-B is the specific trap

Git already refuses the obvious forms when another worktree holds the branch:

git checkout main          → fatal: 'main' is already checked out at '...'
git branch -f main <ref>   → fatal: cannot force update the branch 'main' checked out at '...'

git checkout -B main <ref> is not refused (observed on git 2.39.1). That single call creates the dual-checkout state the other two guards exist to prevent — and from then on every further force-move is legal, because moving a branch your own worktree has checked out is an ordinary reset. The guard never fires again.

Why the symptom does not look like a ref problem

The victim worktree's HEAD is a symref, so it silently follows the branch to the new commit while its index and files stay at the old one. Since "staged" is by definition the HEAD↔index diff, git status there reports every intervening commit as staged modifications, in reverse — a file you added appears as a staged deletion.

It is not cosmetic. A git commit in that worktree would land a commit reverting everything between the two points, and would look entirely routine to whoever ran it.

Provenance

Written from an incident in this repo: eight force-moves of main from a linked worktree, which left the primary worktree reporting 96 phantom staged entries — including D .github/workflows/docs.yml, a file that had just been added by a merged PR. Nothing had been edited.

The prescriptions are what the incident showed were actually needed:

git checkout --detach upstream/main            # clean base, owns no branch
git checkout -b <task-branch> upstream/main    # -b fails loudly on a name clash
git show upstream/main:path/to/file            # read merged content, no checkout at all
git worktree list                              # self-check: each branch name at most once

Verification

  • Both quoted error messages reproduced on this box (git 2.39.1) before being written down
  • The -B bypass reproduced and then reverted; refs/heads/main value confirmed unchanged and the primary worktree confirmed clean afterwards
  • Deliberately no hard-coded worktree count — it changed from 7 to 8 while this rule was being written, which is precisely why the text says to check rather than assume
  • markdownlint-cli2 clean (v0.20.0, the version pre-commit pins)
  • Documentation only

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b208e19d-ef79-4a37-9d56-6c08156f90b2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Git worktree discipline guidance covering safe branch creation, detached-HEAD parking, forced branch resets, cross-worktree state divergence, and verification with git worktree list.

Changes

Worktree branch safety

Layer / File(s) Summary
Safe branch workflow
.claude/rules/discipline.md
Documents detached-HEAD parking, distinguishes upstream/main from main, and recommends creating branches with git checkout -b.
Forced reset consequences and detection
.claude/rules/discipline.md
Explains git checkout -B behavior, describes resulting index and file divergence in another worktree, and adds a git worktree list self-check.

Estimated code review effort: 1 (Trivial) | ~3 minutes

Poem

I’m a rabbit with branches, hopping light,
Detached when I pause, so the paths stay right.
No force-take tricks in my burrow today,
worktree list keeps the tangles away.
Safe little checkouts, hooray!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the documentation rule about shared branch refs across Git worktrees.
Description check ✅ Passed The description is directly related and accurately explains the same worktree branch-ref safety rule.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/rules/discipline.md:
- Around line 116-118: Update the distinction in the relevant guidance around
upstream/main and main to say that the remote-tracking ref is normally updated
by fetch, rather than claiming fetch is the only way it can be moved; preserve
the existing explanation that main is a local branch owned by a worktree.
- Around line 94-95: Update the worktree state explanation around the branch ref
discussion to explicitly include each worktree’s own HEAD/symref alongside its
index and working tree. Preserve the distinction that branch refs are shared,
while HEAD, index, and files are per-worktree, and ensure the later explanation
can rely on this distinction.
- Around line 139-145: Revise the staged-difference explanation in the “staged”
guidance to state that git status reports the net HEAD-to-index tree difference,
not every intervening commit, and that intervening commits may cancel one
another. Qualify the git commit warning so it says the commit reverses those
changes only when the index still exactly matches the old tree.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 646883da-a058-4b25-8342-15ba73853465

📥 Commits

Reviewing files that changed from the base of the PR and between 453ff71 and e2d9b96.

📒 Files selected for processing (1)
  • .claude/rules/discipline.md

Comment thread .claude/rules/discipline.md Outdated
Comment thread .claude/rules/discipline.md
Comment thread .claude/rules/discipline.md Outdated
This repo is worked through many concurrent worktrees, and a branch ref
is shared by all of them — `HEAD`, the index, and the working tree are
what is per worktree. So `git checkout -B <name>` does not merely move
"your" branch: if another worktree has that name checked out, it is
taken out from under it.

Rule 6 states the general form: only ever create a new branch, or reset
one you exclusively own. Park a worktree on a detached HEAD between
tasks rather than on a shared branch, and read merged content with `git
show upstream/main:<path>` instead of checking anything out.

The rule names `-B` as the specific trap, because git already refuses
the obvious forms. Both `git checkout <branch>` and `git branch -f
<branch>` are rejected when another worktree holds the branch, but `git
checkout -B <branch> <ref>` is not (git 2.39.1). That one call creates
the dual-checkout state the other two guards exist to prevent, and
afterwards every further force-move is legal, since moving a branch your
own worktree has checked out is an ordinary reset. The guard never fires
again.

It also records what the victim sees, because the symptom does not look
like a ref problem. The other worktree's HEAD is a symref, so it follows
the branch to the new commit while its index and files stay at the old
one. "Staged" being the HEAD-to-index diff by definition, `git status`
there reports the net tree difference between the two commits, in
reverse, as staged modifications — a file you added appears as a staged
deletion; anything a later commit undid cancels out and never shows. So
long as that index still matches the old tree, a commit made there would
revert the whole net difference and look routine to whoever ran it.

Written from an incident in this repo: eight such force-moves of `main`
from a linked worktree, which left the primary worktree reporting 96
phantom staged entries.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ChaoWao
ChaoWao force-pushed the docs/worktree-shared-ref-rule branch from e2d9b96 to bdc283c Compare July 29, 2026 02:12
@ChaoWao
ChaoWao merged commit 5bc11c2 into hw-native-sys:main Jul 29, 2026
14 checks passed
@ChaoWao
ChaoWao deleted the docs/worktree-shared-ref-rule branch July 29, 2026 02:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant