#!/usr/bin/env bash
#
# Make a patch from one or several commits.
# Copyright (c) Petr Baudis, 2005
#
# Generate a patch with diff statistics and meta info about each commit.
#
# OPTIONS
# -------
# -s::
#	Specify whether to print a short version of the patch without
#	a patch header with meta info such as author and committer.
#
# -r FROM_ID[:TO_ID]::
#	Specify a set of commits to make patches from using either
#	'-r FROM_ID[:TO_ID]' or '-r FROM_ID -r TO_ID'. In both cases the
#	option expects IDs which resolve to commits and will include the
#	specified IDs. If 'TO_ID' is omitted patches for all commits
#	from 'FROM_ID' to the initial commit will be generated. If no
#	`-r` option is given the commit ID defaults to 'HEAD'.
#
# -m::
#	Base the patches at the merge base of the -r arguments
#	(defaulting to master and origin).
#
# EXAMPLE USAGE
# -------------
# To make patches for all commits between two releases tagged as
# 'releasetag-0.9' and 'releasetag-0.10' do:
#
#	$ cg-mkpatch -r releasetag-0.9:releasetag-0.10
#
# The output will be a contionuos dump of patches each separated by
# the line
# `!-------------------------------------------------------------flip-`

USAGE="cg-mkpatch [-m] [-s] [-r FROM_ID[:TO_ID]]"

. ${COGITO_LIB}cg-Xlib

omit_header=
log_start=
log_end=
mergebase=

showpatch () {
	header=$(mktemp -t gitpatch.XXXXXX)
	patch=$(mktemp -t gitpatch.XXXXXX)
	id=$1
	cg-diff -p -r $id >$patch
	git-cat-file commit $id | while read key rest; do
		case "$key" in
		"author"|"committer")
			date=(${rest#*> })
			pdate="$(showdate ${date[*]})"
			[ "$pdate" ] && rest="${rest%> *}> $pdate"
			echo $key $rest >>$header
			;;
		"")
			cat
			if [ ! "$omit_header" ]; then
				echo
				echo ---

				echo commit $id
				cat $header
				echo
				cat $patch | git-apply --stat
			fi
			;;
		*)
			echo $key $rest >>$header
			;;
		esac
	done
	echo
	cat $patch
	rm $header $patch
}


while optparse; do
	if optparse -s; then
		omit_header=1
	elif optparse -r=; then
		if echo "$OPTARG" | grep -q ':'; then
			log_end=$(echo "$OPTARG" | cut -d : -f 2)
			[ "$log_end" ] || log_end="HEAD"
			log_start=$(echo "$OPTARG" | cut -d : -f 1)
		elif [ -z "$log_start" ]; then
			log_start="$OPTARG"
		else
			log_end="$OPTARG"
		fi
	elif optparse -m; then
		mergebase=1
	else
		optfail
	fi
done

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

if [ "$log_end" ]; then
	id1=$(commit-id "$log_start") || exit 1
	id2=$(commit-id "$log_end") || exit 1

	git-rev-tree $id2 ^$id1 | sort -n | while read time commit rest; do
		id=$(echo $commit | cut -d : -f 1)
		showpatch $id
		echo
		echo
		echo -e '\014'
		echo '!-------------------------------------------------------------flip-'
		echo
		echo
	done

else
	id=$(commit-id "$log_start") || exit 1
	showpatch $id
fi
