microsoft/backport
Backport changes (current branch, a PR, a branch, or specific commits/SHAs) onto a target branch (typically a release branch like `rel/*`). Use when the user says "backport this", "backport PR ...", "cherry-pick to <branch>", or asks to port commits to another branch. Handles stashing uncommitted work, branch creation, cherry-picking with conflict handling, pushing, and opening a PR.
npx skills add https://github.com/microsoft/vscode-cosmosdb --skill backport
Create a backport pull request that applies changes from a source (current branch, a PR, another branch, or specific commits) onto a target branch — interactively, from the local workspace. The target is typically a release branch (e.g. rel/0.32), but any existing branch is allowed except the source's own base branch (a backport onto the same base is a no-op).
Before running any git commands, determine:
origin. Typical case is a release branch (rel/*). If the user did not specify, list candidates with git branch -r --list 'origin/rel/*' first; if none match or the user wants something else, fall back to git branch -r and ask. Refuse the source's own base branch (e.g. if the PR or current branch already targets main, refuse main; if it targets rel/0.32, refuse rel/0.32) — a backport onto the same base is a no-op. Verify the target exists on origin before proceeding.If anything is ambiguous, ask once before touching the working tree.
Execute these phases in order. Stop and report on any error.
git --version, gh --version, gh auth status. If gh is missing or unauthenticated, abort with a clear message — do not continue. Cloud-agent override: when running as the GitHub.com cloud agent (see "Running as the GitHub cloud agent" below), a failing gh auth status must not abort the backport — the git work uses the platform's credential helper, not gh. See that section.git rev-parse --abbrev-ref HEAD), git status --porcelain, list of unpushed commits.git stash push -u -m "backport-skill autostash <ISO-timestamp>".git stash list -1 --format=%gd) so it can be restored later.git fetch origin <target>. Abort if the target branch does not exist on origin.gh pr view <num> --json number,title,body,headRefName,baseRefName,mergeCommit,commits,state. If the command fails (non-zero exit, PR not found, or insufficient permissions), abort with a clear error message showing the PR number and the gh error output. Then branch on state:MERGED: prefer the squash-merge commit if the PR was squash-merged; otherwise use the listed commits in order.OPEN: warn that backporting an unmerged PR may include incomplete or intermediate work, and confirm with the user before proceeding. Use the listed commits in order.CLOSED (not merged): legitimate but unusual. Warn the user that the PR was closed without merging (the work may have been abandoned, superseded, or rewritten elsewhere) and confirm before proceeding. Use the listed commits in order, or offer squash-and-reapply if the user prefers a single commit.git log --reverse --format=%H origin/<target>..<branch>.git cat-file -e <sha>).Show the resolved list (count + short log) to the user and confirm before continuing.
backport/<id>-to-<target-slug> where:backport/ for local runs (the default — this skill running in a VS Code workspace). Use the copilot/ prefix only when running as the GitHub.com cloud agent; see "Running as the GitHub cloud agent" below. If you are executing git commands on the user's local machine, you are *not* the cloud agent — use backport/.<id> is the PR number (PR source), the source branch's last segment, or <short-sha> (single-commit / range).<target-slug> is the target branch with every / replaced by -. Compute it, don't hand-write it (e.g. in shell: target_slug="${target//\//-}"). Examples: rel/0.32 → rel-0.32, rel/0.34 → rel-0.34.rel/0.34 gives branch backport/3036-to-rel-0.34 (correct), not backport/3036-to-rel/0.34 (wrong — the / from the target was left in)./ (the separator right after backport). If it contains two, the slug wasn't applied — recompute it before creating the branch.origin/<branch> exists): ask the user — *overwrite* (delete local + remote, recreate) or *use a numeric suffix* (-2, -3, …). Never silently overwrite.git checkout -b <name> origin/<target>.git merge --squash <source> then git commit -m "Backport #<n>: <original-title>" (or a synthesized title for non-PR sources).git cherry-pick <sha…> in order.Conflict policy (relaxed but safe):
> Cloud-agent override: when running inside the GitHub Copilot cloud agent (see "Running as the GitHub cloud agent" below), use the stricter policy: auto-resolve only trivial cases per step 2; for anything ambiguous, do not invent a resolution — commit the conflict markers as a WIP: commit and mark the PR as draft. Skip steps 3 and 4 (no interactive prompts, no squash-and-retry).
git status and git diff to inspect.import / require statements differs; identifiers are identical.After resolving, verify with git diff --check and ! grep -R '<<<<<<<' -- . before staging.
git add + git cherry-pick --continue.git cherry-pick --abort, delete the backport branch, restore (Phase G).Cherry-picks onto older release branches often produce code that compiles on the source's base but breaks on the target (different deps, removed APIs, stricter lint config). Catch this before pushing:
origin/<target> and may have been mutated by the cherry-pick, node_modules is almost certainly stale — always start with npm install. Then run, in this order: npm run build, npm run l10n, npm run prettier-fix, npm run lint. Always run npm run l10n regardless of whether the cherry-pick obviously touches user-facing strings: dependency bumps can alter embedded error messages, and the target release branch may carry pre-existing l10n drift that the CI l10n:check will fail on. If the bundle changes, commit the regenerated bundle as a separate chore: regenerate l10n bundle commit on the backport branch before the Phase F push.l10n/bundle.l10n.json (English bundle), and the source PR is merged. After the original PR merged, a localization bot typically commits translated strings to the source's base (e.g. main) — those translations apply equally to the backport and should not be re-translated by hand. Pull only the affected keys, never wholesale-replace language files: the source base may have other strings the target branch must not gain. Procedure:git show origin/<target>:l10n/bundle.l10n.json and the current l10n/bundle.l10n.json. Record:npm run l10n).package.nls.json (its translations live in package.nls.<lang>.json).l10n/bundle.l10n.<lang>.json and package.nls.<lang>.json, every <lang> present in the repo), read the source-base version with git show origin/<source-base>:<file>, then for each added/modified key copy that key's translated value into the local language file. Leave all other keys in the local file untouched. Preserve the existing line endings and key order of the local file: translation pipelines often write CRLF and use a non-obvious collation order, and re-sorting or re-serializing produces a massive cosmetic diff that reviewers will reject. Insert each new key adjacent to the nearest preceding key (in source-base order) that already exists locally; serialize with JSON.stringify(obj, null, 2), then convert \n back to \r\n if the local file used CRLF. If the source-base version is missing a key (translation bot hasn't run yet), skip it — npm run l10n will leave the English fallback.npm run l10n to refresh the English bundle (the language files are not modified by this script in current builds; it only normalizes l10n/bundle.l10n.json).git add l10n/bundle.l10n.*.json package.nls.*.json) and commit as chore: pull updated translations from <source-base>. Do not stage l10n/bundle.l10n.json or package.nls.json here — those belong to the earlier chore: regenerate l10n bundle commit.git push -u origin <branch> — never --force or --force-with-lease. If the push fails, surface the full git error. If it is a permission or branch-protection error (e.g. protected-branch hook, missing write access), advise the user to check repository settings; do not retry with force flags. Proceed to Phase G failure cleanup.--body-file. Prefer the editor's file-creation tool (cross-platform); if you use a shell heredoc, write to the OS temp directory outside the repo so it can't be accidentally staged or committed — e.g. "${TMPDIR:-/tmp}/backport-body.md" on macOS/Linux or "$env:TEMP\backport-body.md" on Windows PowerShell — and delete it once the PR is created. Then run gh pr create --base <target> --head <branch> --title "[<target>] <original-title>" --body-file <path>. Always use --body-file; never --body "…". An inline multi-line body gets mangled — the shell eats backticks/$/quotes and any literal \n sequences are printed verbatim in the PR description. Authoring rules for the body file:\n to mean a newline..md files in a repo) GitHub honors a single newline as a hard line break, so column-wrapped prose shows up broken mid-sentence; hard-wrapping also makes the body diff noisily and read awkwardly in plaintext previews. If you reuse the original PR description, re-flow it first — strip any existing column wrapping so each paragraph is one continuous line.- [ ] / - [x]). GitHub turns them into a "N tasks done / Progress 100%" bar on the PR list, and pre-checked boxes falsely imply a reviewer checklist is already complete. Use plain - bullets for the validation summary and everything else.[rel/0.34] Fix tree refresh race. Use the exact target branch name (including any rel/ prefix) and keep the rest identical to the original PR title (or first commit subject for non-PR sources).Backport of #<n> (or Backport of <branch> / SHA list for non-PR sources); the original PR description (when applicable); a Conflicts resolved section listing each file + a one-line description, when applicable.gh pr view --web to open it in the browser.git checkout <originalBranch>. If a stash was created in A.3, git stash pop <stashRef>. Print the new PR URL.git checkout <originalBranch> && git stash pop <stashRef>).origin is allowed. main is allowed only when it is not the source's base; if the source already targets main, refuse main per the rule above.--force / --force-with-lease.backport/ for local runs. Use the copilot/ prefix only when running as the GitHub.com cloud agent (it cannot push other prefixes). Never use copilot/ for a local run./ in the target with - when building the branch name; the final name has exactly one / (right after backport/copilot). Compute the slug, don't hand-copy the target branch.--body-file, never inline --body. No literal \n, no hard-wrapped sentences, no task-list checkboxes (- [ ] / - [x]).When done, summarize:
When this skill runs inside the GitHub Copilot cloud agent (e.g. invoked by @copilot on a merged PR or assigned to a backport issue), the environment differs from a local VS Code workspace. Adjust the workflow as follows:
gh auth status failure (Phase A step 1 override). In the cloud agent, git push auth comes from the platform's credential helper and the PR is opened by the platform — neither depends on gh. A failing gh auth status (e.g. GITHUB_TOKEN reported invalid) must not stop the backport. Proceed with all git work (fetch, branch, cherry-pick, push); if the token really is unusable, git push will fail later with its own clear error — that is strictly better than abandoning a completed cherry-pick at pre-flight. Only PR-metadata commands (gh pr edit) need gh: if it is unauthenticated, first retry once as GH_TOKEN="$GITHUB_TOKEN" gh pr edit …; if that still fails, set the base/title/body via the REST API (gh api -X PATCH repos/{owner}/{repo}/pulls/{number} -f base=<target> -f title="[<target>] …", or curl with the token), and if even that is impossible, leave the PR open and state explicitly in the body which fields — especially the base branch — still need to be set manually so it does not silently target main.copilot/backport-<id>-to-<target-slug> instead of backport/.... This copilot/ prefix applies only here, in the GitHub.com cloud agent, because the cloud agent can only push branches starting with copilot/; a local run must use the backport/ prefix from Phase C. The <target-slug> rule is unchanged — replace every / with -, so rel/0.34 yields copilot/backport-<id>-to-rel-0.34 (one / only, right after copilot).WIP: and mark the PR draft instead of asking, aborting, or squash-and-retry).npm install / build / lint to save time and avoid burning Actions minutes.gh pr create. The cloud agent platform opens the PR for the task automatically. Write the body to a file and use gh pr edit --base <target> --title "[<target>] <original-title>" --body-file <path> to set the base branch, title, and body — never inline --body "…", and follow the same Phase F body-authoring rules (real newlines, no literal \n, no hard-wrapped sentences, no task-list checkboxes). The title must be prefixed with the target branch in square brackets — e.g. [rel/0.34] <original-title>. When conflicts remain unresolved, mark the PR as draft (the platform may open it ready by default; use gh pr ready --undo if available, otherwise note the WIP state explicitly in the PR body).All other constraints (no --force, refuse the source's own base, preserve commit messages, never silently overwrite existing branches) apply unchanged.
Take microsoft/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.
The instructions reference npm.
Without those the skill loads but fails at the first command.