#!/bin/sh

# $PostgreSQL: pgsql/src/tools/pgcvslog,v 1.40 2009/08/29 17:09:20 momjian Exp $

# This utility is used to generate a compact list of changes
# for each release, bjm 2000-02-22

# Usage:  pgcvslog [-d] [-h]
# -d delete commits that include back branches
# -h is HTML output
# "-d" is useful for generating release notes for major releases

# This program basically takes a cvs log, groups it by commit timestamp
# and line number, then compares adjacent messages.  If they have the same
# commit message, they are assumed to be part of the same commit and
# appear as one commit message with multiple file names

# All branches:	
# cvs log -d'>1999-06-14 00:00:00 GMT' . > log
#
# HEAD:
# cvs log -d'>2000-05-29 00:00:00 GMT' -b .
#
# Branch:
# cvs log -d'>2000-05-29 00:00:00 GMT' -rREL8_0_STABLE .
#
# Date range
# cvs log -d'2005-05-08<2005-05-29' -rREL8_0_STABLE .
#
# To find branch time, look for "branches:" tag in CVS commit logs
# e.g. "branches:  1.398.4;" matches "REL8_0_STABLE: 1.398.0.4".

HTML="N"
DEL="N"
if [ "X$1" = "X-h" ]
then	HTML="Y"
	shift
fi

if [ "X$1" = "X-d" ]
then	DEL="Y"
	shift
fi

if [ "X$1" = "X-h" ]
then	HTML="Y"
	shift
fi

if [ "$HTML" = "Y" -a "$DEL" = "Y" ]
then	echo "Cannot use -d and -h together" 1>&2
	exit 1
fi

cat "$@" |

# protect HTML input if in HTML mode
if [ "$HTML" = "Y" ]
then	sed 	-e 's/\&/\&amp;/g' \
		-e 's/</\&lt;/g' \
		-e 's/>/\&gt;/g' \
		-e 's/"/\&quot;/g'
else	cat
fi |

# mark each line with a datetime and line number, for sorting and merging
# we are just pre-processing the file at this point
# We don't print anything from the -- or == line and the date:

awk '	BEGIN	{html="'"$HTML"'"; lineno = 0;}
	# store working directory
	$0 ~ /^Working file:/	{workingfile = "/" $3}

	($0 ~ /^====*$/ || $0 ~ /^----*$/) \
	{
		# print blank line to separate entries
		if (datetime != "")
		{
 			if (html != "Y")
				printf ("%s| %10d|%s\n", datetime, lineno++, "");
			printf ("%s| %10d|", datetime, lineno++);
 			if (html != "Y")
				printf ("%s\n", "---");
			else	printf ("<HR>\n");
		}
		datetime="";
	}

	# if we have a saved datetime, print filename, date line, and committer
	datetime != "" && $1 != "branches:" {printf ("%s| %10d| %s\n", datetime, lineno++, $0);}

	$1 == "date:" \
	{
		# get entry date
		datetime=$2"-"$3
		if (workingfile != "")
		{
			printf ("%s| %10d|", datetime, lineno++);
			if (html != "Y")
				printf ("%s%s\n", workingfile, back_branch);
			else	printf ("<SMALL><FONT COLOR=\"red\">%s%s</FONT></SMALL>\n", workingfile, back_branch);

			# output name of committer
			# remove semicolon from committers name
			gsub("/", "-", $2);
			gsub(";", "", $3);
			gsub(";", "", $5);
			printf ("%s| %10d|", datetime, lineno++);
			if (html != "Y")
				printf ("%78s\n", $5);
			else	printf ("<DIV ALIGN=\"right\"><SMALL><FONT COLOR=\"teal\">%s</FONT> <FONT COLOR=\"green\">%s</FONT></SMALL></DIV>\n", $5, $2);
		}
	}

	# mark back branches
	$1 == "revision" \
	{
		# back branches have +2 periods in revision number
		if ($2 ~ /\..*\./)
			back_branch=" <branch>"
		else	back_branch = ""
	}

	/* clear working file */
	$0 ~ /^====*$/	{workingfile=""}' |

sort | cut -d'|' -f3 |

# collect duplicate narratives
# print file names as we get them, then print narrative when a new 
# narrative appears
# have to save two narratives to compare them

awk '   BEGIN   { narr_slot = 0; oldnarr_slot=0; save_working = ""; 
		  html="'"$HTML"'"}
	{
		# We have a filename, so we look at the previous
		# narrative to see if it is new narrative text.
		if ($0 ~ "^/")
		{
			# If there are a different number of narrative
			# lines, they cannot possibly be the same.
			if (narr_slot != oldnarr_slot)
				same = "N";
			else
			{
				same = "Y";
				for (i=1; i <= narr_slot; i++)
				{
					if (oldnarr[i] != narr[i])
					{
						same = "N";
						break;
					}
				}
			}

			# dump out the old narrative if it is new
			if (same == "N")
			{
				if (oldnarr_slot)
					for (i=1; i <= oldnarr_slot; i++)
					{
						print oldnarr[i];
						if (html == "Y" && 
						    oldnarr[i] != "<HR>" &&
						    oldnarr[i] !~ "^<DIV ")
							print "<BR>";
					}

				# save the current narrative
				for (i=1; i <= narr_slot; i++)
					oldnarr[i] = narr[i];
				oldnarr_slot = narr_slot;
			}
			narr_slot = 0;

			# dump out the previous filename
			print save_working;

			if (html == "Y")
				print "<BR>";

			# store the current filename for later printing
			save_working = $0;
		}
		else
		# we have a narrative line
		{
			# accumulate narrative
			narr[++narr_slot] = $0;
		}
	}
	END \
	{
		# If there are a different number of narrative
		# lines, they can not possibly be the same.
		if (narr_slot != oldnarr_slot)
			same = "N";
		else
		{
			same = "Y";
			for (i=1; i <= narr_slot; i++)
			{
				if (oldnarr[i] != narr[i])
				{
					same = "N";
					break;
				}
			}
		}

		# dump out the old narrative if it is new
		if (same == "N")
		{
			if (oldnarr_slot)
				for (i=1; i <= oldnarr_slot; i++)
				{
					print oldnarr[i];
					if (html == "Y" && 
					    oldnarr[i] != "<HR>" &&
					    oldnarr[i] !~ "^<DIV ")
						print "<BR>";
				}
		}

		# dump out the last filename
		print save_working;

		if (html == "Y")
			print "<BR>";

		# dump out the last narrative
		for (i=1; i <= narr_slot; i++)
		{
			print narr[i];
			if (html == "Y" && 
			    narr[i] != "<HR>" &&
			    narr[i] !~ "^<DIV ")
				print "<BR>";
		}
	}' |

# add HTML wrapper
if [ "$HTML" = "Y" ]
then	echo "<HTML>"
	echo "<HEAD>"
	echo "<TITLE>CVS</TITLE>"
	echo "</HEAD>"
	echo "<BODY>"
	cat
	echo "</BODY>"
	echo "</HTML>"
else	cat
fi |

# if requested, remove any commit that has the "<branch>" text
if [ "$DEL" = "Y" ]
then	awk 'BEGIN \
	{
		slot = 0;
	}

	{
		# new commit?
		if ($0 ~ "^---$")
		{
			skip = "N";
			for (i=1; i <= slot; i++)
				if (commit[i] ~ "<branch>")
					skip = "Y";
			if (skip == "N")
				for (i=1; i <= slot; i++)
					print commit[i];
			slot = 0;
		}

		# accumulate commit
		commit[++slot] = $0;
	}

	END \
	{
		skip = "N";
		for (i=1; i <= slot; i++)
			if (commit[i] ~ "<branch>")
				skip = "Y";
		if (skip == "N")
			for (i=1; i <= slot; i++)
				print commit[i];
	}'
else	cat
fi
