#!/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 <http://www.cpan.org>.\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 <changelist number>] [-p <p4 command>]\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;
}