# p4rollback.pl - roll back a submitted Perforce changelist. # Usage: perl p4rollback.pl [-d] [-Ds] [-Dt] change # The optional flags conform to those used by "p4 integ". # This script is basically a translation of the instructions given in # Tech Note 14, "How do you back out a change?" # http://www.perforce.com/perforce/technotes/note014.html # When executed, the script will examine each file in the target changelist, # and figure out what action to take to roll it back. For adds and deletes we # diverge here a bit from TN 14; the script will refuse to roll back an add or # delete if the file has been modified since then (edited or re-added), since # we'd then be implicitly rolling back changes other than the target change. # The -Ds and -Dt options override that reluctance, much like the non-conforming # adds and deletes in p4 integrate (which are there for the same reason). # At the end of it all, the script will leave you with a new numbered changelist # and possibly some scheduled resolves (if you backed out any old edits). # Reasonable attempts are made to check for errors, but common sense must apply. # For example, if you try to roll back files that are outside of your client view, # or that you don't have write access to, the operations on those files will fail. # Similarly, if the files are opened for edit, they've been flushed, modified without # being opened for edit, et cetera, you'll get unexpected results. # This script doesn't touch fixes. Presumably you'll know what you want to do with # those yourself. # And as always, look at what you're submitting before you're submitting, including # testing the build if applicable. Especially if you had to resolve new edits against # the rollback results. # You might notice that the script runs a bit slowly - that's in part because it's # checking each individual file for things like non-conforming adds/deletes, and in # part because I'm calling p4 instead of using API functions. There's plenty of # room for optimization. If you feel like optimizing, I encourage you to submit your # results to the Public Depot so we can all benefit. # This isn't an officially supported script. Direct queries to samwise@perforce.com # and I'll help out if I can. $change = 0; $ncadds = 0; $ncdels = 0; $rbchange = 0; $addsskipped = 0; $delsskipped = 0; $editsrolled = 0; $addsrolled = 0; $delsrolled = 0; #Step 0: Parse args. #Not a lot of error handling here. GIGO. foreach ( @ARGV ) { if ( /-d/ ) { $ncadds = 1; $ncdels = 1; } if ( /-Ds/ ) { $ncdels = 1; } if ( /-Dt/ ) { $ncadds = 1; } if ( /([0-9]+)/ ) { $change = $1; } } $prev = $change - 1; #Step one half: Get list of rollback files. $_ = `p4 -s changes -m1`; if ( /exit: 1/ ) { die "Error talking to Perforce server. Bailing.\n"; } $_ = `p4 files \@$change,$change`; @files = split /\n/, $_; #Step 1: create new changelist to hold the rollback changes. $_ = `p4 change -o`; s/<enter description here>/Rollback change $change./; open TEMP, ">p4rollbacktempfile.tmp" or die "Can't make temp file."; print TEMP $_; close TEMP; open NEWCHANGE, "p4 change -i < p4rollbacktempfile.tmp |" or die "Can't create new changelist."; while ( <NEWCHANGE> ) { if ( /^Change ([0-9]+) created.*/ ) { $rbchange = $1; } } close NEWCHANGE; unlink ( "p4rollbacktempfile.tmp " ); # Step 1 accomplished. # $rbchange is the change that's doing the work. # @files is the raw "p4 files" output of what to roll back. # Step 2: Roll back deletes. foreach ( @files ) { $file = $_; chomp $file; if ( $file !~ /(.*)#[0-9]+ - delete change.*/ ) { next; } #test whether file has been readded $head = `p4 files \"$1\"`; chomp $head; if ( $head ne $file and $ncadds == 0 ) #skip nonconf add { print "Skipping re-add of $1 because it has been modified.\n"; $addsskipped++; next; } print "Rolling back delete of $1.\n"; `p4 sync \"$1\@$prev\"`; if ( $head eq $file or $head =~/(.*)#[0-9]+ - delete change.*/ ) { system( "p4 add -c $rbchange \"$1\"" ); } else { system( "p4 edit -c $rbchange \"$1\"" ); } $delsrolled++; print ( "\n" ); } #step 3: Roll back adds/branches/imports. foreach ( @files ) { $file = $_; chomp $file; if ( $file !~ /(.*)#[0-9]+ - add change.*/ and $file !~ /(.*)#[0-9]+ - branch change.*/ and $file !~ /(.*)#[0-9]+ - import change.*/ ) { next; } #test whether file has been edited $head = `p4 files \"$1\"`; chomp $head; if ( $head ne $file and $ncdels == 0 ) #skip nonconf del { print "Skipping delete of $1 because it has been modified.\n"; $delsskipped++; next; } print "Rolling back add of $1.\n"; `p4 flush $1`; system( "p4 delete -c $rbchange \"$1\"" ); $addsrolled++; print ( "\n" ); } #step 4: Roll back edits/integs/purges. foreach ( @files ) { $file = $_; chomp $file; if ( $file !~ /(.*)#[0-9]+ - edit change.*/ and $file !~ /(.*)#[0-9]+ - integrate change.*/ and $file !~ /(.*)#[0-9]+ - purge change.*/ ) { next; } print "Rolling back edit of $1.\n"; $sync = `p4 sync \"$1\@$prev\"`; if ( $sync =~ /^$1#[0-9] - deleted as .*/ ) { #Must have been a purge. print "Can't roll back $1 because nothing exists at change $prev. (tempobj?)\n"; next; } `p4 edit -c $rbchange \"$1\"`; `p4 sync \"$1\@$change\"`; `p4 resolve -ay \"$1\"`; system( "p4 sync \"$1\"" ); $editsrolled++; print ( "\n" ); } # Step the last: Summary of what has been done. print "\nSUMMARY:\n"; if ( $editsrolled ) { print "Rolled back $editsrolled edits.\n"; } if ( $addsrolled ) { print "Rolled back $addsrolled adds\/branches.\n"; } if ( $delsskipped ) { print "Skipped $delsskipped add\/branch rollbacks. Use -d or -Ds to do 'em anyway.\n"; } if ( $delsrolled ) { print "Rolled back $delsrolled deletes.\n"; } if ( $addsskipped ) { print "Skipped $addsskipped delete rollbacks. Use -d or -Dt to do 'em anyway.\n"; } print "\nThe above changes have been put into changelist $rbchange. Submit when ready.\n";
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#12 | 6443 | Sam Stafford |
Fix a bug with running rollback while there are already files in the default changelist. The regex I was using to clear the files from the "change -o" output was malformed. Seems to work now. Thanks go to Doreen for finding the bug, and to Matt for his superior regex skillz. |
||
#11 | 6296 | Sam Stafford | Add pointer to rollback in p4rollback.pl comments, replacing the exhortation for other users to come up with their own API-based implementations. | ||
#10 | 6255 | Sam Stafford |
Fixing up the comments a bit. Had some annoying typos, and the -D options needed some clarification. |
||
#9 | 6114 | Sam Stafford |
Use "p4 info" to get client name instead of "p4 set". "p4 set" doesn't work with "set" on Windows. |
||
#8 | 5906 | Sam Stafford |
Some minor fixes to p4rollback.pl. Make sure that target changelist is empty (rather than inheriting files from the default changelist, which defeats the purpose of putting the work into its own numbered change), and eliminate some extraneous linebreaks from the output. |
||
#7 | 5843 | Sam Stafford | Superfluous space. | ||
#6 | 5785 | Sam Stafford | Add caveat about filetypes to the comments. | ||
#5 | 5784 | Sam Stafford |
Improvements to p4rollback.pl: 1) -Dx flags rethought and rearranged to be more consistent with their meanings in "p4 integ". The logic I'm following now is that a rollback is like integrating from before the bad change into the head rev. Apologies to anyone who had gotten used to how the flags worked before. 2) Check to see if file is deleted at head rev. If so, the -Dt flag can be used to re-add the "last good" rev as the new head rev, without a resolve. (If you don't check for this and try to schedule the usual resolve, you end up getting stuck with an unsubmittable file, since you can't resolve an edit vs a delete. Nasty.) 3) Files not in your client view will no longer be processed. (I made this change after noticing during testing that the "p4 files" command was hitting a remote depot -- confining the command to the client view makes that easy to dodge.) Awesome. |
||
#4 | 5236 | Sam Stafford |
Fixed an oopsie that would cause the script to fail when attempting to roll back adds/branches of files with spaces in their names. (Thanks go to Diane Holt for spotting this problem.) |
||
#3 | 5095 | Sam Stafford |
Changes from Mark Moraes - header so it can be called directly on Unix, and ability to get the usage message from the command line. |
||
#2 | 5089 | Sam Stafford |
Remove the final resolve schedule from p4rollback.pl. There's no real need for it, and it prevents you from scheduling incremental resolves after you run the script. |
||
#1 | 4535 | Sam Stafford |
Changelist rollback script. See comments for usage notes and stern warnings. |