git-workflows/skills/rebase-pr/SKILL.md
Local rebase-merge workflow for pull requests with signed commits
npx skillsauth add jacobpevans/claude-code-plugins rebase-prInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Merge a PR using local git rebase + signed commits + git push origin main.
gh pr merge --rebase cannot sign commits. Local rebase with commit.gpgsign=true
signs every rebased commit. Pushing main directly auto-closes the PR.
MANDATORY FIRST STEP: Spawn a Haiku subagent using the Agent tool with
mode: "bypassPermissions". Pass all content starting from Prerequisite: Validate Rulesets
through end-of-document as the agent prompt; include the current branch name and PR number.
Do not execute any steps yourself — the subagent runs the complete workflow autonomously with
all permissions auto-accepted.
If the Haiku subagent cannot be spawned, becomes unavailable, or encounters an unrecoverable error while running this workflow, clearly report the failure to the user and ask whether to (a) retry spawning the subagent or (b) proceed manually by following the remaining steps of this document together step-by-step.
Before anything else, check the all-branches ruleset:
gh api repos/{owner}/{repo}/rulesets \
--jq '.[] | select(.conditions.ref_name.include[] == "~ALL") | .rules[].type'
If any of these appear → ABORT with the message shown:
| Rule type | Message |
|-----------|---------|
| non_fast_forward | "Remove from all-branches ruleset. Feature branches need force-push after rebase. Keep on main-only." |
| required_linear_history | "Remove from all-branches ruleset. Keep on main-only." |
| pull_request | "Remove from all-branches ruleset. Keep on main-only." |
| required_status_checks | "Remove from all-branches ruleset. Keep on main-only." |
| code_scanning | "Remove from all-branches ruleset. Keep on main-only." |
Only required_signatures belongs on all-branches.
query {
repository(owner: "{owner}", name: "{repo}") {
pullRequest(number: {PR}) {
state
mergeable
mergeStateStatus
isDraft
reviewDecision
commits(last: 1) {
nodes { commit { statusCheckRollup { state } } }
}
reviewThreads(first: 25) {
nodes { isResolved }
}
}
}
}
Required values — abort if any fail:
| Field | Must be | Abort message |
|-------|---------|---------------|
| state | OPEN | "PR is not open — run /finalize-pr to fix" |
| mergeable | MERGEABLE | "PR has merge conflicts — run /finalize-pr to fix" |
| mergeStateStatus | CLEAN or HAS_HOOKS | "PR merge state is {value} — run /finalize-pr to fix" |
| isDraft | false | "PR is a draft — mark ready first, then run /finalize-pr" |
| reviewDecision | APPROVED or null | "PR needs approval — run /finalize-pr to fix" |
| statusCheckRollup.state | SUCCESS | "CI is not passing: {state} — run /finalize-pr to fix" |
| All reviewThreads.isResolved | true | "Unresolved review threads — run /finalize-pr to fix" |
cd ~/git/{repo}/main
git fetch origin --force main
git pull origin main
For remote-only branches (Renovate, Dependabot, etc.):
# NEVER use FETCH_HEAD — always create from origin/{branch}
git fetch origin --force {branch}
git branch {branch} origin/{branch}
Create worktree and rebase:
git worktree add ~/git/{repo}/{worktree-path} {branch}
cd ~/git/{repo}/{worktree-path}
git rebase origin/main
git log --oneline origin/main..HEAD # verify commits are ahead
git push --force-with-lease origin {branch}
gh pr checks {PR} --watch --interval 15
Do NOT proceed until all checks pass.
If force-with-lease fails on a bot branch (no upstream tracking):
git branch --set-upstream-to=origin/{branch} {branch}
git push --force-with-lease origin {branch}
cd ~/git/{repo}/main
git merge-base --is-ancestor origin/main {branch} # verify FF is possible; exit 0 = yes
git merge --ff-only {branch}
If merge-base --is-ancestor exits non-zero, main moved since rebase — go back to Step 2.
git push origin main
If rejected with "Code scanning waiting":
gh pr checks {PR} --watch --interval 15
git push origin main # retry after checks pass
Verify merged:
gh pr view {PR} --json state --jq '.state' # expect: MERGED
git worktree remove ~/git/{repo}/{worktree-path}
git branch -d {branch} # use -D only after confirming state=MERGED
git push origin --delete {branch}
git worktree prune
gh pr merge — GitHub cannot sign rebase commitsgit push --force origin main — only force-push feature branchesFETCH_HEAD — use origin/{branch}git branch -D without first confirming state=MERGED/finalize-prRebase conflicts:
# git rebase pauses and lists conflicted files
git status # see conflicted files
# edit files to resolve
git add {conflicted-files}
git rebase --continue
Push to main rejected (code scanning):
Wait for CI with gh pr checks {PR} --watch --interval 15, then retry push.
PR already merged: Skip Steps 1–6. Go directly to Step 7 cleanup.
merge-base --is-ancestor exits non-zero: Main moved while you were waiting for CI. Return to Step 2, re-sync main, re-fetch branch, re-rebase, force-push again, wait for CI, then retry merge.
Detection: git push fails, hook output shows "files were modified by this hook"
Action: Commit the auto-fixed files and retry the push:
git add -A
git commit -m "style: apply pre-push hook auto-fixes"
git push --force-with-lease origin {branch}
This commonly occurs with release-please CHANGELOG.md entries that don't conform to markdownlint rules.
tools
Use when installing or choosing CLI tools in a Nix flake repo, editing flake.nix or home-manager config, or when tempted to pip/pipx/uv/brew/npm install anything. Tools come from the dev shell or nix shell — never ad-hoc package managers.
testing
Use when creating or editing GitHub Actions workflows that call reusable workflows (uses: OWNER/repo/.github/workflows/...) — org owner references must be the literal current org, and shared-CI homes are under dryvist.
development
Use when adding or editing .pre-commit-config.yaml, wiring pre-commit hooks into a repo, scaffolding a new repo's lint/hook setup, or deciding where a hook or shared lint config should live. Covers the canonical nix-devenv/dryvist-.github architecture, profiles, and consumer patterns.
testing
Check PR merge readiness, sync local repo, cleanup stale worktrees; optional cross-repo sweep and stale-branch prune modes