| #!/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/</</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 "<html><head><title>Tweak Log - Error</title></head>\n"; |
| print "<body><h1>ERROR</h1>\n<p>$error</p></body></html>\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 "<html>\n<head>\n<title>Tweak Log</title>\n</head>\n"; |
| print "<body>\n<form action=\"$gActionURL\" method=\"post\">\n"; |
| print "<a name=\"__top__\"></a>\n"; |
| print "<p>\n"; |
| print "Boy, I sure would like to modify that log message for \n"; |
| print "revision <input type=\"text\" name=\"rev\" value\"\">\n"; |
| print "<input type=\"submit\" name=\"action\" value=\"Fetch Log\">\n"; |
| print "</p></form>\n"; |
| print "<p>\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 "</p>\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 "<hr />\n"; |
| print "<a href=\"$gActionURL?action=Fetch+Log&rev=$rev\">Revision $rev</a>:<br />\n"; |
| print "<i>Author: $author</i><br />\n"; |
| print "<i>Date: $date</i><br />\n"; |
| print "<i>Log: </i><br /><pre>\n"; |
| map { |
| $_ = &html_escape ($_); |
| } @infolines; |
| print @infolines; |
| print "</pre><br />\n"; |
| print "<a href=\"#__top__\">(back to top)</a>\n"; |
| $rev--; |
| } |
| print "</body></html>\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 "<html>\n<head>\n<title>Tweak Log - Log Edit</title>\n</head>\n"; |
| print "<body>\n"; |
| print "<h1>Editing Log Message for Revision $rev</h1>\n"; |
| print "<h2>Current log message:</h2>\n"; |
| print "<blockquote><hr /><pre>$escaped_log</pre><hr /></blockquote>\n"; |
| print "<p><font color=\"red\">\n"; |
| print "<i>Every change made is logged in <tt>${gHistoryFile}</tt>.\n"; |
| print "If you make a bogus\n"; |
| print "change, you can still recover the old message from there.</i>\n"; |
| print "</font></p>\n"; |
| print "<form action=\"$gActionURL\" method=\"post\">\n"; |
| print "<h2>New log message:</h2>\n"; |
| print "<blockquote>\n"; |
| print "<textarea cols=\"80\" rows=\"25\" wrap=\"off\" name=\"log\">\n"; |
| print $escaped_log; |
| print "</textarea><br />\n"; |
| print "<input type=\"hidden\" name=\"rev\" value=\"$rev\">\n"; |
| print "<input type=\"submit\" name=\"action\" value=\"Commit Changes\">\n"; |
| print "</blockquote>\n"; |
| print "</form></body></html>\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 "<html>\n<head>\n<title>Tweak Log - Log Changed</title>\n</head>\n"; |
| print "<body>\n"; |
| print "<h1>Success!</h1>\n"; |
| print "<h2>New Log Message for Revision $rev</h2>\n"; |
| print "<blockquote><hr /><pre>$log</pre><hr /></blockquote>\n"; |
| print "</body></html>\n"; |
| return; |
| } |