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: //guest/matthew_rice/util/cvs2p4/bin/dochanges#14 $ # # 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.\$ ^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) 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) = @_; my($path); $path = "$prefix"; if ($file_dir) { $path .= "$file_dir/"; } $path .= "$file_name"; return $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 4, "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 4, "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); if ($V > 4) { verbose 5, "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 ([0-9]+) submitted\.$/) { $changenum = $1; } } 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}) { warn "using branch mappings but no mapping found for $line. ", "Is it new?\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 = "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); } if ($sts = &s("$P4 integrate \Q$Depotfile\E \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 = 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, ) || 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";
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#22 | 883 | Matthew Rice | Matt's cvs2p4 1.2.36 release | ||
#21 | 882 | Matthew Rice | Matt's cvs2p4 1.2.35 release | ||
#20 | 881 | Matthew Rice | Matt's cvs2p4 1.2.34 release | ||
#19 | 880 | Matthew Rice | Matt's cvs2p4 1.2.33 release | ||
#18 | 879 | Matthew Rice | Matt's cvs2p4 1.2.32 release | ||
#17 | 878 | Matthew Rice | Matt's cvs2p4 1.2.31 release | ||
#16 | 877 | Matthew Rice | Matt's cvs2p4 1.2.30 release | ||
#15 | 876 | Matthew Rice | Matt's cvs2p4 1.2.29 release | ||
#14 | 875 | Matthew Rice | Matt's cvs2p4 1.2.28 release | ||
#13 | 874 | Matthew Rice | Matt's cvs2p4 1.2.27 release | ||
#12 | 873 | Matthew Rice | Matt's cvs2p4 1.2.26 release | ||
#11 | 872 | Matthew Rice | Matt's cvs2p4 1.2.25 release | ||
#10 | 871 | Matthew Rice | Matt's cvs2p4 1.2.24 release | ||
#9 | 870 | Matthew Rice | Matt's cvs2p4 1.2.23 release | ||
#8 | 869 | Matthew Rice | Matt's cvs2p4 1.2.22 release | ||
#7 | 868 | Matthew Rice | Matt's cvs2p4 1.2.21 release | ||
#6 | 867 | Matthew Rice | Matt's cvs2p4 1.2.20 release | ||
#5 | 866 | Matthew Rice | Matt's cvs2p4 1.2.19 release | ||
#4 | 865 | Matthew Rice | Matt's cvs2p4 1.2.18 release | ||
#3 | 864 | Matthew Rice | Matt's cvs2p4 1.2.17 release | ||
#2 | 863 | Matthew Rice | Matt's cvs2p4 1.2.16 release | ||
#1 | 862 | Matthew Rice | Branched off of Richard's 1.2.15 release. | ||
//guest/richard_geiger/utils/cvs2p4/bin/dochanges | |||||
#8 | 421 | Richard Geiger | Fix to the fix by Thomas Quinot. | ||
#7 | 416 | Richard Geiger | Pull in Thomas Quinot <quinot@inf.enst.fr>'s UTC bugfix, for 1.2.12. | ||
#6 | 250 | Richard Geiger |
Changes to allow coping with shell metacharacters (' ' & '$', at least) in file names. |
||
#5 | 249 | Richard Geiger |
Changes in preparation for supporting spaces in filenames. (In fact, this may work as of this change, but is not yet tested.) Also, add "runtest -gengood" to allow easier generatino of new *.good files. (It just doesn't quick on a miscompare!). |
||
#4 | 240 | Richard Geiger |
Version 1.2.5, to account for post-1999 RCS behavior. (Courtesy of David Simon, Goldman Sachs) |
||
#3 | 228 | Richard Geiger |
Changes for 1.2.4 - recognize alternative "p4 files" message for files not present in the depot. |
||
#2 | 179 | Richard Geiger | CHanges for 1.2.3 | ||
#1 | 130 | Richard Geiger |
CVS-to-Perforce converter. This is release 1.2.2 (first submit to the Perforce Public Depot) |