Re: CVS updates on committers list...
| От | Ian Lance Taylor |
|---|---|
| Тема | Re: CVS updates on committers list... |
| Дата | |
| Msg-id | siely88j26.fsf@daffy.airs.com обсуждение исходный текст |
| Ответ на | RE: CVS updates on committers list... ("Larry Rosenman" <ler@lerctr.org>) |
| Ответы |
Re: CVS updates on committers list...
|
| Список | pgsql-hackers |
Tom Lane <tgl@sss.pgh.pa.us> writes:
> "Larry Rosenman" <ler@lerctr.org> writes:
> > I'm referring to the actual commit messages.
>
> It *would* be awfully nice if the pgsql-committers traffic were one
> message per commit, instead of one per directory touched per commit.
> This has been suggested before, but nothing got done ...
This is easy to set up, for an appropriate definition of ``easy''.
I have included two files below, commit_prep and log_accum. Do cvs co CVSROOT
Copy the files into the CVSROOT directory. Check the definition of
$MAILER near the top of log_accum. Everything else should be fine.
Do this: cvs add commit_prep cvs add log_accum
Edit the file checkoutlist, and add these lines:
commit_prep Won't be able to do mail logging.
log_accum Won't be able to do mail logging.
Edit the file commitinfo, and add something like this line:
DEFAULT /usr/bin/perl $CVSROOT/CVSROOT/commit_prep -r
Edit the file loginfo, and add something like this line (replace
MAILINGLIST with the mailing address to which you want log messages to
be sent):
DEFAULT /usr/bin/perl $CVSROOT/CVSROOT/log_accum -m MAILINGLIST -s %s
Then: cvs commit
Then test it.
Good luck. I didn't write these scripts, but I've used them in a
number of places.
Ian
commit_prep:
==================================================
#!/usr/bin/perl
# -*-Perl-*-
#
# $Id: commit_prep,v 1.1 1998/12/01 03:18:27 ian Exp $
#
# Perl filter to handle pre-commit checking of files. This program
# records the last directory where commits will be taking place for
# use by the log_accum.pl script. For new files, it forces the
# existence of a RCS "Id" keyword in the first ten lines of the file.
# For existing files, it checks version number in the "Id" line to
# prevent losing changes because an old version of a file was copied
# into the direcory.
#
# Possible future enhancements:
#
# Check for cruft left by unresolved conflicts. Search for
# "^<<<<<<<$", "^-------$", and "^>>>>>>>$".
#
# Look for a copyright and automagically update it to the
# current year. [[ bad idea! -- woods ]]
#
#
# Contributed by David Hampton <hampton@cisco.com>
#
# Hacked on lots by Greg A. Woods <woods@web.net>
#
# Configurable options
#
# Constants (remember to protect strings from RCS keyword substitution)
#
$LAST_FILE = "/tmp/#egcscvs.lastdir"; # must match name in log_accum.pl
$ENTRIES = "CVS/Entries";
# Patterns to find $Log keywords in files
#
$LogString1 = "\\\$\\Log: .* \\\$";
$LogString2 = "\\\$\\Log\\\$";
$NoLog = "%s - contains an RCS \$Log keyword. It must not!\n";
# pattern to match an RCS Id keyword line with an existing ID
#
$IDstring = "\"@\\(#\\)[^:]*:.*\\\$\Id: .*\\\$\"";
$NoId = "
%s - Does not contain a properly formatted line with the keyword \"Id:\".I.e. no lines match \"" . $IDstring .
"\".Pleasesee the template files for an example.\n";
# pattern to match an RCS Id keyword line for a new file (i.e. un-expanded)
#
$NewId = "\"@(#)[^:]*:.*\\$\Id\\$\"";
$NoName = "
%s - The ID line should contain only \"@(#)module/path:\$Name\$:\$\Id\$\"for a newly created file.\n";
$BadName = "
%s - The file name '%s' in the ID line does not matchthe actual filename.\n";
$BadVersion = "
%s - How dare you!!! You replaced your copy of the file '%s',which was based upon version %s, with an %s version
basedupon%s. Please move your '%s' out of the way, perform anupdate to get the current version, and them merge your
changesintothat file, then try the commit again.\n";
#
# Subroutines
#
sub write_line { local($filename, $line) = @_; open(FILE, ">$filename") || die("Cannot open $filename, stopped");
print(FILE$line, "\n"); close(FILE);
}
sub check_version { local($i, $id, $rname, $version); local($filename, $cvsversion) = @_;
open(FILE, "<$filename") || return(0);
@all_lines = (); $idpos = -1; $newidpos = -1; for ($i = 0; <FILE>; $i++) {chop;push(@all_lines, $_);if ($_ =~
/$IDstring/){ $idpos = $i;}if ($_ =~ /$NewId/) { $newidpos = $i;} }
if (grep(/$LogString1/, @all_lines) || grep(/$LogString2/, @all_lines)) {print STDERR sprintf($NoLog,
$filename);return(1); }
if ($debug != 0) {print STDERR sprintf("file = %s, version = %d.\n", $filename, $cvsversion{$filename}); }
if ($cvsversion{$filename} == 0) {if ($newidpos != -1 && $all_lines[$newidpos] !~ /$NewId/) { print STDERR
sprintf($NoName,$filename); return(1);}return(0); }
if ($idpos == -1) {print STDERR sprintf($NoId, $filename);return(1); }
$line = $all_lines[$idpos]; $pos = index($line, "Id: "); if ($debug != 0) {print STDERR sprintf("%d in '%s'.\n",
$pos,$line); } ($id, $rname, $version) = split(' ', substr($line, $pos)); if ($rname ne "$filename,v") {print
STDERRsprintf($BadName, $filename, substr($rname, 0, length($rname)-2));return(1); } if ($cvsversion{$filename} <
$version){print STDERR sprintf($BadVersion, $filename, $filename, $cvsversion{$filename}, "newer",
$version,$filename);return(1); } if ($cvsversion{$filename} > $version) {print STDERR sprintf($BadVersion,
$filename,$filename, $cvsversion{$filename}, "older", $version, $filename);return(1); } return(0);
}
#
# Main Body
#
$id = getpgrp(); # You *must* use a shell that does setpgrp()!
# Check each file (except dot files) for an RCS "Id" keyword.
#
$check_id = 0;
# Record the directory for later use by the log_accumulate stript.
#
$record_directory = 0;
# parse command line arguments
#
while (@ARGV) { $arg = shift @ARGV;
if ($arg eq '-d') {$debug = 1;print STDERR "Debug turned on...\n"; } elsif ($arg eq '-c') {$check_id = 1; }
elsif($arg eq '-r') {$record_directory = 1; } else {push(@files, $arg); }
}
$directory = shift @files;
if ($debug != 0) { print STDERR "dir - ", $directory, "\n"; print STDERR "files - ", join(":", @files), "\n";
printSTDERR "id - ", $id, "\n";
}
# Suck in the CVS/Entries file
#
open(ENTRIES, $ENTRIES) || die("Cannot open $ENTRIES.\n");
while (<ENTRIES>) { local($filename, $version) = split('/', substr($_, 1)); $cvsversion{$filename} = $version;
}
# Now check each file name passed in, except for dot files. Dot files
# are considered to be administrative files by this script.
#
if ($check_id != 0) { $failed = 0; foreach $arg (@files) {if (index($arg, ".") == 0) { next;}$failed +=
&check_version($arg); } if ($failed) {print STDERR "\n";exit(1); }
}
# Record this directory as the last one checked. This will be used
# by the log_accumulate script to determine when it is processing
# the final directory of a multi-directory commit.
#
if ($record_directory != 0) { &write_line("$LAST_FILE.$id", $directory);
}
exit(0);
==================================================
log_accum:
==================================================
#!/usr/bin/perl
# -*-Perl-*-
#
# Perl filter to handle the log messages from the checkin of files in
# a directory. This script will group the lists of files by log
# message, and mail a single consolidated log message at the end of
# the commit.
#
# This file assumes a pre-commit checking program that leaves the
# names of the first and last commit directories in a temporary file.
#
# Contributed by David Hampton <hampton@cisco.com>
#
# hacked greatly by Greg A. Woods <woods@web.net>
# Usage: log_accum.pl [-d] [-s] [-M module] [[-m mailto] ...] [-f logfile]
# -d - turn on debugging
# -m mailto - send mail to "mailto" (multiple)
# -M modulename - set module name to "modulename"
# -f logfile - write commit messages to logfile too
# -s - *don't* run "cvs status -v" for each file
#
# Configurable options
#
# Set this to something that takes "-s"
$MAILER = "/bin/mail";
# Constants (don't change these!)
#
$STATE_NONE = 0;
$STATE_CHANGED = 1;
$STATE_ADDED = 2;
$STATE_REMOVED = 3;
$STATE_LOG = 4;
$LAST_FILE = "/tmp/#egcscvs.lastdir";
$CHANGED_FILE = "/tmp/#egcscvs.files.changed";
$ADDED_FILE = "/tmp/#egcscvs.files.added";
$REMOVED_FILE = "/tmp/#egcscvs.files.removed";
$LOG_FILE = "/tmp/#egcscvs.files.log";
$FILE_PREFIX = "#egcscvs.files";
#
# Subroutines
#
sub cleanup_tmpfiles { local($wd, @files);
$wd = `pwd`; chdir("/tmp") || die("Can't chdir('/tmp')\n"); opendir(DIR, "."); push(@files,
grep(/^$FILE_PREFIX\..*\.$id$/,readdir(DIR))); closedir(DIR); foreach (@files) {unlink $_; } unlink $LAST_FILE
."." . $id;
chdir($wd);
}
sub write_logfile { local($filename, @lines) = @_;
open(FILE, ">$filename") || die("Cannot open log file $filename.\n"); print FILE join("\n", @lines), "\n";
close(FILE);
}
sub format_names { local($dir, @files) = @_; local(@lines);
if ($dir =~ /^\.\//) {$dir = $'; } if ($dir =~ /\/$/) {$dir = $`; } if ($dir eq "") {$dir = "."; }
$format = "\t%-" . sprintf("%d", length($dir) > 15 ? length($dir) : 15) . "s%s ";
$lines[0] = sprintf($format, $dir, ":");
if ($debug) {print STDERR "format_names(): dir = ", $dir, "; files = ", join(":", @files), ".\n"; } foreach
$file(@files) {if (length($lines[$#lines]) + length($file) > 65) { $lines[++$#lines] = sprintf($format, " ", "
");}$lines[$#lines].= $file . " "; }
@lines;
}
sub format_lists { local(@lines) = @_; local(@text, @files, $lastdir);
if ($debug) {print STDERR "format_lists(): ", join(":", @lines), "\n"; } @text = (); @files = (); $lastdir =
shift@lines; # first thing is always a directory if ($lastdir !~ /.*\/$/) {die("Damn, $lastdir doesn't look like a
directory!\n"); } foreach $line (@lines) {if ($line =~ /.*\/$/) { push(@text, &format_names($lastdir, @files));
$lastdir = $line; @files = ();} else { push(@files, $line);} } push(@text, &format_names($lastdir, @files));
@text;
}
sub accum_subject { local(@lines) = @_; local(@files, $lastdir);
$lastdir = shift @lines; # first thing is always a directory @files = ($lastdir); if ($lastdir !~ /.*\/$/)
{die("Damn,$lastdir doesn't look like a directory!\n"); } foreach $line (@lines) {if ($line =~ /.*\/$/) {
$lastdir= $line; push(@files, $line);} else { push(@files, $lastdir . $line);} }
@files;
}
sub compile_subject { local(@files) = @_; local($text, @a, @b, @c, $dir, $topdir);
# find the highest common directory $dir = '-'; do {$topdir = $dir;foreach $file (@files) { if ($file =~
/.*\/$/){ if ($dir eq '-') { $dir = $file; } else { if (index($dir,$file) == 0) { $dir =
$file; } elsif (index($file,$dir) != 0) { @a = split /\//,$file; @b = split /\//,$dir; @c =
(); CMP: while ($#a > 0 && $#b > 0) { if ($a[0] eq $b[0]) { push(@c, $a[0]);
shift@a; shift @b; } else { last CMP; } } $dir = join('/',@c)
.'/'; } } }} } until $dir eq $topdir;
# strip out directories and the common prefix topdir. chop $topdir; @c = ($modulename . '/' . $topdir);
foreach$file (@files) {if (!($file =~ /.*\/$/)) { push(@c, substr($file, length($topdir)+1));} }
# put it together and limit the length. $text = join(' ',@c); if (length($text) > 50) {$text = substr($text, 0,
46). ' ...'; }
$text;
}
sub append_names_to_file { local($filename, $dir, @files) = @_;
if (@files) {open(FILE, ">>$filename") || die("Cannot open file $filename.\n");print FILE $dir, "\n";print FILE
join("\n",@files), "\n";close(FILE); }
}
sub read_line { local($line); local($filename) = @_;
open(FILE, "<$filename") || die("Cannot open file $filename.\n"); $line = <FILE>; close(FILE); chop($line);
$line;
}
sub read_logfile { local(@text); local($filename, $leader) = @_;
open(FILE, "<$filename"); while (<FILE>) {chop;push(@text, $leader.$_); } close(FILE); @text;
}
sub build_header { local($header); local($sec,$min,$hour,$mday,$mon,$year) = localtime(time); $header =
sprintf("CVSROOT:\t%s\nModulename:\t%s\n", $cvsroot, $modulename); if (defined($branch)) {$header
.=sprintf("Branch: \t%s\n", $branch); } $header .= sprintf("Changes by:\t%s@%s\t%02d/%02d/%02d
%02d:%02d:%02d", $login, $hostdomain, $year%100, $mon+1, $mday, $hour, $min, $sec);
}
sub mail_notification { local($name, $subject, @text) = @_; open(MAIL, "| $MAILER -s \"$subject\" $name"); print
MAILjoin("\n", @text), "\n"; close(MAIL);
}
sub write_commitlog { local($logfile, @text) = @_;
open(FILE, ">>$logfile"); print FILE join("\n", @text), "\n\n"; close(FILE);
}
#
# Main Body
#
# Initialize basic variables
#
$debug = 0;
$id = getpgrp(); # note, you *must* use a shell which does setpgrp()
$state = $STATE_NONE;
$login = $ENV{'USER'} || (getpwuid($<))[0] || "nobody";
chop($hostname = `hostname`);
if ($hostname !~ /\./) { chop($domainname = `domainname`); $hostdomain = $hostname . "." . $domainname;
} else { $hostdomain = $hostname;
}
$cvsroot = $ENV{'CVSROOT'};
$do_status = 1;
$modulename = "";
# parse command line arguments (file list is seen as one arg)
#
while (@ARGV) { $arg = shift @ARGV;
if ($arg eq '-d') {$debug = 1;print STDERR "Debug turned on...\n"; } elsif ($arg eq '-m') {$mailto = "$mailto " .
shift@ARGV; } elsif ($arg eq '-M') {$modulename = shift @ARGV; } elsif ($arg eq '-s') {$do_status = 0; } elsif
($argeq '-f') {($commitlog) && die("Too many '-f' args\n");$commitlog = shift @ARGV; } else {($donefiles) && die("Too
manyarguments! Check usage.\n");$donefiles = 1;@files = split(/ /, $arg); }
}
($mailto) || die("No -m mail recipient specified\n");
# for now, the first "file" is the repository directory being committed,
# relative to the $CVSROOT location
#
@path = split('/', $files[0]);
# XXX there are some ugly assumptions in here about module names and
# XXX directories relative to the $CVSROOT location -- really should
# XXX read $CVSROOT/CVSROOT/modules, but that's not so easy to do, since
# XXX we have to parse it backwards.
#
if ($modulename eq "") { $modulename = $path[0]; # I.e. the module name == top-level dir
}
if ($commitlog ne "") { $commitlog = $cvsroot . "/" . $modulename . "/" . $commitlog unless ($commitlog =~ /^\//);
}
if ($#path == 0) { $dir = ".";
} else { $dir = join('/', @path[1..$#path]);
}
$dir = $dir . "/";
if ($debug) { print STDERR "module - ", $modulename, "\n"; print STDERR "dir - ", $dir, "\n"; print STDERR
"path - ", join(":", @path), "\n"; print STDERR "files - ", join(":", @files), "\n"; print STDERR "id - ",
$id,"\n";
}
# Check for a new directory first. This appears with files set as follows:
#
# files[0] - "path/name/newdir"
# files[1] - "-"
# files[2] - "New"
# files[3] - "directory"
#
if ($files[2] =~ /New/ && $files[3] =~ /directory/) { local(@text);
@text = (); push(@text, &build_header()); push(@text, ""); push(@text, $files[0]); push(@text, "");
while (<STDIN>) {chop; # Drop the newlinepush(@text, $_); }
&mail_notification($mailto, $files[0], @text);
if ($commitlog) {&write_commitlog($commitlog, @text); }
exit 0;
}
# Iterate over the body of the message collecting information.
#
while (<STDIN>) { chop; # Drop the newline
if (/^Modified Files/) { $state = $STATE_CHANGED; next; } if (/^Added Files/) { $state = $STATE_ADDED; next;
} if (/^Removed Files/) { $state = $STATE_REMOVED; next; } if (/^Log Message/) { $state = $STATE_LOG; next;
} if (/^Revision\/Branch/) { /^[^:]+:\s*(.*)/; $branch = $+; next; }
s/^[ \t\n]+//; # delete leading whitespace s/[ \t\n]+$//; # delete trailing whitespace if
($state== $STATE_CHANGED) { push(@changed_files, split); } if ($state == $STATE_ADDED) { push(@added_files,
split);} if ($state == $STATE_REMOVED) { push(@removed_files, split); } if ($state == $STATE_LOG) {
push(@log_lines, $_); }
}
# Strip leading and trailing blank lines from the log message. Also
# compress multiple blank lines in the body of the message down to a
# single blank line.
#
while ($#log_lines > -1) { last if ($log_lines[0] ne ""); shift(@log_lines);
}
while ($#log_lines > -1) { last if ($log_lines[$#log_lines] ne ""); pop(@log_lines);
}
for ($i = $#log_lines; $i > 0; $i--) { if (($log_lines[$i - 1] eq "") && ($log_lines[$i] eq "")) {splice(@log_lines,
$i,1); }
}
# Check for an import command. This appears with files set as follows:
#
# files[0] - "path/name"
# files[1] - "-"
# files[2] - "Imported"
# files[3] - "sources"
#
if ($files[2] =~ /Imported/ && $files[3] =~ /sources/) { local(@text);
@text = (); push(@text, &build_header()); push(@text, "");
push(@text, "Log message:"); while ($#log_lines > -1) {push (@text, " " . $log_lines[0]);shift(@log_lines);
}
&mail_notification($mailto, "Import $file[0]", @text);
if ($commitlog) {&write_commitlog($commitlog, @text); }
exit 0;
}
if ($debug) { print STDERR "Searching for log file index...";
}
# Find an index to a log file that matches this log message
#
for ($i = 0; ; $i++) { local(@text);
last if (! -e "$LOG_FILE.$i.$id"); # the next available one @text = &read_logfile("$LOG_FILE.$i.$id", ""); last
if($#text == -1); # nothing in this file, use it last if (join(" ", @log_lines) eq join(" ", @text)); # it's the
samelog message as another
}
if ($debug) { print STDERR " found log file at $i.$id, now writing tmp files.\n";
}
# Spit out the information gathered in this pass.
#
&append_names_to_file("$CHANGED_FILE.$i.$id", $dir, @changed_files);
&append_names_to_file("$ADDED_FILE.$i.$id", $dir, @added_files);
&append_names_to_file("$REMOVED_FILE.$i.$id", $dir, @removed_files);
&write_logfile("$LOG_FILE.$i.$id", @log_lines);
# Check whether this is the last directory. If not, quit.
#
if ($debug) { print STDERR "Checking current dir against last dir.\n";
}
$_ = &read_line("$LAST_FILE.$id");
if ($_ ne $cvsroot . "/" . $files[0]) { if ($debug) {print STDERR sprintf("Current directory %s is not last directory
%s.\n",$cvsroot . "/" .$files[0], $_); } exit 0;
}
if ($debug) { print STDERR sprintf("Current directory %s is last directory %s -- all commits done.\n", $files[0],
$_);
}
#
# End Of Commits!
#
# This is it. The commits are all finished. Lump everything together
# into a single message, fire a copy off to the mailing list, and drop
# it on the end of the Changes file.
#
#
# Produce the final compilation of the log messages
#
@text = ();
@status_txt = ();
@subject_files = ();
push(@text, &build_header());
push(@text, "");
for ($i = 0; ; $i++) { last if (! -e "$LOG_FILE.$i.$id"); # we're done them all! @lines =
&read_logfile("$CHANGED_FILE.$i.$id",""); if ($#lines >= 0) {push(@text, "Modified files:");push(@text,
&format_lists(@lines));push(@subject_files,&accum_subject(@lines)); } @lines = &read_logfile("$ADDED_FILE.$i.$id",
""); if ($#lines >= 0) {push(@text, "Added files:");push(@text, &format_lists(@lines));push(@subject_files,
&accum_subject(@lines)); } @lines = &read_logfile("$REMOVED_FILE.$i.$id", ""); if ($#lines >= 0) {push(@text,
"Removedfiles:");push(@text, &format_lists(@lines));push(@subject_files, &accum_subject(@lines)); } if ($#text >=
0){push(@text, ""); } @lines = &read_logfile("$LOG_FILE.$i.$id", "\t"); if ($#lines >= 0) {push(@text, "Log
message:");push(@text,@lines);push(@text, ""); } if ($do_status) {local(@changed_files);
@changed_files = ();push(@changed_files, &read_logfile("$CHANGED_FILE.$i.$id", ""));push(@changed_files,
&read_logfile("$ADDED_FILE.$i.$id",""));push(@changed_files, &read_logfile("$REMOVED_FILE.$i.$id", ""));
if ($debug) { print STDERR "main: pre-sort changed_files = ", join(":", @changed_files),
".\n";}sort(@changed_files);if($debug) { print STDERR "main: post-sort changed_files = ", join(":", @changed_files),
".\n";}
foreach $dofile (@changed_files) { if ($dofile =~ /\/$/) { next; # ignore the silly "dir" entries }
if($debug) { print STDERR "main(): doing status on $dofile\n"; } open(STATUS, "-|") || exec 'cvs', '-n',
'status','-Qqv', $dofile; while (<STATUS>) { chop; push(@status_txt, $_); }} }
}
$subject_txt = &compile_subject(@subject_files);
# Write to the commitlog file
#
if ($commitlog) { &write_commitlog($commitlog, @text);
}
if ($#status_txt >= 0) { push(@text, @status_txt);
}
# Mailout the notification.
#
&mail_notification($mailto, $subject_txt, @text);
# cleanup
#
if (! $debug) { &cleanup_tmpfiles();
}
exit 0;
==================================================
В списке pgsql-hackers по дате отправления: