#!/bin/bash
# Performs some miscellaneous handy functions on SVN repositories.

# Equivalent keywords separated by comma's, keyword to be reported last:
KEYWORDS='HeadURL,URL LastChangedBy,Author LastChangedDate,Date
          Rev,LastChangedRevision,Revision Id'

DIFF_SVN_OPTS="--no-diff-deleted --notice-ancestry --diff-cmd $HOME/bin/svn-diff"
DIFF_EXT_OPTS='-U1'

STASH=stashed.diff

if [ -z "$1" ]; then
	echo "$0 -- Run miscellaneous commands on Subversion checkout."
	echo "No command specified; select one of:"
	grep '[#]CMD:' $0 | sed 's/).*[#]CMD:/ /' | \
	while read CMD DESC ; do
		printf '  %-20s  %s\n' "$CMD" "$DESC"
	done
	exit 1
fi
COMMAND=$1
shift

svnroot ()
{
	OWD=$PWD
	while [ -d ../.svn ]; do cd .. ; done
	[ -d .svn ] && echo "$PWD"
	cd $OWD
}

SVNROOT="`svnroot`"

case "$COMMAND" in
	kc|keyword-check) #CMD: Check that svn:keywords are set
# Search for svn:keywords in files within directory tree and check
# whether the corresponding svn:keywords property is set and vice versa.
		for keys in $KEYWORDS ; do
			TMP=`mktemp`
			for k in ${keys//,/ } ; do
				wcgrep -lIE '\$'$k'[^a-zA-Z]' | sed 's,^\./,,'
			done | sort | uniq > $TMP
			cat $TMP | while read -r f ; do
				props=`svn propget svn:keywords "$f" 2>/dev/null` || continue
				found=''
				for k in ${keys//,/ } ; do
					[ "${props//$k/}" != "$props" ] && found=1
				done
				[ ! "$found" ] && echo "$f: SVN keyword '$k' found in file, but not set."
			done
			k=${keys##*,}
			svn propget --depth infinity svn:keywords | \
				grep -E " $k"'( |$)' | sed 's/ - .*$//' | sort | \
				diff --unchanged-line-format='' \
                     --old-line-format='%L' \
				     --new-line-format='' - $TMP | \
 			while read -r f ; do
				echo "$f: SVN keyword set, but not found in file."
 			done
		done
		;;

	kr|keyword-reset) #CMD: Reset svn:keywords data
# Removes svn:keywords data. E.g. for outside SVN comparing of
# different trees, where tags would show up as differences.
		for keys in $KEYWORDS ; do
			for k in ${keys//,/ } ; do
				wcgrep -lIE '\$'$k'[^a-zA-Z]' | while read -r f ; do
					sed -i 's/\$'$k':[^\$]*\$/\$'$k'$/g' "$f"
				done
			done
		done
		;;

	ic|ignore-check) #CMD: Check for files in the repository that are ignored.
		for d in `find . -type d -name .svn | sed 's,/\.svn$,,'` ; do
			svn propget svn:ignore "$d" 2>/dev/null | while read -r pat ; do
				[ -z "$pat" ] && continue
				ls -d $d/$pat 2>/dev/null | while read -r f ; do
					if svn info "$f" >/dev/null 2>&1 ; then
						echo "In dir '$d', SVN ignore '$pat' matches versioned file '$f'"
					fi
				done
			done
		done
		;;

	ir|ignore-remove) #CMD: Remove all SVN ignored files.
		for d in `find . -type d -name .svn | sed 's,/\.svn$,,'` ; do
			svn propget svn:ignore "$d" 2>/dev/null | while read -r pat ; do
				[ -z "$pat" ] && continue
				rm -rf $d/$pat
			done
		done
		;;

	di|diff) #CMD: Call external diff with extra options
# SVN diff with extra options for better overview
		svn diff $DIFF_SVN_OPTS -x "$DIFF_EXT_OPTS" "$@"
		;;

	pu|push) #CMD: Save local changes in svnroot/stashed.diff
		cd "$SVNROOT"
		if svn status | grep -vE '^(M|\?) ' >/dev/null ; then
			echo "Error: can only save modifications."
			exit 1
		fi
		if [ -f $STASH ]; then
			echo "Error: stashed.diff already present."
			exit 1
		fi
		svn diff > $STASH && svn revert -R .
		cd - > /dev/null
		;;

	po|pop) #CMD: Restore local changes from svnroot/stashed.diff
		cd "$SVNROOT"
		if svn status | grep -vE '^\? ' >/dev/null ; then
			echo "Error: cannot restore on modified checkout."
			exit 1
		fi
		if [ ! -f $STASH ]; then
			echo "Error: stashed.diff not present."
			exit 1
		fi
		patch -p0 < $STASH && rm -f $STASH
		cd - > /dev/null
		;;

	*)
		echo "Unknown command '$COMMAND'."
		exit 1
		;;
esac
