Submit a diverged-fork commit to upstream as a clean PR via cherry-pick with re-derive fallback, message scrubbing, and regression checks. Use when direct rebase fails.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Submit a diverged-fork commit to upstream as a clean PR via cherry-pick with re-derive fallback, message scrubbing, and regression checks. Use when direct rebase fails.
Submit a single commit from a heavily-diverged fork back to upstream as a clean, regression-free PR. The simpler /git:upstream-pr covers aligned-fork cherry-picks; this skill handles the case where direct rebase fails.
When to Use This Skill
Use this skill when...
Use /git:upstream-pr instead when...
Fork has substantially diverged from upstream
Fork and upstream are roughly aligned
Cherry-pick may produce many conflicts on shared modules
Single commit applies cleanly
You need patch-id matching for already-applied content
You want a quick cherry-pick + cross-fork PR
The change touches files that may be fork-only
All touched files exist upstream
The PR needs commit-message scrubbing (fork issue refs, Claude trailers)
Commit messages are already upstream-clean
Pre-flight regression check against upstream baseline matters
The change is trivial enough to skip pre-flight
Configuration
Per-project configuration lives at .claude/upstream-pr.local.md (gitignored). All fields are optional; sensible defaults apply.
---
upstream_remote: upstream
upstream_repo: owner/repo
branch_prefix: pr-upstream/
linter_cmd: uv run ruff check
test_cmd: uv run pytest -q
pr_body_template_path: docs/UPSTREAM_PR_TEMPLATE.md
---
# Notes
Free-form notes โ fork drift hotspots, files to never touch upstream, etc.
Creates <branch_prefix><topic-slug> from <upstream_remote>/main (or /master)
Cherry-picks <sha>
Reports any conflict files
Step 3: Resolve conflicts (if any)
When the cherry-pick produces conflicts, preserve upstream's surrounding shape, not the fork's. The goal is the smallest readable diff against upstream โ a maintainer must see the change make sense in upstream's current code, not in the fork's.
When to abort and re-derive
If the cherry-pick produces dozens of conflict blocks across multiple files (typical when upstream and fork have drifted heavily on shared modules), abort and re-derive instead of fighting hunks:
git cherry-pick --abort
git checkout -b <branch> <upstream_remote>/main
# Re-apply the change against upstream's actual current files.
Re-derive is the right call when:
The original commit is a mechanical, re-applicable transform (e.g. print() โ logging, deprecation rename, lint-rule auto-fixes) โ the rules transfer cleanly even if line numbers don't.
Upstream's version of the file has additional lines the fork removed โ re-derive lets you cover them with the same heuristic rather than rationalizing missing hunks.
The cherry-pick conflict count exceeds roughly 20 blocks across >2 files.
Heuristic: extract the original commit's before โ after map (e.g. git show <sha> | grep -E '^[-+].*pattern') and use it as the rulebook when re-applying against upstream. The PR body should disclose the re-derive (see template below).
Step 4: Pre-flight regression check
For refactor / cleanup PRs, verify lint and test parity against the pristine upstream baseline, not against the fork. This catches stray formatter touches, accidental import reordering, and indentation drift from Edit-tool replacements.
If linter_cmd and test_cmd are configured, run a stash roundtrip:
# Baseline (pristine upstream):
git stash push -- <changed-files>
<linter_cmd> <changed-files> 2>&1 | tail -1 # error count
<test_cmd> 2>&1 | tail -1 # pass count
git stash pop
# After (with your changes): run the same two commands and compare.
Both numbers must match (or improve). Quote both in the PR body's "Testing Performed" section.
For Python files, also run python3 -c "import ast; ast.parse(open(F).read())" on every edited file โ catches indentation breaks that ruff might miss on already-warning-laden upstream code.
Strip local issue references โ Closes #74, Addresses #19, etc. point at the fork's tracker, not upstream's.
Strip Claude trailers โ Co-authored-by: Claude ..., Generated with [Claude Code]. Upstream doesn't follow that convention.
Soften fork-specific tooling โ if the body cites a tool upstream doesn't run (bandit B607, ty, vulture), describe the underlying problem instead.
Keep the conventional-commit prefix โ fix(security):, feat(parser):, etc.
The amend wraps git commit --amend with PRE_COMMIT_ALLOW_NO_CONFIG=1 because upstream may have no .pre-commit-config.yaml and a locally-installed pre-commit hook would otherwise refuse the commit.
Step 6: Verify diff hygiene
Before pushing, verify with:
git diff <upstream_remote>/main..HEAD
Every hunk should be defensible to a maintainer who has never seen the fork.
Don't bundle formatter cleanups with the fix. Keep upstream's existing import order even if the fork's linter would reformat. Upstream may have unusual indentation (e.g. 21-space rather than 20-space blocks) โ preserve it; Edit-tool replacements that change leading whitespace by even one character will break the parse.
One commit per PR. If the cherry-pick produced multiple commits, squash before pushing.
If pr_body_template_path is configured, use that template; otherwise use the built-in template:
## Description<oneortwoparagraphsexplainingtheproblemandthefixfromthemaintainer'sperspective โ notfromthefork'sperspective>## Related Issue(s)
None. <orupstreamissuenumbersonly>## Type of Change- [x] Bug fix (non-breaking change that fixes an issue)
## Testing Performed- [x] `<linter_cmd> <changed-files>` โ N issues (matches upstream baseline)
- [x] `<test_cmd>` โ M passed (matches upstream baseline)
## Notes
Originally authored on a downstream fork; the branch was cut from
`<upstream_repo>:main` and a single commit cherry-picked onto it so
it applies cleanly.
<Ifre-derived: "Thefork'sversionofthischangecollidedheavilywithupstream'scurrentcode; thediffwasre-derivedagainstupstream'sfilesusingtheoriginalcommit'stransformationrules.">