-
-
Save ruudud/f8a2b3146aed6a5b12fd to your computer and use it in GitHub Desktop.
GitHub Pull Request Applier
This file contains 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 | |
# 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 <[email protected]>, @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##[email protected]:}" | |
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