#!/usr/bin/perl -w # # VSS to Perforce converter, phase III: construct Perforce depot # # Copyright 1998 Perforce Software. All rights reserved. # Written by James Strickland, April 1998 # # This script uses the metadata produced by earlier phases to direct a loop # which extracts VSS revisions and performs the required Perforce commands # to construct a Perforce depot corresponding to the (improved) VSS data. # # RHGC - Modifed to use new depot and depot_root as specified in config. require 5.0; use strict; use integer; use lib '.'; use convert; use Change; use DB_File; use Win32::OLE; use Win32::OLE qw(in); use Win32::OLE::Const; use Win32::OLE::Variant; # open all our input files my $msg="can't open for read"; open(FILES, "<$convert::metadata_dir/files") or die $msg; open(LABELS_SUMMARY, "<$convert::metadata_dir/labels_summary") or die $msg; open(CHANGES, "<$convert::metadata_dir/changes") or die $msg; open(MAPPING, ">$convert::metadata_dir/mapping.ns") or die "can't open for write: $!"; # Initialise VSS bia OLE interface my $VSSDB = Win32::OLE->new('SourceSafe', 'Quit'); my $consts = Win32::OLE::Const->Load($VSSDB); # Declare the Username, password and SourceSafe path variables my $UserName = "tjm"; my $SrcSafeIni = "C:\\work\\wallstreet\\real-vss\\SRCSAFE.INI"; my $Password = "tjm"; $VSSDB->Open($SrcSafeIni, $UserName, $Password); # variables to be initialized with metadata read from files my (%file_type, # maps archive -> file type # e.g. "$/foo" -> "text" %labels, # maps "revision archive" -> list of labels # e.g. "1 $/foo" -> "itworks","bobsyouruncle" ); # initialize %file_type while() { chomp; my ($file_type,$file) = split(/ /,$_,2); $file_type{$file} = $file_type; } close(FILES); # initialize %label and run p4 label once for each label which is not a # "delete" label while() { chomp; my ($label,$timestamp) = split(/ /,$_,2); my $comment = ''; $comment .= while ($comment !~ /\*{10}\n/); $comment =~ s/\*{10}\n$//; $comment = "vsstop4 label" if ($comment =~ /^\s*$/); # Set default comment. my $form=convert::p4run(" label -o $label"); # label names cannot contain spaces $comment =~ s@\n@\n\t@gs; my $label_view = "//$convert::depot/$convert::depot_root/..."; # Update the Description field in the label. $form =~ s@(\nDescription:\s*)\n\s+\S[^\n]*\n@$1\n\t$comment@s; # RHGC - Create a more specific view for the label - not just all depots which is the default. $form =~ s@(\nView:\s*)\n\s+.*\n$@$1\n\t$label_view@s; # adjust the update time of the label my (@tm,$date); @tm=localtime($timestamp); $date=sprintf("%4d/%02d/%02d %02d:%02d:%02d",(($tm[5]>=70) ? $tm[5]+1900 : $tm[5]+2000), $tm[4]+1,$tm[3],$tm[2],$tm[1],$tm[0]); if ($form =~ /\nUpdate:[^\n]*/) { $form =~ s@\nUpdate:[^\n]*@\nUpdate: $date@s; convert::log("Label input:\n$form"); } else { $form =~ s@(\nLabel:.*?\n)@$1\nUpdate: $date\n@s; } convert::p4run(" label -i",$form); $labels{$label} = 1; unlink ("$convert::metadata_dir/labels/$label"); } close(LABELS_SUMMARY); # print timestamp print "Depot creation started " . scalar(localtime()) . "\n"; my %label_files; tie %label_files, "DB_File", "$convert::metadata_dir/db.labels", O_RDONLY, $DB_HASH or die "Cannot open file '$convert::metadata_dir/db.labels': $!\n"; # For each change, # - retrieve revisions from VSS using 'get' # - mark files as added or edited with 'p4 add' or 'p4 edit', # followed by 'p4 submit' # - update list of file#rev associated with any pertinent labels my $start_time = $convert::start_time; my ($c,$op); CHG: while( $c = get Change(\*CHANGES) ) { my ($change_number,@checkins); # Ignore change if a start_time is specified if ($start_time > $c->timestamp) { print "Ignoring changelist with timestamp: ". $c->datetime ."\n"; next CHG; } my $index; foreach $index (@{$c->changelist}) { my $p4rev; my ($revision,$vss_file) = split(/ /,$index,2); my ($client_rel_dir,$client_file) = convert::p4dir_and_file( $vss_file); my $client_dir = convert::join_paths( $convert::client_root, $client_rel_dir ); # create all needed directories on the path convert::emkdir($client_dir); # for p4 - could use $client_file, but I'd rather make sure we use # the same string that's written out to the mapping file my $depot_file = convert::join_paths("//$convert::depot", $client_rel_dir, $client_file ); # get the file from VSS unlink("${client_dir}/${client_file}"); my $item = $VSSDB->VSSItem($vss_file, 0); my $version = $item->Version("$revision"); $version->Get("${client_dir}/${client_file}"); # convert::get_vss_file($vss_file,($file_type{$vss_file} eq "tempobj") ? "" : $revision,$client_dir,$client_file); # see if the file is indeed there now if (-f "${client_dir}/${client_file}") { # schedule p4 add or edit operation # depending on whether file has already been added push(@checkins, [ $vss_file,$revision,$depot_file ] ); # RHGC - because of restartability, add extra check if (convert::p4run(" -s fstat \"$depot_file\"") !~ /- no such file\(s\)/) { convert::p4run(" edit \"$depot_file\"") =~ /#(\d+) - opened for edit/ or die "p4 edit $depot_file failed"; $p4rev=$1 +1; } else { my $addcmd = " add -t $file_type{$vss_file} \"$depot_file\""; if ($convert::typemap_regexp) { # don't specify type when file is handled with p4 typemap if ($depot_file =~ /$convert::typemap_regexp/) { $addcmd = " add \"$depot_file\""; } else { print "file $depot_file not matched by typemap.\n"; } } convert::p4run($addcmd) =~ /opened for add/ or die "p4 add $depot_file failed"; $p4rev=1; } # check if this revision is labelled if(exists($label_files{$index})) { my $labels = $label_files{$index}; my @label_list = split(/ /, $labels); for (@label_list) { open(LABELFILE,">>$convert::metadata_dir/labels/$_") or die "can't open label file $_: $!\n"; print LABELFILE "$depot_file#$p4rev\n"; close(LABELFILE); } } } else { # the file isn't there - the ss get failed if ($convert::skip_ss_get_errors) { print STDERR "ERROR: VSS file not found: ${vss_file}#${revision}\n"; } else { die "get_vss_file() revision $revision to $client_dir/$client_file failed"; } } } # submit the change, and write out the association between VSS # archive and revision number and Perforce file and change number $change_number = $c->submit; # submit the change my $checkin; foreach $checkin (@checkins) { my ($vss_file,$r,$depot_filename) = @$checkin; # write change number#filename to avoid confusion with filename#revision print MAPPING "$r#$change_number#$depot_filename#$vss_file\n"; # NOTE: $vss_file has to go last since it can contain # characters } } # now do the labelling print "\nLabelling started " . scalar(localtime()) . "\n"; for (keys(%labels)) { convert::p4run(" -x $convert::metadata_dir/labels/$_ labelsync -l $_"); } untie %label_files;