| name | cherry-pick-prod |
| description | Use when a specific fix that's already on main needs to land on a production/release branch without dragging along everything else — a hotfix to backport, a "cherry-pick this commit onto release-2.4", a "we need just that one PR on prod" request. Reach for this whenever someone wants to port one or a few commits to a release branch and open a PR for it, especially before doing it by hand in their main checkout, which pollutes their working tree and routinely leaves conflict markers committed or loses the original commit's provenance. |
Cherry-pick to Prod
Overview
Backporting a fix to a release branch is a small operation with sharp edges. Done
in your normal checkout it stomps your working tree, mixes the backport with
whatever you had in progress, and — if a conflict shows up — tempts you to commit
a half-resolved file with <<<<<<< markers still in it. And a cherry-pick that
drops the -x provenance line leaves the release branch with a commit nobody can
trace back to its origin on main.
This skill does the backport in an isolated git worktree so your main checkout
is never touched, cherry-picks with -x so every backported commit records
the SHA it came from, pushes the branch, and opens a PR from a template that
states what's being backported, why, and the source commits. If a conflict can't
be resolved cleanly it aborts and cleans up the worktree rather than leaving a
broken tree or stray markers behind. On success or failure, the temporary
worktree is removed — it never lingers.
The discipline: the operation is isolated, traceable, and self-cleaning. You
should be able to run it, get a PR link, and have your own checkout exactly as you
left it.
When to Use
Reach for this when:
- A fix is merged to
main and needs to ride to a release/production branch
(release-*, prod, hotfix/*) as its own PR.
- You have one or several specific commit SHAs to port and want them applied in
order with provenance, not a whole-branch merge.
- A hotfix needs to go out on the release line now, separately from the next
full release train.
- You want the backport opened as a reviewable PR with a filled-in template, not
pushed straight to the release branch.
Do NOT use this when:
- You want everything since the last release on the branch — that's a merge or
a release cut, not a cherry-pick. Cherry-picking a long range one commit at a
time is the wrong tool.
- The commit isn't on
main yet (still an open PR). Land it first; cherry-pick
picks existing commits.
- The change needs to be different on the release branch (different API, an older
dependency). That's a fresh adapted commit authored against the release branch,
not a clean cherry-pick — the diffs won't apply.
- You're backporting to many release branches at once — loop this per branch, but
review each PR; a conflict on one branch doesn't mean the resolution is right on
another.
Running it
cd .claude/skills/cherry-pick-prod
./scripts/cherry_pick.sh --onto release-2.4 a1b2c3d 9f8e7d6
./scripts/cherry_pick.sh --onto release-2.4 --pr 4821 --pr 4830
What it does, in order:
- Creates an isolated worktree off the target branch under a temp dir (your main
checkout is untouched).
- Cherry-picks each commit with
-x (records (cherry picked from commit …)).
- On a clean pick: pushes the new branch and opens a PR using
assets/pr_template.md, substituting the source commits, target branch, and
summary.
- On a conflict it can't trivially resolve: aborts the cherry-pick, tears down
the worktree, and reports exactly which commit conflicted so you can backport
it by hand.
The PR URL is printed on success; exit code is non-zero on any conflict or
failure so it composes into automation.
Why a worktree (and not just a branch)
git worktree add checks out the release branch into a separate directory with
its own working tree and index, sharing the same .git object store. That means:
- Your current checkout's working tree, staged changes, and current branch are
completely unaffected — no stash dance, no "please commit or stash before
switching".
- The cherry-pick, conflict resolution, and push all happen in that scratch
directory, which is deleted afterward. Nothing leaks into your main checkout.
- If anything goes wrong mid-operation, removing the worktree cleans up the entire
attempt atomically — there's no half-switched branch state to recover from.
Gotchas
ALWAYS treat these as real failure modes — each has produced a bad backport or a
messed-up checkout for someone.
- Always work in an isolated worktree, never your main checkout. Cherry-picking
in your day-to-day checkout means a conflict leaves your tree in a
conflicted state, mixes the backport with your in-progress work, and forces a
stash dance. The worktree isolates the whole operation; your checkout stays
exactly as you left it. This is the non-negotiable core of the skill.
- Cherry-pick with
-x for provenance. Without -x, the backported commit on
the release branch has no record of where it came from. Six months later,
someone diffing prod against main can't tell whether a commit is a backport or a
divergence. -x appends (cherry picked from commit <sha>) to the message —
it's the only trace linking the two. Never drop it.
- Never commit conflict markers. A cherry-pick that hits a conflict and gets
git add-ed without actually resolving it will happily commit a file with
<<<<<<<, =======, >>>>>>> still in it — which then builds in some
languages and ships broken. The script refuses to continue a cherry-pick while
the tree still contains markers; if it can't cleanly resolve, it aborts rather
than commit garbage.
- Clean up the worktree on failure, not just success. A failed run that leaves
a worktree behind means the next run collides on the same path, and
git worktree list slowly fills with corpses. Teardown belongs in a trap that fires
on any exit — success, conflict, or interruption — so a worktree never
lingers. If the directory is already gone, teardown is a no-op.
- Apply commits in the right order. Cherry-picking SHAs out of their original
order can fail or, worse, apply cleanly into a subtly wrong state when later
commits depended on earlier ones. Pass them in the order they landed on
main;
the script applies them in the order given and stops at the first conflict
rather than barreling on.
- A clean cherry-pick is not a correct one. The diff applying without conflict
doesn't mean the fix is right on the release branch — surrounding code may have
diverged so the patch lands in the wrong place semantically. That's why this
opens a PR for review instead of pushing to the branch: the backport still needs
human eyes and the release branch's CI before it merges.
Files
scripts/cherry_pick.sh — the backport driver. Adds an isolated git worktree
off the target branch, cherry-picks each commit (or PR merge commit) with -x,
refuses to commit conflict markers and aborts cleanly on unresolvable conflict,
pushes the branch, opens a PR via gh pr create filled from the template, and
tears down the worktree in a trap on every exit. Referenced by Running it.
assets/pr_template.md — the backport PR body. States the target branch, the
source commits/PRs with their SHAs, a summary of what's being backported and
why, and a verification checklist. Substituted into the PR by the script.