#!/bin/bash

# Set up a git repo used for deploying -- this repo always accepts pushes
# and updates the working tree on a push.

repo=${1:-.}
cd "$repo"
git_dir=$(git rev-parse --git-dir); ret=$?
if [[ $ret -ne 0 ]]
then
	echo Not a git repo. >&2
	exit 128
fi

is_bare=$(git config --get --bool core.bare)

if [[ $is_bare = "true" ]]
then
	echo No point doing this for a bare repo. >&2
	exit 1
fi

cd "$git_dir"
if [[ -e hooks/post-update ]]
then
	echo There is already a post-update hook. Move it out of the way first. >&2
	exit 2
fi

set -e

git config receive.denyCurrentBranch ignore
git config receive.denyNonFastForwards false
git config receive.denyDeleteCurrent refuse

cat - > hooks/post-update <<'__EOF__'
#!/bin/bash
#
# This hook does two things:
#
#  1. update the "info" files that allow the list of references to be
#     queries over dumb transports such as http
#
#  2. if this repository looks like it is a non-bare repository, and
#     the checked-out branch is pushed to, then update the working copy.
#     This makes "push" function somewhat similarly to darcs and bzr.
#
# To enable this hook, make this file executable by "chmod +x post-update".

git update-server-info

is_bare=`git config --get --bool core.bare`

if test X = "X$is_bare"
then
	# for compatibility's sake, guess
	git_dir_full=`cd $GIT_DIR; pwd`
	case $git_dir_full in */.git) is_bare=false;; *) is_bare=true;; esac
fi

update_wc() {
	ref=$1
	echo "Push to checked out branch $ref" >&2
	if test ! -f $GIT_DIR/logs/HEAD
	then
		echo "E:push to non-bare repository requires a HEAD reflog" >&2
		exit 1
	fi
	# --diff-filter=d means ignore deleted files. This is undocumented.
	# Contrast --diff-filter=D which means only look at deleted files.
	if (cd $GIT_WORK_TREE; git diff-files --quiet --exit-code --diff-filter=d >/dev/null)
	then
		wc_dirty=0
	else
		echo "W:unstaged changes found in working copy" >&2
		wc_dirty=1
		desc="working copy"
	fi
	if git diff-index --cached HEAD@{1} >/dev/null
	then
		index_dirty=0
	else
		echo "W:uncommitted, staged changes found" >&2
		index_dirty=1
		if test X != "X$desc"
		then
			desc="$desc and index"
		else
			desc="index"
		fi
	fi
	if test 0 -ne "$wc_dirty" -o 0 -ne "$index_dirty"
	then
		new=`git rev-parse HEAD`
		echo "W:stashing dirty $desc - see git-stash(1)" >&2
		( trap 'echo trapped $$; git symbolic-ref HEAD "'"$ref"'"' 2 3 13 15 ERR EXIT
		git update-ref --no-deref HEAD HEAD@{1}
		cd $GIT_WORK_TREE
		git stash save "dirty $desc before update to $new";
		git symbolic-ref HEAD "$ref"
		)
	fi

	# eye candy - show the WC updates :)
	echo "Updating working copy" >&2
	(cd $GIT_WORK_TREE
	git diff-index -R --name-status HEAD >&2
	git reset --hard HEAD)
}

if test false = "$is_bare"
then
	active_branch=`git symbolic-ref HEAD`
	export GIT_DIR=`cd $GIT_DIR; pwd`
	GIT_WORK_TREE=${GIT_WORK_TREE-..}
	for ref
	do
		if test "$ref" = "$active_branch"
		then
			update_wc $ref
		fi
	done
fi
__EOF__

chmod +x hooks/post-update

echo Successful.

# vim:noet:sw=8:sts=8:ts=8
