#!/bin/bash

version=0.20
program=${0##*/}
progdir=${0%/*}
if [ "$progdir" = "$program" ]; then progdir="."; fi

# ----------------------------------------------------------------------
function usage() {
    cat <<EOF
NAME
	$program - merge and commit a sprint branch

SYNOPSIS
	$program [OPTIONS] BRANCH SVNREV

DESCRIPTION
	Merge and commit a sprint branch

EOF
    echo -e "OPTIONS"
    if   [ "$(uname)" = "Linux" ]; then
        egrep "^[	]+[-][A-Za-z| -]+\*?\)[	]+[A-Za-z].+#" $0 | tr -s "\t|" "\t," | sed -r -e 's/\)[ \t]+([A-Z]+)=\$2[^#]*#/=\1\t/' -e 's/\)[^#]*#/\t/'
    else
        egrep "^[	]+[-][A-Za-z| -]+\*?\)[	]+[A-Za-z].+#" $0 | sed 's/\|.*\$2[^#]*#/	/'| sed -E 's/\|.*\)[^#]*#/	/'
    fi
    cat <<EOF

FILES

AUTHOR
	Written by Henrik Levkowetz, <henrik@tools.ietf.org>

COPYRIGHT
	Copyright 2010 Henrik Levkowetz.

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or (at
	your option) any later version. There is NO WARRANTY; not even the
	implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
	PURPOSE. See the GNU General Public License for more details.
	
EOF

}

# ----------------------------------------------------------------------
function die() {
    echo -e "\n$program: error: $*" > /dev/stderr
    exit 1
}

function note() { 
	if [ -n "$VERBOSE" ]; then echo -e "$*"; fi
}

# ----------------------------------------------------------------------
function version() {
	echo -e "$program $version"
}

# ----------------------------------------------------------------------
trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; exit 1' ERR


# ----------------------------------------------------------------------
# Option parsing

# Options
shortopts=cnhvV
longopts=commit,no-commit,help,verbose,version

# Default values
ARG_COMMIT=1

if   [ "$(uname)" = "Linux" ]; then
    args=$(getopt -o "$shortopts" --long "$longopts" -n '$program' -- $SV "$@")
    if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi
    eval set -- "$args"
    sed="sed -r"
else
    # Darwin, BSDs
    args=$(getopt -o$shortopts $SV $*)
    if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi
    set -- $args
    sed="sed -E"
fi

while true ; do
    case "$1" in
	-c| --commit)	ARG_COMMIT=1;;		#	 Run commit in addition to merge
	-n| --no-commit)	ARG_COMMIT=0;;	#	 Don't commit after merge
	-h| --help)	usage; exit;;		#	 Show this help, then exit
	-v| --verbose)	VERBOSE=1;;		#	 Be more talkative
	-V| --version)	version; exit;;		#	 Show program version, then exit
	--)		shift; break;;
	*) die "Internal error, inconsistent option specification: '$1'";;
    esac
    shift
done

# ----------------------------------------------------------------------
# The program itself

# Argument validation
[[ $1 =~ @ ]] && set ${1/@/ }
[ $# -ge 2 ] || die "Expected branch and repository revision on the command line"
[ ${PWD##*/} = trunk ] || die "Expected this script to be run in trunk"

# Global settings
cwd=${PWD##*/}
branch=$1
rev=$2
fix=$3

# remove leading 'r' from rev, if present
rev=${rev#r}

repo=$(echo -n "$(svn info | grep "^Repository Root: " | sed 's/Repository Root: //')")

[ -z "$by" ] && by=${RELEASER_REAL_NAME}
[ -z "$by" ] && by=$(getent passwd $(whoami) | cut -d ':' -f 5 | tr -d ',')
[ -z "$by" ] && die "Can't determine the real name of the user running this script"

python -c 'import django' || die "Can't find django - can't run tests"

note "Identify the branch:"
if   svn info $branch > /dev/null 2>&1; then
    branch="${branch#^/}"
elif svn info ${repo}/personal/$branch > /dev/null 2>&1; then
    branch="personal/$branch"
elif svn info ${repo}/branch/$branch > /dev/null 2>&1; then
    branch="branch/$branch"
elif svn info ${repo}/$branch > /dev/null 2>&1; then
    true
else
    die "Could not find a branch matching '$branch'"
fi

note "Svn update, to make sure we don't have a mixed revision working copy"
svn update -q

mergelog=$(mktemp)
svn propget svn:mergeinfo . > $mergelog

if grep "@$rev $branch" $mergelog; then die "Changeset $branch@$rev is already in the merge log.  Skipping it."; exit 0; fi

note "Will attempt merge from $branch@$rev"

# "Check there's no uncommitted changes ..."
echo ""
$do svn st | grep "^[AMGRD] " && {
    echo ""
    read -p "There are uncommitted changes.  Really do merge? [y/N] "
    [ "$REPLY" = "Y" -o  "$REPLY" = "y" ] || exit
}

note "Extract who and what:"
info=$(svn log ${repo}/ -r $rev --incremental)
set $(echo "$info" | tail -n +2 | head -n 1 | tr "|" "\t")
who=$2; echo -e "$who"
comment=$(echo "$info" | tail -n +3); echo -e "$comment\n"
comment=$(echo "$comment" | sed -r -e 's/(commit )?ready (for|to) merge\.?//i' -e '/^$/d')
files=$(svn diff ${repo}/ -c $rev --summarize | awk '{$1=""; print;}' | while read file; do echo "${file/$repo\/$branch\//}"; done)

echo -e "Files: \n$files\n"

read -p "Continue with diff? [Y/n] "
[ "$REPLY" = "Y" -o  "$REPLY" = "y" -o  "$REPLY" = "" ] || exit

note "Diff:"
note "svn diff -c $rev $repo/$branch"
svn diff -c $rev $repo/$branch | less

echo ""
read -p "Additional descriptive text (hit return for none): "
if [ "$REPLY" != "" ]; then
    comment="$REPLY

    $comment"
fi

echo ""
read -p "Continue with the merge? [Y/n] "
[ "$REPLY" = "Y" -o  "$REPLY" = "y" -o  "$REPLY" = "" ] || exit

note "Do the merge:"
if [[ $rev =~ : ]]; then
    svn merge -r $rev ${repo}/$branch . || die "Merge of $branch @$rev failed.  The merge command was:
  svn merge -r $rev ${repo}/$branch ."
else
    svn merge -c $rev ${repo}/$branch . || die "Merge of $branch @$rev failed.  The merge command was:
  svn merge -c $rev ${repo}/$branch ."
fi

note "Writing commit script"
echo -e "#!/bin/bash\n\nsvn commit -m \"Merged in [$rev] from $who:\n    ${comment//\"/\'} ${fix//\"/\'}\"" > ../cicmd/commit-${rev}-merge.sh
chmod +x ../cicmd/commit-${rev}-merge.sh

M=$(svn st | cut -c 1-7 | grep -oh 'M' | head -n 1)
C=$(svn st | cut -c 1-7 | grep -oh 'C' | head -n 1)
G=$(svn st | cut -c 1-7 | grep -oh 'G' | head -n 1)

##cd ../
##rsync -a $cwd/ merged@$rev/
##cp cicmd/commit-${rev}-merge.sh merged@$rev/commit
##cd -

# Potentially run flake8 at this point
# read -p "Run flake8? [y/N] "
# if [ "$REPLY" = "Y" -o  "$REPLY" = "y"]; then
#     mod=$(svn st | cut -c 9- | grep '\.py$')
#     flake8 $mod | less
# fi

read -p "Continue with tests? [Y/n] "
[ "$REPLY" = "Y" -o  "$REPLY" = "y" -o  "$REPLY" = "" ] || exit

echo -e "\nRunning tests"
time { ietf/manage.py test --settings=settings_sqlitetest	\
	|| die "Tests failed.\nThe commit script is ../cicmd/commit-${rev}-merge.sh"; } 3>&1 1>&2 2>&3 | bin/count
echo ""

note "Sending email to changeset author: <$who>"
SEND_ARGS=""
[ "${RELEASER_EMAIL}" ] && SEND_ARGS="-r ${RELEASER_EMAIL}"
mail "${SEND_ARGS}" -s "Merged datatracker branch personal/$branch@$rev to trunk" $who -c rjsparks@nostrum.com <<-EOF
Hi,

This is an automatic merge info message.  Your code in personal/$branch@$rev
has been merged to trunk, and will be part of the next release if nothing
goes wrong during final testing.

Regards,

	$by
	(via the mergesprintbranch script)
EOF

echo ""
read -p "Continue with the commit? [Y/n] "
[ "$REPLY" = "Y" -o  "$REPLY" = "y" -o  "$REPLY" = "" ] || ARG_COMMIT=0

if [ "$ARG_COMMIT" != 0 ]; then
    echo "Svn update:"
    svn update -q
    echo "Committing the merge:"
    echo ""
    svn commit -m "Merged in [$rev] from $who:
    ${comment//\"/\'} ${fix//\"/\'}"
else
    echo "This merge has not been committed yet."
    echo "To commit it, run this commit command: ../cicmd/commit-$rev-merge.sh"
fi

echo -e "\n------------------------------------------------------------------------\n\n"