4 >&2 echo "Usage: ghmerge <options including prnum and reviewer(s)>
5 or ghmerge [<options>] -- <prnum> <reviewer>...
6 Options may include addrev options and gitaddrev filter args.
8 Option style arguments:
10 --help Print this help and exit
11 --tools Merge a tools PR (rather than openssl PR)
12 --web Merge a web PR (rather than openssl PR)
13 --fuzz-corpora Merge a PR against fuzz-corpora (rather than openssl PR)
14 --remote <remote> Repo to merge with (rather than git.openssl.org), usually 'upstream'
15 --target <branch> Merge target (rather than current branch), usually 'master'
16 --ref <branch> A synonym for --target
17 --cherry-pick [<n>] Cherry-pick the last n (1 <= n <= 99, default: 1) commits, rather than rebasing
18 --squash Squash new commits non-interactively (allows editing msg)
19 --noautosquash Do not automatically squash fixups in interactive rebase
20 --nobuild Do not call 'openssbuild' before merging
24 ghmerge 12345 mattcaswell
25 ghmerge 12345 paulidale t8m --nobuild --myemail=dev@ddvo.net
26 ghmerge edd05b7^^^^..19692bb2c32 --squash -- 12345 levitte
27 ghmerge 12345 slontis --target openssl-3.0
28 ghmerge --nobuild --target openssl-3.0 --cherry-pick 3 16051 paulidale"
37 AUTOSQUASH="--autosquash"
41 [ -z ${CC+x} ] && CC="ccache gcc" # opensslbuild will otherwise use "ccache clang-3.6"
43 if [ ! -d .git ] ; then
44 echo Not at a top-level git directory
53 while [ $# -ne 0 ]; do
59 WHAT=tools ; BUILD=no ; shift
62 WHAT=web ; BUILD=no ; shift
65 WHAT=fuzz-corpora ; BUILD=no ; shift
70 if [ "$1" != "" ] && [ $1 -ge 1 ] 2>/dev/null && [ $1 -le 99 ]; then
78 INTERACTIVE=no ; shift
84 if [ $# -lt 2 ] ; then
85 echo "Missing argument of '$1'"
88 shift; REMOTE=$1; shift
91 if [ $# -lt 2 ] ; then
92 echo "Missing argument of '$1'"
95 shift; TARGET=$1; shift
98 if [ $# -lt 3 ] ; then
99 echo "Missing <prnum> <reviewer>... after '--'"
102 shift; PRNUM=$1 ; shift
106 -*) # e.g., --verbose, --trivial, --myemail=...
107 ADDREVOPTS="$ADDREVOPTS $1"
110 +([[:digit:]]) ) # e.g., 1453
114 TEAM="$TEAM $1"; shift
116 +([[:alnum:]-]) ) # e.g., levitte
117 if [[ $1 =~ ^[0-9a-f]{7,}+$ ]]; then # e.g., edd05b7
118 ADDREVOPTS="$ADDREVOPTS $1"
124 *) # e.g., edd05b7^^^^..19692bb2c32
125 ADDREVOPTS="$ADDREVOPTS $1"; shift
130 if [ "$WHAT" = "" ] ; then
133 ADDREVOPTS="$ADDREVOPTS --$WHAT"
135 ADDREVOPTS=${ADDREVOPTS# } # chop any leading ' '
137 [ "$REMOTE" = "" ] && REMOTE=`git remote -v | awk '/github.openssl.org:(openssl|omc|otc).*(push)/{ print $1; }' | head -n 1` # usually this will be 'upstream'
138 if [ "$REMOTE" = "" ] ; then
139 echo Cannot find git remote with URL including 'github.openssl.org'
143 if [ "$PRNUM" = "" -o "$TEAM" = "" ] ; then
147 PR_URL=https://api.github.com/repos/openssl/$WHAT/pulls/$PRNUM
148 if ! wget --quiet $PR_URL -O /tmp/gh$$; then
149 echo "Error getting $PR_URL"
153 from __future__ import print_function
155 input = json.load(sys.stdin)
156 print(str(input["head"]["label"]).replace(":", " "),
157 str(input["head"]["repo"]["ssh_url"]))' </tmp/gh$$`
163 if [ -z "$WHO" -o -z "$BRANCH" -o -z "$REPO" ]; then
164 echo "Could not determine from $PR_URL which branch of whom to fetch from where"
168 ORIG_REF=`git rev-parse --abbrev-ref HEAD` # usually this will be 'master'
169 STASH_OUT=`git stash`
170 WORK="copy-of-${WHO}-${BRANCH}"
172 (git branch | grep -q "$WORK") && (echo "Branch already exists: $WORK"; exit 1)
176 echo # make sure to enter new line, needed, e.g., after Ctrl-C
177 [ $rv -ne 0 ] && echo -e "\nghmerge failed"
178 if [ "$REBASING" == 1 ] ; then
179 git rebase --abort 2>/dev/null || true
181 if [ "$CHERRYPICKING" == 1 ] ; then
182 echo "Hint: maybe --cherry-pick was not given a suitable <n> parameter."
183 git cherry-pick --abort 2>/dev/null || true
185 if [ "$ORIG_TARGET_HEAD" != "" ]; then
186 echo Restoring original commit HEAD of $TARGET
187 git reset --hard "$ORIG_TARGET_HEAD"
189 if [ "$TARGET" != "$ORIG_REF" ] || [ "$WORK_USED" != "" ]; then
190 echo Returning to previous branch $ORIG_REF
191 git checkout -q $ORIG_REF
193 if [ "$WORK_USED" != "" ]; then
194 git branch -qD $WORK_USED
196 if [ "$STASH_OUT" != "No local changes to save" ]; then
197 git stash pop -q # restore original state of $ORIG_REF, pruning any leftover commits added locally
202 [ "$TARGET" = "" ] && TARGET=$ORIG_REF
203 if [ "$TARGET" != "$ORIG_REF" ]; then
204 echo -n "Press Enter to checkout $TARGET: "; read foo
208 echo -n "Press Enter to pull the latest $REMOTE/$TARGET: "; read foo
210 git pull $REMOTE $TARGET || exit 1
213 # append new commits from $REPO/$BRANCH
214 if [ "$PICK" == "no" ]; then
215 echo Rebasing $REPO/$BRANCH on $TARGET...
216 git fetch $REPO $BRANCH && git checkout -b $WORK FETCH_HEAD
219 git rebase $TARGET || (echo -ne "Press Ctrl-d to abort, or fix the issue in another shell,\n run 'git rebase --continue' there, and on success press Enter here: "; read || exit 1)
222 echo Cherry-picking $REPO/$BRANCH to $TARGET...
223 git checkout -b $WORK $TARGET
226 git fetch $REPO $BRANCH && (git cherry-pick FETCH_HEAD~$PICK..FETCH_HEAD || (echo -ne "Press Ctrl-d to abort, or fix the issue in another shell,\n run 'git cherry-pick --continue' there, and on success press Enter here: "; read || exit 1))
231 echo Log since $REMOTE/$TARGET:
232 git log $REMOTE/$TARGET..
235 echo Diff against $REMOTE/$TARGET:
236 git diff $REMOTE/$TARGET
238 if [ "$INTERACTIVE" == "yes" ] ; then
240 echo -n "Press Enter to interactively rebase $AUTOSQUASH on $REMOTE/$TARGET: "; read foo
242 git rebase -i $AUTOSQUASH $REMOTE/$TARGET || (echo -ne "Press Ctrl-d to abort, or fix the issue in another shell,\n run 'git rebase --continue' there, and on success press Enter here: "; read || exit 1)
243 if [ -e .git/rebase-merge ] ; then # likely, user tried 'b' or 'e' command to break or enter edit mode
244 echo -e "\nRebasing was stopped; please do your changes in parallel using another\n shell in the same directory, then give 'git rebase --continue' there"
245 while [ -e .git/rebase-merge ] ; do sleep 1 ; done
249 echo "Calling addrev $ADDREVOPTS --prnum=$PRNUM $TEAM $REMOTE/$TARGET.."
250 addrev $ADDREVOPTS --prnum=$PRNUM $TEAM $REMOTE/$TARGET..
254 if [ "$INTERACTIVE" != "yes" ] ; then
256 echo -n "Press Enter to non-interactively merge --squash $BRANCH to $REMOTE/$TARGET: "; read foo
257 ORIG_TARGET_HEAD=`git show -s --format="%H"`
258 git merge --ff-only --no-commit --squash $WORK
259 AUTHOR=`git show --no-patch --pretty="format:%an <%ae>" $WORK`
260 git commit --author="$AUTHOR"
261 addrev $ADDREVOPTS --prnum=$PRNUM $TEAM $REMOTE/${TARGET}..
263 # echo -n "Press Enter to merge to $TARGET: "; read foo
265 echo "Merging to $TARGET"
266 ORIG_TARGET_HEAD=`git show -s --format="%H"`
267 git merge --ff-only $WORK
271 echo New log including addrev output since $REMOTE/$TARGET:
272 git log $REMOTE/$TARGET..
274 if [ "$BUILD" == "yes" ] ; then
276 CC="$CC" opensslbuild >/dev/null # any STDERR output will be shown
281 echo -n "Enter 'y'/'yes' to push to $REMOTE/$TARGET or 'n'/'no' to abort: "
283 x="`echo $x | tr A-Z a-z`"
284 if [ "$x" = "y" -o "$x" = "yes" -o "$x" = "n" -o "$x" = "no" ] ; then
289 if [ "$x" = "y" -o "$x" = "yes" ] ; then
290 git push -v $REMOTE $TARGET