blob: 49efb43c447bd1bfc725ab588d300daa52269380 [file] [log] [blame]
#!/bin/bash
# ==============================================================================
# *** BackFLiP (a.k.a BACKporting Fun with LInaro People) ***
#
# Usage: backflip rev_number
# ==============================================================================
# ==============================================================================
# Configuration
# ==============================================================================
VERSION=7
REF_BRANCH=linaro-local/gcc-${VERSION}-integration-branch
DATE=`date +%Y-%m-%d`
NAME=`git config user.name`
FNAME=`echo ${NAME} | cut -d ' ' -f 1`
EMAIL=`git config user.email`
EDITOR="${EDITOR:-vim}"
STACKED=false
CMPTOOL="meld"
INTERACTIVE=true
COLOR=true
TMPDIR=/tmp/$(basename "$0")-$$
PREFIX=${TMPDIR}/backport
# Style
if $COLOR; then
NC='\e[0m'
red='\e[0;31m'
blue='\e[1;34m'
green='\e[0;32m'
bold='\e[1m'
fi
lines=`perl -E 'say "-" x 80'`
# ==============================================================================
# Functions
# ==============================================================================
usage() {
tool="$(basename "$0")"
echo -e "$tool [-v GCC_VERSION] [-r REF_BRANCH] [-b DEV_BRANCH] [-x] REVISION"
}
print_step() {
echo -e "${blue}${lines}\n${red}** ${1}\n${blue}${lines}${NC}\n"
}
print_info() {
echo -e "${blue}** ${NC}${1}"
}
die() {
echo -e "${red}${bold}ERROR: ${NC}${bold}${1}${NC}"
exit 1
}
open_shell() {
PROMPT_COMMAND="echo -n -e '${bold}[${red}BackFLiP${NC}${bold}]${NC} '" bash
}
# arg1 : a sentence
# arg2 : a variable
# print arg1 and gather user input in arg2
ask() {
echo -n -e "${blue}** ${NC}${1} ${NC}"
eval "read ${2}"
}
# arg1 : svn rev number
# arg2 : ChangeLog file
# Create ChangeLog entry for arg1 in arg2.arg1.frag
forge_entry() {
FRAGFILE=${TMPDIR}/$(echo $2 | sed "s:/:-:g")-$1.frag
echo -e "\t"`dirname $2`/ >> ${PREFIX}.msg
echo -e "\tBackport from trunk r$1." >> ${PREFIX}.msg
git show --format=oneline ${SHA1} -- $2 \
| grep ^+ \
| sed -e '1d' -e 's:^+::' \
| awk '$1 ~ /^2.*$/ {print "\t"$0} $1 !~ /^2.*$/ {print $0}' > $FRAGFILE
# Try to fix ChangeLog format frequent issue when the same contributor
# commits 2 patches in a row. Git diff messes up with the context and the
# regular first 2 lines of the entry (DATE NAME MAIL\n\n) ended at the
# bottom of the patch.
if ! head -n 1 $FRAGFILE | grep "<.*@.*>" \
&& tail -n 2 $FRAGFILE | grep "<.*@.*>" > /dev/null; then
tail -n 2 $FRAGFILE > ${FRAGFILE}.tmp
head -n -2 $FRAGFILE >> ${FRAGFILE}.tmp
mv ${FRAGFILE}.tmp $FRAGFILE
fi
print_info "${bold}Forged ChangeLog entry:${NC}"
cat $FRAGFILE
if $INTERACTIVE; then
ask "${bold}Edit ChangeLog entry [N/y] ?" user_edit
if [ "$user_edit" == "y" ]; then
"$EDITOR" $FRAGFILE
fi
fi
cat $FRAGFILE >> ${PREFIX}.msg
}
# arg1 : backported revision sha1
compare() {
git show $1 > ${TMPDIR}/$REV.orig
git diff --cached > ${TMPDIR}/$REV.linaro
print_info "Generated diff files: ${bold}$REV.orig $REV.linaro${NC}"
"$CMPTOOL" ${TMPDIR}/$REV.orig ${TMPDIR}/$REV.linaro
ask "${bold}Do you need a Shell [N/y] ?" user_shell
if [ "$user_shell" == "y" ]; then
open_shell
fi
}
clean() {
git reset --hard
git checkout ${REF_BRANCH}
if [ $1 -ne 0 ]; then
echo -e "\n${lines}"
echo -e "INTERACTIVE MODE COMMAND LINE TO REPRODUCE AND/OR FIX THE ISSUE:"
echo -e "$0 -v $VERSION -b $DEV_BRANCH -r $REF_BRANCH $RARGS"
echo -e "${lines}"
if ! $INTERACTIVE; then
exec 1>&3 2>&4
echo -e "Failed to backport rev $REV"
fi
fi
exit $1
}
# Arguments
while getopts ":b:r:v:x" options
do
case $options in
r ) REF_BRANCH=$OPTARG
;;
b ) DEV_BRANCH=$OPTARG
STACKED=true
;;
v ) VERSION=$OPTARG
REF_BRANCH=linaro-local/gcc-${VERSION}-integration-branch
;;
x ) INTERACTIVE=false
COLOR=false
;;
\?) print_info "Option -${bold}$OPTARG${NC} Unsupported."
exit 1
;;
: ) print_info "Option -${bold}$OPTARG${NC} requires an argument."
usage
exit 1
;;
esac
done
if [ $# -lt 1 ]; then
usage ; exit 1
fi
shift $(($OPTIND - 1))
if [ -z ${DEV_BRANCH+x} ]; then
DEV_BRANCH=${REF_BRANCH}
fi
# ==============================================================================
# Checking Configuration
# ==============================================================================
if ! git remote -v 2>&1 | grep "ssh.*review\.linaro\.org" > /dev/null ; then
die "You're not in the right directory !"
fi
if [ ! -e .gitreview ]; then
print_info "Gerrit configuration (.gitreview) is missing, creating it."
cat <<- EOF > .gitreview
[gerrit]
host=review.linaro.org
port=29418
project=toolchain/gcc
EOF
fi
if $INTERACTIVE; then
if ! which $CMPTOOL > /dev/null ; then
print_info "Default comparison tool is missing"
ask "Enter tool name: " CMPTOOL
fi
fi
if ! $INTERACTIVE; then
exec 3>&1 4>&2
fi
# ==============================================================================
# Check that all the requested backports are available
# ==============================================================================
for REV in "$@"
do
SHA1=`git log --format=format:"%H" --grep=trunk@${REV} origin/master`
if [ -z "$SHA1" ]; then
die "${red}Revision ${REV} is missing in origin/master history${NC}"
fi
done
mkdir $TMPDIR
# ------------------------------------------------------------------------------
for REV in "$@"; do # Beginning of Stacked Backport Loop
# ------------------------------------------------------------------------------
# Keep remaining args for error messages
RARGS=$@
shift
PREFIX=${PREFIX}-${REV}
if [ -n "${PREV}" ]; then
STACKED=true
DEV_BRANCH=$bname
fi
if ! $INTERACTIVE; then
exec >${PREFIX}.log 2>&1
fi
echo -e "${blue}${lines}"
print_info "Backport will de done on top of branch: ${bold}$DEV_BRANCH${NC}"
print_info "Reference branch used for review is: ${bold}$REF_BRANCH${NC}"
print_info "To change GCC version in a Linaro branch use ${bold}-v${NC}"
print_info "example: ${bold}backflip -v 4.8${NC}"
print_info "To change reference branch use ${bold}-r${NC}"
print_info "example: ${bold}backflip -r master${NC}"
# ==============================================================================
print_step "Finding SHA1 for SVN revision $REV"
# ==============================================================================
SHA1=`git log --format=format:"%H" --grep=trunk@${REV} origin/master`
print_info "${NC}SVN rev ${bold}${REV}${NC} SHA1: ${bold}${SHA1}${NC}"
if $INTERACTIVE; then
ask "${bold}View the commit [N/y] ?" user_view
if [ "$user_view" == "y" ]; then
git show $SHA1
fi
else
git show $SHA1
fi
# ==============================================================================
print_step "Cherry-picking Revision"
# ==============================================================================
git checkout $DEV_BRANCH
if [ $? -ne 0 ]; then
print_info "Cannot checkout $DEV_BRANCH branch"
clean 1
fi
git cherry-pick -n $SHA1 2>/dev/null
# ==============================================================================
print_step "Handling ChangeLogs Conflicts"
# ==============================================================================
CHLOGS=`git diff-tree --no-commit-id --name-only -r ${SHA1} | grep ChangeLog`
# For stacked backports reuse the previous commit message.
PMSG=`find ${TMPDIR} -name \*.msg`
if [ -n "$PMSG" ]; then
mv $PMSG ${PREFIX}.msg
fi
if [ -z "$CHLOGS" ]; then
print_info "${red}WARNING: No ChangeLog in this commit.${NC}"
forge_entry $REV "missing/ChangeLog"
else
for i in $CHLOGS; do
print_info "${green}$i"
# Revert ChangeLog corruption
git reset -q HEAD $i
git checkout $i
# Create ChangeLog entry
forge_entry $REV $i
done
fi
# ==============================================================================
print_step "Status Checking"
# ==============================================================================
git status
if $INTERACTIVE; then
ask "${bold}Open a Shell [N/y] ?" user_shell
if [ "$user_shell" == "y" ]; then
open_shell
fi
else
git status --porcelain | grep "^.U" && \
{ mv ${PREFIX}.log ${PREFIX}-ko.log; clean 1; }
# Avoid creating branches and review request for empty modification
# (like ChangeLog fixes) but it can be needed for stacked backports.
if ! $STACKED && git diff --cached --quiet; then
print_info "Nothing to commit"
mv ${PREFIX}.log ${PREFIX}-empty.log;
clean 1
fi
fi
# ==============================================================================
print_step "Verification"
# ==============================================================================
if $INTERACTIVE; then
compare $SHA1
else
print_info "None in batch mode."
fi
# ==============================================================================
print_step "Branch/Commit"
# ==============================================================================
if $STACKED; then
bname=${DEV_BRANCH}-${REV}
else
bname=${FNAME}-${VERSION}-backport-${REV}
fi
if $INTERACTIVE; then
ask "${bold}Create backport branch [Y/n] ?" user_bc
if [ "$user_bc" != "n" ]; then
ask "${bold}Change name (${blue}${bname}${NC}${bold}) [N/y] ?" user_bname
if [ "$user_bname" == "y" ]; then
ask "${bold}Enter name :" bname
fi
git checkout -b ${bname} ${DEV_BRANCH}
ask "${bold}Commit backport [Y/n] ?" user_commit
if [ "$user_commit" != "n" ]; then
if $STACKED; then
git commit --amend -F ${PREFIX}.msg
else
git commit -F ${PREFIX}.msg
fi
echo
print_info "If you want to stack another backport on top of this one"
print_info "you can stop here, and invoke backflip with command:"
print_info "${bold}backflip -b ${bname} REV_NUMBER${NC}"
print_info "The next steps are only needed for review and validation.\n"
ask "${bold}Create patch [Y/n] ?" user_patch
if [ "$user_patch" != "n" ]; then
git format-patch -o ../ --suffix=.${bname}.patch -1
fi
fi
fi
else
print_info "Create backport branch."
git checkout -b ${bname} ${DEV_BRANCH}
if [ $? -ne 0 ]; then
print_info "Cannot checkout $DEV_BRANCH branch"
clean 1
fi
print_info "Commit backport."
if $STACKED; then
git commit --amend -F ${PREFIX}.msg
else
git commit -F ${PREFIX}.msg
fi
fi
PREV=$REV
# ------------------------------------------------------------------------------
done # End of stacked backport loop
# ------------------------------------------------------------------------------
# ==============================================================================
print_step "Review"
# ==============================================================================
if $INTERACTIVE; then
if [ "$user_commit" != "n" ]; then
ask "${bold}Request review [Y/n] ?" user_review
if [ "$user_review" != "n" ]; then
git review $REF_BRANCH
fi
fi
else
print_info "Request review."
git review $REF_BRANCH
if [ $? -ne 0 ]; then
print_info "Cannot request review"
clean 1
fi
fi
# ==============================================================================
print_step "Cleanup"
# ==============================================================================
clean 0