1#!/usr/bin/env bash 2 3# Land a pull request 4# Creates a PR-### branch, pulls the commits, opens up an interactive rebase to 5# squash, and then annotates the commit with the changelog goobers 6# 7# Usage: 8# pr <url|number> [<upstream remote>=origin] 9 10main () { 11 if [ "$1" = "finish" ]; then 12 shift 13 finish "$@" 14 return $? 15 fi 16 17 local url="$(prurl "$@")" 18 local num=$(basename $url) 19 local prpath="${url#git@github.com:}" 20 local repo=${prpath%/pull/$num} 21 local prweb="https://github.com/$prpath" 22 local root="$(prroot "$url")" 23 local api="https://api.github.com/repos/${repo}/pulls/${num}" 24 local user=$(curl -s $api | json user.login) 25 local ref="$(prref "$url" "$root")" 26 local curhead="$(git show --no-patch --pretty=%H HEAD)" 27 local curbranch="$(git rev-parse --abbrev-ref HEAD)" 28 local cleanlines 29 IFS=$'\n' cleanlines=($(git status -s -uno)) 30 if [ ${#cleanlines[@]} -ne 0 ]; then 31 echo "working dir not clean" >&2 32 IFS=$'\n' echo "${cleanlines[@]}" >&2 33 echo "aborting PR merge" >&2 34 fi 35 36 # ok, ready to rock 37 branch=PR-$num 38 if [ "$curbranch" == "$branch" ]; then 39 echo "already on $branch, you're on your own" >&2 40 return 1 41 fi 42 43 me=$(git config github.user || git config user.name) 44 if [ "$me" == "" ]; then 45 echo "run 'git config --add github.user <username>'" >&2 46 return 1 47 fi 48 49 exists=$(git show --no-patch --pretty=%H $branch 2>/dev/null) 50 if [ "$exists" == "" ]; then 51 git fetch origin pull/$num/head:$branch 52 git checkout $branch 53 else 54 git checkout $branch 55 git pull --rebase origin pull/$num/head 56 fi 57 58 git rebase -i $curbranch # squash and test 59 60 if [ $? -eq 0 ]; then 61 finish "${curbranch}" 62 else 63 echo "resolve conflicts and run: $0 finish "'"'${curbranch}'"' 64 fi 65} 66 67# add the PR-URL to the last commit, after squashing 68finish () { 69 if [ $# -eq 0 ]; then 70 echo "Usage: $0 finish <branch> (while on a PR-### branch)" >&2 71 return 1 72 fi 73 74 local curbranch="$1" 75 local ref=$(cat .git/HEAD) 76 local prnum 77 case $ref in 78 "ref: refs/heads/PR-"*) 79 prnum=${ref#ref: refs/heads/PR-} 80 ;; 81 *) 82 echo "not on the PR-## branch any more!" >&2 83 return 1 84 ;; 85 esac 86 87 local me=$(git config github.user || git config user.name) 88 if [ "$me" == "" ]; then 89 echo "run 'git config --add github.user <username>'" >&2 90 return 1 91 fi 92 93 set -x 94 95 local url="$(prurl "$prnum")" 96 local num=$prnum 97 local prpath="${url#git@github.com:}" 98 local repo=${prpath%/pull/$num} 99 local prweb="https://github.com/$prpath" 100 local root="$(prroot "$url")" 101 102 local api="https://api.github.com/repos/${repo}/pulls/${num}" 103 local user=$(curl -s $api | json user.login) 104 105 local lastmsg="$(git log -1 --pretty=%B)" 106 local newmsg="${lastmsg} 107 108PR-URL: ${prweb} 109Credit: @${user} 110Close: #${num} 111Reviewed-by: @${me} 112" 113 git commit --amend -m "$newmsg" 114 git checkout $curbranch 115 git merge PR-${prnum} --ff-only 116 set +x 117} 118 119 120prurl () { 121 local url="$1" 122 if [ "$url" == "" ] && type pbpaste &>/dev/null; then 123 url="$(pbpaste)" 124 fi 125 if [[ "$url" =~ ^[0-9]+$ ]]; then 126 local us="$2" 127 if [ "$us" == "" ]; then 128 us="origin" 129 fi 130 local num="$url" 131 local o="$(git config --get remote.${us}.url)" 132 url="${o}" 133 url="${url#(git:\/\/|https:\/\/)}" 134 url="${url#git@}" 135 url="${url#github.com[:\/]}" 136 url="${url%.git}" 137 url="https://github.com/${url}/pull/$num" 138 fi 139 url=${url%/commits} 140 url=${url%/files} 141 url="$(echo $url | perl -p -e 's/#issuecomment-[0-9]+$//g')" 142 143 local p='^https:\/\/github.com\/[^\/]+\/[^\/]+\/pull\/[0-9]+$' 144 if ! [[ "$url" =~ $p ]]; then 145 echo "Usage:" 146 echo " $0 <pull req url>" 147 echo " $0 <pull req number> [<remote name>=origin]" 148 type pbpaste &>/dev/null && 149 echo "(will read url/id from clipboard if not specified)" 150 exit 1 151 fi 152 url="${url/https:\/\/github\.com\//git@github.com:}" 153 echo "$url" 154} 155 156prroot () { 157 local url="$1" 158 echo "${url/\/pull\/+([0-9])/}" 159} 160 161prref () { 162 local url="$1" 163 local root="$2" 164 echo "refs${url:${#root}}/head" 165} 166 167main "$@" 168