eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
& eval 'exec perl -w -S $0 $argv:q'
if 0;
# THE PRECEEDING STUFF EXECS perl via $PATH
# -*-Fundamental-*-
require 5.000;
use POSIX;
use P4::Modules;
# $Id: //depot/clients/atg/cvs2p4/main/bin/dochanges#44 $
#
# Richard Geiger
#
# These re's specify the set of expected messages from various p4
# operations which are successful. I.e., if a message is seen that
# doe *not* match one of these, it's considered an error.
#
# ... for "p4 submit"s:
#
$check_submits = <<MSGS;
Change [0-9]+ created with [0-9]+ open file\\(s\\).
^Change [0-9]+ submitted.\$
^Change \\d+ renamed change \\d+ and submitted\.\$
^Locking [0-9]+ files ...\$
^Submitting change [0-9]+.\$
^(add|branch|edit|delete) //.+#[0-9]+\$
^//.+ - refreshing\$
MSGS
@check_submits = split(/\n/, $check_submits);
#
# ... for "p4 change -f"s:
#
$check_changefs = <<MSGS;
Change [0-9]+ updated.\$
MSGS
@check_changefs = split(/\n/, $check_changefs);
sub dirname
{
local($dir) = @_;
$dir =~ s%^$%.%; $dir = "$dir/";
if ($dir =~ m%^/[^/]*//*$%) { return "/"; }
if ($dir =~ m%^.*[^/]//*[^/][^/]*//*$%)
{ $dir =~ s%^(.*[^/])//*[^/][^/]*//*$%$1%; { return $dir; } }
return ".";
}
use Carp; # ...or flounder. (This will fail unless 'perl' is a perl5!)
$| = 1;
($Myname = $0) =~ s%^.*/%%;
$Mydir = &dirname($0);
$Here = `/bin/pwd`; chop $Here;
if ($Mydir ne ".") { chdir "$Mydir" || die "$Myname: can't chdir \"$Mydir\": $!"; }
chdir ".." || die "$Myname: can't chdir \"..\": $!";
$Mydir = `/bin/pwd`; chop $Mydir;
chdir $Here || die "$Myname: can't chdir \"$Here\": $!";
require "$Mydir/lib/util.pl";
$P4 = 'p4';
$Convclient = "";
$mod = '';
$DefaultClient = "cvs2p4";
($total_revs, $total_changes) = (0, 0);
($rev_cnt, $change_cnt) = (0, 0);
$partial_change = 0;
sub usage
{
print <<_EOF_;
Usage: $Myname [ common options ] -f conversiondir
$Myname [ common options ] [ -d -i -t -e end -r -l ] conversiondir
$Myname -h
common options:
-a file file listing directory root to abbreviate
-b file file listing the branch mappings
-c client use client spec 'client' (def. cvs2p4)
-f only place rlog(1) in file locations (fast conversion)
-h print this message
-k killfile stop cleanly if 'killfile' exists (def. \$HOME/cvs2p4.stop)
-m modules use P4::Modules file 'modules' to place files into depot
-o omit a 'p4 protect' style listing of directories to omit
-p report progress during conversion
-q be really quiet with output
-s start start at change group 'start' (or >1 w/-q to not restart)
-v verbose output (can use more than once)
--warn-nobranchmap when using -b, warn of missing maps (default is exit)
--warn-nomap skip files that don't map into the depot (default is exit)
--warn-omit skip files that are in the omission list (default is exit)
--warn-reinteg allow reintegration of branched files (default is exit)
full conversion options:
-d produce donelog file in conversiondir
-e end stop after finishing change group 'end'
-i restart after last change in donelog (implies -d)
-l produce revlog file in conversiondir
-r produce revmap DB files in conversiondir (see caveats)
-t only import the first and head revisions
_EOF_
exit $_[0];
}
# Guarantee that the name directory (may be multiple levels deep)
# exists; create intermediate directories as needed (like mkdir -p)
#
sub insuredir
{
my($d) = @_;
if (! -d $d)
{
my($p) = &dirname($d);
&insuredir($p);
mkdir $d, 0755 or die "couldn't make $d: $!\n";
}
}
sub path
{
my($prefix, $file_dir, $file_name) = @_;
$prefix =~ s|/+$||;
my $path = "/$file_name";
$path = "/$file_dir$path" if $file_dir;
$path =~ s|/+|/|g;
return $prefix . $path;
}
sub verbose {
my $level = @_ > 1 ? shift : 1;
print @_, "\n" if $level <= $V;
}
sub p4 {
my $cmd = shift;
my @list = `$P4 $cmd`;
if ($?) {
verbose "p4 return: @{[$?>>8]}";
die "error doing \"$P4 $cmd\"";
}
@list;
}
sub p4where
{
my ($file) = @_;
my $cl = $Convclient;
my (@output) = p4 "where \Q$file\E";
verbose 2, "p4 where \Q$file\E";
my ($root) = grep /^Root:/, p4 "client -o \Q$cl\E";
chomp @output;
warn "warning: ambiguous mapping for $file\n" if @output > 1;
pop @output while $output[$#output] =~ /^-/;
my $output = pop @output;
chomp $root;
$root =~ s/^Root:\s*//;
my ($depot, $client, $local) = split (/ (?:\/\/$cl|$root)\//, $output);
$client = "//$cl/$client";
$local = "$root/$local";
($depot, $client, $local);
}
# Hopefully, you don't have to call this ...
#
%REVERSEMAPS = ();
$openReverseLog = 0;
sub get_oldrev {
my $filerev = join '/', @_;
my $depot_br = undef;
verbose 3, "looking for match to $filerev";
if (!makeREVLOG && !makeREVMAP) {
die "looking for a filerev for an old branch point but you\n",
"have no revlog/map. Please restart dochange at change 1.\n";
} elsif (defined($REVERSEMAP{$filerev})) {
$depot_br = $REVERSEMAP{$filerev};
verbose 3, "found cached match of $depot_br for $filerev";
} elsif ($makeREVMAP) {
while (my ($p4, $cvs) = each %REVMAP) {
($br, $cvs) = split /$S/, $cvs;
$REVERSEMAP{"$br/$cvs"} = $p4
unless defined $REVERSEMAP{"$br/$cvs"};
verbose 5, "comparing:\n\t$br/$cvs\n\t$filerev";
$depot_br = $p4, last if "$br/$cvs" eq $filerev;
}
verbose 3, "found revmap match of $depot_br for $filerev";
} else {
if (!$openReverseLog) {
verbose 3, "opening $Revlog for reading old versions";
open(REVERSELOG, "<$Revlog") || die "can't open read $Revlog: $!";
$openReverseLog = 1;
}
while (my $line = <REVERSELOG>) {
my ($p4, $br, $cvs) = split /$S/, $line;
$REVERSEMAP{"$br/$cvs"} = $p4
unless defined $REVERSEMAP{"$br/$cvs"};
verbose 5, "comparing:\n\t$br/$cvs\n\t$filerev";
$depot_br = $p4, last if "$br/$cvs" eq $filerev;
}
verbose 3, "found revlog match of $depot_br for $filerev";
}
die "couldn't find a filerev for an old branch of $filerev\n"
unless $depot_br;
verbose 2, "get_oldrev returning: $depot_br";
return $depot_br;
}
# Check a message; if it matches any of the expected ones on @res,
# it's OK; otherwise, declare a problem and rithlessly exit. We
# demand perfection!
#
sub checkmsg
{
my($msg, $from, @res) = @_;
chop $msg;
foreach $re (@res)
{ if ($msg =~ /$re/) { return; } }
die "$Myname: checkmsg: *** $from *** $msg\n";
}
# A temp file used to hold p4 command output
#
$tmpout = POSIX::tmpnam();
# A convenient switch for disabling actual exection of commands.
#
$Do = 1;
# Submit the current default change.
# $msg is the log message to use.
# $who is the original author of the RCS revision(s)
# $time is the original time of the RCS revsision(s)
# $files is the file list for the change.
# @change is the change set; required for building revmap
# (Used to construct the map of RCS revisions -> p4 changes.
#
sub p4submit
{
my($msg, $who, $time, $files, $wheres, @change) = @_;
my $changenum;
# This bit is to allow us to pass multiple arrays.
# Hipper folks would use refs to arrays, I know.
# I must be as old as Larry Wall's Mom.
#
my (@files) = split(/\001/, $files);
verbose 4, "submitting:\n ", join("\n", @files);
verbose 5, "wheres:\n ", join("\n ", map {
$t = $$wheres{$_};
"$$t[1] $$t[0]\n $_"
} keys %$wheres);
# Start the submit...
#
$p4submit = "$P4 submit -i >$tmpout 2>&1";
verbose "$Myname: ...| $p4submit";
if (! open(SUBMITW, "| $p4submit"))
{
die "$Myname: open \"| $p4submit\" failed: $!.\n";
}
$P1 = <<P1;
Change:\tnew
Client:\t$Convclient
User:\t$Username
Status:\tnew
Description:
P1
if ($V > 1) { my $vl; foreach $vl (split(/\n/, $P1)) { print "->SUBMITW: $vl\n"; } }
print SUBMITW $P1 or die "couldn't print to $p4submit: $!\n";
# Insert the log message...
#
if ($LOGNOTE)
{
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time);
my($ts) = sprintf("%04d/%02d/%02d %02d:%02d:%02d",
$year+1900, $mon+1, $mday, $hour, $min, $sec);
$msg .= "\n[imported from CVS by $Convclient at $ts]\n";
}
@msg = split(/\n/, $msg);
foreach $line (@msg)
{
verbose 2, "->SUBMITW:\t$line";
print SUBMITW "\t$line\n" or die "couldn't print to $p4submit: $!\n";
}
$P2 = <<P2;
Files:
P2
if ($V > 1) { my $vl; foreach $vl (split(/\n/, $P2)) { print "->SUBMITW: $vl\n"; } }
print SUBMITW $P2 or die "couldn't print to $p4submit: $!\n";
# Insert the files list...
#
foreach $file (@files)
{
verbose 2, "->SUBMITW:\t$file";
print SUBMITW "$file\n" or die "couldn't print to $p4submit: $!\n";
}
close SUBMITW;
# OK, now we inspect the output from "p4 submit". (Hey, we used to
# use open2, but it kept leaving zombies around, and I got tired of
# trying to understand why...)
#
if (! open(SUBMITR, "<$tmpout"))
{
die "$Myname: open \"<$tmpout\" failed: $!.\n";
}
# Build %chrevs - the whole first field of the change line, indexed
# by the file path part.
#
my %chrev = ();
foreach $chrev (@change)
{
my $revinfo = $chrev;
chomp $revinfo;
my ($rev) = split "$S", $revinfo;
$rev =~ m%(.*)/[0-9\.]+$%;
verbose 4, "chrev: $1 and $revinfo";
$chrev{$1} = $revinfo;
}
while (<SUBMITR>)
{
verbose "SUBMITR->:$_";
&checkmsg($_, "p4 submit", @check_submits);
# Now update the rev map for each Perforce rev we see...
#
if (/^(add|branch|delete|edit) (.*)#([0-9]+)$/)
{
my $p4rev = $2."#".$3;
my ($chpath, $codeline) = @{$wheres->{$2}};
my $cvsrev = $chrev{$chpath} ||
die "couldn't find CVSREV info for $chpath\n";
$codeline = $TRUNKLINE if $codeline eq "main";
verbose 4, "revlog: $codeline and $chpath";
$REVMAP{$p4rev} = join("$S", $codeline, $cvsrev)
if $makeREVMAP;
print REVLOG join("$S", $p4rev, $codeline, "$cvsrev\n")
or die "couldn't print to REVLOG: $!\n" if $makeREVLOG;
}
if ($_ =~ /^Change (\d+) submitted\.$/) { $changenum = $1; }
if ($_ =~ /^Change \d+ renamed change (\d+) and submitted\.$/)
{
$changenum = $1;
warn "warning: change number was renumbered (other users are",
"probably active\n";
}
}
close SUBMITR;
# Now we spoof the change to appear as done by $who at $time:
#
if ($changenum !~ /[0-9]+/)
{ die "$Myname: assert: didn't see change number <$changenum>.\n"; }
# Start "p4 change -i -f"
#
$p4changef = "$P4 change -i -f >$tmpout 2>&1";
verbose "$Myname: ...| $p4changef";
if (! open(CHANGEW, "| $p4changef"))
{
die "$Myname: open \"| $p4changef\" failed: $!.\n";
}
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time);
$Date = sprintf("%4d/%02d/%02d %02d:%02d:%02d", $year+1900, $mon+1, $mday, $hour, $min, $sec);
$C1 = <<C1;
Change:\t$changenum
Date:\t$Date
Client:\t$Convclient
User:\t$who
Status:\tsubmitted
Description:
C1
if ($V > 1) { my $vl; foreach $vl (split(/\n/, $C1)) { print "->CHANGEW: $vl\n"; } }
print CHANGEW $C1 or die "couldn't print to $p4changef: $!\n";
# Insert the log message. Again.
#
foreach $line (@msg)
{
verbose 2, "->CHANGEW:\t$line";
print CHANGEW "\t$line\n" or die "couldn't print to $p4changef: $!\n";
}
close CHANGEW or die "couldn't close $p4changef: $!\n";
# Now inspect the p4 output for errors...
#
if (! open(CHANGER, "<$tmpout"))
{
die "$Myname: open \"<$tmpout\" failed: $!.\n";
}
while (<CHANGER>)
{
verbose "SUBMITR->:$_";
&checkmsg($_, "p4 change -f", @check_changefs);
}
close CHANGER;
unlink $tmpout;
}
$ext = <<EXTS;
a
bin
bmp
class
coff
com
dll
doc
dvi
dwarf
exe
fm
gif
gz
ico
jar
jpg
lib
o
obj
pdf
ps
tar
xbm
xls
zip
z
EXTS
@ext = split(/\n/, $ext); foreach $ext (@ext) { $ext{$ext} = 1; }
sub is_binary_file
{
my ($path) = @_;
my ($filename) = $path;
$filename =~ s/^.*\///;
my $fileext;
$filename =~ m/\.([^\.]+)$/;
$fileext = $1;
$fileext =~ tr/A-Z/a-z/;
if (defined($ext{$fileext})) { return 1; }
return 0;
}
# Determine whether the depot already has this file on this code line.
# (Used to determine when we need to branch).
#
sub depot_has
{
my($clientfile) = @_;
my($line);
$p4files = "$P4 files \Q$clientfile\E 2>&1";
verbose "$Myname: $p4files |...\n";
if (! open(FILES, "$p4files |"))
{
die "$Myname: open \"$p4files |\" failed: $!.\n";
}
$line = <FILES>;
close FILES;
if ($line =~ /- no such file\(s\)\./
|| $line =~ / - file\(s\) not in client view\./
|| $line =~ / - delete change /)
{ return 0; }
else
{ return 1; }
}
# This function is similar to the one in ckclientview.
# If a change is required, determine if the same change
# should be made there.
#
sub prepFiles {
my ($file, $line, $p4wheres) = @_;
$file =~ s|^/?|/|;
my $file_dir = &dirname($file);
(my $file_name = $file) =~ s%^.*/%%;
my ($Depotfile, $Clientfile) = ('', '');
my $abbr = '';
for my $a (@ABBRS) {
$abbr = $a, last if $file =~ /^$a/;
}
if (%BRANCHES and !$BRANCHES{$line} and $line ne 'main') {
warn "warning: using branch mappings but no mapping found for $line. ",
"Is it new?\nfile checked: $file\n";
die "can't continue\n" if !$warnOnNobranchmap;
warn "Continuing with original name\n";
}
my $newbr = $BRANCHES{$line} || $line;
if ($newbr =~ m|^//|) {
$Depotfile = $newbr . "$file_dir/$file_name";
if ($Convclient ne $DefaultClient) {
$Clientfile = &p4where($Depotfile);
} else {
$Clientfile = $Depotfile;
$Clientfile =~ s|^//|$Client/|;
}
} elsif ($mod) {
$Depotfile = $mod->where($newbr, "$file_dir/$file_name");
($Clientfile = $Depotfile) =~ s|^//|$Client/|;
} else {
$Clientfile = &path("$Client$Depot/" . $newbr,
$file_dir, $file_name);
($Depotfile) = &p4where($Clientfile);
}
unless ($Depotfile) {
warn "couldn't map $newbr and ",
"$file_dir/$file_name into the depot... skipping\n";
die "can't continue\n" if !$warnOnNomap;
return (undef, undef, undef);
}
if ($abbr) {
$newbr =~ s|.*/|/|;
verbose 2, "removing abbrevation $abbr after $newbr";
$Depotfile =~ s|(.*$newbr.*?)$abbr|$1/|;
$Clientfile =~ s|(.*$newbr.*?)$abbr|$1/|;
}
for my $o (@OMIT) {
verbose 3, "checking $Depotfile against omission '$o'";
if ($Depotfile =~ /$o/) {
warn "$Depotfile is in the omission list '$o'... skipping\n";
die "can't continue\n" if !$warnOnOmit;
return (undef, undef, undef);
}
}
verbose 4, "mapped $file on $line into $Depotfile and $Clientfile";
$p4wheres->{$Depotfile} = [ &path("", $file_dir, $file_name), $line ];
&insuredir(&dirname($Clientfile));
($Depotfile, $Clientfile, "$CVS_MODULE$file,v");
}
# Process one "change" from RCS.
# Called with the set of RCS files/revs to process in @change
#
sub dochange
{
my @map;
chomp @change;
verbose 0, "\n========== change group $do_change_num ===============";
if ($V > 0)
{
foreach $c (@change)
{
($filerev, $time, $who, $state, $line, $branches,
$prevrev, $options, $md5msg) = split(/$S/, $c);
@filerev = split(/\//, $filerev);
$rev = pop(@filerev);
$file = join("/", @filerev);
verbose "$who $time $filerev $state $line $branches";
}
}
# Do the revisions...
#
my ($tstart, $tmid, $tstop) = (time(), 0, 0);
$cvsid = '';
if ($partial_ch <= 1) {
verbose 0, "=== deletes $do_change_num.1 ===";
$nc = 0; undef $files;
%p4wheres = ();
foreach $c (@change)
{
($filerev, $time, $who, $state, $line, $branches,
$prevrev, $options, $md5msg) = split(/$S/, $c);
next if $branches =~ /^-:/;
if ($line eq "$TRUNKLINE") { $line = "main"; }
@filerev = split(/\//, $filerev);
$rev = pop(@filerev);
$file = join("/", @filerev);
if ($state eq "dead" && $onlyTips && $branches eq "-"
&& !($rev =~ m/\.1$/ || $rev eq $TIPS{"$file$S$line"})) {
verbose 1,
"$Myname: skipping non-tip revision \"$rev\" for $file",
" on branch \"$line\"";
$rev_cnt++;
next;
}
my ($Depotfile, $Clientfile) = prepFiles($file, $line, \%p4wheres);
next unless $Depotfile;
if ($state eq "dead")
{
$Exists_in_depot = &depot_has($Clientfile);
if ($Exists_in_depot)
{
if (! -e $Clientfile)
{
# Create a dummy one to remove (p4/ must be stateless)
#
if (! open(C, ">$Clientfile"))
{
die "$Myname: can't create \"$Clientfile\": $!.\n";
}
close C;
}
if ($sts = &s("$P4 delete \Q$Clientfile\E $DEVNULL", $Do)) { exit $sts; } $nc++;
$rev_cnt++;
$cvsid = $rev;
if ($files) { $files .= "\001"; }
$files .= "\t$Depotfile\t# Delete";
}
}
}
$tmid = time();
if ($nc > 0) {
my $msg = $MSGS{$md5msg} || "Deleting\n";
$msg .= "\nCVSID: $cvsid\n" if $nc == 1;
&p4submit($msg, $who, $time, $files, \%p4wheres, @change);
}
$tstop = time();
print DONELOG "$do_change_num.1 $tstart $tmid $tstop $nc\n"
or die "can't write to DONELOG: $!\n" if $doLog;
verbose 0, "=== deletes done";
}
if ($partial_ch <= 2) {
verbose 0, "=== adds/edits $do_change_num.2 ===";
$tstart = time();
$nc = 0; undef $files;
%p4wheres = ();
foreach $c (@change)
{
($filerev, $time, $who, $state, $line, $branches,
$prevrev, $options, $md5msg) = split(/$S/, $c);
next if $branches =~ /^-:/;
if ($line eq "$TRUNKLINE") { $line = "main"; }
@filerev = split(/\//, $filerev);
$rev = pop(@filerev);
$file = join("/", @filerev);
if ($state ne "dead" && $onlyTips && $branches eq "-"
&& !($rev =~ m/\.1$/ || $rev eq $TIPS{"$file$S$line"})) {
verbose 1,
"$Myname: skipping non-tip revision \"$rev\" for $file",
" on branch \"$line\"";
$rev_cnt++;
next;
}
my ($Depotfile, $Clientfile, $Rcsfile) =
prepFiles($file, $line, \%p4wheres);
next unless $Depotfile;
-f $Rcsfile or $Rcsfile =~ s|(.*)/(.*)|$1/Attic/$2|;
-f $Rcsfile or die "$Myname: can't find RCS ,v file for \"$file\".\n";
# Note: we assume binary files never become text files or vice versa
#
if ($state ne "dead")
{
# This is where we decide if this pup is a text or a binary file.
#
# We used to just trust the CVS options to tell us when we have
# a binary... but many times binary files get checked in to CVS
# and nobody remembers to tell CVS! So.... we now try to be more
# clever.
my $type = "text";
$type = "binary" if $options =~ /[ob]/ || -B $Clientfile ||
&is_binary_file($Clientfile);
if ($options =~ /x/) { $type = "x$type"; }
if ($type =~ /text/) { $type = "k$type"; }
my $op = &depot_has($Clientfile) ? "edit" : "add";
if (($sts = &rm($Clientfile)) ||
($sts = &co("$CO -p$rev \Q$Rcsfile\E >\Q$Clientfile\E $DEVNULL2",
$Do, $Clientfile)) ||
($sts = &s("$P4 $op -t $type \Q$Clientfile\E $DEVNULL", $Do))) {
exit $sts;
}
$nc++;
$rev_cnt++;
$cvsid = $rev;
if ($files) { $files .= "\001"; }
$files .= "\t$Depotfile\t# \u$op";
}
}
$tmid = time();
if ($nc > 0) {
my $msg = $MSGS{$md5msg};
$msg .= "\nCVSID: $cvsid\n" if $nc == 1;
&p4submit($msg, $who, $time, $files, \%p4wheres, @change);
}
$tstop = time();
print DONELOG "$do_change_num.2 $tstart $tmid $tstop $nc\n"
or die "can't write to DONELOG: $!\n" if $doLog;
verbose 0, "=== adds/edits done";
}
verbose 0, "=== branches $do_change_num.3 ===";
$tstart = time();
$nc = 0; undef $files;
%p4wheres = ();
foreach $c (@change)
{
($filerev, $time, $who, $state, $line, $branches,
$prevrev, $options, $md5msg) = split(/$S/, $c);
my $oldrevline = $line;
if ($line eq "$TRUNKLINE") { $line = "main"; }
if ($state ne "dead" && $branches ne "-")
{
@filerev = split(/\//, $filerev);
$rev = pop(@filerev);
$file = join("/", @filerev);
my ($Depotfile, $Clientfile) = prepFiles($file, $line, \%p4wheres);
next unless $Depotfile;
my $need_oldrev = 0;
foreach $branch (split(/:/, $branches))
{
$need_oldrev = 1, next if $branch eq "-";
my ($Depot_branchfile, $Client_branchfile) =
prepFiles($file, $branch, \%p4wheres);
next unless $Depot_branchfile;
if ($need_oldrev) {
$Depotfile = get_oldrev($oldrevline, $file, $rev);
my $tmpDepotfile = $Depotfile;
$tmpDepotfile =~ s/#.*//;
$p4wheres{$tmpDepotfile} = &path("", $file_dir, $file_name);
}
my $there = &depot_has($Depot_branchfile);
if (!$there) {
if ($sts = &s("$P4 integrate \Q$Depotfile\E \Q$Depot_branchfile\E $DEVNULL", $Do)) {
exit $sts;
}
} else {
warn "warning: trying to branch a file onto an existing ",
"target file.\nIs it a moved branchpoint?\n",
"source file: $Depotfile\n",
"target file: $Depot_branchfile\n";
die "can't continue\n" if !$warnOnReinteg;
if ($sts = &s("$P4 integrate -i -f -t \Q$Depotfile\E \Q$Depot_branchfile\E $DEVNULL", $Do)) {
exit $sts;
}
if ($sts = &s("$P4 resolve -at \Q$Depot_branchfile\E $DEVNULL", $Do)) {
exit $sts;
}
}
$nc++;
$rev_cnt++;
if ($files) { $files .= "\001"; }
$cvsid = $rev;
$files .= "\t$Depot_branchfile\t# Branch";
}
}
}
$tmid = time();
if ($nc > 0) {
my $msg = "Branching\n";
$msg .= "\nCVSID: $cvsid\n" if $nc == 1;
&p4submit($msg, $who, $time, $files, \%p4wheres, @change);
}
$tstop = time();
print DONELOG "$do_change_num.3 $tstart $tmid $tstop $nc\n"
or die "can't write to DONELOG: $!\n" if $doLog;
verbose 0, "=== branches done";
$partial_ch = 0;
}
# Expect messages from "p4d checkpoint"
#
$check_checkpoints = <<MSGS;
^command "checkpoint"\$
^p4d_admin>
^: Checkpointing to
^: Saving journal to
^: Truncating
MSGS
@check_checkpoints = split(/\n/, $check_checkpoints);
sub docheckpoint
{
my($cmd) = "rsh $P4HOST -l $P4USER /u/p4/bin/p4d_admin port $P4PORTNUM rsh checkpoint 2>&1 </dev/null |";
verbose "\n=== checkpoint after $do_change_num";
if (! open(RSH, $cmd))
{
die "$Myname: can't open \"$cmd\": $!.\n";
}
verbose "$Myname: $cmd";
while (<RSH>)
{
&checkmsg($_, "p4d_admin rsh checkpoint", @check_checkpoints);
chomp;
verbose "$Myname: CHECKPOINT: $_";
}
close RSH;
}
###### main starts here
#
(@pwent) = getpwuid($<);
if ($#pwent < 7)
{
die "$Myname: can't get your passwd file entry.\n";
}
$Username = $pwent[0];
if ($CHECKPOINT_INTERVAL &&
-x "/bin/domainname" &&
`/bin/domainname` =~ /^netapp.com$/)
{ $Docheckpointing = 1; }
else
{ $Docheckpointing = 0; }
# option switch variables get defaults here...
$Metadata = "metadata";
# Used if the user doesn't specify a client spec of modules file.
$Depot = "/depot";
# option switch variables get defaults here...
$Boolopt = 0;
$V = 0;
$Start_at_ch = 1;
$End_at_ch = 999999999; #Magic number are bad but ...
$makeREVMAP = 0;
$makeREVLOG = 0;
$onlyTips = 0;
$Modules = '';
$help = 0;
$afile = '';
@ABBRS = ();
$bfile = '';
%BRANCHES = ();
$progress = 0;
$DEVNULL2 = "2>/dev/null";
$DEVNULL = ">/dev/null";
$incremental = 0;
$doLog = 0;
$ofile = 0;
$warnOnOmit = $warnOnNobranchmap = $warnOnNomap = $warnOnReinteg = 0;
$quick = 0;
$quiet = 0;
$killfile = "$ENV{HOME}/cvs2p4.stop";
use Getopt::Long;
GetOptions(
"abbreviations=s" => \$afile,
"branches=s" => \$bfile,
"client=s" => \$Convclient,
"dolog" => \$doLog,
"end=i" => \$End_at_ch,
"fast" => \$quick,
"help" => \$help,
"incremental" => \$incremental,
"killfile=s" => \$killfile,
"log" => \$makeREVLOG,
"modules=s" => \$Modules,
"omit=s" => \$ofile,
"progress" => \$progress,
"quiet+" => \$quiet,
"revmap" => \$makeREVMAP,
"start=i" => \$Start_at_ch,
"tip" => \$onlyTips,
"verbose+" => \$V,
"warn-nobranchmap" => \$warnOnNobranchmap,
"warn-nomap" => \$warnOnNomap,
"warn-omit" => \$warnOnOmit,
"warn-reinteg" => \$warnOnReinteg,
) || die usage(1);
$help && usage(0);
$doLog = 1 if $incremental;
$Depot = "" if $Convclient || $Modules;
$Convclient = $DefaultClient unless $Convclient;
$V = -$quiet if $quiet;
$DEVNULL = "", $DEVNULL2 = "" if $V > 0;
$Convdir = shift || usage(1);
$mod = P4::Modules->new($Modules) if $Modules;
warn "incremental without a revlog/map is dangerous\nSee FAQ\n"
if $incremental && !($makeREVLOG || $makeREVMAP);
die "You can't specify incremental and a start group\n"
if $incremental && $Start_at_ch > 1;
die "You can't specify both the quick and tip imports together\n"
if $quick && $onlyTips;
die "Ending change group must be higher or equal to the starting change group\n"
unless $Start_at_ch <= $End_at_ch;
verbose "Watching for killfile: $killfile";
chdir $Convdir || die "$Myname: can't chdir \"$Convdir\": $!";
$Convdir = `/bin/pwd`; chop $Convdir; $Convdir =~ s|/*$||;
chdir $Here || die "$Myname: can't chdir \"$Here\": $!";
$Metadata = "$Convdir/metadata";
$Logmsgs = "$Convdir/logmsgs";
$Tips = "$Convdir/tips";
$Changes = "$Convdir/changes";
$Donelog = "$Convdir/donelog";
$Revlog = "$Convdir/revlog";
$Revmap = "$Convdir/revmap";
$Client = "$Convdir/p4";
$LOGNOTE = 1;
require "$Convdir/config";
if (! defined($P4PORT))
{ die "$Myname: no P4PORT in \"$Convdir/config\".\n"; }
else
{ $ENV{"P4PORT"} = $P4PORT; }
($P4HOST = $P4PORT) =~ s/:.*//;
($P4PORTNUM = $P4PORT) =~ s/^.*://;
$P4USER = "p4";
$ENV{"P4CLIENT"} = $Convclient;
# These defaults can be overriden in the config file
#
# Path the the RCS co and rlog commands
#
if (! defined($CO)) { $CO = "/usr/local/bin/co"; }
if (! -x ($CO))
{ die "$Myname: No executable \"co\" command at \"$CO\".\n"; }
if (! defined($RLOG)) { $RLOG = "/usr/local/bin/rlog"; }
if (! -x ($RLOG))
{ die "$Myname: No executable \"rlog\" command at \"$RLOG\".\n"; }
# Path the the p4 client command
#
if (! defined($P4)) { $P4 = "/usr/local/bin/p4"; }
if (! -x ($P4))
{ die "$Myname: No executable \"p4\" command at \"$P4\".\n"; }
if ($incremental && open(RDONELOG, "<$Donelog")) {
my $change = 0;
while (<RDONELOG>) {
($change) = split / /;
}
close RDONELOG;
if ($change) {
($Start_at_ch, $partial_ch) = split /\./, $change;
$partial_ch++;
$Start_at_ch++, $partial_ch = 0 if $partial_ch > 3;
}
}
verbose "starting at Change $Start_at_ch and partial $partial_ch";
if ($Start_at_ch == 1)
{
# We only do this to start (not when restarting from a chackpoint)
#
$P4CMD = "$P4 client -i";
# Remove $Convdir/p4 if it already exists...
# (This is where the client tree gets build)
#
&s("/bin/rm -rf '$Convdir/p4'");
# Remove any revmap files...
#
&s("/bin/rm -f $Revlog $Revmap.dir $Revmap.pag $Donelog");
# Remove any existing client defined with this name.
#
if ($Convclient eq $DefaultClient) {
&s("$P4 client -d $Convclient");
if (! open(P4, "| $P4CMD"))
{
die "$Myname: open \"/usr/local/bin/p4 client -i\" failed: $!.\n";
}
my $Map = '';
for my $dview (split /\s+/, $P4_DEPOTS) {
(my $cview = $dview) =~ s|^//|//$Convclient/|;
$Map .= "\t$dview/... $cview/...\n";
}
print P4 <<CLI;
Client: $Convclient
Root: $Client
View:
$Map
CLI
close P4;
if ($?) {
die "$Myname: \"$P4CMD\" failed to create client.\n";
}
}
}
# Read in the abbreviation and branch mappings
#
if ($afile) {
open ABBRS, "<$afile" or die "can't open $afile: $!\n";
while (<ABBRS>) {
chomp;
next if /^#/;
next if /^\s*$/;
s|^/?|/|;
s|/?$|/|;
verbose "found abbreviation: $_";
push @ABBRS, $_;
}
close ABBRS;
}
if ($bfile) {
open BRANCHES, "<$bfile" or die "can't open $bfile: $!\n";
while (<BRANCHES>) {
chomp;
next if /^#/;
next if /^\s*$/;
my ($p4branch, $cvsbranch) = split /\s+/;
$p4branch =~ s|/$||;
$p4branch =~ s|^/|| unless $p4branch =~ m|^//|;
$cvsbranch = $p4branch unless $cvsbranch;
verbose "mapping branch $cvsbranch to $p4branch";
$BRANCHES{$cvsbranch} = $p4branch;
}
close BRANCHES;
}
# Read in the omission list.
if ($ofile) {
open OMIT, "<$ofile" or die "can't open $ofile: $!\n";
while (<OMIT>) {
chomp;
next if /^#/;
next if /^\s*$/;
verbose "found omission: $_";
s/\*/[^\/]*/g;
s/\.\.\./.*/g;
verbose 2, "converted to regex: $_";
push @OMIT, $_;
}
close OMIT;
}
if (! dbmopen(TIPS, $Tips, 0444))
{ die "$Myname: can't dbmopen \"$Tips\": $!\n"; }
if ($quick)
{
my @files = ();
if ($progress)
{
$total_revs = keys %TIPS;
$total_changes = int($total_revs/$QUICK_ADD_COUNT);
$total_changes += $total_revs % $QUICK_ADD_COUNT ? 1 : 0;
verbose -2, "expecting $total_revs new revisions in ",
"$total_changes change groups";
}
while (my ($file_line, $tip) = each %TIPS)
{
my ($file, $line) = split /$S/, $file_line;
($file_dir = &dirname($file)) =~ s%^/%%;
($file_name = $file) =~ s%^.*/%%;
verbose 2, "checking: $file_dir";
verbose 2, "checking: $file_name";
my ($Depotfile, $Clientfile, $Rcsfile) =
prepFiles($file, $line, \%p4wheres);
next unless $Depotfile;
-f $Rcsfile or $Rcsfile =~ s|(.*)/(.*)|$1/Attic/$2|;
-f $Rcsfile or die "$Myname: can't find RCS ,v file for \"$file\".\n";
$rev_cnt++;
unless (&depot_has($Clientfile))
{
&rlog("$RLOG \Q$Rcsfile\E >\Q$Clientfile\E", $Do, $Clientfile);
unless (@files)
{
open(P4, "|$P4 -x - add -t text") or die "can't open $P4\n";
}
print P4 "$Clientfile\n" or die "couldn't print to p4 add: $!\n";
push @files, $Depotfile;
}
if (@files == $QUICK_ADD_COUNT)
{
close P4 or die "can't close $P4\n";
if ($?)
{
die "$P4 add exited badly\n";
}
&p4submit("quick import\n", "cvs2p4", time(),
"\t" . join("\t# Add\001\t", @files) . "\t# Add",
{}, ());
@files = ();
if ($progress)
{
$change_cnt = int($rev_cnt / $QUICK_ADD_COUNT);
verbose -2, "rev status: $rev_cnt of $total_revs";
verbose -2, "change status: $change_cnt of $total_changes";
}
}
if (-e $killfile)
{
verbose 0, "$Myname: stopping because $killfile present";
last;
}
}
if (@files)
{
close P4 or die "can't close $P4\n";
if ($?)
{
die "$P4 add exited badly\n";
}
&p4submit("quick import\n", "cvs2p4", time(),
"\t" . join("\t# Add\001\t", @files) . "\t# Add",
{}, ());
}
if ($progress)
{
$change_cnt = int($rev_cnt / $QUICK_ADD_COUNT);
$change_cnt += $total_revs % $QUICK_ADD_COUNT ? 1 : 0;
verbose -2, "rev status: $rev_cnt of $total_revs";
verbose -2, "change status: $change_cnt of $total_changes";
}
}
else
{
$ch = $Changes;
if ($progress)
{
$cnt = 1;
while (-e $Changes)
{
open CHGS, "<$Changes"
or die "$Myname can't open \"$Changes\": $!\n";
while (<CHGS>)
{
/^#/ && do {
$total_changes++;
$change_cnt++ if $total_changes < $Start_at_ch;
next;
};
my @a = split /$S/;
my ($del, $add, $int) = (0, 0, 0);
if ($a[3] eq 'dead') {
$del = 1 unless $a[6] eq '-';
} else {
$add = 1 unless $a[5] =~ /^-:/;
$a[5] =~ s/^-://;
$int = 1 + ($a[5] =~ tr/:/:/) if $a[5] ne "-";
}
$total_revs += ($del + $add + $int);
if ($total_changes < $Start_at_ch) {
$del = 0 if $partial > 1;
$add = 0 if $partial > 2;
$rev_cnt += ($del + $add + $int);
}
}
close CHGS or die "can't close $Changes: $!\n";
$Changes = $ch . "." . ++$cnt;
}
verbose -2, "expecting $total_revs new revisions in ",
"$total_changes change groups";
$Changes = $ch;
}
# Open the REVLOG file and/or REVMAP database...
#
if ($makeREVLOG) {
if (! open(REVLOG, ">>$Revlog"))
{ die "$Myname: can't open \"$Revlog\": $!\n"; }
# Turn of buffering so that get_oldrev will be able to get all info.
my $oldfh = select(REVLOG); $| = 1; select($oldfh);
}
if ($makeREVMAP) {
if (! dbmopen(REVMAP, $Revmap, 0666))
{ die "$Myname: can't dbmopen \"$Revmap\": $!\n"; }
}
if ($doLog) {
if (! open(DONELOG, ">>$Donelog"))
{ die "$Myname: can't open \"$Donelog\": $!\n"; }
my $oldfh = select(DONELOG); $| = 1; select($oldfh);
}
# Open the log messages database
#
if (! dbmopen(MSGS, $Logmsgs, 0444))
{ die "$Myname: can't dbmopen \"$Logmsgs\": $!\n"; }
$cnt = 1;
while (-e $Changes)
{
if (! open(CHGS, "<$Changes"))
{ die "$Myname: can't open \"$Changes\": $!\n"; }
$gather_change_num = $do_change_num = 0;
while (<CHGS>)
{
if (/^# ([0-9]+)$/)
{
$gather_change_num = $1;
if ($do_change_num)
{
&dochange(@change);
if (($CHECKPOINT_INTERVAL > 0) && (($do_change_num % $CHECKPOINT_INTERVAL) == 0))
{ if ($Docheckpointing) { &docheckpoint; } }
$do_change_num = 0;
if ($progress)
{
$change_cnt++;
verbose -2, "rev status: $rev_cnt of $total_revs";
verbose -2, "change status: $change_cnt of $total_changes";
}
}
if ($gather_change_num < $Start_at_ch) { $gather_change_num = 0; next; }
my $stop_msg = -e $killfile ? "$killfile present" : "";
if ($gather_change_num > $End_at_ch) {
$stop_msg = "last requested change group processed" ;
}
if ($stop_msg) {
verbose 0, "$Myname: stopping because $stop_msg";
last;
}
# Clear the change
#
undef @change;
}
elsif ($gather_change_num)
{ push(@change, $_); $do_change_num = $gather_change_num; }
}
if ($do_change_num)
{
&dochange(@change);
if ($progress)
{
$change_cnt++;
verbose -2, "rev status: $rev_cnt of $total_revs";
verbose -2, "change status: $change_cnt of $total_changes";
}
}
if ($Docheckpointing) { &docheckpoint; }
close CHGS;
$Changes = $ch . "." . ++$cnt;
}
dbmclose MSGS;
close DONELOG if $doLog;
close REVLOG if $makeREVLOG;
dbmclose REVMAP if $makeREVMAP;
}
dbmclose TIPS;
verbose 0, "dochanges is finished";