Last active
September 25, 2023 13:23
-
-
Save douglascayers/fefd09a9a317796aaec22a309228c59f to your computer and use it in GitHub Desktop.
Iteratively merges into local branch a list of pull requests in sequence using the GitHub and Git CLIs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -e | |
# Name of the branch to merge all the pull requests into. | |
MERGE_BRANCH="staging" | |
# Name of the branch to hard reset to before merging others into it. | |
RESET_BRANCH="master" | |
# Multi-line string of open, non-draft pull requests to merge locally. | |
# Each line is a space-delimited string of "pr_number base_branch head_branch". | |
PULL_REQUESTS=$( | |
gh pr list \ | |
--state open \ | |
--draft=false \ | |
--json number,baseRefName,headRefName \ | |
-q '.[] | "\(.number) \(.baseRefName) \(.headRefName)"' | |
) | |
# Map each line of the pull requests query result into a simple array | |
# so that we can more easily iterate the data later. | |
PR_INFO=() | |
while read -r pr_number base_branch head_branch; do | |
PR_INFO+=("$pr_number $base_branch $head_branch") | |
done <<< "$PULL_REQUESTS" | |
# Simple array of pull request numbers in the order they should be merged. | |
ORDERED_PRS=() | |
# ---------------------------------------------------------------------------- | |
# $1 = message to log | |
function log_info() { | |
echo "" >&2 | |
echo -e "> ${1}" >&2 | |
echo "" >&2 | |
} | |
# Fetches all history. | |
function git_fetch() { | |
git fetch --all --force --prune --prune-tags --no-tags --quiet | |
} | |
# Checks out a pull request into the current git repo. | |
# $1 = branch to checkout and merge pull requests into (required) | |
# $2 = remote branch to hard reset to (defaults to $1) | |
function git_checkout() { | |
local branch="${1}" | |
local reset_to="${2:-$1}" | |
log_info "checking out branch '${branch}'" | |
git checkout -B ${branch} origin/${branch} | |
git reset --hard origin/${reset_to} | |
} | |
# Get the head branch name of the currently checked out git repository. | |
# $1 = pull request url or number (optional) | |
function get_head_ref() { | |
local pr_id="${1}" | |
gh pr view ${pr_id} --json headRefName -q '.headRefName' | |
} | |
# $1 = pull request url or number (optional) | |
function merge_head_ref() { | |
local pr_id="${1}" | |
local head_ref="$(get_head_ref ${pr_id})" | |
log_info "merging branch '${head_ref}'" | |
git merge "origin/${head_ref}" --no-edit --no-ff --no-verify | |
} | |
# Iterates the pull requests to identify the dependency order for merging. | |
# Outputs the pull request numbers to new array variable `ORDERED_PRS`. | |
function calculate_merge_order() { | |
local branch="${1}" | |
for pr_info in "${PR_INFO[@]}"; do | |
read -r pr_number base_branch head_branch <<< "${pr_info}" | |
if [[ "${base_branch}" == "${branch}" ]]; then | |
ORDERED_PRS+=("${pr_number}") | |
calculate_merge_order "${head_branch}" | |
fi | |
done | |
} | |
# Iterates the pull requests in their dependency order and merges them locally. | |
function merge_ordered_prs() { | |
for pr_number in "${ORDERED_PRS[@]}"; do | |
head_ref=$(get_head_ref ${pr_number}) | |
merge_result=$(merge_head_ref ${head_ref}) | |
has_conflicts=$(git status | grep --quiet "fix conflicts" && echo 1 || echo 0) | |
if [[ "${has_conflicts}" == "1" ]]; then | |
log_info "merge conflicts, aborting" | |
git status | |
exit 1 | |
fi | |
done | |
} | |
# ---------------------------------------------------------------------------- | |
git_fetch | |
git_checkout "${MERGE_BRANCH}" "${RESET_BRANCH}" | |
calculate_merge_order "${RESET_BRANCH}" | |
merge_ordered_prs |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment