| #!/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' \ |
| -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 ("<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 |