blob: bdf25c150bdf406148bfe17fdf4519e8878e9b33 [file] [log] [blame]
#!/bin/bash
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Forked and adapted from https://github.com/factorial-io/cherrypicker - MIT license
# Copyright (c) 2017 Shibin Das - @d34dman
function LOG() {
local STATUS=$1
local MESSAGE=$2
echo "[$(date)] ${STATUS} ${MESSAGE}"
}
function usage() {
cat << EOF
Usage: dev-tools/scripts/cherrypick.sh [<options>] <commit-hash> [<commit-hash>...]
-b <branch> Sets the branch(es) to cherry-pick to, typically branch_Nx or branch_x_y
-s Skips precommit test. WARNING: Always run precommit for code- and doc changes
-t Run the full test suite during check, not only precommit
-n Skips git pull of target branch. Useful if you are without internet access
-a Enters automated mode. Aborts cherry-pick and exits on error
-r <remote> Specify remote to push to. Defaults to whatever the branch is tracking.
-p Push to remote. Only done if both cherry-pick and tests succeeded
WARNING: Never push changes to a remote branch before a thorough local test
Simple script for aiding in back-porting one or more (trivial) commits to other branches.
On merge conflict the script will run 'git mergetool'. See 'git mergetool --help'
for help on configuring your favourite merge tool. Check out Sublime Merge (smerge).
Example:
# Backport two commits to both stable and release branches
dev-tools/scripts/cherrypick.sh -b branch_9x -b branch_9_0 deadbeef0000 cafebabe1111
EOF
}
function yesno() {
question=$1
unset answer
echo "$question"
while [[ "$answer" != "y" ]] && [[ "$answer" != "n" ]]; do
read -r answer
if [[ "$answer" == "y" ]]; then
true
else
false
fi
done
}
GIT_COMMAND=git
PRECOMMIT="true"
TESTARG="-x test"
TEST=
TESTS_PASSED=
PUSH=
REMOTE=
NOPULL=
AUTO_MODE=
unset BRANCHES
while getopts ":b:phstnar:" opt; do
case ${opt} in
b)
BRANCHES+=("$OPTARG")
;;
r)
REMOTE=$OPTARG
;;
p)
PUSH=true
;;
s)
PRECOMMIT=
;;
a)
AUTO_MODE="true"
;;
n)
NOPULL="true"
;;
t)
TEST="true"
TESTARG=""
;;
h)
usage
exit 0
;;
\?)
echo "Unknown option $OPTARG"
usage
exit 1
esac
done
shift $((OPTIND -1))
COMMITS=( "$@" )
if [ ${#BRANCHES[@]} -eq 0 ]; then
LOG INFO "Lacking -b option, must specify target branch. Supports multiple -b options too"
usage
exit
fi
if [ ${#COMMITS[@]} -eq 0 ]; then
LOG ERROR "Please specify one or more commits"
usage
exit
fi
## Loop over branches
for BRANCH in "${BRANCHES[@]}"; do
echo ""
LOG "INFO" "============================================";
LOG "INFO" "Git branch: $BRANCH"
LOG "INFO" "============================================";
echo ""
LOG INFO "Checking out target branch $BRANCH"
# shellcheck disable=SC2086
$GIT_COMMAND checkout $BRANCH
if [ $? -gt 0 ]; then
LOG ERROR "Failed checking out branch $BRANCH"
exit 2
fi
if [[ ! "$NOPULL" ]]; then
# shellcheck disable=SC2086
$GIT_COMMAND fetch $REMOTE
# shellcheck disable=SC2086
$GIT_COMMAND pull --ff-only $REMOTE
else
LOG INFO "Skipping git pull"
fi
# Loop over commits
for i in "${COMMITS[@]}"
do
LOG "INFO" "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::";
LOG "INFO" "Processing commit : $i"
LOG "INFO" "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::";
$GIT_COMMAND cherry-pick "$i" -x
if [ $? -eq 0 ]; then
LOG "INFO" "Cherry pick of $i completed successfully."
continue;
fi
LOG "ERROR" "Cherry pick encountered an error."
if [[ "$AUTO_MODE" ]]; then
LOG ERROR "Aborting cherry-pick and exiting. Please handle this manually"
$GIT_COMMAND cherry-pick --abort
exit 2
fi
if yesno "Do you want me to open mergetool? (y/n) "; then
$GIT_COMMAND mergetool
fi
if yesno "Have you resolved the merge conflict? (y/n) "; then
LOG INFO "Continuing..."
$GIT_COMMAND cherry-pick --continue
else
if yesno "Clean up by aborting the cherry-pick? (y/n) "; then
$GIT_COMMAND cherry-pick --abort
fi
LOG INFO "Bye"
exit 1
fi
done
LOG INFO "All cherry-picks to branch $BRANCH succeeded"
if [[ "$PRECOMMIT" ]] || [[ "$TEST" ]]; then
LOG "INFO" "Testing the cherry-pick on $BRANCH by running 'gradlew check ${TESTARG}'"
# shellcheck disable=SC2086
./gradlew check -q $TESTARG
if [ $? -gt 0 ]; then
LOG "WARN" "Tests failed. Please fix and push manually"
exit 2
fi
TESTS_PASSED="true"
LOG "INFO" "Tests passed on $BRANCH"
else
LOG "INFO" "Skipping tests"
fi
if [[ "$PUSH" ]]; then
if [[ ! $TESTS_PASSED ]]; then
LOG "WARN" "Cannot auto push if tests are disabled"
exit 1
fi
if [[ -z "$REMOTE" ]]; then
LOG "WARN" "Remote not specified, attempting to auto detect"
REMOTE=$(git config --get "branch.$BRANCH.remote")
fi
LOG "INFO" "Pushing changes to $REMOTE/$BRANCH"
# shellcheck disable=SC2086
$GIT_COMMAND push $REMOTE $BRANCH
if [ $? -gt 0 ]; then
LOG "WARN" "PUSH to $REMOTE/$BRANCH failed, please clean up and proceed manually"
exit 2
fi
LOG "INFO" "Pushed $BRANCH to remote. Cherry-pick complete."
fi
done
LOG "INFO" "SUCCESS on branches ${BRANCHES[*]} for commits ${COMMITS[*]}"