mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-31 12:03:46 +00:00 
			
		
		
		
	The commit hashes that appear in the `pull/12345/head` ref are the ones _before_ any final rebase occurs, and as such may not match to any commit hashes which exist in `main`. Use the GitHub GraphQL API to pull the last "merge commit" on the PR, which is post-rebase, and use that as the target commit when cherry-picking. Then walk backwards from that commit, including every sequential commit which is still associated with the PR; we do this because during the merge, commits may be added or removed, so the PR is not reliable in the commit count.
		
			
				
	
	
		
			103 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			103 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env bash
 | |
| 
 | |
| set -e
 | |
| 
 | |
| usage() {
 | |
|     cat >&2 <<EOF
 | |
| usage: $0 PULL_REQUEST_ID COMMIT_COUNT [REMOTE]
 | |
| 
 | |
| Fetch the given GitHub pull request branch and backport it to
 | |
| the current branch using 'git cherry-pick -x'.
 | |
| 
 | |
| Typical usage is:
 | |
|   git fetch upstream
 | |
|   git checkout -b 8.x upstream/8.x
 | |
|   $0 FIRST_PR_ID
 | |
|   $0 SECOND_PR_ID
 | |
|   git push origin +HEAD:backport-changes
 | |
| EOF
 | |
|     exit 1
 | |
| }
 | |
| 
 | |
| pr_id="$1"
 | |
| 
 | |
| if [ -z "$pr_id" ]; then
 | |
|     usage
 | |
| fi
 | |
| 
 | |
| fail() {
 | |
|     echo "$1"
 | |
|     exit 1
 | |
| }
 | |
| 
 | |
| type gh >/dev/null 2>&1 \
 | |
|     || fail "The 'gh' CLI tool is not installed; see https://cli.github.com/"
 | |
| gh auth status 2>/dev/null || fail "Not authenticated to github"
 | |
| 
 | |
| # Find the last commit that was merged.  We will look back in `main`
 | |
| # for other commits from the same PR.
 | |
| # shellcheck disable=SC2016
 | |
| merge_commit="$(
 | |
|     gh api graphql \
 | |
|         -q '.data.repository.pullRequest.timelineItems.nodes[0].commit.oid' \
 | |
|         -F pr_id="$1" \
 | |
|         -f query='
 | |
| query($pr_id:Int!) {
 | |
|   repository(name: "zulip", owner: "zulip") {
 | |
|     pullRequest(number:$pr_id) {
 | |
|       timelineItems(last:1, itemTypes: [MERGED_EVENT]) {
 | |
|         nodes {
 | |
|           ... on MergedEvent {
 | |
|             commit {
 | |
|               oid
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| '
 | |
| )"
 | |
| 
 | |
| # We cannot trust the "commits" count on the PR, since only part of it
 | |
| # may get merged, or it may have commits squashed during the merge.
 | |
| # Walk backwards on `main` from the merge commit we found, checking
 | |
| # that each of those commits is still associated with the same PR.
 | |
| commit_id="$merge_commit"
 | |
| while true; do
 | |
|     # shellcheck disable=SC2016
 | |
|     this_pr="$(gh api graphql -F "commit_id=$commit_id 0" \
 | |
|         --jq '.data.repository.ref.target.history.edges[].node.associatedPullRequests.nodes[].number' \
 | |
|         -f query='
 | |
| query($commit_id: String!) {
 | |
|   repository(owner: "zulip", name:"zulip") {
 | |
|     ref(qualifiedName:"main") {
 | |
|       target {
 | |
|         ... on Commit {
 | |
|           history(first:1, after: $commit_id) {
 | |
|             edges {
 | |
|               node {
 | |
|                 oid
 | |
|                 associatedPullRequests(first: 1) {
 | |
|                   nodes {
 | |
|                     number
 | |
|                   }
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }')"
 | |
|     if [ "$this_pr" != "$pr_id" ]; then
 | |
|         break
 | |
|     fi
 | |
|     commit_id="$(git rev-parse "$commit_id"~)"
 | |
| done
 | |
| 
 | |
| set -x
 | |
| git cherry-pick -x "$commit_id~..$merge_commit"
 |