| name | pr-cherry-picking |
| description | Assess whether an upstream commit/PR can be cleanly cherry-picked into a fork. Checks for followup bug fixes, prerequisite commits, and actual merge conflicts. Produces a concrete cherry-pick plan or explains why one is not possible. |
| license | MIT |
| metadata | {"version":"1.0","author":"James Xu"} |
PR Cherry-Picking Advisor
Given an upstream commit or PR, perform a systematic investigation and produce a concrete cherry-pick plan for applying it to a downstream fork.
When to Use
Use when the user wants to:
- Backport a bug fix or optimization from an upstream repo into a fork
- Understand whether a specific upstream commit is safe to cherry-pick
- Identify what else needs to land before the target commit can be applied
- Find out whether the upstream introduced any bugs that were later fixed
Don't use for:
- Merging entire release branches (use
git merge instead)
- Porting features that require significant API changes
- Questions about whether to cherry-pick at all (that's a product decision)
Investigation Process
Run all four steps in order. Steps 1 and 2 can be done in parallel; step 3 requires step 2's output; step 4 requires step 3's output.
Step 1 — Identify the exact commit
If the user provides a PR number or JIRA ID rather than a commit hash, resolve it first.
gh pr view <number> --repo <owner/repo> --json mergeCommit
cd <upstream-repo>
git log --oneline --all --grep="<JIRA-ID or keyword>"
Record:
- Full commit SHA
- Author and merge date
- Files changed (
git show <sha> --stat)
Step 2 — Check for followup fixes
The upstream may have discovered bugs in this commit after merging it. Find them before recommending the cherry-pick.
2a. Search by JIRA/issue ID:
cd <upstream-repo>
git log --oneline --all --after="<commit-date>" --grep="<JIRA-ID>"
2b. Search by key symbols introduced in the commit:
Extract the new function names, class names, or variable names the commit introduced, then search for later commits referencing them:
git show <sha> | grep "^+" | grep -E "def |val |class |object " | head -20
git log --oneline --all --after="<commit-date>" --grep="<symbol1>\|<symbol2>"
git log --oneline --all --after="<commit-date>" -- <file1> <file2> | head -40
2c. Search by semantic keywords (broader net):
git log --oneline --all --after="<commit-date>" --grep="<feature area>" -i | head -20
Classify each hit:
- Direct followup fix: explicitly references the original commit/JIRA and fixes a bug it introduced → must cherry-pick together
- Unrelated change to same file: touches the same file but for an independent reason → no dependency
- Feature built on top: extends the new behavior → optional, evaluate separately
Step 3 — Find prerequisite commits
Determine whether the target commit depends on changes that landed between the fork point and the target commit in the same files.
3a. Find the fork divergence point:
cd <fork-repo>
git merge-base HEAD <upstream-sha>
3b. List all upstream commits to the same files, between fork point and target:
cd <upstream-repo>
git log --oneline <fork-point>..<target-sha> -- <file1> <file2>
3c. Assess each intermediate commit:
For each commit listed in 3b, check whether the target commit's diff context depends on it:
- If the target diff modifies lines that an intermediate commit also modified → likely prerequisite
- If the intermediate commit only adds unrelated lines to the same file → probably not required
A quick heuristic: attempt the cherry-pick (step 4) and see what actually conflicts. If Analyzer.scala conflicts but the intermediate commits are minor refactors, the conflict is probably trivial. If an intermediate commit fundamentally restructured the function the target modifies, it's a hard prerequisite.
Step 4 — Attempt the cherry-pick (dry run)
cd <fork-repo>
git cherry-pick --no-commit <upstream-sha>
git status
git diff HEAD
Classify the conflicts:
| Conflict location | Meaning | Action |
|---|
Core logic files (.scala, .java, .py, etc.) | Real conflict — needs manual resolution | Resolve carefully, understand why |
| Test golden files / snapshots | Expected: behavior changed, snapshots are stale | Regenerate via test suite, don't merge manually |
| Test source files (new test added by the commit) | Usually minor context conflict | Resolve manually or port the test logic |
| Config / build files | Evaluate case by case | Usually minor |
If Analyzer.scala (or the equivalent core file) merges cleanly but only test golden files conflict, the cherry-pick is low risk — the conflicts are artifacts of the test framework, not logic disagreements.
Always abort after the dry run:
git cherry-pick --abort
git reset --hard HEAD
Output
Produce a report with the following sections:
1. Commit identity
- SHA, author, date, upstream PR/issue link
- One-line summary of what it does
2. Followup fixes
- List any followup commits found in step 2
- For each: SHA, description, classification (must-include / unrelated / optional)
- If none found: state that explicitly
3. Prerequisites
- List any intermediate commits assessed as prerequisites
- For each: SHA, description, why it's needed (or why it's not despite touching the same file)
- If none needed: state that explicitly
4. Cherry-pick feasibility
CLEAN: core files merged without conflict, only test artifacts conflicted
REQUIRES_RESOLUTION: core files conflicted, describe what needs manual work
BLOCKED: depends on a prerequisite that itself has broad conflicts or API changes
5. Recommended steps
Concrete shell commands the user can run, in order, to perform the cherry-pick including how to handle any non-core conflicts (e.g. regenerate golden files).
Example Output
### Commit identity
SHA: 6f31566094a
Author: Kun Wan, 2023-01-09
PR: apache/spark#39333 (SPARK-41805)
Summary: Replace ArrayBuffer with LinkedHashMap keyed on canonicalized expression
in ExtractWindowExpressions.extractExpr, so repeated identical PARTITION BY
expressions share one Alias/ExprId and collapse into one Window+Exchange node.
### Followup fixes
No commits found that reference SPARK-41805 or the introduced symbols
(extractedExprMap, getOrExtract) after the merge date. The two later commits
touching ExtractWindowExpressions (SPARK-43611, SPARK-55500) are unrelated:
- SPARK-43611: retain PLAN_ID_TAG for Spark Connect — unrelated
- SPARK-55500: fix analyzer cycle with ApplyDefaultCollation — unrelated, 3.4+ feature
### Prerequisites
Commits between fork point f84018a481 and target 6f31566094a that touch Analyzer.scala:
- eb30a27e531 [SPARK-38308] Eagerly iterate window expressions (toIndexedSeq) — already in fork
- 3eaf5b5f98e [SPARK-41720] Rename UnresolvedFunc — already in fork
- 3c40be2dddc [SPARK-41405] Centralize column resolution — already in fork
All are already present in the fork (same divergence base). No prerequisites needed.
### Cherry-pick feasibility
CLEAN — Analyzer.scala merges without conflict. Only TPC-DS plan stability
golden files and DataFrameWindowFramesSuite.scala (new test) conflict.
### Recommended steps
# 1. Apply the commit without committing
git cherry-pick --no-commit 6f31566094a
# 2. Drop the golden file conflicts — regenerate instead of merging
git checkout HEAD -- sql/core/src/test/resources/tpcds-plan-stability/
# 3. Drop the test file conflict — port the new test manually
git checkout HEAD -- sql/core/src/test/scala/org/apache/spark/sql/DataFrameWindowFramesSuite.scala
# 4. Stage the core fix and commit
git add sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/Analyzer.scala
git commit -m "[SPARK-41805][SQL] Reuse expressions in WindowSpecDefinition (backport)"
# 5. Regenerate TPC-DS golden files
build/sbt "sql/testOnly *TPCDSQuerySuite -- -Dspark.test.regeneratePlanStabilityFiles=true"
# 6. Port the new test from DataFrameWindowFramesSuite manually (or copy from upstream
# and adjust line context to match the fork's test class)