#!/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;
# open all our input files
my $msg="can't open for read";
open(FILES, "<$convert::metadata_dir/files") or die $msg;
open(LABELS, "<$convert::metadata_dir/labels") 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: $!";
# 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"
%filesinlabel, # maps label name -> list of "file#rev"
%added, # maps depot file -> true/false
# e.g. "//depot/foo" -> 1
);
# initialize %file_type
while(<FILES>) {
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
my %created;
while(<LABELS>) {
chomp;
my ($revision,$label,$timestamp,$file) = split(/ /,$_,4);
my $index = "$revision $file";
$labels{$index} = defined($labels{$index}) ? [ @{$labels{$index}} , $label ]
: [ $label ];
my $comment = '';
$comment .= <LABELS> while ($comment !~ /\*{10}\n/);
$comment =~ s/\*{10}\n$//;
$comment = "vsstop4 label" if ($comment =~ /^\s*$/); # Set default comment.
next if($created{$label});
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);
$created{$label}=1;
}
close(LABELS);
undef %created; # we're finished with this hash, so free up the space
# print timestamp
print "Depot creation started " . scalar(localtime()) . "\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
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 ] );
my $operation;
if($added{$depot_file}) {
convert::p4run(" edit \"$depot_file\"") =~ /#(\d+) - opened for edit/ or die "p4 edit $depot_file failed";
$p4rev=$1 +1;
} else {
# 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";
$added{$depot_file}=1;
$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";
$added{$depot_file}=1;
$p4rev=1;
}
}
# check if this revision is labelled
if(exists($labels{$index})) {
for (@{$labels{$index}}) {
$filesinlabel{$_} = defined($filesinlabel{$_})
? [ @{$filesinlabel{$_}} , "$depot_file#$p4rev" ]
: [ "$depot_file#$p4rev" ];
}
}
} 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(%filesinlabel)) {
convert::p4run(" -x - labelsync -l $_",join("\n",@{$filesinlabel{$_}})."\n");
}