coinbase/git.backport
Back-port a specific commit from master to a release branch via cherry-pick. Creates a dedicated backport branch, attempts the cherry-pick, pushes it, and opens a PR by default. Returns to the original branch when done (success or failure). If there are merge conflicts, diagnoses the root cause without attempting an autonomous resolution. Use when asked to "backport", "cherry-pick to release", or "port a fix to a release branch".
npx skills add https://github.com/coinbase/cds --skill git.backport
Back-port the commit given in $ARGUMENTS to a release branch using git cherry-pick. Your arguments are in the format <commit-sha> <target-branch>.
Parse $ARGUMENTS now: the first token is COMMIT_SHA, the second is TARGET_BRANCH.
Before touching anything, capture where the user currently is so you can return them there at the end:
git rev-parse --abbrev-ref HEAD
Store this as ORIGINAL_BRANCH. You will check out this branch at the very end, regardless of whether the backport succeeds or fails.
git fetch origin
COMMIT_SHA resolves using the remote ref (do NOT check it out locally): git rev-parse --verify <COMMIT_SHA>^{commit}
If it still fails after fetching, stop and tell the user the SHA could not be resolved. Checkout ORIGINAL_BRANCH before stopping.
origin/<TARGET_BRANCH> exists on the remote: git rev-parse --verify origin/<TARGET_BRANCH>
Always use the remote ref (origin/<TARGET_BRANCH>) as the source of truth — do not rely on a local checkout of the target branch. If the remote ref does not exist, stop and tell the user. Checkout ORIGINAL_BRANCH before stopping.
git status --porcelain). If it is not clean, stop and tell the user to stash or commit their in-progress work before proceeding. Do NOT checkout ORIGINAL_BRANCH in this case (they're already on it and have local changes).Show the user what they are about to cherry-pick. Use the remote ref for all inspection — never check out the source commit locally:
git show --stat <COMMIT_SHA>
Print:
Extract the repo's GitHub URL from the remote:
git remote get-url origin
Convert SSH form ([email protected]:org/repo.git) or HTTPS form to a base URL: https://github.com/org/repo.
Build two links:
https://github.com/org/repo/commit/<COMMIT_SHA>(#NNN) pattern in the commit subject line. If found, build https://github.com/org/repo/pull/NNN. If the pattern is absent, omit the PR link and note that it could not be determined automatically.Derive SHORT_SHA = first 8 chars of COMMIT_SHA. Check the backport branch does not already exist:
git rev-parse --verify backport/<SHORT_SHA>-to-<TARGET_BRANCH>
If it exists locally or on origin, stop and tell the user rather than overwriting it. Checkout ORIGINAL_BRANCH before stopping.
Create the backport branch directly from the remote ref (no local checkout of target branch needed):
git checkout -b backport/<SHORT_SHA>-to-<TARGET_BRANCH> origin/<TARGET_BRANCH>
Tell the user the branch name.
git show --name-only <COMMIT_SHA>
Separate all changed files into two buckets:
/package.json or /CHANGELOG.mdIf the commit only touches versioning files, stop and tell the user — there is nothing meaningful to backport. Return to ORIGINAL_BRANCH before stopping.
Extract and apply only the source-file diffs as a patch against the current TARGET_BRANCH state:
git show <COMMIT_SHA> -- <source-file1> <source-file2> ... | git apply --index
CRITICAL: Never use git checkout <COMMIT_SHA> -- <file>. That replaces the entire file with the master version, bringing in all unrelated changes that have accumulated between the two branches. Always use git show | git apply --index so only the diff lines from the commit are applied.
git apply succeeds — commit and proceed to Step 6git apply --index stages the changes automatically. Commit:
git commit -m "<original commit subject> (backport to <TARGET_BRANCH>)
Backport of <commit-link> from master[, originally merged via <pr-link>].
Code-only backport — versioning and changelog applied separately.
Generated with Claude Code
Co-Authored-By: Claude <[email protected]>"
Then proceed to Step 6 (versioning reminder).
git apply fails — CONFLICT PATHThe source-file patch does not apply cleanly. This usually means one of:
TARGET_BRANCH has a structurally different version of the code that the patch's context lines no longer matchmaster but not in TARGET_BRANCH — making the backport potentially impossible without additional workDo NOT attempt to modify files or force the patch. Return to ORIGINAL_BRANCH and report (see Step 7).
Before pushing or opening a PR, tell the user:
> The code changes have been committed. You'll need to add the version bump and changelog entry yourself before I open the PR, since the versioning files (package.json, CHANGELOG.md) are intentionally excluded from the backport.
>
> Run the changelog script with the release branch as the base so it detects your changes correctly:
>
> `
> GITHUB_BASE_REF=origin/<TARGET_BRANCH> yarn changelog
> `
>
> Let me know when you're done and I'll push the branch and open the PR.
Then stop and wait for the user to confirm versioning is complete before continuing.
Once the user confirms:
git push -u origin backport/<SHORT_SHA>-to-<TARGET_BRANCH>
## Summary
Backport of commit [`<SHORT_SHA>`](commit-link) from `master`[, originally merged via [#NNN](pr-link)].
<one or two plain-language bullet points summarising what the commit does, derived from the commit message and changed files — do not just copy the commit message verbatim>
## Test plan
- [ ] Verify no CI steps in the `<TARGET_BRANCH>` pipeline depend on anything removed or changed by this commit
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Omit the "originally merged via" clause if the original PR number could not be determined.
gh pr create \
--base <TARGET_BRANCH> \
--head backport/<SHORT_SHA>-to-<TARGET_BRANCH> \
--title "<original commit subject> (backport to <TARGET_BRANCH>)" \
--body "<body from 6b>"
If gh pr create fails due to missing authentication:
gh is not authenticated for this hostgit push echoed (looks like https://github.com/org/repo/pull/new/<branch>) so they can open the PR manuallygit checkout <ORIGINAL_BRANCH>
Then report to the user:
git rev-parse backport/<SHORT_SHA>-to-<TARGET_BRANCH>)gh was not authenticated)<ORIGINAL_BRANCH>Your goal here is to explain _why_ the patch didn't apply so the user can resolve it themselves. Do NOT attempt to modify any files or retry the apply. Use remote refs for all file inspection — never check out anything locally.
git show --name-only <COMMIT_SHA>
Collect the list of files the commit touches.
git merge-base <COMMIT_SHA> origin/<TARGET_BRANCH>
This gives you MERGE_BASE.
For each file in the cherry-picked commit, inspect using remote refs only:
git show <COMMIT_SHA> -- <file>
git show origin/<TARGET_BRANCH>:<file>
git show <MERGE_BASE>:<file>
git diff <MERGE_BASE>..origin/<TARGET_BRANCH> -- <file>
Determine the most likely cause:
| Cause | Signs |
| --------------------------------- | -------------------------------------------------------------------------------- |
| Code deleted on TARGET_BRANCH | The lines the commit modifies no longer exist in the target file |
| Code moved or refactored | The lines exist but in a different function, class, or file |
| Conflicting parallel change | TARGET_BRANCH already modified the same lines differently |
| File renamed or deleted | The file does not exist on TARGET_BRANCH at all |
| API / import change | The commit references a symbol that was renamed or removed on the release branch |
git checkout <ORIGINAL_BRANCH>
Then write the conflict report:
## Backport patch failed
Patch from <COMMIT_SHA> did not apply cleanly onto <TARGET_BRANCH>.
You are back on `<ORIGINAL_BRANCH>`.
### Conflicting files
<list each file>
### Diagnosis
For each file:
- What the cherry-picked commit was trying to do to this file
- What the current state of this file is on TARGET_BRANCH
- Why those two things conflict (pick the most precise cause from the table above)
- A concrete suggested approach for manual resolution (e.g., "the function was renamed from X to Y on release-8.x — apply the logic change to the renamed function")
Be specific. Quote relevant line ranges or symbol names. Give the user enough context to know exactly where to look and what to do.
ORIGINAL_BRANCH at the very start and always return to it at the end — success, failure, or early stop (except when stopping due to a dirty working tree, since you haven't moved).git checkout <sha> -- <file> to apply changes from a commit. This replaces the whole file with the master version and brings in unrelated changes. Always use git show <sha> -- <files> | git apply --index.package.json or CHANGELOG.md changes. Always exclude versioning files from the patch. Remind the user to run GITHUB_BASE_REF=origin/<TARGET_BRANCH> yarn changelog and wait for them to confirm before pushing or opening the PR.git apply fails, diagnose and explain only.origin/<TARGET_BRANCH> and inspect commits via git show / git diff. The only local branches you should create or switch to are the backport branch and the return to ORIGINAL_BRANCH.ORIGINAL_BRANCH rather than overwriting.Take coinbase/git.backport from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.