• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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