mit einem Klick
backport-failed
// Create a manual backport to recover from a Patchback auto-backport failure.
// Create a manual backport to recover from a Patchback auto-backport failure.
| name | backport-failed |
| description | Create a manual backport to recover from a Patchback auto-backport failure. |
When patchback (the auto-backport bot used by aio-libs and similar orgs) fails to cherry-pick a merged PR onto a stable branch, it leaves a comment like:
Backport to 3.13: 💔 cherry-picking failed — conflicts found
❌ Failed to cleanly apply
<sha>on top ofpatchback/backports/3.13/<sha>/pr-<num>
This skill produces the manual backport PRs that should have been created, matching patchback's exact shape so the PR list stays uniform.
Arguments passed: $ARGUMENTS — usually a PR number (e.g. 12581). If empty, ask the user for it via AskUserQuestion.
Every backport PR this skill opens must match this shape (study PRs authored by patchback[bot] — e.g. aio-libs/aiohttp#12574 — for the canonical form):
[PR #<orig_num>/<short_sha> backport][<branch>] <original PR title>
<short_sha> is the first 8 chars of the merge commit on master<branch> is the target branch, e.g. 3.13, 3.14**This is a backport of PR #<orig_num> as merged into master (<full_sha>).**3.13, 3.14, etc.)patchback/backports/<branch>/<full_sha>/pr-<orig_num>Do not add a "Drafted with Claude Code" footer, do not add Co-Authored-By, do not edit or summarize the original body. The goal is byte-for-byte indistinguishability from a successful patchback PR.
If $ARGUMENTS is empty, ask the user for the PR number with AskUserQuestion. Then in parallel:
gh pr view <num> --repo <owner/repo> --json number,title,body,mergeCommit,headRepositoryOwner,baseRefName,labels,url
gh pr view <num> --repo <owner/repo> --comments
Detect the repo from the working directory's upstream remote (git remote get-url upstream). If there is no upstream remote, ask the user which remote points at the canonical repo.
Extract:
orig_num — the PR numberorig_title — the PR title (use as-is, do not modify)orig_body — the PR body (use as-is, do not modify, do not strip HTML comments)full_sha — the merge commit SHA on master (from mergeCommit.oid)short_sha — first 8 chars of full_shaFrom the comments output, find each patchback comment that says "💔 cherry-picking failed — conflicts found" and extract the target branch from the heading (Backport to <branch>:).
Sanity check against PR labels: patchback is driven by labels like backport-3.13, backport-3.14. The set of failed branches should be a subset of those labels. If a backport label has no patchback failure comment and no existing backport PR, that target probably succeeded automatically — skip it.
To confirm a branch genuinely needs a manual backport, check whether a successful backport PR already exists:
gh pr list --repo <owner/repo> --search "[PR #<orig_num>/<short_sha> backport]" --state all
If a PR with the expected title already exists for that branch, skip it.
git status --porcelain
If there are staged/unstaged changes to tracked files, stop and tell the user. Untracked files are fine — patchback's workflow doesn't touch them.
Remember the current branch so you can return to it at the end:
git rev-parse --abbrev-ref HEAD
Run these steps sequentially per branch — never parallelize across target branches (they share the working tree).
git fetch upstream
git checkout -b patchback/backports/<branch>/<full_sha>/pr-<orig_num> upstream/<branch>
git cherry-pick -x <full_sha>
If the commit is a merge commit (git cat-file -p <full_sha> | grep -c '^parent ' returns > 1), use git cherry-pick -m1 -x <full_sha> instead. For squash-merged PRs (the common case on aio-libs) it's a single-parent commit, so plain -x works.
If cherry-pick reports conflicts:
git status to list conflicted files.<<<<<<</=======/>>>>>>> markers, and resolve.CHANGES/*.rst) the file usually doesn't exist on the stable branch yet — just take the master version.git add <file> for each resolved file.git cherry-pick --continue (use a non-interactive editor: GIT_EDITOR=true git cherry-pick --continue to keep the cherry-pick's default message which already contains the (cherry picked from commit ...) trailer from -x).The user's fork remote is conventionally origin for aio-libs contributors (or named after their GitHub handle). Detect it:
git remote -v | grep -E "(fetch)" | grep -v upstream
If multiple non-upstream remotes exist, prefer one matching the GitHub user from gh api user --jq .login. Ask the user if ambiguous.
git push <fork_remote> patchback/backports/<branch>/<full_sha>/pr-<orig_num>
Use a HEREDOC so the body is preserved exactly. The body of the new PR is the original PR's body, prepended with the **This is a backport...** line and a blank line:
gh pr create \
--repo <owner/repo> \
--base <branch> \
--head <fork_owner>:patchback/backports/<branch>/<full_sha>/pr-<orig_num> \
--title "[PR #<orig_num>/<short_sha> backport][<branch>] <orig_title>" \
--body "$(cat <<'EOF'
**This is a backport of PR #<orig_num> as merged into master (<full_sha>).**
<orig_body verbatim>
EOF
)"
If <orig_body> contains an EOF marker itself (rare), pick a different HEREDOC delimiter (e.g. PATCHBACK_BODY).
Report the new PR URL.
After all backports are done (or after stopping for user input):
git checkout <original_branch>
Leave the patchback branches in place locally — the user may want to amend them.
At the end, print a short summary:
skipped (already exists), or paused for user (conflict in <file>)Keep it terse — one line per branch.
-m1 as noted above.git push --force-with-lease only if the user confirms (AskUserQuestion). Never --force blindly.gh api repos/<owner>/<repo>/branches/<branch> 404s, tell the user — the backport label may be stale.