#!/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. # # ==================================================================== # Copyright (c) 2001-2003 CollabNet. All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://subversion.tigris.org/license.html. # If newer versions of this license are posted there, you may use a # newer version instead, at your option. # # This software consists of voluntary contributions made by many # individuals. For exact contribution history, see the revision # history and logs, available at http://subversion.tigris.org/. # ==================================================================== ############################################################################### 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/param; my $lField; my %lCGIData = (); foreach $lField ( @lFields ) { $lCGIData{ uc $lField } = $lCGI->param( $lField ); } return( %lCGIData ); } #-----------------------------------------------------------------------------# sub doError # (error) #-----------------------------------------------------------------------------# { my $error = shift @_; print "Tweak Log - Error\n"; print "

ERROR

\n

$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\nTweak Log\n\n"; print "\n
\n"; print "\n"; print "

\n"; print "Boy, I sure would like to modify that log message for \n"; print "revision \n"; print "\n"; print "

\n"; print "

\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"; print "Revision $rev:
\n"; print "Author: $author
\n"; print "Date: $date
\n"; print "Log:
\n";
        map {
            $_ = &html_escape ($_);
        } @infolines;
	print @infolines;
	print "

\n"; print "(back to top)\n"; $rev--; } print "\n"; return; } #-----------------------------------------------------------------------------# sub isValidRev # (rev) #-----------------------------------------------------------------------------# { my $youngest = `$gSvnlookCmd youngest $gReposPath`; my $rev = shift @_; if(not (( $youngest =~ /^\d+$/) and ( $youngest > 0 ))) { &doError( "Unable to determine youngest revision" ); return 0; } if(not (( $rev =~ /^\d+$/) and ( $rev <= $youngest ))) { &doError( "'$rev' is not a valid revision number" ); return 0; } return 1; } #-----------------------------------------------------------------------------# sub doFetchLog # (void) #-----------------------------------------------------------------------------# { my $rev = $gCGIValues{'REV'}; my $log; my $escaped_log; ## HTML-escaped version of $log # Make sure we've requested a valid revision. if( not &isValidRev( $rev )) { return; } # Fetch the log for that revision. $log = `$gSvnlookCmd log $gReposPath -r $rev`; $escaped_log = &html_escape ($log); # Display the form for editing the revision print "\n\nTweak Log - Log Edit\n\n"; print "\n"; print "

Editing Log Message for Revision $rev

\n"; print "

Current log message:

\n"; print "

$escaped_log

\n"; print "

\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"; print "

New log message:

\n"; print "
\n"; print "
\n"; print "\n"; print "\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\nTweak Log - Log Changed\n\n"; print "\n"; print "

Success!

\n"; print "

New Log Message for Revision $rev

\n"; print "

$log

\n"; print "\n"; return; }