A CLI tool for managing cherry-picks across GitHub repositories using a YAML configuration file to track state.
It is currently highly opinionated and only supports GitHub repositories.
- It uses cursor-agent for AI-assisted conflict resolution.
- It uses semantic versioning for target branches.
- It expects squash merges for PRs.
My use case is to cherry-pick PRs from the main branch to release branches for argoproj/argo-workflows.
I'm open to contributions to make it more flexible and support other versioning schemes and repositories.
Build the tool from source:
make cherry-picker
Or run all checks and build:
make all
This will format the code, run tests, and build the binary.
This tool requires a GitHub Personal Access Token (PAT) with specific permissions to interact with the GitHub API.
For fine-grained personal access tokens, the following repository permissions are required:
- Contents: Read and Write
- Issues: Read and Write
- Pull requests: Read and Write
- Actions: Read and Write
- Metadata: Read
- Create a fine-grained personal access token at: https://github.com/settings/personal-access-tokens/new
- Select the repository you want to manage cherry-picks for
- Grant the permissions listed above
- Export the token as an environment variable:
export GITHUB_TOKEN="your_github_token_here"
- Contents (Read+Write): Used to read repository files and perform merge operations
- Issues (Read+Write): Used to read PR metadata and update status
- Pull requests (Read+Write): Used to fetch PR details, create cherry-pick PRs, and merge PRs
- Actions (Read+Write): Used to retry failed CI workflows and check CI status
- Metadata (Read): Used to access repository metadata required for API operations
If you can merge PRs in the GitHub UI but the merge
command fails:
- Check PAT Permissions: Ensure your token has Contents: Read and Write permission (most common issue)
- Verify Branch Protection: Repository branch protection rules may block API merges while allowing UI merges
- Organization Policies: Some organizations restrict fine-grained PAT merge capabilities
- Test with Classic PAT: Try a classic PAT with
repo
scope to isolate permission issues
Common Error: 403 Forbidden
or merge not allowed
usually indicates missing Contents permission.
Create a new cherry-picks.yaml
configuration file:
./cherry-picker config --org myorg --repo myrepo
This creates a configuration file with the default source branch set to main
.
Fetch merged PRs from GitHub that need cherry-picking decisions:
export GITHUB_TOKEN="your_github_token"
./cherry-picker fetch
This command will:
- Fetch merged PRs to the source branch since the last fetch date (or 30 days ago for first run)
- Show each PR with title and URL
- Interactively ask whether you want to pick or ignore each PR
- For PRs marked to pick: create pending status for all target branches
- Store your decisions and update the last fetch date
Cherry-pick a PR to specific target branches:
./cherry-picker pick 123 release-1.0
./cherry-picker pick 123 # Pick to all pending branches
This command will:
- Create a new branch (
cherry-pick-<prnum>-<target>
) - Perform
git cherry-pick -x
with AI-assisted conflict resolution via Cursor - Push the branch and create a cherry-pick PR
- Update the configuration with the new PR details
Add a PR to the tracking list manually:
./cherry-picker add 12345 # Add to all target branches
./cherry-picker add 12345 release-1.0 # Add to specific target branch only
This command will:
- Fetch PR details from GitHub (title, CI status, etc.)
- Validate the PR exists and is accessible
- Add the PR to the tracking list with pending status
- Allow you to add PRs that weren't picked up by
fetch
Mark a PR as ignored for specific branches:
./cherry-picker ignore 123 release-1.0
Retry failed CI workflows for picked PRs:
./cherry-picker retry 123 release-1.0 # Retry specific branch
./cherry-picker retry 123 # Retry all branches with failed CI
Squash and merge picked PRs with passing CI:
./cherry-picker merge 123 release-1.0 # Merge specific branch
./cherry-picker merge 123 # Merge all eligible branches
This command performs a squash merge by default, combining all commits in the PR into a single commit. This matches the "Squash and merge" button in the GitHub UI and works with repositories that have merge commits disabled.
View the current status of all tracked PRs:
./cherry-picker status
Generate a development progress summary for a target branch:
./cherry-picker summary release-1.0
This command will:
- Query GitHub for commits to the specified target branch since the last release
- Generate markdown output showing cherry-picked PRs and their status
- Include in-progress items (picked but not yet merged)
- Show open PRs against the target branch
- Determine the next patch version based on existing tags
View the current status of all tracked PRs:
./cherry-picker status
This command will:
- Show all non-ignored PRs and their status across target branches
- Display pending (β³), picked (β /π), ignored (π«), and not tracked (β) states
- Fetch PR details from GitHub (when
GITHUB_TOKEN
is set):- PR title and GitHub URL
- Merge status (β merged / β not merged)
- CI status (β passing / β failing / π pending / β unknown)
- Show contextual commands directly under each branch status:
- Pending branches:
pick
andignore
commands - Picked branches with failing CI:
retry
command - Picked branches with passing CI:
merge
command - Ignored branches:
pick
command to un-ignore
- Pending branches:
- Provide a summary of total pending, completed, and ignored picks
Cherry-pick status for myorg/myrepo (source: main)
Target branches: release-1.0, release-2.0
Fix critical bug (https://github.com/myorg/myrepo/pull/123)
release-1.0 : β³ pending
π‘ ./cherry-picker pick 123 release-1.0
π‘ ./cherry-picker ignore 123 release-1.0
release-2.0 : π picked (https://github.com/myorg/myrepo/pull/456)
Fix critical bug (cherry-pick release-2.0) [β not merged, β CI failing]
π‘ ./cherry-picker retry 123 release-2.0
Add new feature (https://github.com/myorg/myrepo/pull/125)
release-1.0 : β
picked (https://github.com/myorg/myrepo/pull/457)
Add new feature (cherry-pick release-1.0) [β
merged, β
CI passing]
release-2.0 : π picked (https://github.com/myorg/myrepo/pull/458)
Add new feature (cherry-pick release-2.0) [β not merged, β
CI passing]
π‘ ./cherry-picker merge 125 release-2.0
Summary: 2 PR(s) active, 1 branch pick(s) pending, 3 branch pick(s) completed, 0 branch pick(s) ignored
Note: PR details are only fetched when GITHUB_TOKEN
environment variable is set. Without it, only PR numbers are shown.
Initialize or update configuration:
--org, -o
: GitHub organization or username (auto-detected from git if available)--repo, -r
: GitHub repository name (auto-detected from git if available)--source-branch, -s
: Source branch name (auto-detected from git if available, defaults to "main")--target-branches, -t
: Target branches for cherry-picking (comma-separated)--config, -c
: Configuration file path (default: "cherry-picks.yaml")
Fetch new merged PRs:
--config, -c
: Configuration file path (default: "cherry-picks.yaml")--since, -s
: Fetch PRs since this date (YYYY-MM-DD), defaults to last fetch date
Add a PR to the tracking list:
--config, -c
: Configuration file path (default: "cherry-picks.yaml")
Cherry-pick a PR to target branches:
--config, -c
: Configuration file path (default: "cherry-picks.yaml")
Mark a PR as ignored for specific branches:
--config, -c
: Configuration file path (default: "cherry-picks.yaml")
Retry failed CI workflows:
--config, -c
: Configuration file path (default: "cherry-picks.yaml")
Squash and merge picked PRs:
--config, -c
: Configuration file path (default: "cherry-picks.yaml")
View current status of tracked PRs:
--config, -c
: Configuration file path (default: "cherry-picks.yaml")
Generate development progress summary for a target branch:
--config, -c
: Configuration file path (default: "cherry-picks.yaml")
Initialize with custom source branch:
./cherry-picker config --org myorg --repo myrepo --source-branch develop
Initialize with target branches:
./cherry-picker config --org myorg --repo myrepo --target-branches release-1.0,release-2.0,staging
Initialize with custom config file:
./cherry-picker config --config my-picks.yaml --org myorg --repo myrepo
Fetch PRs since a specific date:
./cherry-picker fetch --since 2024-01-01
When a cherry-pick encounters merge conflicts, the tool launches an interactive AI session to help you resolve them:
- Automatic Detection: When
git cherry-pick
fails due to conflicts, the tool detects this automatically - Initial Context: Sends a detailed prompt to the AI about the specific conflicts and cherry-pick context
- Interactive Handover: After providing context, you take control of the conversation
- Guided Resolution: Work with the AI to understand and resolve conflicts step-by-step
- User Decision: After the AI session, you decide whether to proceed with the cherry-pick
The initial context + interactive approach gives you:
- Informed AI: AI starts with full context about the cherry-pick and conflicts
- Direct Communication: Ask specific questions and guide the resolution
- Educational Value: Learn why conflicts occurred and how to resolve them
- Flexible Approach: Guide the AI toward your preferred resolution strategy
- Real-time Feedback: See and approve changes before they're applied
- Cursor installed: The
cursor-agent
CLI must be available - Authentication: Run
cursor-agent login
to authenticate if needed - Git Context: The AI can see your repository state and conflicted files
# When conflicts occur during cherry-pick:
./cherry-picker pick 123 release-1.0
# Output:
π― Cherry-pick PR #123: Fix critical bug to branch: release-1.0
β οΈ Cherry-pick conflicts detected. Attempting Cursor AI-assisted resolution...
π Found 2 conflicted file(s): [src/main.go src/config.go]
π€ Launching cursor-agent with initial context...
π‘ Starting AI session with conflict context, then handing control to you.
- The AI will receive details about the cherry-pick conflicts
- You can then guide the resolution process
- Exit the agent when you're satisfied with the resolution
π― Sending initial context to AI...
# AI receives this context automatically:
# "I need help resolving cherry-pick conflicts. Here's the situation:
#
# **Cherry-pick Context:**
# - Attempting to cherry-pick: 448c492 Fix critical bug
# - Number of conflicted files: 2
# - Conflicted files: [src/main.go src/config.go]
#
# Please start by examining the conflicted files and let me know what you see."
# Then you take control of the conversation:
# > "What conflicts do you see in main.go?"
# > "Can you resolve the conflicts in config.go first?"
# > "Please make the changes to merge both approaches"
# After you exit the cursor-agent session:
π Cursor-agent session completed.
- Assuming conflicts have been resolved during the AI session
- Checking if cherry-pick is complete...
π― No conflicts remaining. Completing cherry-pick commit...
β
Cherry-pick completed with AI-assisted conflict resolution
After the interactive cursor-agent session, the tool automatically:
- Checks for remaining conflicts: Verifies no conflict markers remain in files
- Detects completion status: Checks if the AI already completed the cherry-pick
- Completes the commit: Runs
git cherry-pick --continue
if needed - Proceeds with PR creation: Continues with pushing the branch and creating the PR
If conflicts still remain after the AI session:
- The tool will list the problematic files
- You'll need to resolve them manually and run
git cherry-pick --continue
- Or run
git cherry-pick --abort
to cancel
For effective AI conflict resolution:
- Be Specific: Ask for help with particular files or conflict types
- Explain Context: Tell the AI about the purpose of the changes being cherry-picked
- Review Changes: Check each file as the AI modifies it
- Ask Questions: Use the AI to understand why conflicts occurred
- Iterate: Work through complex conflicts step-by-step with the AI
When the interactive AI session encounters issues:
- Session Failure: If
cursor-agent
fails to launch, you'll get clear instructions for manual resolution - User Control: You can exit the AI session at any time and handle conflicts manually
- Git State: Cherry-pick remains in progress, allowing you to continue with standard Git tools
- No Automated Changes: The interactive approach doesn't make changes without your approval
If cursor-agent
is not available, the cherry-pick process will abort with a clear error message:
Example failure output:
β Failed to launch cursor-agent: exec: "cursor-agent": executable file not found in $PATH
- You can resolve conflicts manually using standard Git tools
- Run 'git cherry-pick --abort' to cancel, or resolve and 'git cherry-pick --continue'
The cherry-picks.yaml
file stores the repository configuration and tracked PRs:
org: myorg
repo: myrepo
source_branch: main
target_branches:
- release-1.0
- release-2.0
- staging
last_fetch_date: 2024-01-15T10:30:00Z
tracked_prs:
- number: 123
title: "Fix critical bug"
ignored: false
branches:
release-1.0:
status: pending
release-2.0:
status: merged
pr:
number: 456
title: "Fix critical bug (cherry-pick release-2.0)"
ci_status: "passing"
staging:
status: ignored
- number: 124
title: "Add new feature"
ignored: true
Each tracked PR has per-branch status tracking:
- number: Original PR number
- title: PR title (fetched from GitHub)
- ignored:
true
if the PR should not be cherry-picked at all - branches: Map of target branch names to their status:
- status:
pending
,picked
,ignored
, ormerged
- pr: Details of the cherry-pick PR (when status is
picked
ormerged
):- number: Cherry-pick PR number
- title: Cherry-pick PR title
- ci_status: CI status (
passing
,failing
,pending
,unknown
)
- status:
Run all checks (format, vet, test):
make check
Build the binary:
make cherry-picker
Clean build artifacts:
make clean