mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-09-24 19:08:21 +08:00
chore(claude): harden the rebase-onto-main skill against losing work (#28368)
The skill force-pushes over the only copy of the pre-rebase head without ever checking that the rebase preserved it, and says nothing about the two things that most often go wrong around it. Adds a git range-diff gate before the push, the worktree collision that makes git checkout fail mid-loop, and the --onto follow-up a stacked child branch needs once its base moves. Assisted-by: Claude:claude-opus-5 Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
This commit is contained in:
@@ -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.
|
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: '<branch>' is already used by worktree at ...`. Rebase it in place instead: `git -C <worktree-path> 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:**
|
2. **Fetch and update main:**
|
||||||
```
|
```
|
||||||
git fetch origin main: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.
|
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 <old-merge-base>..<old-head> main..<branch>
|
||||||
|
```
|
||||||
|
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 <old-merge-base>..<old-head> main..<branch>)
|
||||||
|
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:
|
11. **Ask the user** before force-pushing. When approved:
|
||||||
```
|
```
|
||||||
git push origin <branch> --force-with-lease
|
git push origin <branch> --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 <branch> <old-head-of-branch> <branch-above>
|
||||||
|
```
|
||||||
|
Skipping this makes the upper PR show every commit on `main`.
|
||||||
|
|
||||||
12. **Clean up** the old branch:
|
12. **Clean up** the old branch:
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user