#!/bin/sh -efu
#
# Copyright (C) 2006  Alexey Gladkov <legion@altlinux.org>
# Copyright (C) 2006  Dmitry V. Levin <ldv@altlinux.org>
# Copyright (C) 2006  Sergey Vlasov <vsu@altlinux.org>
#
# gear-update updates subdirectory from source archive/directory.
#
# This file 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.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
#

. gear-sh-functions

srctype=archive
archive_help=
case "${PROG##*-}" in
	archive)
		srctype=archive
		archive_help="
  -t,--tardir=NAME    define tar archive directory name;"
		;;
	directory) srctype=directory ;;
esac

show_help()
{
	cat <<EOF
Usage: $PROG [Options] <src-$srctype> <dst-directory>

Update subdirectory from source $srctype.

Options: ${archive_help:+$archive_help}
  --ignore-exclude    ignore .gitignore and .git/info/exclude;
  -f,--force          remove files from the <dst-directory>
                      even if untracked or modified files found;
  -v,--verbose        print a message for each action;
  -V,--version        print program version and exit;
  -h,--help           show this text and exit.

Report bugs to http://bugs.altlinux.ru/

EOF
	exit
}

print_version()
{
	cat <<EOF
$PROG version $PROG_VERSION
Written by Alexey Gladkov <legion@altlinux.org>

Copyright (C) 2006  Alexey Gladkov <legion@altlinux.org>
Copyright (C) 2006  Dmitry V. Levin <ldv@altlinux.org>
Copyright (C) 2006  Sergey Vlasov <vsu@altlinux.org>
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
EOF
	exit
}

workdir=
destdir=
exit_handler()
{
	local rc=$?
	trap - EXIT
	[ -z "$workdir" ] || rm -rf -- "$workdir"
	if [ "$rc" -gt 0 ]; then
		# Rollback actions
		[ ! -e "$destdir" ] || rm -rf -- "$destdir"
		mkdir -p -- "$destdir"
		cd "$destdir"
		git-checkout-index -a
		msg_info "unable to unpack new archive"
	fi
	exit $rc
}

TEMP=`getopt -n $PROG -o f,t:,v,V,h -l ignore-exclude,force,tardir:,verbose,version,help -- "$@"` || show_usage
eval set -- "$TEMP"

force=
tardir=
ignore_exclude=
while :; do
	case "$1" in
	    --ignore-exclude) ignore_exclude=1
		;;
	    -f|--force) force=1
		;;
	    -t|--tardir) shift
		[ "$srctype" = "archive" ] && tardir="$1" ||
		    fatal "unrecognized option: $1"
		;;
	    -v|--verbose) verbose=-v
		;;
	    -V|--version) print_version
	    	;;
	    -h|--help) show_help
		;;
	    --) shift; break
		;;
	    *) fatal "unrecognized option: $1"
		;;
	esac
	shift
done

[ "$#" -ge 2 ] ||
	show_usage 'Not enough arguments.'
[ "$#" -eq 2 ] ||
	show_usage 'Too many arguments.'

source="$(readlink -ev "$1")"; shift
destdir="$(readlink -ev "$1")"; shift

[ "$source" != "$destdir" ] ||
	fatal "\`$source' and \`$destdir' are the same place"

[ -d "$destdir" ] ||
	fatal "$destdir: directory not available"

# Move to topdir
cd "$(git-rev-parse --show-cdup)"
topdir="$(readlink -ev .)"
destdir="$destdir/"
destdir="${destdir#$topdir/}"

[ "$destdir" = "${destdir#/}" ] ||
	fatal "$destdir: directory out of \`$topdir' tree."

[ -n "$destdir" ] && destdir="${destdir%/}" || destdir="."

git-rm -n "$destdir" >/dev/null

if [ -z "$force" ]; then
	out="$(git diff --name-only -- "$destdir"; git-ls-files --directory --others --exclude-per-directory=.gitignore -- "$destdir")"
	[ -z "$out" ] || fatal "$destdir: untracked or modified files found"
fi

if [ "$srctype" = "archive" ]; then
	if [ -n "$tardir" ]; then
		quoted_tardir="$(quote_sed_regexp "$tardir")"
		tar -tf "$source" |grep -qs "^$quoted_tardir\(/\|\$\)" ||
			fatal "tar archive directory not found"
	else
		tardir="$(tar -tf "$source"|cut -d\/ -f1 |sort -u)"
	fi

	n="$(printf %s\\n "$tardir" |wc -l)"
	if [ "$n" -gt 1 ]; then
		fatal "too many tar archive directories: $(printf %s\\n "$tardir" |tr \\n \ )"
	elif [ "$n" -lt 1 ]; then
		fatal "tar archive directory not found"
	fi
fi

# Trap for rollback
trap exit_handler HUP INT QUIT TERM EXIT

# Remove obsolete source
find "$destdir" -mindepth 1 -maxdepth 1 ! -name '.git' -print0 |
	xargs -r0 rm $verbose -rf --
[ "$destdir" = "." ] || rmdir $verbose -- "$destdir"

# Copy new sources
case "$srctype" in
	archive)
		workdir="$(mktemp -d "$PROG.XXXXXXXXX")"
		printf %s\\n "$tardir/" |tar -T- -C "$workdir" -xf "$source"
		mv -f -- "$workdir/$tardir" "$destdir"
		;;
	directory)
		cp -a -- "$source" "$destdir"
		;;
esac
[ -d "$destdir" ] ||
	fatal "$destdir: result of extraction is not a directory"

if [ -n "$ignore_exclude" ]; then
	git-ls-files -z "$destdir" |
		git-update-index --remove --force-remove -z --stdin
	git-ls-files -z --others --modified "$destdir" |
		git-update-index --add ${verbose:+--verbose} -z --stdin
else
	git-rm $verbose "$destdir"
	git-add $verbose "$destdir"
fi
