#!/bin/sh # $PostgreSQL: pgsql/src/tools/pgcvslog,v 1.39 2007/10/09 02:56:44 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". # # Remove these from the log file before processing to reduce the number # of unneeded log entries: # # /cvsroot/pgsql/doc/TODO # /cvsroot/pgsql/doc/FAQ # /cvsroot/pgsql/doc/src/FAQ/TODO.html # /cvsroot/pgsql/doc/src/FAQ/FAQ.html # 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/\&/\&/g' \ -e 's//\>/g' \ -e 's/"/\"/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} # no need to show TODO or FAQ changes in the output $0 !~ /^====*$/ && (workingfile == "/doc/TODO" || workingfile == "/doc/src/FAQ/TODO.html" || workingfile == "/doc/FAQ" || workingfile == "/doc/src/FAQ/FAQ.html") \ {next} ($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 ("
\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 ("%s%s\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 ("
%s %s
\n", $5, $2); } } # mark back branches $1 == "revision" \ { # back branches have +2 periods in revision number if ($2 ~ /\..*\./) back_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] != "
" && oldnarr[i] !~ "^
"; } # 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 "
"; # 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] != "
" && oldnarr[i] !~ "^
"; } } # dump out the last filename print save_working; if (html == "Y") print "
"; # dump out the last narrative for (i=1; i <= narr_slot; i++) { print narr[i]; if (html == "Y" && narr[i] != "
" && narr[i] !~ "^
"; } }' | # add HTML wrapper if [ "$HTML" = "Y" ] then echo "" echo "" echo "CVS" echo "" echo "" cat echo "" echo "" else cat fi | # if requested, remove any commit that has the "" text if [ "$DEL" = "Y" ] then awk 'BEGIN \ { slot = 0; } { # new commit? if ($0 ~ "^---$") { skip = "N"; for (i=1; i <= slot; i++) if (commit[i] ~ "") 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] ~ "") skip = "Y"; if (skip == "N") for (i=1; i <= slot; i++) print commit[i]; }' else cat fi