#!/usr/bin/env bash
#
# Get SHA1 ID of commit or tree associated with given ID or HEAD.
# Copyright (c) Petr Baudis, Pavel Roskin, Philip Pokorny  2005
#
# If the ID is not provided, HEAD is used.  The default behavior is to
# show the commit ID.
#
# OPTIONS
# -------
# -c::
#	Get ID of commit matching the object ID (error out if it is not
#	a commit). This is the default if you do not pass any parameter
#	as well, but that is only for the human usage. For clarity, all
#	scripted usage of cg-object-id should use -c explicitly if it
#	wants a commit.
#
# -n::
#	Normalize only - don't check the object type.
#
# -p::
#	Get ID of the first parent commit of a given revision or HEAD.
#	NOTE: Multiple SHA1s separated by newlines will be returned for
#	commits with multiple parents.
#
# -t::
#	Get ID of tree associated with given commit or HEAD.
#
# OBJECT_ID::
#	An ID resolving to a commit.

USAGE="cg-object-id [-c | -n | -p | -t] [OBJECT_ID]"

. ${COGITO_LIB}cg-Xlib
deprecated_alias cg-object-id commit-id cg-commit-id parent-id cg-parent-id tree-id cg-tree-id


# Normalize argument.  The normalized SHA1 ID is put to $normid,
# type is put to $type.
normalize_id()
{
	local id="$1"

	if [ "${id:(-1):1}" = "^" ]; then
		# find first parent
		normalize_id "${id%^}"
		normid=$(git-cat-file commit "$normid" | \
			 awk '/^parent/{print $2; exit};/^$/{exit}') || exit 1
		type="commit"
		return
	fi

	if [ ! "$id" ] || [ "$id" = "this" ] || [ "$id" = "HEAD" ]; then
		read id < "$_git/$(git-symbolic-ref HEAD)"

	elif [ -r "$_git/refs/tags/$id" ]; then
		read id < "$_git/refs/tags/$id"

	elif [ -r "$_git/refs/heads/$id" ]; then
		read id < "$_git/refs/heads/$id"

	# Short id's must be lower case and at least 4 digits.
	elif [[ "$id" == [0-9a-f][0-9a-f][0-9a-f][0-9a-f]* ]]; then
		idpref=${id:0:2}
		idpost=${id:2}

		# Assign array elements to matching names
		idmatch=($_git_objects/$idpref/$idpost*)

		if [ ${#idmatch[*]} -eq 1 ] && [ -r "$idmatch" ]; then
			exid=$idpref${idmatch#$_git_objects/$idpref/}
			[ ${#exid} -eq 40 ] && [ "$(git-rev-parse --revs-only "$exid")" ] && id="$exid"
		elif [ ${#idmatch[*]} -gt 1 ]; then
			echo "Ambiguous id: $id" >&2
			echo "${idmatch[@]}" >&2
			exit 1
		fi
	fi

	valid=; [ ${#id} -eq 40 ] && [ "$(git-rev-parse --revs-only "$id")" ] && valid=1
	# date does the wrong thing for empty and single-letter ids
	if [ ${#id} -gt 1 ] && [ ! "$valid" ]; then
		reqsecs=$(date --date="$id" +'%s' 2>/dev/null)

		if [ "$reqsecs" ]; then
			id=$(git-rev-list --min-age=$reqsecs --max-count=1 HEAD)
			valid=; [ ${#id} -eq 40 ] && [ "$(git-rev-parse --revs-only "$id")" ] && valid=1
		fi
	fi

	# If we don't have a 40-char ID by now, it's an error
	if [ ! "$valid" ]; then
		echo "Invalid id: $id" >&2
		exit 1
	fi

	type="$(git-cat-file -t "$id")"
	if [ "$type" = "tag" ]; then
		id=$(git-cat-file tag "$id" | head -n 1)
		id="${id#object }"
		type=
	fi

	normid="$id"
}



show_commit= # this is really only for the options-exclusivity-checking
show_parent=
show_tree=
normalize_only=
while optparse; do
	if optparse -c; then
		show_commit=1
	elif optparse -n; then
		normalize_only=1
	elif optparse -p; then
		show_parent=1
	elif optparse -t; then
		show_tree=1
	else
		optfail
	fi
done

# Compatibility code
case $_cg_cmd in
	*parent*) show_parent=1;;
	*tree*) show_tree=1;;
esac

case "$show_commit$show_parent$show_tree$normalize_only" in
	11*)	usage;;
esac

id="${ARGS[0]}"
normalize_id "$id"

if [ "$normalize_only" ]; then
	echo $normid
	exit 0
fi


if [ "$show_parent" ]; then
	git-cat-file commit "$normid" | awk '/^parent/{print $2};/^$/{exit}'
	exit 0
fi

[ "$type" ] || type=$(git-cat-file -t "$normid")
if [ "$show_tree" ]; then
	if [ "$type" = "commit" ]; then
		normid=$(git-cat-file commit "$normid" | sed -e 's/tree //;q')
		type=$(git-cat-file -t "$normid")
	fi

	if [ "$type" != "tree" ]; then
		echo "Invalid tree id: $normid" >&2
		exit 1
	fi
else
	if [ "$type" != "commit" ]; then
		echo "Invalid commit id: $normid" >&2
		exit 1
	fi
fi

echo $normid

