{"id":15008159,"url":"https://github.com/python/cherry-picker","last_synced_at":"2025-04-12T20:42:17.488Z","repository":{"id":34922225,"uuid":"189480743","full_name":"python/cherry-picker","owner":"python","description":"🐍🍒⛏ Utility script for backporting/cherry-picking CPython changes from master into one of the maintenance branches.","archived":false,"fork":false,"pushed_at":"2025-04-07T20:49:04.000Z","size":284,"stargazers_count":58,"open_issues_count":15,"forks_count":43,"subscribers_count":39,"default_branch":"main","last_synced_at":"2025-04-12T01:21:55.676Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/python.png","metadata":{"funding":{"custom":"https://www.python.org/psf/donations/python-dev/","github":["python"]},"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-30T20:49:46.000Z","updated_at":"2025-04-11T22:03:59.000Z","dependencies_parsed_at":"2022-07-10T18:00:51.620Z","dependency_job_id":"cef12d0d-2758-4f85-a4f1-eb70099cb8da","html_url":"https://github.com/python/cherry-picker","commit_stats":{"total_commits":189,"total_committers":34,"mean_commits":"5.5588235294117645","dds":0.5873015873015873,"last_synced_commit":"b8f7d16ad21ad6d53c976214051556c5414283b0"},"previous_names":["python/cherry_picker"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/python%2Fcherry-picker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/python%2Fcherry-picker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/python%2Fcherry-picker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/python%2Fcherry-picker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/python","download_url":"https://codeload.github.com/python/cherry-picker/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248631668,"owners_count":21136554,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-24T19:15:26.687Z","updated_at":"2025-04-12T20:42:17.466Z","avatar_url":"https://github.com/python.png","language":"Python","funding_links":["https://www.python.org/psf/donations/python-dev/","https://github.com/sponsors/python"],"categories":[],"sub_categories":[],"readme":"# cherry_picker\n\n[![PyPI version](https://img.shields.io/pypi/v/cherry-picker.svg?logo=pypi\u0026logoColor=FFE873)](https://pypi.org/project/cherry-picker)\n[![Supported Python versions](https://img.shields.io/pypi/pyversions/cherry-picker.svg?logo=python\u0026logoColor=FFE873)](https://pypi.org/project/cherry-picker)\n[![tests](https://github.com/python/cherry-picker/actions/workflows/main.yml/badge.svg)](https://github.com/python/cherry-picker/actions/workflows/main.yml)\n\nUsage (from a cloned CPython directory):\n\n```\nUsage: cherry_picker [OPTIONS] [COMMIT_SHA1] [BRANCHES]...\n\n  cherry-pick COMMIT_SHA1 into target BRANCHES.\n\nOptions:\n  --version                  Show the version and exit.\n  --dry-run                  Prints out the commands, but not executed.\n  --pr-remote REMOTE         git remote to use for PR branches\n  --upstream-remote REMOTE   git remote to use for upstream branches\n  --abort                    Abort current cherry-pick and clean up branch\n  --continue                 Continue cherry-pick, push, and clean up branch\n  --status                   Get the status of cherry-pick\n  --push / --no-push         Changes won't be pushed to remote\n  --auto-pr / --no-auto-pr   If auto PR is enabled, cherry-picker will\n                             automatically open a PR through API if GH_AUTH\n                             env var is set, or automatically open the PR\n                             creation page in the web browser otherwise.\n  --config-path CONFIG-PATH  Path to config file, .cherry_picker.toml from\n                             project root by default. You can prepend a colon-\n                             separated Git 'commitish' reference.\n  -h, --help                 Show this message and exit.\n```\n\n## About\n\nThis tool is used to backport CPython changes from `main` into one or more\nof the maintenance branches (e.g. `3.12`, `3.11`).\n\n`cherry_picker` can be configured to backport other projects with similar\nworkflow as CPython. See the configuration file options below for more details.\n\nThe maintenance branch names should contain some sort of version number (`X.Y`).\nFor example: `3.12`, `stable-3.12`, `1.5`, `1.5-lts`, are all supported branch\nnames.\n\nIt will prefix the commit message with the branch, e.g. `[3.12]`, and then\nopen up the pull request page.\n\nWrite tests using [pytest](https://docs.pytest.org/).\n\n\n## Setup info\n\n```console\n$ python3 -m venv venv\n$ source venv/bin/activate\n(venv) $ python -m pip install cherry_picker\n```\n\nThe cherry-picking script assumes that if an `upstream` remote is defined, then\nit should be used as the source of upstream changes and as the base for\ncherry-pick branches. Otherwise, `origin` is used for that purpose.\nYou can override this behavior with the `--upstream-remote` option\n(e.g. `--upstream-remote python` to use a remote named `python`).\n\nVerify that an `upstream` remote is set to the CPython repository:\n\n```console\n$ git remote -v\n...\nupstream\thttps://github.com/python/cpython (fetch)\nupstream\thttps://github.com/python/cpython (push)\n```\n\nIf needed, create the `upstream` remote:\n\n```console\n$ git remote add upstream https://github.com/python/cpython.git\n```\n\nBy default, the PR branches used to submit pull requests back to the main\nrepository are pushed to `origin`. If this is incorrect, then the correct\nremote will need be specified using the `--pr-remote` option (e.g.\n`--pr-remote pr` to use a remote named `pr`).\n\n\n## Cherry-picking 🐍🍒⛏️\n\n(Setup first! See previous section.)\n\nFrom the cloned CPython directory:\n\n```console\n(venv) $ cherry_picker [--pr-remote REMOTE] [--upstream-remote REMOTE] [--dry-run] [--config-path CONFIG-PATH] [--abort/--continue] [--status] [--push/--no-push] [--auto-pr/--no-auto-pr] \u003ccommit_sha1\u003e \u003cbranches\u003e\n```\n\n### Commit sha1\n\nThe commit sha1 for cherry-picking is the squashed commit that was merged to\nthe `main` branch.  On the merged pull request, scroll to the bottom of the\npage.  Find the event that says something like:\n\n```\n\u003ccoredeveloper\u003e merged commit \u003ccommit_sha1\u003e into python:main \u003csometime\u003e ago.\n```\n\nBy following the link to `\u003ccommit_sha1\u003e`, you will get the full commit hash.\nUse the full commit hash for `cherry_picker.py`.\n\n\n### Options\n\n```\n--dry-run                 Dry Run Mode.  Prints out the commands, but not executed.\n--pr-remote REMOTE        Specify the git remote to push into.  Default is 'origin'.\n--upstream-remote REMOTE  Specify the git remote to use for upstream branches.\n                          Default is 'upstream' or 'origin' if the former doesn't exist.\n--status                  Do `git status` in cpython directory.\n```\n\nAdditional options:\n\n```\n--abort        Abort current cherry-pick and clean up branch\n--continue     Continue cherry-pick, push, and clean up branch\n--no-push      Changes won't be pushed to remote\n--no-auto-pr   PR creation page won't be automatically opened in the web browser or\n               if GH_AUTH is set, the PR won't be automatically opened through API.\n--config-path  Path to config file\n               (`.cherry_picker.toml` from project root by default)\n```\n\nConfiguration file example:\n\n```toml\nteam = \"aio-libs\"\nrepo = \"aiohttp\"\ncheck_sha = \"f382b5ffc445e45a110734f5396728da7914aeb6\"\nfix_commit_msg = false\ndefault_branch = \"devel\"\nrequire_version_in_branch_name = false\ndraft_pr = false\n```\n\nAvailable config options:\n\n```\nteam                            github organization or individual nick,\n                                e.g \"aio-libs\" for https://github.com/aio-libs/aiohttp\n                                (\"python\" by default)\n\nrepo                            github project name,\n                                e.g \"aiohttp\" for https://github.com/aio-libs/aiohttp\n                                (\"cpython\" by default)\n\ncheck_sha                       A long hash for any commit from the repo,\n                                e.g. a sha1 hash from the very first initial commit\n                                (\"7f777ed95a19224294949e1b4ce56bbffcb1fe9f\" by default)\n\nfix_commit_msg                  Replace # with GH- in cherry-picked commit message.\n                                It is the default behavior for CPython because of external\n                                Roundup bug tracker (https://bugs.python.org) behavior:\n                                #xxxx should point on issue xxxx but GH-xxxx points\n                                on pull-request xxxx.\n                                For projects using GitHub Issues, this option can be disabled.\n\ndefault_branch                  Project's default branch name,\n                                e.g \"devel\" for https://github.com/ansible/ansible\n                                (\"main\" by default)\n\nrequire_version_in_branch_name  Allow backporting to branches whose names don't contain\n                                something that resembles a version number\n                                (i.e. at least two dot-separated numbers).\n\ndraft_pr                        Create PR as draft\n                                (false by default)\n```\n\nTo customize the tool for used by other project:\n\n1. Create a file called `.cherry_picker.toml` in the project's root\n   folder (alongside with `.git` folder).\n\n2. Add `team`, `repo`, `fix_commit_msg`, `check_sha` and\n   `default_branch` config values as described above.\n\n3. Use `git add .cherry_picker.toml` / `git commit` to add the config\n   into Git.\n\n4. Add `cherry_picker` to development dependencies or install it\n   by `pip install cherry_picker`\n\n5. Now everything is ready, use `cherry_picker \u003ccommit_sha\u003e \u003cbranch1\u003e\n   \u003cbranch2\u003e` for cherry-picking changes from `\u003ccommit_sha\u003e` into\n   maintenance branches.\n   Branch name should contain at least major and minor version numbers\n   and may have some prefix or suffix.\n   Only the first version-like substring is matched when the version\n   is extracted from branch name.\n\n### Demo\n\n- Installation: https://asciinema.org/a/125254\n\n- Backport: https://asciinema.org/a/125256\n\n\n### Example\n\nFor example, to cherry-pick `6de2b7817f-some-commit-sha1-d064` into\n`3.12` and `3.11`, run the following command from the cloned CPython\ndirectory:\n\n```console\n(venv) $ cherry_picker 6de2b7817f-some-commit-sha1-d064 3.12 3.11\n```\n\nWhat this will do:\n\n```console\n(venv) $ git fetch upstream\n\n(venv) $ git checkout -b backport-6de2b78-3.12 upstream/3.12\n(venv) $ git cherry-pick -x 6de2b7817f-some-commit-sha1-d064\n(venv) $ git push origin backport-6de2b78-3.12\n(venv) $ git checkout main\n(venv) $ git branch -D backport-6de2b78-3.12\n\n(venv) $ git checkout -b backport-6de2b78-3.11 upstream/3.11\n(venv) $ git cherry-pick -x 6de2b7817f-some-commit-sha1-d064\n(venv) $ git push origin backport-6de2b78-3.11\n(venv) $ git checkout main\n(venv) $ git branch -D backport-6de2b78-3.11\n```\n\nIn case of merge conflicts or errors, the following message will be displayed:\n\n```\nFailed to cherry-pick 554626ada769abf82a5dabe6966afa4265acb6a6 into 2.7 :frowning_face:\n... Stopping here.\n\nTo continue and resolve the conflict:\n    $ cherry_picker --status  # to find out which files need attention\n    # Fix the conflict\n    $ cherry_picker --status  # should now say 'all conflict fixed'\n    $ cherry_picker --continue\n\nTo abort the cherry-pick and cleanup:\n    $ cherry_picker --abort\n```\n\nPassing the `--dry-run` option will cause the script to print out all the\nsteps it would execute without actually executing any of them. For example:\n\n```console\n$ cherry_picker --dry-run --pr-remote pr 1e32a1be4a1705e34011770026cb64ada2d340b5 3.12 3.11\nDry run requested, listing expected command sequence\nfetching upstream ...\ndry_run: git fetch origin\nNow backporting '1e32a1be4a1705e34011770026cb64ada2d340b5' into '3.12'\ndry_run: git checkout -b backport-1e32a1b-3.12 origin/3.12\ndry_run: git cherry-pick -x 1e32a1be4a1705e34011770026cb64ada2d340b5\ndry_run: git push pr backport-1e32a1b-3.12\ndry_run: Create new PR: https://github.com/python/cpython/compare/3.12...ncoghlan:backport-1e32a1b-3.12?expand=1\ndry_run: git checkout main\ndry_run: git branch -D backport-1e32a1b-3.12\nNow backporting '1e32a1be4a1705e34011770026cb64ada2d340b5' into '3.11'\ndry_run: git checkout -b backport-1e32a1b-3.11 origin/3.11\ndry_run: git cherry-pick -x 1e32a1be4a1705e34011770026cb64ada2d340b5\ndry_run: git push pr backport-1e32a1b-3.11\ndry_run: Create new PR: https://github.com/python/cpython/compare/3.11...ncoghlan:backport-1e32a1b-3.11?expand=1\ndry_run: git checkout main\ndry_run: git branch -D backport-1e32a1b-3.11\n```\n\n### `--pr-remote` option\n\nThis will generate pull requests through a remote other than `origin`\n(e.g. `pr`)\n\n### `--upstream-remote` option\n\nThis will generate branches from a remote other than `upstream`/`origin`\n(e.g. `python`)\n\n### `--status` option\n\nThis will do `git status` for the CPython directory.\n\n### `--abort` option\n\nCancels the current cherry-pick and cleans up the cherry-pick branch.\n\n### `--continue` option\n\nContinues the current cherry-pick, commits, pushes the current branch to\n`origin`, opens the PR page, and cleans up the branch.\n\n### `--no-push` option\n\nChanges won't be pushed to remote.  This allows you to test and make additional\nchanges.  Once you're satisfied with local changes, use `--continue` to complete\nthe backport, or `--abort` to cancel and clean up the branch.  You can also\ncherry-pick additional commits, by:\n\n```console\n$ git cherry-pick -x \u003ccommit_sha1\u003e\n```\n\n### `--no-auto-pr` option\n\nPR creation page won't be automatically opened in the web browser or\nif GH_AUTH is set, the PR won't be automatically opened through API.\nThis can be useful if your terminal is not capable of opening a useful web browser,\nor if you use cherry-picker with a different Git hosting than GitHub.\n\n### `--config-path` option\n\nAllows to override default config file path\n(`\u003cPROJ-ROOT\u003e/.cherry_picker.toml`) with a custom one. This allows cherry_picker\nto backport projects other than CPython.\n\n\n## Creating pull requests\n\nWhen a cherry-pick was applied successfully, this script will open up a browser\ntab that points to the pull request creation page.\n\nThe url of the pull request page looks similar to the following:\n\n```\nhttps://github.com/python/cpython/compare/3.12...\u003cusername\u003e:backport-6de2b78-3.12?expand=1\n```\n\nPress the `Create Pull Request` button.\n\nBedevere will then remove the `needs backport to ...` label from the original\npull request against `main`.\n\n\n## Running tests\n\n```console\n$ # Install pytest\n$ pip install -U pytest\n$ # Run tests\n$ pytest\n```\n\nTests require your local version of Git to be 2.28.0+.\n\n## Publishing to PyPI\n\n- See the [release checklist](https://github.com/python/cherry-picker/blob/main/RELEASING.md).\n\n\n## Local installation\n\nIn the directory where `pyproject.toml` exists:\n\n```console\n$ pip install\n```\n\n## Changelog\n\nSee the [changelog](https://github.com/python/cherry-picker/blob/main/CHANGELOG.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpython%2Fcherry-picker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpython%2Fcherry-picker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpython%2Fcherry-picker/lists"}