#!/bin/bash
# Lets user select a subset of the current changes to commit.
# Arguments can be a set of files and/or directories; no arguments
# defaults to current directory (the same behaviour as svn commit/diff/...)
#
# Depends on patchutils: diff, patch, interdiff, rediff
#
# Copyright (C) 2008,2009  Jaap Eldering <eldering@a-eskwadraat.nl>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

# Default patch command and options:
PATCH="patch -sf -p0"

# Editor to edit patch files (leave empty to default to $EDITOR)
#LOC_EDITOR=emacsclient

if [ $# -gt 0 ]; then
	ARGS="$@"
else
	ARGS="."
fi

# Check for differences which cannot be handled
if svn status $ARGS | egrep -v '^[MI?]   [ S] ' ; then
	echo "The changes above cannot be handled, commit manually."
	exit 1
fi

# Create diff set
SUFFIX="$$.`date '+%F_%X'`.diff"
DIFFORIG="svn-pcommit-orig.$SUFFIX"
DIFFEDIT="svn-pcommit-edit.$SUFFIX"

svn diff $ARGS > "$DIFFORIG"
svn revert -R $ARGS

# Remember whether we have applied the edited diff
EDIT_APPLIED=''

cat <<EOF
Changes to commit in can be edited in diff file '$DIFFEDIT'.
Press one of the following keys to proceed:

   e  - Start '${LOC_EDITOR:-$EDITOR}' to edit the file.
   c  - Commit current changes.
   a  - Apply current changes, don't commit.
   q  - Return to original state and quit.
EOF

REPLY=''
while read -n1 -s ; do
    case "$REPLY" in
		a)
			if [ "$EDIT_APPLIED" ] ; then
				echo "Already applied 'DIFFEDIT': ignoring."
				continue
			fi
			if diff -q "$DIFFORIG" "$DIFFEDIT" > /dev/null ; then
				echo "No changes made to '$DIFFEDIT': ignoring."
				continue
			fi
			$PATCH < "$DIFFEDIT" && EDIT_APPLIED=1
			;;
		c)
			if [ "$EDIT_APPLIED" ] ; then
				echo "Already committed 'DIFFEDIT': ignoring."
				continue
			fi
			$PATCH < "$DIFFEDIT" && EDIT_APPLIED=1
			svn commit $ARGS
			;;
		e)
			if [ "$EDIT_APPLIED" ] ; then
				echo "Already applied 'DIFFEDIT': ignoring."
				continue
			fi
			cp "$DIFFORIG" "$DIFFEDIT"
			${LOC_EDITOR:-$EDITOR} "$DIFFEDIT"
			rediff "$DIFFORIG" "$DIFFEDIT" > "$DIFFEDIT.rediff"
			mv "$DIFFEDIT.rediff" "$DIFFEDIT"
			echo "Edit session finished."
			;;
		q)
			echo "Reverting to original state."
			if [ "$EDIT_APPLIED" ] ; then
				interdiff "$DIFFEDIT" "$DIFFORIG" | $PATCH
			else
				$PATCH < "$DIFFORIG"
			fi
			exit 0
			;;
	esac
done

exit 0
