Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

eclipse-ecal/cherry-pick-to

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace
Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cherry Pick To

A reusable GitHub Action that automatically cherry-picks merged pull requests onto release / support branches, driven by labels on the pull request.

Why this action?

  • From pull request to pull request. Label a PR cherry-pick-to-<branch>, and when it merges (squash, rebase, or merge commit — see Merge strategies) a review-ready PR appears on each target branch. The action never commits or merges to your release branches directly — every backport goes through the same review and merge process as any other change.
  • A cherry-pick is never forgotten. Even when it fails on conflicts, a PR is still created (optionally as a draft): labeled as failed, listing the conflicting files, with copy-paste instructions for resolving them on that same branch. A conflict becomes a visible open PR to act on instead of a silently missing backport.
  • CI runs on the backport PRs. Pass a fine-grained PAT and the created PRs trigger your workflows like any human-opened PR. (The zero-setup default GITHUB_TOKEN works too, but cannot trigger workflows — see Token.)
  • Token expiry won't surprise you. The token is verified before anything else, failures come with actionable messages instead of cryptic git errors, and the action warns ahead of time when the PAT is about to expire — optionally right in the PR bodies, where reviewers see it.

How it works

  1. A pull request gets one or more labels of the form cherry-pick-to-<target-branch> (e.g. cherry-pick-to-support/v6.1) before it is merged.

  2. When the PR is merged and its commits are pushed to the main branch, the consuming workflow runs once.

  3. The action looks up the merged PR the pushed commits belong to and derives the target branches from its cherry-pick-to-* labels — no pre-configured branch list needed.

  4. For each target branch (sequentially), the action:

    • cherry-picks the pushed commit range onto a new branch cherry-pick/<short-sha>/<target-branch> created from the target branch (commit by commit along the first-parent line; merge commits are picked with -m 1 — see Merge strategies),
    • opens a pull request against the target branch.

    A hard failure on one target (e.g. the branch does not exist) does not stop the remaining targets; the run fails at the end if any target had a hard error.

If the cherry-pick succeeds, the PR contains the cherry-picked commits and is labeled with the success label. If it fails (conflicts), the cherry-pick is aborted, the PR is created (optionally as a draft) with an empty placeholder commit, labeled with the failure label, and its description lists the conflicting files together with copy-paste instructions for resolving the conflicts manually on the same branch.

Forgot to add the label before merging? The action runs on the push created by the merge, so adding a label afterwards does not trigger it again. However, the labels are read live when the action runs — so you can add the label after the fact and simply re-run the merge commit's workflow run from the Actions tab; the re-run will then pick the label up.

Merge strategies

Squash merges and rebase merges are the recommended strategies: the pushed commits are exactly the pull request's changes and are cherry-picked onto the target branch one-to-one.

Merge commits are handled too, with limitations. A merge commit cannot be cherry-picked as-is — it has two parents, so there is no single well-defined "change". The action therefore picks it with git cherry-pick -m 1, which applies the merge's full diff against the main branch as one combined commit on the target branch. That reproduces exactly what landed on the main branch, including any conflict resolutions made in the merge itself, but:

  • the pull request's individual commits are not preserved on the target branch — the result is equivalent to a squash, and
  • the combined commit carries the merge commit's message (e.g. "Merge pull request #123 from ...").

When a merge commit conflicts, the failure PR's copy-paste instructions list the exact git cherry-pick -m 1 <sha> commands instead of the usual compact range form.

Usage

Create a workflow in the consuming repository, e.g. .github/workflows/cherry-pick-to.yml (see examples/ for the full file):

name: Cherry-pick to release branches

on:
  push:
    branches: [ "main" ]

jobs:
  cherry-pick:
    runs-on: ubuntu-latest
    permissions:
      contents: write        # push the cherry-pick branches
      pull-requests: write   # create the pull requests
      issues: write          # create the success/failure labels

    steps:
      - name: Cherry-pick to labeled target branches
        uses: eclipse-ecal/cherry-pick-to@v1

This runs with the workflow's default GITHUB_TOKEN — no secret setup needed, but it requires a one-time repository setting and CI will not run on the created cherry-pick PRs (see Token). For production use, pass a fine-grained PAT instead: token: ${{ secrets.CHERRY_PICK_GITHUB_TOKEN }} (the permissions block and the repository setting are then not needed).

The action verifies the token (see Token expiry checking) and then checks out the repository itself with full history (fetch-depth: 0) and the given token — no separate actions/checkout step is needed. If your workflow has to manage the checkout itself, set checkout: 'false' and check out with the same token and fetch-depth: 0 before calling the action.

Token

Quick start: the default GITHUB_TOKEN

Without a token input, the action uses the workflow's GITHUB_TOKEN. No secret or PAT setup is needed — only the permissions block shown in Usage (the default workflow token is read-only) and a one-time repository setting: under Settings → Actions → General → Workflow permissions, enable "Allow GitHub Actions to create and approve pull requests". Creating pull requests is gated by that checkbox and cannot be granted by the workflow's permissions block. For repositories that belong to an organization, the checkbox may be greyed out until the same setting is enabled in the organization settings. This is the easiest way to try the action out, and fine for repositories that don't need CI on the cherry-pick PRs.

Limitation: commits and pull requests created with GITHUB_TOKEN cannot trigger other workflows, so CI will not run on the created cherry-pick PRs. If branch protection requires status checks, such a PR waits for them forever. To run CI on one anyway, close and re-open the PR (or push a commit to it) — actions performed by a human do trigger workflows. The action prints a notice on every run with the default token as a reminder.

Production: a fine-grained PAT

To get CI on the cherry-pick PRs, create a fine-grained personal access token (typically owned by a machine user), store it as a repository or organization secret (e.g. CHERRY_PICK_GITHUB_TOKEN), and pass it via the token input.

Required token permissions on the consuming repository:

Permission Access
Actions Read and write
Contents Read and write
Metadata Read-only
Pull requests Read and write
Workflows Read and write

Fine-grained PATs expire and must be renewed. The action checks the token before doing anything else, so an expired token fails the run with a clear error message — see Token expiry checking.

Token expiry checking

The very first step of the action verifies the token against the repository, before the repository is checked out:

  • Token invalid or expired → the run fails with a meaningful error ("The cherry-pick token is invalid or has expired (HTTP 401). Renew the fine-grained PAT and update the secret ..."), instead of the cryptic failure an expired token otherwise produces in actions/checkout:

    fatal: could not read Username for 'https://github.com': terminal prompts disabled
    
  • Token valid but lacking access/permissions → the run fails with a message listing the required permissions.

  • Token valid but expiring soon → the run continues normally and emits a workflow warning when the token expires within token-expiry-warning-days (default: 14) days, so upcoming expiry shows up on every push while everything still works. (GitHub reports the expiration date of an expiring token in the github-authentication-token-expiration header of every API response; fine-grained PATs always have one.) Workflow warnings are easy to miss if nobody opens the run page — set token-expiry-warning-in-pr-body: 'true' to additionally put the warning into the body of every pull request the action creates during that period, where reviewers will see it. Disabled by default.

  • Token lacking write access (e.g. a read-only default GITHUB_TOKEN without the permissions block) → the run fails immediately with the fix, instead of much later at the push step.

With the default GITHUB_TOKEN — which is ephemeral by design — the expiry check is skipped; instead, a notice about the CI-trigger limitation is printed on every run.

Use the error-hint input to append e.g. a link to your renewal runbook or helpdesk ticket to these messages.

Checking without the main action

The same check is available as a standalone sub-action. It needs no checkout and is useful in two situations:

  • You set checkout: 'false' and manage the checkout yourself: your checkout step then runs before the action's token check, so place the sub-action before your checkout step to keep the friendly error.
  • Scheduled checks independent of push activity (see below).
- name: Check cherry-pick token
  uses: eclipse-ecal/cherry-pick-to/check-token@v1
  with:
    token: ${{ secrets.CHERRY_PICK_GITHUB_TOKEN }}
    # warning-days: '14'
    # error-hint: 'Renewal runbook: https://example.com/renew-cherry-pick-token'

Inputs: token (required), warning-days (default 14), error-hint (optional text appended to error/warning messages). Outputs: expiration-date, days-until-expiration.

Warnings on push-triggered runs only appear when someone pushes. For advance notice that does not depend on push activity, add a small scheduled workflow:

name: Check cherry-pick token

on:
  schedule:
    - cron: '0 6 * * 1'   # every Monday, 06:00 UTC
  workflow_dispatch:

jobs:
  check-token:
    runs-on: ubuntu-latest
    steps:
      - uses: eclipse-ecal/cherry-pick-to/check-token@v1
        with:
          token: ${{ secrets.CHERRY_PICK_GITHUB_TOKEN }}
          warning-days: '30'

Once the token has expired, this scheduled run fails — and GitHub notifies the author of a workflow when its scheduled runs fail.

Inputs

Input Required Default Description
token no ${{ github.token }} Token for checkout, pushing, and the GitHub CLI calls. The default GITHUB_TOKEN works but its PRs trigger no CI; use a fine-grained PAT for production (see Token).
label-prefix no cherry-pick-to- Labels of the form <label-prefix><branch> on the source PR select the target branches.
allowed-target-branches no '' Optional space-separated glob patterns (e.g. support/* release/*) restricting which branches may be targeted via labels. Empty allows any branch.
success-label no Auto cherry-pick success ✅ Label put on a created PR when the cherry-pick succeeded. Created (green) if missing.
failure-label no Auto cherry-pick failure ⚠️ Label put on a created PR when the cherry-pick failed. Created (red) if missing.
use-draft-pr no false Create failed-cherry-pick PRs as drafts. Off by default because repositories without draft PR support (e.g. private repos on free plans) would fail to create the PR.
branch-prefix no cherry-pick Each created branch is named <branch-prefix>/<short-sha>/<target-branch>.
before-commit no github.event.before Start (exclusive) of the commit range to cherry-pick, as a full 40-char SHA.
after-commit no github.event.after End (inclusive) of the commit range to cherry-pick, as a full 40-char SHA.
git-user-name no pusher of the push event user.name for the cherry-picked commits.
git-user-email no pusher of the push event user.email for the cherry-picked commits.
checkout no true Whether the action checks out the repository itself (full history, using token). Set to false to manage the checkout in the calling workflow.
token-expiry-warning-days no 14 Emit a workflow warning when the token expires within this many days.
token-expiry-warning-in-pr-body no false Also append the token expiry warning to the body of every created PR while the token expires within token-expiry-warning-days days. No effect with the default GITHUB_TOKEN.
error-hint no '' Extra text appended to token error/warning messages, e.g. a link to your renewal runbook.

Outputs

Output Description
performed true if at least one cherry-pick PR was created.
pr-urls Newline-separated URLs of the created cherry-pick PRs. Empty if none.
results JSON array with one entry per discovered target branch: [{"target": "...", "outcome": "...", "pr-url": "..."}]. Outcomes: success, conflict, skipped-branch-exists, skipped-nothing-to-pick, skipped-not-allowed, error.
source-pr-number Number of the merged PR the pushed commits belong to. Empty if none was found.
token-expiration-date Expiration date of the token. Empty if the token does not expire.

Note: the action succeeds even when a cherry-pick fails with conflicts — that case is reported through the failure-labeled PR (conflict outcome), not through a red workflow run. The action only fails on real errors (invalid inputs, missing target branch, API failures, push failures, ...), and even then only after all other targets have been processed.

Behavior notes

  • The action expects squash-merge or rebase-merge workflows. Merge commits inside the pushed range make git cherry-pick fail and land on the failure path (conflict-style PR).
  • Pushes without an associated merged PR (e.g. direct pushes) and PRs without a matching label are skipped silently (a notice is written to the log).
  • Pushes that create a branch (before is all zeros) and force pushes (before no longer reachable, or not an ancestor of after) are skipped with a notice.
  • If a cherry-pick branch already exists on the remote — a re-run for the same commits, or someone is already resolving conflicts on it — that target is skipped with a notice. Delete the branch to re-run the cherry-pick.
  • Commits whose changes already exist on the target branch are silently dropped (git cherry-pick --empty=drop). If all commits are dropped, no pull request is created for that target.
  • Targets are processed sequentially; a hard error on one target does not stop the others (the run fails at the end instead).
  • Every target reports its result on the workflow run's summary page.

Security

All externally controlled values (branch names, PR titles, labels, pusher name/email, conflicting file names) are passed into the shell scripts as environment variables and only used as quoted variables — they are never interpolated into script text, so they cannot inject shell commands (GitHub docs on script injection). Branch names are additionally validated with git check-ref-format and may not start with -; commit SHAs must match ^[0-9a-f]{40}$.

Since the target branches come from labels, anyone who can label pull requests (triage permission and up) can direct cherry-picks at any branch. The result is always a pull request — nothing is merged automatically — but if you want to bound this, set allowed-target-branches (e.g. support/*) to restrict which branches labels may target.

Development

tests/run-tests.sh tests the step scripts against local fixture repositories and a stubbed gh CLI — no GitHub access needed (requires git, jq, and python3 with PyYAML). CI runs the tests plus shellcheck and actionlint on every push and pull request.

License

MIT

About

Action for creating git cherry-pick PRs for commits coming from PRs

Topics

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.