diff --git a/.claude/skills/rebase-onto-main/SKILL.md b/.claude/skills/rebase-onto-main/SKILL.md index 04e57882f2b..be58355da58 100644 --- a/.claude/skills/rebase-onto-main/SKILL.md +++ b/.claude/skills/rebase-onto-main/SKILL.md @@ -17,6 +17,16 @@ When a parent branch is squash-merged, its individual commits become a single ne 1. **Identify the branch.** Use `$ARGUMENTS` if provided, otherwise use the current branch. + Check `git worktree list` first. A branch checked out in another worktree cannot be checked out here — `git checkout` fails with `fatal: '' is already used by worktree at ...`. Rebase it in place instead: `git -C rebase main`. + + When rebasing several branches in a loop, test the checkout's exit status explicitly: + + ``` + git checkout -q "$b" || { echo "SKIP $b (worktree?)"; continue; } + ``` + + Without that guard the loop rebases whatever is *currently* checked out, once per failed iteration, and reports success — the rebase really did work, just on the wrong branch. `set -e` is not a substitute: it is ignored in a Claude Code Bash tool call (it only takes effect when bash runs a script file). + 2. **Fetch and update main:** ``` git fetch origin main:main @@ -62,10 +72,29 @@ When a parent branch is squash-merged, its individual commits become a single ne ``` Confirm only the expected commits are present. + Then prove no content changed, because the next step overwrites the only copy of the old head: + ``` + git range-diff .. main.. + ``` + Every line must be marked `=` (identical patch). `!` means a commit's diff changed, `<` means one was dropped, `>` means one appeared. Anything but `=` is a rebase that lost or altered work — stop and look before pushing. Scripted: + ``` + rd=$(git range-diff --no-color .. main..) + tot=$(echo "$rd" | grep -cE '^ *[0-9]+:') + eq=$(echo "$rd" | grep -cE '^ *[0-9]+: +[0-9a-f]+ = ') + [ "$tot" = "$eq" ] && echo CLEAN || echo REVIEW + ``` + 11. **Ask the user** before force-pushing. When approved: ``` git push origin --force-with-lease ``` + Use `--force-with-lease`, never a bare `--force`. Push to the remote that holds the branch's PR head. + + If this branch is the base of a stacked PR, the branch above it still points at the pre-rebase commits. Rebase it onto the new head and force-push it too: + ``` + git rebase --onto + ``` + Skipping this makes the upper PR show every commit on `main`. 12. **Clean up** the old branch: ```