Skip to content

Instantly share code, notes, and snippets.

@ruudud
Last active August 6, 2018 09:44
Show Gist options
  • Save ruudud/f8a2b3146aed6a5b12fd to your computer and use it in GitHub Desktop.
Save ruudud/f8a2b3146aed6a5b12fd to your computer and use it in GitHub Desktop.
GitHub Pull Request Applier
#!/bin/bash
# Motivation:
# http://blog.spreedly.com/2014/06/24/merge-pull-request-considered-harmful/
#
# This script aids in the process of getting a linear Git history when using
# GitHub Pull Requests.
# A special GitHub remote ref is added, which is fetched and used to create a
# patch of changes to be applied with `git am`. The PR is then closed.
#
# INSTALL
# Copy this script to a folder in your $PATH, and make it executable.
# Eg. `cp ghpr ~/bin && chmod u+x ~/bin/ghpr`
#
# To be able to filter out closed PRs, you need to create a token on GitHub and
# save it it your git config.
# 1. Go to https://github.com/settings/tokens
# 2. Generate new token with at least the "repo" scope enabled.
# 3. Run `git config --global github.token <TOKEN_FROM_GITHUB>`
#
# Author: Pål Ruud <ruudud@gmail.com>, @ruudud
RESET=$(tput sgr0);
BOLD=$(tput bold);
scriptname="$(basename $0)"
mainbranch="master"
remotename="origin"
token=$(git config --get github.token)
ghapi="https://api.github.com"
usage() {
echo "GitHub Pull Request applier."
echo ""
echo "Usage:"
echo " ${scriptname} <command>"
echo ""
echo "Available commands:"
echo " list Lists available pull requests"
echo " apply <pr> Apply given pull request to current branch"
echo ""
}
cd_to_nearest_git() {
local current="$PWD"
while [[ "$PWD" != '/' && ! -d '.git' ]]; do cd ..; done
if [[ "$PWD" == '/' ]]; then
cd $current
echo "fatal: Could not find .git directory in current tree."
exit 1
fi
}
ensure_github_token() {
if [[ -z $token ]]; then
echo "fatal: Could not get GitHub token from git config."
echo ""
echo "To be able to filter out closed PRs, you need a GH token."
echo " 1. Go to https://github.com/settings/tokens"
echo " 2. Generate new token with at least the 'repo' scope enabled."
echo " 3. Run \`git config --global github.token <TOKEN_FROM_GITHUB>\`"
exit 1
fi
}
ensure_master_branch() {
local current=$(git rev-parse --abbrev-ref HEAD)
if [[ "$current" != "$mainbranch" ]]; then
echo "fatal: Current branch is not ${mainbranch}."
echo "Run ${BOLD}git checkout ${mainbranch}${RESET} first."
exit 1
fi
}
ensure_pr_fetch() {
local linebefore insertat
local gitconf=".git/config"
local fetchline=" fetch = +refs/pull/*/head:refs/remotes/${remotename}/pr/*"
if ! grep -q 'refs\/pull' ${gitconf}; then
linebefore=$(grep -n "remote \"${remotename}\"" .git/config | grep -Eow '[0-9]+')
insertat=$((linebefore + 1))
awk -v n=${insertat} -v s="${fetchline}" 'NR == n {print s} {print}' \
${gitconf} > ${gitconf}.new
mv ${gitconf}.new ${gitconf}
fi
}
do_fetch() {
echo "Fetching ${remotename}..."
git fetch -q "$remotename"
}
get_user_repo() {
local remoteurl=$(cat .git/config | grep 'url =' | awk '{print $3}')
remoteurl="${remoteurl##git@github.com:}"
echo "${remoteurl%%.git}"
}
# GitHub response cache
ghresponse=""
is_pr_open() {
local prid="$1"
local userrepo=$(get_user_repo)
local url="${ghapi}/repos/${userrepo}/pulls?state=open"
if [[ -z $ghresponse ]]; then
ghresponse=$(curl -s -H"Authorization: token ${token}" "$url")
fi
if echo "$ghresponse" | grep -q "\"number\": ${prid},"; then
return 0
else
return 1
fi
}
list() {
local prid
local pr_branches=$(git branch -r | grep '/pr/')
echo "Checking for open PR branches..."
for branch in $pr_branches; do
prid=${branch##${remotename}/pr/}
if ! is_pr_open "$prid"; then continue; fi
echo -e "\n${BOLD}Pull request: ${prid}${RESET}"
git log --oneline ${remotename}/${mainbranch}..${branch}
echo "To apply: ${BOLD}${scriptname} apply ${prid}${RESET}"
echo ""
done
}
apply() {
local prid=$1
git format-patch --stdout ${remotename}/${mainbranch}..${remotename}/pr/${prid} | git am -3
}
close_pr() {
local prid=$1
local userrepo=$(get_user_repo)
local url="${ghapi}/repos/${userrepo}/pulls/${prid}"
local responsecode=$(curl -s -o /dev/null --write-out "%{http_code}" \
-XPATCH -d '{"state": "closed"}' \
-H"Authorization: token ${token}" "$url")
if [[ "$responsecode" != "200" ]]; then
echo "fatal: Got status ${responsecode} when trying to close PR ${prid}."
exit 1
fi
}
cd_to_nearest_git
ensure_github_token
ensure_pr_fetch
case $1 in
list)
do_fetch
list
;;
apply)
ensure_master_branch
if [[ -z $2 ]]; then
echo "fatal: Please specify pull request ID to apply."
exit 1
fi
apply "$2"
close_pr "$2"
;;
*)
usage
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment