#!/usr/bin/env bash
#
# Make a diff between two GIT trees.
# Copyright (c) Petr Baudis, 2005
#
# Outputs a diff for converting the first tree to the second one.
# By default compares the current working tree to the state at the
# last commit. The output will automatically be displayed in a pager
# unless it is piped to a program.
#
# OPTIONS
# -------
# -c::
#	Colorize the diff output
#
# -p::
#	Instead of one ID denotes a parent commit to the specified ID
#	(which must not be a tree, obviously).
#
# -r FROM_ID[:TO_ID]::
#	Specify the revisions to diff using either '-r rev1:rev2' or
#	'-r rev1 -r rev2'. If no revision is specified, the current
#	working tree is implied. Note that no revision is different from
#	empty revision which means '-r rev:' compares between 'rev' and
#	'HEAD', while '-r rev' compares between 'rev' and working tree.
#
# -m::
#	Base the diff at the merge base of the -r arguments (defaulting
#	to master and origin).
#
# ENVIRONMENT VARIABLES
# ---------------------
# PAGER::
#	The pager to display log information in, defaults to `less`.
#
# PAGER_FLAGS::
#	Flags to pass to the pager. By default `R` is added to the `LESS`
#	environment variable to allow displaying of colorized output.

USAGE="cg-diff [-c] [-m] [-p] [-r FROM_ID[:TO_ID]] [FILE]..."

. ${COGITO_LIB}cg-Xlib


id1=" "
id2=" "
parent=
opt_color=
mergebase=

# TODO: Make cg-log use this too.
setup_colors()
{
	local C="diffhdr=1;36:diffhdradd=1;32:diffadd=32:diffhdrmod=1;35:diffmod=35:diffhdrrem=1;31:diffrem=31:diffhunk=36:diffctx=34:diffcctx=33:default=0"
	[ -n "$COGITO_COLORS" ] && C="$C:$COGITO_COLORS"

	C=${C//=/=\'$'\e'[}
	C=col${C//:/m\'; col}m\'
	#coldefault=$(tput op)
	eval $C
}

while optparse; do
	if optparse -c; then
		opt_color=1
		setup_colors
	elif optparse -p; then
		parent=1
	elif optparse -r=; then
		if echo "$OPTARG" | grep -q ':'; then
			id2=$(echo "$OPTARG" | cut -d : -f 2)
			[ "$id2" ] || log_end="HEAD"
			id1=$(echo "$OPTARG" | cut -d : -f 1)
		elif [ "$id1" = " " ]; then
			id1="$OPTARG"
		else
			id2="$OPTARG"
		fi
	elif optparse -m; then
		mergebase=1
	else
		optfail
	fi
done

colorize() {
	if [ "$opt_color" ]; then
		gawk '
		{ if (/^(Index:|diff --git) /)
		    print "'$coldiffhdr'" $0 "'$coldefault'"
		  else if (/^======*$/)
		    print "'$coldiffhdr'" $0 "'$coldefault'"
		  else if (/^\+\+\+/)
		    print "'$coldiffhdradd'" $0 "'$coldefault'"
		  else if (/^\*\*\*/)
		    print "'$coldiffhdrmod'" $0 "'$coldefault'"
		  else if (/^---/)
		    print "'$coldiffhdrrem'" $0 "'$coldefault'"
		  else if (/^(\+|new( file)? mode )/)
		    print "'$coldiffadd'" $0 "'$coldefault'"
		  else if (/^(-|(deleted file|old) mode )/)
		    print "'$coldiffrem'" $0 "'$coldefault'"
		  else if (/^!/)
		    print "'$coldiffmod'" $0 "'$coldefault'"
		  else if (/^@@ \-[0-9]+(,[0-9]+)? \+[0-9]+(,[0-9]+)? @@/)
		    print gensub(/^(@@[^@]*@@)([ \t]*)(.*)/,
		         "'$coldiffhunk'" "\\1" "'$coldefault'" \
			 "\\2" \
			 "'$coldiffctx'" "\\3" "'$coldefault'", "")
		  else if (/^\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*/)
		    print "'$coldiffcctx'" $0 "'$coldefault'"
		  else
		    print
		}'
	else
		cat
	fi
}

if [ "$parent" ]; then
	id2="$id1"
	id="$id2"; [ "$id" = " " ] && id=""
	id1=$(parent-id "$id" | head -n 1) || exit 1
fi

if [ "$mergebase" ]; then
	[ "$id1" != " " ] || id1="master"
	[ "$id2" != " " ] || id2="origin"
	id1=$(git-merge-base $(commit-id "$id1") $(commit-id "$id2"))
fi


filter=$(mktemp -t gitdiff.XXXXXX)
for file in "${ARGS[@]}"; do
	echo "$file" >>$filter
done

if [ "$id2" = " " ]; then
	if [ "$id1" != " " ]; then
		tree=$(tree-id "$id1") || exit 1
	else
		tree=$(tree-id) || exit 1
	fi

	# Ensure to only diff modified files
	git-update-cache --refresh >/dev/null

	# FIXME: Update ret based on what did we match. And take "$@"
	# to account after all.
	ret=
	cat $filter | xargs git-diff-cache -r -p $tree | colorize | pager

	rm $filter

	[ "$ret" ] && die "no files matched"
	exit $ret
fi


id1=$(tree-id "$id1") || exit 1
id2=$(tree-id "$id2") || exit 1

[ "$id1" = "$id2" ] && die "trying to diff $id1 against itself"

cat $filter | xargs git-diff-tree -r -p $id1 $id2 | colorize | pager

rm $filter
exit 0
