168 lines
4.0 KiB
Plaintext
168 lines
4.0 KiB
Plaintext
|
#!/usr/bin/env bash
|
||
|
|
||
|
# Land a pull request
|
||
|
# Creates a PR-### branch, pulls the commits, opens up an interactive rebase to
|
||
|
# squash, and then annotates the commit with the changelog goobers
|
||
|
#
|
||
|
# Usage:
|
||
|
# pr <url|number> [<upstream remote>=origin]
|
||
|
|
||
|
main () {
|
||
|
if [ "$1" = "finish" ]; then
|
||
|
shift
|
||
|
finish "$@"
|
||
|
return $?
|
||
|
fi
|
||
|
|
||
|
local url="$(prurl "$@")"
|
||
|
local num=$(basename $url)
|
||
|
local prpath="${url#git@github.com:}"
|
||
|
local repo=${prpath%/pull/$num}
|
||
|
local prweb="https://github.com/$prpath"
|
||
|
local root="$(prroot "$url")"
|
||
|
local api="https://api.github.com/repos/${repo}/pulls/${num}"
|
||
|
local user=$(curl -s $api | json user.login)
|
||
|
local ref="$(prref "$url" "$root")"
|
||
|
local curhead="$(git show --no-patch --pretty=%H HEAD)"
|
||
|
local curbranch="$(git rev-parse --abbrev-ref HEAD)"
|
||
|
local cleanlines
|
||
|
IFS=$'\n' cleanlines=($(git status -s -uno))
|
||
|
if [ ${#cleanlines[@]} -ne 0 ]; then
|
||
|
echo "working dir not clean" >&2
|
||
|
IFS=$'\n' echo "${cleanlines[@]}" >&2
|
||
|
echo "aborting PR merge" >&2
|
||
|
fi
|
||
|
|
||
|
# ok, ready to rock
|
||
|
branch=PR-$num
|
||
|
if [ "$curbranch" == "$branch" ]; then
|
||
|
echo "already on $branch, you're on your own" >&2
|
||
|
return 1
|
||
|
fi
|
||
|
|
||
|
me=$(git config github.user || git config user.name)
|
||
|
if [ "$me" == "" ]; then
|
||
|
echo "run 'git config --add github.user <username>'" >&2
|
||
|
return 1
|
||
|
fi
|
||
|
|
||
|
exists=$(git show --no-patch --pretty=%H $branch 2>/dev/null)
|
||
|
if [ "$exists" == "" ]; then
|
||
|
git fetch origin pull/$num/head:$branch
|
||
|
git checkout $branch
|
||
|
else
|
||
|
git checkout $branch
|
||
|
git pull --rebase origin pull/$num/head
|
||
|
fi
|
||
|
|
||
|
git rebase -i $curbranch # squash and test
|
||
|
|
||
|
if [ $? -eq 0 ]; then
|
||
|
finish "${curbranch}"
|
||
|
else
|
||
|
echo "resolve conflicts and run: $0 finish "'"'${curbranch}'"'
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
# add the PR-URL to the last commit, after squashing
|
||
|
finish () {
|
||
|
if [ $# -eq 0 ]; then
|
||
|
echo "Usage: $0 finish <branch> (while on a PR-### branch)" >&2
|
||
|
return 1
|
||
|
fi
|
||
|
|
||
|
local curbranch="$1"
|
||
|
local ref=$(cat .git/HEAD)
|
||
|
local prnum
|
||
|
case $ref in
|
||
|
"ref: refs/heads/PR-"*)
|
||
|
prnum=${ref#ref: refs/heads/PR-}
|
||
|
;;
|
||
|
*)
|
||
|
echo "not on the PR-## branch any more!" >&2
|
||
|
return 1
|
||
|
;;
|
||
|
esac
|
||
|
|
||
|
local me=$(git config github.user || git config user.name)
|
||
|
if [ "$me" == "" ]; then
|
||
|
echo "run 'git config --add github.user <username>'" >&2
|
||
|
return 1
|
||
|
fi
|
||
|
|
||
|
set -x
|
||
|
|
||
|
local url="$(prurl "$prnum")"
|
||
|
local num=$prnum
|
||
|
local prpath="${url#git@github.com:}"
|
||
|
local repo=${prpath%/pull/$num}
|
||
|
local prweb="https://github.com/$prpath"
|
||
|
local root="$(prroot "$url")"
|
||
|
|
||
|
local api="https://api.github.com/repos/${repo}/pulls/${num}"
|
||
|
local user=$(curl -s $api | json user.login)
|
||
|
|
||
|
local lastmsg="$(git log -1 --pretty=%B)"
|
||
|
local newmsg="${lastmsg}
|
||
|
|
||
|
PR-URL: ${prweb}
|
||
|
Credit: @${user}
|
||
|
Close: #${num}
|
||
|
Reviewed-by: @${me}
|
||
|
"
|
||
|
git commit --amend -m "$newmsg"
|
||
|
git checkout $curbranch
|
||
|
git merge PR-${prnum} --ff-only
|
||
|
set +x
|
||
|
}
|
||
|
|
||
|
|
||
|
prurl () {
|
||
|
local url="$1"
|
||
|
if [ "$url" == "" ] && type pbpaste &>/dev/null; then
|
||
|
url="$(pbpaste)"
|
||
|
fi
|
||
|
if [[ "$url" =~ ^[0-9]+$ ]]; then
|
||
|
local us="$2"
|
||
|
if [ "$us" == "" ]; then
|
||
|
us="origin"
|
||
|
fi
|
||
|
local num="$url"
|
||
|
local o="$(git config --get remote.${us}.url)"
|
||
|
url="${o}"
|
||
|
url="${url#(git:\/\/|https:\/\/)}"
|
||
|
url="${url#git@}"
|
||
|
url="${url#github.com[:\/]}"
|
||
|
url="${url%.git}"
|
||
|
url="https://github.com/${url}/pull/$num"
|
||
|
fi
|
||
|
url=${url%/commits}
|
||
|
url=${url%/files}
|
||
|
url="$(echo $url | perl -p -e 's/#issuecomment-[0-9]+$//g')"
|
||
|
|
||
|
local p='^https:\/\/github.com\/[^\/]+\/[^\/]+\/pull\/[0-9]+$'
|
||
|
if ! [[ "$url" =~ $p ]]; then
|
||
|
echo "Usage:"
|
||
|
echo " $0 <pull req url>"
|
||
|
echo " $0 <pull req number> [<remote name>=origin]"
|
||
|
type pbpaste &>/dev/null &&
|
||
|
echo "(will read url/id from clipboard if not specified)"
|
||
|
exit 1
|
||
|
fi
|
||
|
url="${url/https:\/\/github\.com\//git@github.com:}"
|
||
|
echo "$url"
|
||
|
}
|
||
|
|
||
|
prroot () {
|
||
|
local url="$1"
|
||
|
echo "${url/\/pull\/+([0-9])/}"
|
||
|
}
|
||
|
|
||
|
prref () {
|
||
|
local url="$1"
|
||
|
local root="$2"
|
||
|
echo "refs${url:${#root}}/head"
|
||
|
}
|
||
|
|
||
|
main "$@"
|