#!/usr/bin/perl ############################################################################### # Tweak Subversion log messages # ----------------------------- # # It sure would be nice to be able to change the log messages on # committed revisions of the Subversion repository via the web. This # is a quick attempt at making that happen. # # The idea here is that you visit this script at the web page. With # no action supplied, it will present a form asking for the revision # of the log you wish to change. # # Upon submitting the form, it will come back with yet another form, # which will: # # - Display the current log message as static text. # - Present a textarea for editing, initialized with the current # log message. # # The user can edit the message in the textarea, then submit that form, # which will return a confirmation and show the new log message. ############################################################################### use strict; use CGI qw(:standard); ############################################################################### # Configuration Section my $gSvnlookCmd = '/usr/local/bin/svnlook'; my $gSvnadminCmd = '/usr/local/bin/svnadmin'; my $gReposPath = '/usr/www/repositories/svn'; my $gActionURL = './tweak-log.cgi'; my $gTempfilePrefix = '/tmp/tweak-cgi'; my $gHistoryFile = './TWEAKLOG'; my $gBypassRevpropHooks = 0; # set to 1 to bypass the repository hook system my $gNumRecentCommits = 20; # number of recent commits to show on init form ############################################################################### my %gCGIValues = &doCGI( ); &main( ); #-----------------------------------------------------------------------------# sub html_escape # (log) #-----------------------------------------------------------------------------# { my $str = shift; $str =~ s/&/&/g; $str =~ s/>/>/g; $str =~ s/</g; return $str; } #-----------------------------------------------------------------------------# sub doCGI # (void) #-----------------------------------------------------------------------------# { my $lCGI = new CGI; my @lFields = $lCGI->param; my $lField; my %lCGIData = (); foreach $lField ( @lFields ) { $lCGIData{ uc $lField } = $lCGI->param( $lField ); } return( %lCGIData ); } #-----------------------------------------------------------------------------# sub doError # (error) #-----------------------------------------------------------------------------# { my $error = shift @_; print "
$error
\n"; return; } #-----------------------------------------------------------------------------# sub main # (void) #-----------------------------------------------------------------------------# { # Print out HTTP headers. print "Content-type: text/html; charset=UTF-8\n\n"; # Figure out what action to take. if( $gCGIValues{'ACTION'} =~ /fetch/i ) { &doFetchLog(); } elsif( $gCGIValues{'ACTION'} =~ /commit/i ) { &doCommitLog(); } else { &doInitialForm(); } return; } #-----------------------------------------------------------------------------# sub doInitialForm # (void) #-----------------------------------------------------------------------------# { my $youngest = `$gSvnlookCmd youngest $gReposPath`; my $rev; my $oldest; print "\n\n\n"; print "For convenience, here are the most recent $gNumRecentCommits\n"; print "commits (click the revision number to edit that revision's log):\n"; print "
\n"; chomp $youngest; $oldest = $youngest - $gNumRecentCommits + 1; $oldest = 1 if( $oldest < 1 ); $rev = $youngest; while( $rev >= $oldest ) { my @infolines = `$gSvnlookCmd info $gReposPath -r $rev`; my $author = shift @infolines; my $date = shift @infolines; my $log_size = shift @infolines; print "\n"; map { $_ = &html_escape ($_); } @infolines; print @infolines; print "
\n"; print "$escaped_log
\n"; print "Every change made is logged in ${gHistoryFile}.\n"; print "If you make a bogus\n"; print "change, you can still recover the old message from there.\n"; print "
\n"; print "\n"; return; } #-----------------------------------------------------------------------------# sub doCommitLog # (void) #-----------------------------------------------------------------------------# { my $rev = $gCGIValues{'REV'}; my $log = $gCGIValues{'LOG'}; my $orig_log; my $tempfile = "$gTempfilePrefix.$$"; # Make sure we are about to change a valid revision. if (not &isValidRev( $rev )) { return; } # Get the original log from the repository. $orig_log = `$gSvnlookCmd log $gReposPath -r $rev`; # If nothing was changed, go complain to the user (shame on him for # wasting our time like that!) if ($log eq $orig_log) { &doError ("Log message doesn't appear to have been edited."); return; } # Open a tempfile if (not (open( LOGFILE, "> $tempfile"))) { &doError ("Unable to open temporary file."); return; } # Dump the new log into the tempfile (and close it) print LOGFILE $log; close LOGFILE; # Tell our history file what we're about to do. if ($gHistoryFile) { if (not (open (HISTORY, ">> $gHistoryFile"))) { &doError ("Unable to open history file."); return; } print HISTORY "====================================================\n"; print HISTORY "REVISION $rev WAS:\n"; print HISTORY "----------------------------------------------------\n"; print HISTORY $orig_log; print HISTORY "\n"; } # Now, make the mods if ($gBypassRevpropHooks) { `$gSvnadminCmd setlog $gReposPath -r$rev $tempfile --bypass-hooks`; } else { `$gSvnadminCmd setlog $gReposPath -r$rev $tempfile`; } # ...and remove the tempfile. It is, after all, temporary. unlink $tempfile; # Now, tell the history file what we did. if ($gHistoryFile) { print HISTORY "----------------------------------------------------\n"; print HISTORY "REVISION $rev IS:\n"; print HISTORY "----------------------------------------------------\n"; print HISTORY $log; print HISTORY "\n"; close HISTORY; } # Now, re-read that logfile $log = `$gSvnlookCmd log $gReposPath -r $rev`; $log = &html_escape ($log); print "\n\n\n"; print "\n"; return; }$log