Skip to main content

backport-pr

Backport a merged PR to a maintenance major branch (v10 by default) in getsentry/sentry-javascript. Cherry-picks the PR's squash-merge commit onto the target branch, namespaces the commit/PR title scope (e.g. fix(core) -> fix(v10/core)), opens a draft backport PR, and documents any deviation from the original PR in its body. Use when asked to backport a PR, or port a fix to v10 (or an older major like v9). Trigger phrases include "backport", "port to v10", "release this on v10".

Source facts

Repository
getsentry/sentry-javascript
Last source activity
August 10, 2026 at 08:23
Detected SKILL.md language
English
Stars
8,750
Forks
1,860

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
backport-pr
description
Backport a merged PR to a maintenance major branch (v10 by default) in getsentry/sentry-javascript. Cherry-picks the PR's squash-merge commit onto the target branch, namespaces the commit/PR title scope (e.g. fix(core) -> fix(v10/core)), opens a draft backport PR, and documents any deviation from the original PR in its body. Use when asked to backport a PR, or port a fix to v10 (or an older major like v9). Trigger phrases include "backport", "port to v10", "release this on v10".
argument-hint
<pr-number-or-url> [target-major] # e.g. 18211 v10; target defaults to v10
# Backport a PR to a maintenance major branch `develop` is the current major (v11). A change that also needs to ship on a still-maintained older major has to land on that major's branch too (`v10` by default). This skill cherry-picks a merged `develop` PR onto that branch and opens a draft backport PR. ## Inputs - **PR** (required): the already-merged PR on `develop` to backport, given as either a full GitHub URL or a bare number. `gh pr view` accepts both, so pass whichever the user gave through unchanged; `<PR>` in the commands below is that value. - **Target major** (optional, default `v10`): the maintenance branch to backport onto. Accept `v10`, `10`, `v9`, etc. Normalize to a branch name like `v10`. If no PR is given, ask for it. Do not guess. ## Convention - **Base branch** = the target major branch (`v10`), which must already exist on `origin`. - **Commit + PR title**: keep the original conventional-commit prefix but namespace the scope with the major, e.g. - `fix(core): Fix logs flush starvation` -> `fix(v10/core): Fix logs flush starvation` - `feat(node): Add X` -> `feat(v10/node): Add X` - If the original has no scope (e.g. `fix: ...`), use `fix(v10): ...`. - For a multi-scope title, prefix the whole group once, not each scope: `fix(cloudflare,deno,node): ...` -> `fix(v10/cloudflare,deno,node): ...`. - **PR body** starts with a single line: `Backport of: #<original-pr-number>`. If the backported change deviates from the original in substance, append a `## Differences to the original PR` section listing each deviation and why it was necessary (see step 4). A clean cherry-pick keeps the body at the single line. - **PR is opened as a draft.** - **Working branch**: branch off the target major and give it a descriptive name. - The changes come from the PR's **squash-merge commit** on `develop` (one commit per PR), so a single `git cherry-pick` normally covers the whole PR. ## Steps ### 1. Resolve the PR and target branch ```bash # Fetch PR metadata (title, merge commit, base branch) gh pr view <PR> --json number,title,baseRefName,mergeCommit,state,url ``` Verify: - The PR is **merged** (`state == "MERGED"`). If not, stop and tell the user. - Its `baseRefName` is `develop` (or the expected parent major). If it targeted something else, confirm with the user before continuing. Grab `mergeCommit.oid` — this is the squash commit to cherry-pick. Also grab `number`: use that bare number (not the raw input) wherever `#<PR>` appears below, so `Backport of:` reads `Backport of: #18211` even when the user passed a URL. Make sure the target branch exists and is up to date: ```bash git fetch origin <major> develop git rev-parse --verify origin/<major> # errors if the branch doesn't exist ``` If `origin/<major>` doesn't exist, stop: the maintenance branch hasn't been created yet. Then check whether the change is already on the target. A freshly cut major often still shares history with `develop`, so a recent PR may already be present: ```bash git merge-base --is-ancestor <mergeCommit-oid> origin/<major> && echo "ALREADY ON <major>" ``` If it prints `ALREADY ON`, the commit is in the target's history — usually meaning nothing to backport. It's not conclusive on its own, though: a commit that was later reverted on the maintenance branch still shows as an ancestor. So treat this as a strong signal to stop and tell the user, but if you have reason to think the change was reverted, confirm the fix is actually present (e.g. `git log origin/<major> -- <a changed file>`, or grep for the change) before deciding. The cherry-pick in step 3 is the real backstop — it comes up empty only when the change is genuinely still applied. ### 2. Create the backport branch off the target major ```bash git checkout -b <branch> origin/<major> ``` ### 3. Cherry-pick the merge commit ```bash git cherry-pick <mergeCommit-oid> ``` - If git reports the pick is **empty** ("nothing to commit" / "the previous cherry-pick is now empty"), the change is already on the target. Run `git cherry-pick --abort` and stop — do not force it through with `--allow-empty`. This is the same situation the ancestor check in step 1 guards against, caught here for changes that landed via a different commit. - On **conflicts**: resolve them by consulting the original diff (`git show <oid>`). The target major may lack refactors that landed on `develop`, so adapt the change to the older code rather than force-porting it. Stage the resolved files with `git add -u` (tracked files only, so stray untracked workspace files don't get baked in), then `git cherry-pick --continue`. If the change can't be cleanly adapted, stop and surface the conflict to the user instead of guessing. Keep a running note of every adaptation you make here — which file, what you changed relative to the original, and why the target major needed it. Step 6 turns these notes into the PR body, and reconstructing them after the fact is unreliable. - If the PR was **not** squash-merged (multiple commits, e.g. a merge commit), cherry-pick each relevant commit in order, or use `git cherry-pick -m 1 <merge-oid>` for a merge commit. ### 4. Build and verify Run the repo's pre-commit checks. Do this **before** finalizing the commit in step 5, because `yarn format` writes changes to the working tree — those fixes must end up inside the backport commit, not left dangling after it (otherwise you'd push an unformatted tree and CI would fail on a commit that doesn't match your local state). ```bash yarn format yarn lint:fix yarn build:dev ``` Use `lint:fix`, not `lint` — plain `yarn lint` only reports, so auto-fixable issues would otherwise survive to fail CI. Run tests scoped to the touched packages when possible (full `yarn test` if unsure). If the target major's toolchain differs and a check fails for reasons unrelated to the change, note it for the user rather than silently skipping. ### 5. Finalize the commit (fold in verification changes) First stage the format/lint fixes. Use `git add -u` so only tracked files the cherry-pick and verification touched are staged, not unrelated local edits — sanity-check with `git status` first if `yarn format` may have reformatted files outside the backport. ```bash git add -u ``` Then finalize, depending on how step 3 went: **Single squash commit (the usual case)** — amend HEAD to both namespace the subject scope and fold in the staged fixes. The message is the namespaced title plus the one-line `Backport of:` body (this replaces the squash-merge body, matching the convention above). Do **not** add a `Co-Authored-By` line — the backport commit mirrors an existing commit, not new authored work. Build the title as in the convention: `<prefix>(<major>/<scope>):` when the original had a scope, or `<prefix>(<major>):` when it didn't (never emit an empty `<major>/`). ```bash git commit --amend -m "<namespaced-title>" -m "Backport of: #<PR>" ``` Example subject: `fix(v10/core): Fix logs flush timeout starvation with continuous logging` **Multiple commits (non-squash merge)** — leave the individual commit messages as-is; the namespaced title lives on the PR (step 7), not on each commit. Just fold the staged fixes into HEAD without rewording: ```bash git commit --amend --no-edit ``` Confirm the tree is clean so nothing is left uncommitted before you push: ```bash git status --porcelain # expect no output ``` ### 6. Diff the backport against the original PR Even a cherry-pick that reported no conflicts can end up different — `yarn format`/`lint:fix` on the older toolchain, dropped hunks, adaptations you made in step 3. Compare the final commit against the original before writing the PR body, rather than trusting your memory of step 3: ```bash git show <mergeCommit-oid> --stat git show HEAD --stat # file lists should match diff <(git show <mergeCommit-oid>) <(git show HEAD) ``` Ignore noise that carries no meaning: commit hashes/headers, hunk line offsets, surrounding context lines, and the reworded commit message. What counts as a **difference** is a change in what the patch does — a file added or missing, a hunk dropped or added, changed code or test expectations, an API adapted to the older major, a version/changelog entry that only exists on one branch. If the only differences are noise, the PR body stays the single `Backport of:` line. Otherwise draft a section like: ```markdown Backport of: #<PR> ## Differences to the original PR - `packages/core/src/foo.ts`: used `getClient()` instead of `getCurrentScope().getClient()` — v10 has no `getClient()` on the scope. - Dropped the `bar.test.ts` case for `baz()`; that helper doesn't exist on v10. ``` One bullet per deviation: what changed, and why the target major required it. Keep it factual — this is the reviewer's diff-of-the-diff, not a summary of the original PR. If a difference means the backport doesn't fully deliver the original fix, say so explicitly in the PR body **and** to the user — that's a review decision, not a footnote. ### 7. Push and open the draft PR The `Backport of: #<PR>` line references the original PR, so GitHub cross-links the two automatically — no separate comment needed. ```bash git push -u origin <branch> gh pr create \ --draft \ --base <major> \ --title "<namespaced-title>" \ --body "Backport of: #<PR>" ``` When step 6 produced a differences section, pass the multi-line body via `--body-file` instead (write it to a scratch file first) so the markdown survives intact. ## Notes - Never push directly to `develop`, `master`, or the major branch. Work only on your backport branch and open a PR. - One PR per backport. If asked to backport several PRs, repeat the whole flow per PR (each gets its own branch and draft PR). - If asked to backport to multiple majors at once (e.g. v10 and v9), do them as separate branches/PRs, each based off its own `origin/<major>`.
View on GitHub