Last active
September 21, 2021 02:11
-
-
Save CourteousCoder/5da5ab148d088ab4d8087bfb99875178 to your computer and use it in GitHub Desktop.
Script to merge pull requests from github, in order, into a locally cloned github repository
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
#!/usr/bin/env zsh | |
# Merge pull requests in the given order into a locally cloned github repository. | |
# Run with no arguments to print help text. | |
SCRIPT_NAME="$0" | |
# Path to repository root. | |
REPOSITORY_ROOT="$1" | |
# An array of pull-request IDs to merge locally | |
PULL_REQUEST_IDS="${@:2}" | |
# Handle conflicts by accepting changes already merged, disregarding the PR's conflicitng changes. | |
# Change to "theirs" to accept the PR's changes when they conflict with the already-merged changes. | |
CONFLICT_RESOLUTION_STRATEGY="ours" | |
# Print help text. | |
show_help() { | |
cat << END_OF_HELP_TEXT | |
Merge pull requests in the given order into a locally cloned github repository | |
usage: $SCRIPT_NAME repository_root pr_number... | |
Or run with no arguments to show this help text. | |
exit codes: | |
0 all given PRs were merged successfully into the local repository | |
1 error due to invalid input | |
2 one or more the PRs were not merged for git- or github-related reasons | |
END_OF_HELP_TEXT | |
} | |
# Print an error message stderr, show hep text, and then exit. | |
# If the first argument is a positive integer less than 256, it is used as an exit code, and the remainaing arguments are treated as the message. | |
# Otherwise, all arguments are treated as the message, and the value `1' is used as the exit code. | |
error_exit() { | |
local exit_code="$1" | |
[[ $exit_code =~ '^[0-9]+$' ]] && (( exit_code > 0 && exit_code < 256 )) && shift || exit_code=1 | |
>&2 echo 'ERROR' "$exit_code:" "$@" | |
show_help | |
echo 'Exiting now.' | |
exit $exit_code | |
} | |
# Run git commands as if the current working directory were $REPOSITORY_ROOT | |
cgit() { | |
git -C "$REPOSITORY_ROOT" "$@" | |
} | |
# Return 0 if the source branch was already merged into the destination branch. Return 1 otherwise. | |
is_already_merged() { | |
local source_branch=$(cgit rev-parse "$1") | |
local destination_branch=$(cgit rev-parse "$2") | |
cgit merge-base --is-ancestor "$source_branch" "$destination_branch" | |
} | |
# Merge the given pr into the destination branch. Return 0 if the pr has been merged. Return 1 otherwise. | |
merge_pr_locally() { | |
local pr_id="$1" | |
local pr_branch_name="pr-${pr_id}" | |
local destination_branch_name="$2" | |
cgit fetch --quiet origin pull/"${pr_id}"/head:"${pr_branch_name}" \ | |
&& cgit checkout --quiet "${pr_branch_name}" \ | |
&& cgit checkout --quiet "$destination_branch_name" \ | |
&& cgit merge --quiet --no-commit --strategy recursive --strategy-option patience --strategy-option "$CONFLICT_RESOLUTION_STRATEGY" "$pr_branch_name" \ | |
|| cgit checkout --quiet "$destination_branch_name" | |
is_already_merged "$pr_branch_name" "$destination_branch_name" | |
} | |
# Handle and validate input. | |
process_arguments() { | |
[[ "$#" -eq "0" ]] && show_help && exit | |
[[ "$#" -lt "2" ]] && error_exit "Expected arguments \`repository_root' and at least one \`pr_number'" | |
[[ $(cgit rev-parse --show-toplevel 2>/dev/null) = "$REPOSITORY_ROOT" ]] || error_exit "$REPOSITORY_ROOT is not the root of a git repository" | |
[[ $(cgit config --get remote.origin.url) == *github.com* ]] || error_exit "$REPOSITORY_ROOT does not have a remote \`origin' from github.com" | |
} | |
main() { | |
process_arguments "$@" | |
local start_branch="$(cgit rev-parse --abbrev-ref HEAD)" | |
local failed=() | |
for pr_id in "$PULL_REQUEST_IDS"; do | |
merge_pr_locally "$pr_id" "$start_branch" || failed+=("$pr_id") | |
done | |
[[ -n "$failed" ]] && echo "$failed" && error_exit 2 "Could not merge pull-request IDs \`$failed'" || true | |
} | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment