p4rollback.pl #12

  • //
  • guest/
  • sam_stafford/
  • scripts/
  • p4rollback.pl
  • View
  • Commits
  • Open Download .zip Download (7 KB)
#!/usr/bin/env perl
# p4rollback.pl - roll back a submitted Perforce changelist.

$USAGE=<<End_of_USAGE;
Usage: $0 [-d] [-Di] [-Ds] [-Dt] changenumber
The optional flags conform to those used by "p4 integ".
End_of_USAGE
die $USAGE if (scalar @ARGV < 1);

# Perforce client settings are read from the current CLI environment.
#
# Be wary using this script on Windows with the Cygwin version of Perl.  Funny 
# things happen with the environment that can cause some commands to end up 
# going to the wrong server.
#
# This script is based on 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 behavior, much like the options for
# non-conforming adds and deletes in p4 integrate:
#
#  -Ds: Rolling back an edit to a subsequently deleted file will re-add the 
#       file at the pre-edit rev.
#  -Dt: Rolling back an add of a subsequently edited file will delete the file.
#  -Di: Rolling back a delete of a subsequently re-added file will open it for 
#       edit at the pre-delete rev.
#										
# At the end of it all, the script will leave you with a new numbered changelist
# and possibly some unscheduled 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; jobs which were closed by a changelist that
# you're rolling back will remain closed, since the script doesn't have any
# easy way to determine what the old status was.
#
# This script also doesn't touch filetypes, since filetypes are easy to change 
# but hard to "resolve".  Just use "p4 edit -t OLDFILETYPE" to rollback 
# filetype changes.
#										
# As always, look at what you're submitting before you submit, 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 a C++ API implementation under:
#   //guest/sam_stafford/rollback/...
#
# This script is not supported by Perforce Software, Inc.  All warranties
# are hereby disclaimed, et cetera.

$change = 0;
$ncadds = 0;
$ncdels = 0;
$ncedit = 0;
$rbchange = 0;

$addsskipped = 0;
$delsskipped = 0;
$editskipped = 0;
$editsrolled = 0;
$addsrolled = 0;
$delsrolled = 0;

#Step 0: Parse args.

#Not a lot of error handling here.  GIGO.

foreach ( @ARGV )
{
	die $USAGE if ( /-h/ );
	if ( /-d/ )
	{
		$ncadds = 1;
		$ncdels = 1;
		$ncedit = 1;
	}
	if ( /-Di/ )
	{
		$ncdels = 1;
	}
	if ( /-Ds/ )
	{
		$ncadds = 1;
	}
	if ( /-Dt/ )
	{
		$ncedit = 1;
	}
	if ( /([0-9]+)/ )
	{
		$change = $1;
	}
}

die $USAGE if ( $change <= 1 );
$prev = $change - 1;

#Step one half: Get list of rollback files.

$_ = `p4 info`;
if ( !/Client name: (.+)\n/ )
{
	print $_;
	die "Error talking to Perforce server.  Bailing.\n";
}
$client = $1;

$_ = `p4 files \/\/$client\/\.\.\.\@$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./;
s/\t\/\/[^\n]+\n//gm;
open TEMP, ">p4rollbacktempfile.tmp" or die "Can't make temp file.";
print TEMP $_;
close TEMP;

$_ = `p4 change -i < p4rollbacktempfile.tmp`;
if ( /^Change ([0-9]+) created.*/ )
{
	$rbchange = $1;
}

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 $ncdels == 0 ) #skip nonconf add
	{
		print "Skipping re-add of $1 because it has been modified.\n";
		$delsskipped++;
		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++;
}



#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 $ncadds == 0 ) #skip nonconf del
	{
		print "Skipping delete of $1 because it has been modified.\n";
		$addsskipped++;
		next;
	}
	print "Rolling back add of $1.\n";
	`p4 flush \"$1\"`;
	system( "p4 delete -c $rbchange \"$1\"" );
	$addsrolled++;
}


#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; 
	}

	# Test to see if file is deleted at head rev.
	$head = `p4 files \"$1\"`;
	chomp $head;
	$cmd = "edit";
	if ( $head =~ /(.*)#[0-9]+ - delete change.*/ )
	{
		if ( $ncedit == 0 ) # skip nonconf add
		{
			print "Unable to rollback edit of $1 because it has been deleted.\n";
			$editskipped++;
			next;
		}
		$cmd = "add";
	}

	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 $cmd -c $rbchange \"$1\"`;
	`p4 sync \"$1\@$change\"`;
	`p4 resolve -ay \"$1\"`;
	$editsrolled++;
}


# Step the last: Summary of what has been done.

print "\nSUMMARY:\n";

if ( $editsrolled )
{
	print "Rolled back $editsrolled edits.\n";
}
if ( $editskipped )
{
	print "Skipped $editskipped edit rollbacks of deleted files.  Use -d or -Dt to open for add.\n";
}
if ( $addsrolled )
{
	print "Rolled back $addsrolled adds\/branches.\n";
}
if ( $addsskipped )
{
	print "Skipped $addsskipped add rollbacks of edited files.  Use -d or -Ds to open for delete.\n";
}
if ( $delsrolled )
{
	print "Rolled back $delsrolled deletes.\n";
}
if ( $delsskipped )
{
	print "Skipped $delsskipped delete rollbacks of re-added files.  Use -d or -Di to open for edit.\n";
}
if ( $editsrolled == 0 and $addsrolled == 0 and $delsrolled == 0 )
{
	`p4 change -d $rbchange`;
}
else
{
	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.