1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
|
#! /bin/bash
function usage_exit {
>&2 echo "Usage: ghmerge <options including prnum and reviewer(s)>
or ghmerge [<options>] -- <prnum> <reviewer>...
Options may include addrev options and gitaddrev filter args.
Option style arguments:
--help Print this help and exit
--tools Merge a tools PR (rather than openssl PR)
--web Merge a web PR (rather than openssl PR)
--remote <remote> Repo to merge with (rather than git.openssl.org), usually 'upstream'
--ref <branch> Branch to merge with (rather than current branch), usually 'master'
--cherry-pick Use cherry-pick (rather than pull --rebase)
--squash Squash new commits non-interactively (allows editing msg)
--noautosquash Do not automatically squash fixups in interactive rebase
--nobuild Do not call 'openssbuild' before merging
Examples:
ghmerge 12345 mattcaswell
ghmerge 12345 paulidale t8m --nobuild --myemail=dev@ddvo.net
ghmerge edd05b7^^^^..19692bb2c32 --squash -- 12345 levitte
ghmerge 12345 slontis --ref openssl-3.0"
exit 9
}
set -o errexit
WHAT=""
PICK=no
INTERACTIVE=yes
AUTOSQUASH="--autosquash"
REMOTE=""
REF=""
BUILD=yes
[ -z ${CC+x} ] && CC="ccache gcc" # opensslbuild will otherwise use "ccache clang-3.6"
if [ ! -d .git ] ; then
echo Not at a top-level git directory
exit 1
fi
PRNUM=
TEAM=""
ADDREVOPTS=""
# Parse JCL.
shopt -s extglob
while [ $# -ne 0 ]; do
case "$1" in
--help)
usage_exit
;;
--tools)
WHAT=tools ; BUILD=no ; shift
;;
--web)
WHAT=web ; BUILD=no ; shift
;;
--cherry-pick)
PICK=yes ; shift
;;
--noautosquash)
AUTOSQUASH="" ; shift
;;
--squash)
INTERACTIVE=no ; shift
;;
--nobuild)
BUILD=no ; shift
;;
--remote)
if [ $# -lt 2 ] ; then
echo "Missing argument of '$1'"
usage_exit
fi
shift; REMOTE=$1; shift
;;
--ref)
if [ $# -lt 2 ] ; then
echo "Missing argument of '$1'"
usage_exit
fi
shift; REF=$1; shift
;;
--)
if [ $# -lt 3 ] ; then
echo "Missing <prnum> <reviewer>... after '--'"
usage_exit
fi
shift; PRNUM=$1 ; shift
TEAM="$TEAM $*"
break
;;
-*) # e.g., --verbose, --trivial, --myemail=...
ADDREVOPTS="$ADDREVOPTS $1"
shift
;;
+([[:digit:]]) ) # e.g., 1453
PRNUM=$1; shift
;;
@*) # e.g., @t8m
TEAM="$TEAM $1"; shift
;;
+([[:alnum:]-]) ) # e.g., levitte
if [[ $1 =~ ^[0-9a-f]{7,}+$ ]]; then # e.g., edd05b7
ADDREVOPTS="$ADDREVOPTS $1"
else
TEAM="$TEAM $1"
fi
shift
;;
*) # e.g., edd05b7^^^^..19692bb2c32
ADDREVOPTS="$ADDREVOPTS $1"; shift
;;
esac
done
if [ "$WHAT" = "" ] ; then
WHAT="openssl"
else
ADDREVOPTS="$ADDREVOPTS --$WHAT"
fi
ADDREVOPTS=${ADDREVOPTS# } # chop any leading ' '
[ "$REMOTE" = "" ] && REMOTE=`git remote -v | awk '/git.openssl.org.*(push)/{ print $1; }' | head -n 1` # usually this will be 'upstream'
if [ "$REMOTE" = "" ] ; then
echo Cannot find git remote with URL including 'git.openssl.org'
exit 1
fi
if [ "$PRNUM" = "" -o "$TEAM" = "" ] ; then
usage_exit
fi
PR_URL=https://api.github.com/repos/openssl/$WHAT/pulls/$PRNUM
if ! wget --quiet $PR_URL -O /tmp/gh$$; then
echo "Error getting $PR_URL"
exit 1
fi
set -- `python -c '
from __future__ import print_function
import json, sys;
input = json.load(sys.stdin)
print(str(input["head"]["label"]).replace(":", " "),
str(input["head"]["repo"]["ssh_url"]))' </tmp/gh$$`
WHO=$1
BRANCH=$2
REPO=$3
rm /tmp/gh$$
if [ -z "$WHO" -o -z "$BRANCH" -o -z "$REPO" ]; then
echo "Could not determine from $PR_URL which branch of whom to fetch from where"
exit 1
fi
ORIG_REF=`git rev-parse --abbrev-ref HEAD` # usually this will be 'master'
STASH_OUT=`git stash`
WORK="copy-of-${WHO}-${BRANCH}"
(git branch | grep -q "$WORK") && (echo "Branch already exists: $WORK"; exit 1)
function cleanup {
rv=$?
echo # make sure to enter new line, needed, e.g., after Ctrl-C
[ $rv -ne 0 ] && echo -e "\nghmerge failed"
if [ "$REBASING" == 1 ] ; then
git rebase --abort 2>/dev/null || true
fi
if [ "$CHERRYPICKING" == 1 ] ; then
echo "Hint: maybe --cherry-pick was not given a suitable <n> parameter."
git cherry-pick --abort 2>/dev/null || true
fi
if [ "$REF" != "$ORIG_REF" ] || [ "$WORK_USED" != "" ]; then
echo Returning to previous branch $ORIG_REF
git checkout -q $ORIG_REF
fi
if [ "$WORK_USED" != "" ]; then
git branch -qD $WORK_USED
fi
if [ "$STASH_OUT" != "No local changes to save" ]; then
git stash pop -q # restore original state, pruning any leftover commits added locally
fi
}
trap 'cleanup' EXIT
[ "$REF" = "" ] && REF=$ORIG_REF
if [ "$REF" != "$ORIG_REF" ]; then
echo -n "Press Enter to checkout $REF: "; read foo
git checkout $REF
fi
echo -n "Press Enter to pull the latest $REMOTE/$REF: "; read foo
REBASING=1
git pull $REMOTE $REF || exit 1
REBASING=
WORK_USED=$WORK
# append new commits from $REPO/$BRANCH
if [ "$PICK" != "yes" ]; then
echo Rebasing $REPO/$BRANCH on $REF...
git fetch $REPO $BRANCH && git checkout -b $WORK FETCH_HEAD
WORK_USED=$WORK
REBASING=1
git rebase $REF || (echo 'Fix or Ctrl-d to abort' ; read || exit 1)
REBASING=
else
echo Cherry-picking $REPO/$BRANCH to $REF...
git checkout -b $WORK $REF
WORK_USED=$WORK
CHERRYPICKING=1
git fetch $REPO $BRANCH && (git cherry-pick FETCH_HEAD || exit 1)
CHERRYPICKING=
fi
echo Diff against $REMOTE/$REF
git diff $REMOTE/$REF
if [ "$INTERACTIVE" == "yes" ] ; then
echo -n "Press Enter to interactively rebase $AUTOSQUASH on $REMOTE/$REF: "; read foo
REBASING=1
git rebase -i $AUTOSQUASH $REMOTE/$REF || exit 1
REBASING=
echo "Calling addrev $ADDREVOPTS --prnum=$PRNUM $TEAM $REMOTE/$REF.."
addrev $ADDREVOPTS --prnum=$PRNUM $TEAM $REMOTE/$REF..
fi
echo Log since $REMOTE/$REF
git log $REMOTE/$REF..
git checkout $REF
if [ "$INTERACTIVE" != "yes" ] ; then
echo -n "Press Enter to non-interactively merge --squash $BRANCH to $REMOTE/$REF: "; read foo
git merge --ff-only --no-commit --squash $WORK
AUTHOR=`git show --no-patch --pretty="format:%an <%ae>" $WORK`
git commit --author="$AUTHOR"
addrev $ADDREVOPTS --prnum=$PRNUM $TEAM $REMOTE/${REF}..
else
# echo -n "Press Enter to merge to $REMOTE/$REF: "; read foo
git merge --ff-only $WORK
fi
echo New log since $REMOTE/$REF
git log $REMOTE/$REF..
if [ "$BUILD" == "yes" ] ; then
echo Rebuilding...
CC="$CC" opensslbuild >/dev/null # any STDERR output will be shown
fi
while true ; do
echo -n "Enter 'y'/'yes' to push to $REMOTE/$REF or 'n'/'no' to abort: "
read x
x="`echo $x | tr A-Z a-z`"
if [ "$x" = "y" -o "$x" = "yes" -o "$x" = "n" -o "$x" = "no" ] ; then
break
fi
done
if [ "$x" = "y" -o "$x" = "yes" ] ; then
git push -v $REMOTE $REF
fi
|