#!/bin/bash
# vi:ts=4
#
# mkChangLog - produce a ChangeLog from git history on stdout
#
# SYNOPSIS
#    mkChangeLog [OPTION]
#
# DESCRIPTION
#     Extract commit comments and label each tag with a date
#     from most recent to oldest.
#     Default output is suitable for including in markdown files
# 
#    -c curRev  - use CurRev as tag name if current rev (latest commit) has no tag
#
#    -s stopRev - will stop the log with the specified tag
#                 changelog will not show commit messages earlier than the stopRev tag
#                 To indicate stopping at very first tag, use -
#
#    -H heading - use heading instead of "CHANGELOG" for heading1 of log
#
#    -h heading - use heading instead of "------------" for heading2 of log
#
# AUTHOR
# Bill Perry  bperrybap@opensource.billsworld.billandterrie.com
# 2016-08-08
#
# NOTES:
# The reason for the curRev option is that when creating a changelog file that
# needs to be part of a tagged revision, there is a chicken & egg problem of
# how do you create the changelog for that revision and also set the tag for
# the revision that includes the updated changelog file?
# If you set the tag to get the changelog output to include the new tag, then
# when you commit the changelog file created from the log output, there is an
# additional commit that affects the log and the log file for the commit is
# *after* the tag instead of being at the tag.
# The answer to this dilema: "CHEAT" a bit.
# So the curRev option will fake the tagname on the most recent commits since
# the previous tag to be the name "curRev" even though the tag has not yet been
# set. There is still one limitation the commit log message for updating the
# changelog file will not be shown for the changes associated with the new tag.
# There is no way to work around this.
# However, the log message entry will show when the next tag is created, so it
# is only the most recent tag that will be missing the commit message for the
# changelog file update.
#
# This allows a process of:
#
# - generate the ChangeLog with curRev as tag about to be created
# - commit ChangeLog 
# - tag with the tag used as curRev
#
# This will create a single commit as well as the desired tag pointing
# to the commit that contains the log for new tag.
# 
#

pname=`basename $0`

HEADING1="CHANGELOG"
HEADING2="----------------------"

function USAGE {
	printf "usage:\n"
	printf "$pname [option]\n"
	printf "\t-c curRev\n\t\tuse CurRev as tagname if current rev has no tag\n"
	printf "\t-s stopRev\n\t\tno log output prior to stopRev tag\n"
	printf "\t-H heading\n\t\tuse heading instead of \"$HEADING1\" for heading1 of log\n"
	printf "\t-h heading\n\t\tuse heading instead of \"$HEADING2\" for heading2 of log\n"
}

# check for arguments
while getopts ":s:c:H:h:" opt; do
	case $opt in
		c)
			curRev=$OPTARG
			;;
   		s)
			stopRev=$OPTARG
			;;
   		H)
			HEADING1=$OPTARG
			;;
   		h)
			HEADING2=$OPTARG
			;;
		\?)
			echo "$pname: invalid option: -$OPTARG" >&2
			USAGE
			exit 1
			;;
		:)
			echo "Option -$OPTARG requires an argument." >&2
			USAGE
			exit 1
			;;
	esac
done


# if stopRev is "-" then use first tag in repo
if [ "$stopRev" = "-" ]
then
	stopRev=$(git tag -l | head -1)
fi

#now generate the change log

# if headings are empty, don't output them
if [ "$HEADING1" != "" ]
then
	echo "$HEADING1"
fi
if [ "$HEADING2" != "" ]
then
	echo "$HEADING2"
fi

git for-each-ref --sort='*authordate' --format='%(tag)' refs/tags |tac |grep -v '^$' | while read TAG ; do
	echo 
	if [ $NEXT ];then
		rev=$NEXT
	else
		if [ "$curRev" != "" ]
		then
	        rev=$curRev
		else
        	rev=`git describe --dirty`
		fi
	fi
	if [ "$rev" != "$TAG" ]
	then
		echo [$rev] - `git log -1 --format=%ad --date=short $NEXT`
		if [ "$rev" = "$stopRev" ]
		then
# note: the exit below exits the loop command not the script
# the exit value will be the status code of the loop
			exit 9
		fi
		GIT_PAGER=cat git log --no-merges --format=" * %s" $TAG..$NEXT
	fi
	NEXT=$TAG
done

# if loop exited because it stopped early from a stopRev, status will be non zero
# so exit script
if [ $? -ne 0 ]
then
	exit 0
fi
FIRST=$(git tag -l | head -1)
echo
echo [$FIRST] - `git log -1 --format=%ad --date=short $FIRST`
if [ "$FIRST" = "$stopRev" ]
then
	exit 0
fi
# if this GIT_PAGER below is commented out then the logs for the first tag are not emitted
GIT_PAGER=cat git log --no-merges --format=" * %s" $FIRST
