#!/usr/bin/perl # check for some required modules BEGIN { for (qw/ Getopt::Std /) { eval qq{ use $_ (); }; if ($@) { print "Module '$_' failed to load:\n\n$@\n"; print "It may not be installed -- visit .\n"; exit; } } } use Getopt::Std; use vars qw'$opt_h $opt_c $opt_p $opt_f'; getopts('hc:p:f') || die usage(); if ($opt_h or ! $opt_c) { usage(); } $opt_p ||= "p4"; my ($p4) = $opt_p; my ($changenumber) = $opt_c; my (@p4describe) = `$p4 describe -s $changenumber 2>&1`; my ($exit) = 0; my (@files); my (@describe) = grep (/\.\.\./, @p4describe); unless ($#describe >= 0) { die join("",@p4describe); } foreach my $line (@describe) { if ($line =~ /^\.\.\. (\/\/.*)#(\d+) (.*)/) { chomp ($line); my ($file) = $1; my ($rev) = $2; my ($action) = $3; #put quotes around file -- in case of spaces $file =~ s/^/"/; $file =~ s/$/"/; #get some info about file my ($info) = `$p4 files $file 2>&1`; my ($currev); my ($curchange); if ($info =~ /#(\d+) - .* change (\d+) /) { $currev = $1; $curchange = $2; } else { die "files error: $info\n"; } if ($rev != $currev) { unless ($opt_f) { $exit = 1; } print "WARNING: change $changenumber does not contain the most recent change to\n\t$file\n\t(current rev: $currev, change $curchange)\n"; } my (%hsh); $hsh{name} = $file; $hsh{rev} = $rev; $hsh{action} = $action; push (@files, \%hsh); } } if ($exit) { die "Run $0 with the '-f' flag to ignore warnings.\n"; } my ($number) = 1; my ($total) = scalar(@files); foreach my $file (@files) { print "$number of $total\n"; # sync the file my ($output) = `$p4 sync $$file{name} 2>&1`; unless ($output =~ /up-to-date/ || $output =~ /updating/ || $output =~ /added as/ || $output =~ /deleted as/) { die "sync error: $output\n"; } my ($oldrev) = $$file{rev} - 1; # if it's an add, open for delete if (($$file{action} eq "add") or ($$file{action} eq "branch")) { $output = `$p4 delete $$file{name} 2>&1`; unless ($output =~ /opened for delete/) { die "delete error: $output\n"; } # if it's a delete, open for add } elsif ($$file{action} eq "delete") { $output = `$p4 sync $$file{name}#$oldrev 2>&1`; unless ($output =~ /up-to-date/ || $output =~ /updating/ || $output =~ /added as/) { die "sync error: $output\n"; } $output = `$p4 add $$file{name} 2>&1`; unless ($output =~ /opened for add/) { die "add error: $output\n"; } # if it's an edit, roll it back } elsif ($$file{action} eq "edit" || $$file{action} eq "integrate") { $output = `$p4 sync $$file{name}#$oldrev 2>&1`; unless ($output =~ /up-to-date/ || $output =~ /updating/) { die "$output\n"; } $output = `$p4 edit $$file{name} 2>&1`; unless ($output =~ /opened for edit/) { die "edit error: $output\n"; } $output = `$p4 sync $$file{name} 2>&1`; unless ($output =~ /is opened/ || $output =~ /added as/) { die "sync error: $output\n"; } $output = `$p4 resolve -ay $$file{name} 2>&1`; unless ($output =~ / - vs /) { die "resolve error: $output\n"; } } else { die "'$$file{action}' is an unknown action.\n"; } $number ++; } sub usage { print "NAME\n\t$0 - rollback a changelist\n"; print "SYNOPSIS\n\t$0 [-h] [-f] [-c ] [-p ]\n"; print "\takalaveshi\@mahinetworks.com\n"; print "OPTIONS\n"; print "\t-c\n\t\t(c)hange: Changelist number to roll back.\n"; print "\t-h\n\t\t(h)elp: Display this page.\n"; print "\t-f\n\t\t(f)orce: Ignore warnings.\n"; print "\t-p\n\t\t(p)4: perforce command (default 'p4', use 'p4 -c rollback' to specify client).\n"; exit; }