From cdee67169b3737bdeaeae1be6af29b58fbafc59d Mon Sep 17 00:00:00 2001 From: toby Date: Mon, 13 Feb 2006 02:48:41 +0000 Subject: [PATCH] * Initial dumpfile creation logic * Detect binary/non-binary flag and use to set mime-type * Start SVN revnums at 0 instead of 1 git-svn-id: http://vss2svn.googlecode.com/svn/trunk@116 2cfd5912-9055-84bd-9a12-e3c18a4b6e42 --- script/Vss2Svn/ActionHandler.pm | 4 +- script/Vss2Svn/DataCache.pm | 15 +- script/Vss2Svn/Dumpfile.pm | 385 ++++++++++++++++++++++++++++++++++++++++ script/Vss2Svn/Dumpfile/Node.pm | 92 ++++++++++ script/Vss2Svn/SvnRevHandler.pm | 13 +- script/vss2svn.pl | 225 +++++++++++++---------- 6 files changed, 636 insertions(+), 98 deletions(-) create mode 100644 script/Vss2Svn/Dumpfile.pm create mode 100644 script/Vss2Svn/Dumpfile/Node.pm diff --git a/script/Vss2Svn/ActionHandler.pm b/script/Vss2Svn/ActionHandler.pm index 0950d26..8eda438 100644 --- a/script/Vss2Svn/ActionHandler.pm +++ b/script/Vss2Svn/ActionHandler.pm @@ -3,7 +3,7 @@ package Vss2Svn::ActionHandler; use warnings; use strict; -our %handlers = +our %gHandlers = ( ADD => \&_add_handler, COMMIT => \&_commit_handler, @@ -45,7 +45,7 @@ sub handle { my($self, $action) = @_; $self->{action} = $action; - my $handler = $handlers{$action}; + my $handler = $gHandlers{$action}; if (!defined($handler)) { $self->{errmsg} .= "Unknown action '$action'"; diff --git a/script/Vss2Svn/DataCache.pm b/script/Vss2Svn/DataCache.pm index f0c6966..f7b2070 100644 --- a/script/Vss2Svn/DataCache.pm +++ b/script/Vss2Svn/DataCache.pm @@ -5,6 +5,12 @@ use strict; our(%gCfg); +# SQLite can get a bit slow when doing lots of sequential inserts, so we speed +# that up by using the sqlite2 "COPY" command, which allows reading in a tab- +# delimited file of data all at once. Each table that will be filled has a +# DataCache object created; the 'add' method adds a row of data to the cache, +# and the 'commit' method performs the COPY operation. + ############################################################################### # new ############################################################################### @@ -15,7 +21,7 @@ sub new { { table => $table, autoinc => $autoinc, - pkey => 0, + pkey => -1, verbose => $gCfg{verbose}, fh => undef, file => "$gCfg{cachedir}\\datachache.$table.tmp.txt", @@ -29,7 +35,10 @@ sub new { $self->_delete_table(); - unlink($self->{file}); + if ((-e $self->{file}) && !(unlink($self->{file}))) { + print "\nERROR: Could not delete existing cache file '$self->{file}'\n"; + return undef; + } if ( !open($self->{fh}, ">$self->{file}") ) { print "\nERROR: Could not open file '$self->{file}'\n"; @@ -88,8 +97,6 @@ sub commit { $sth = $gCfg{dbh}->prepare($sql); $sth->execute(); - unlink $self->{file} unless $self->{verbose}; - } # End commit ############################################################################### diff --git a/script/Vss2Svn/Dumpfile.pm b/script/Vss2Svn/Dumpfile.pm new file mode 100644 index 0000000..1d4c33f --- /dev/null +++ b/script/Vss2Svn/Dumpfile.pm @@ -0,0 +1,385 @@ +package Vss2Svn::Dumpfile; + +use Vss2Svn::Dumpfile::Node; + +use warnings; +use strict; + +our %gHandlers = + ( + ADD => \&_add_handler, + COMMIT => \&_commit_handler, + RENAME => \&_rename_handler, + SHARE => \&_share_handler, + BRANCH => \&_branch_handler, + MOVE => \&_move_handler, + DELETE => \&_delete_handler, + RECOVER => \&_recover_handler, + ); + +# keep track of when paths were modified or deleted, for subsequent copies +# or recovers +our %gModified = (); +our %gDeleted = (); + +############################################################################### +# new +############################################################################### +sub new { + my($class, $fh) = @_; + + my $self = + { + fh => $fh, + revision => 0, + errors => [], + }; + + # prevent perl from doing line-ending conversions, but this means we'll + # need to explicitly output DOS-style line endings between info lines + binmode($fh); + + my $old = select($fh); + $| = 1; + select($old); + + #TODO: take out UUID + print $fh "SVN-fs-dump-format-version: 2\n\nUUID: 2d233e98-0cb8-4f47-9081-4b0a55eb6c6b\n"; + + $self = bless($self, $class); + return $self; + +} # End new + +############################################################################### +# finish +############################################################################### +sub finish { + my($self) = @_; + + my $fh = $self->{fh}; + + print $fh "\n\n"; + +} # End finish + +############################################################################### +# begin_revision +############################################################################### +sub begin_revision { + my($self, $data) = @_; + my($revision, $author, $timestamp, $comment) = + @{ $data }{qw(revision_id author timestamp comment)}; + + my $props = []; + my $fh = $self->{fh}; + + print $fh "\nRevision-number: $revision\n"; + + $comment = '' if !defined($comment); + $author = '' if !defined($author); + + if ($revision > 0) { + push @$props, ['svn:log', $comment]; + push @$props, ['svn:author', $author]; + } + + push @$props, ['svn:date', $self->svn_timestamp($timestamp)]; + + $self->output_content($props); + $self->{revision} = $revision; + +} # End begin_revision + +############################################################################### +# do_action +############################################################################### +sub do_action { + my($self, $data, $expdir) = @_; + #physname VARCHAR, + #version INTEGER, + #action VARCHAR, + #itempaths VARCHAR, + #itemtype INTEGER, + #is_binary INTEGER, + #info VARCHAR + + my $action = $data->{action}; + my $handler = $gHandlers{$action}; + + my $nodes = []; + + foreach my $itempath (split "\t", $data->{itempaths}) { + $self->$handler($itempath, $nodes, $data, $expdir); + } + + foreach my $node (@$nodes) { + $self->output_node($node); + } +} # End do_action + +############################################################################### +# _add_handler +############################################################################### +sub _add_handler { + my($self, $itempath, $nodes, $data, $expdir) = @_; + + my $node = Vss2Svn::Dumpfile::Node->new(); + $node->set_initial_props($itempath, $data); + $node->{action} = 'add'; + + if ($data->{itemtype} == 2) { + $self->get_export_contents($node, $data, $expdir); + } + + $gModified{$itempath} = $data->{revision_id}; + + push @$nodes, $node; + +} # End _add_handler + +############################################################################### +# _commit_handler +############################################################################### +sub _commit_handler { + my($self, $itempath, $nodes, $data, $expdir) = @_; + + my $node = Vss2Svn::Dumpfile::Node->new(); + $node->set_initial_props($itempath, $data); + $node->{action} = 'change'; + + if ($data->{itemtype} == 2) { + $self->get_export_contents($node, $data, $expdir); + } + + $gModified{$itempath} = $data->{revision_id}; + + push @$nodes, $node; + +} # End _commit_handler + +############################################################################### +# _rename_handler +############################################################################### +sub _rename_handler { + my($self, $itempath, $nodes, $data, $expdir) = @_; + + # to rename a file in SVN, we must add "with history" then delete the orig. + + my $newname = $data->{info}; + + my $newpath = $itempath; + + if ($data->{itemtype} == 1) { + $newpath =~ s:(.*/)?.+$:$1$newname/:; + } else { + $newpath =~ s:(.*/)?.*:$1$newname:; + } + + my $node = Vss2Svn::Dumpfile::Node->new(); + $node->set_initial_props($newpath, $data); + $node->{action} = 'add'; + + $node->{copypath} = $itempath; + + my $copyrev = ($data->{itemtype} == 1)? + $data->{revision_id} - 1 : + $gModified{ $itempath }; + + $node->{copyrev} = $copyrev; + + $gModified{$newpath} = $data->{revision_id}; + + push @$nodes, $node; + + $node = Vss2Svn::Dumpfile::Node->new(); + $node->{path} = $itempath; + $node->{action} = 'delete'; + $node->{hideprops} = 1; + + push @$nodes, $node; + + # we don't add this to %gDeleted since VSS doesn't treat a rename as an + # add/delete + +} # End _rename_handler + +############################################################################### +# _share_handler +############################################################################### +sub _share_handler { + my($self, $itempath, $nodes, $data, $expdir) = @_; + + my $node = Vss2Svn::Dumpfile::Node->new(); + $node->set_initial_props($itempath, $data); + $node->{action} = 'add'; + + $node->{copypath} = $data->{info}; + $node->{copyrev} = $gModified{ $data->{info} }; + + $gModified{$itempath} = $data->{revision_id}; + + push @$nodes, $node; + +} # End _share_handler + +############################################################################### +# _branch_handler +############################################################################### +sub _branch_handler { + my($self, $itempath, $nodes, $data, $expdir) = @_; + + # branching is a no-op in SVN + +} # End _branch_handler + +############################################################################### +# _move_handler +############################################################################### +sub _move_handler { + my($self, $itempath, $nodes, $data, $expdir) = @_; + + push @{ $self->{errors} }, "Not yet implemented: $data->{action}" + +} # End _move_handler + +############################################################################### +# _delete_handler +############################################################################### +sub _delete_handler { + my($self, $itempath, $nodes, $data, $expdir) = @_; + + my $node = Vss2Svn::Dumpfile::Node->new(); + $node->{path} = $itempath; + $node->{action} = 'delete'; + $node->{hideprops} = 1; + + push @$nodes, $node; + + $gDeleted{$itempath} = $data->{revision_id}; + +} # End _delete_handler + +############################################################################### +# _recover_handler +############################################################################### +sub _recover_handler { + my($self, $itempath, $nodes, $data, $expdir) = @_; + + my $node = Vss2Svn::Dumpfile::Node->new(); + $node->set_initial_props($itempath, $data); + $node->{action} = 'add'; + + if (!defined $gDeleted{$itempath}) { + push @{ $self->{errors} }, + "Could not recover path $itempath at revision $data->{revision_id};" + . " unable to determine deleted revision"; + return 0; + } + + $node->{copypath} = $itempath; + $node->{copyrev} = $gDeleted{$itempath} - 1; + + $gModified{$itempath} = $data->{revision_id}; + + push @$nodes, $node; + +} # End _recover_handler + +############################################################################### +# get_export_contents +############################################################################### +sub get_export_contents { + my($self, $node, $data, $expdir) = @_; + + my $file = "$expdir\\$data->{physname}.$data->{version}"; + + if (!open EXP, "$file") { + push @{ $self->{errors} }, + "Could not open export file '$file'"; + return 0; + } + + binmode(EXP); + + $node->{text} = join('', ); + + close EXP; + + return 1; + +} # End get_export_contents + +############################################################################### +# output_node +############################################################################### +sub output_node { + my($self, $node) = @_; + my $fh = $self->{fh}; + + print $fh $node->get_headers(); + $self->output_content($node->{hideprops}? undef : $node->{props}, + $node->{text}); +} # End output_node + +############################################################################### +# output_content +############################################################################### +sub output_content { + my($self, $props, $text) = @_; + + my $fh = $self->{fh}; + + $text = '' unless defined $text; + + my $proplen = 0; + my $textlen = 0; + my($propout, $textout) = ('') x 2; + + my($key, $value); + + if (defined($props)) { + foreach my $prop (@$props) { + ($key, $value) = @$prop; + $propout .= 'K ' . length($key) . "\n$key\nV " . length($value) + . "\n$value\n"; + } + + $propout .= "PROPS-END\n"; + $proplen = length($propout); + } + + $textlen = length($text); + return if ($textlen + $proplen == 0); + + if ($proplen > 0) { + print $fh "Prop-content-length: $proplen\n"; + } + + if ($textlen > 0) { + print $fh "Text-content-length: $textlen\n"; + } + + print $fh "Content-length: " . ($proplen + $textlen) + . "\n\n$propout$text\n"; + +} # End output_content + +############################################################################### +# svn_timestamp +############################################################################### +sub svn_timestamp { + my($self, $vss_timestamp) = @_; + + my($sec, $min, $hour, $day, $mon, $year) = gmtime($vss_timestamp); + + $year += 1900; + $mon += 1; + + return sprintf("%4.4i-%2.2i-%2.2iT%2.2i:%2.2i:%2.2i.%6.6iZ", + $year, $mon, $day, $hour, $min, $sec, 0); + +} # End svn_timestamp + + +1; \ No newline at end of file diff --git a/script/Vss2Svn/Dumpfile/Node.pm b/script/Vss2Svn/Dumpfile/Node.pm new file mode 100644 index 0000000..62e2553 --- /dev/null +++ b/script/Vss2Svn/Dumpfile/Node.pm @@ -0,0 +1,92 @@ +package Vss2Svn::Dumpfile::Node; + +use warnings; +use strict; + +our @gHeaderInfo = + ( + {property => 'path', + header => 'Node-path'}, + {property => 'kind', + header => 'Node-kind'}, + {property => 'action', + header => 'Node-action'}, + {property => 'copyrev', + header => 'Node-copyfrom-rev'}, + {property => 'copypath', + header => 'Node-copyfrom-path'}, + ); + +############################################################################### +# new +############################################################################### +sub new { + my($class) = @_; + + my $self = + { + path => undef, + kind => undef, + action => undef, + copyrev => undef, + copypath => undef, + props => [], + hideprops => 0, + text => undef, + }; + + $self = bless($self, $class); + return $self; + +} # End new + +############################################################################### +# set_initial_props +############################################################################### +sub set_initial_props { + my($self, $itempath, $data) = @_; + + $self->{kind} = ($data->{itemtype} == 1)? 'dir' : 'file'; + $self->{path} = $itempath; + + if ($data->{is_binary}) { + $self->add_prop('svn:mime-type', 'application/octet-stream'); + } + +} # End set_initial_props + +############################################################################### +# add_prop +############################################################################### +sub add_prop { + my($self, $key, $value) = @_; + push @{ $self->{props} }, [$key, $value]; +} # End add_prop + +############################################################################### +# get_headers +############################################################################### +sub get_headers { + my($self) = @_; + + my $headers = "\n"; + my($property, $header, $value); + + foreach my $info (@gHeaderInfo) { + ($property, $header) = @{ $info }{ qw(property header) }; + if (defined($value = $self->{$property})) { + if ($header =~ m/-path$/) { + $value =~ s:^/::; + $value =~ s:/$::; + } + + $headers .= "$header: $value\n"; + } + } + + return $headers; +} # End get_headers + + + +1; diff --git a/script/Vss2Svn/SvnRevHandler.pm b/script/Vss2Svn/SvnRevHandler.pm index acb7b41..a8fbaa0 100644 --- a/script/Vss2Svn/SvnRevHandler.pm +++ b/script/Vss2Svn/SvnRevHandler.pm @@ -52,15 +52,22 @@ sub _init { sub check { my($self, $data) = @_; - my($physname, $timestamp, $author, $comment) = - @{ $data }{qw( physname timestamp author comment )}; + my($physname, $itemtype, $actiontype, $timestamp, $author, $comment) = + @{ $data }{qw( physname itemtype actiontype timestamp author comment )}; my($prevtimestamp, $prevauthor, $prevcomment) = @{ $self }{qw( timestamp author comment )}; + # Any of the following cause a new SVN revision: + # * same file touched more than once + # * different author or comment + # * time range exceeds threshold num. of seconds (default 3600) + # * any action on a directory other than add + no warnings 'uninitialized'; if(($author ne $prevauthor) || ($comment ne $prevcomment) || $self->{seen}->{$physname}++ || - ($timestamp - $prevtimestamp > $gCfg{revtimerange})) { + ($timestamp - $prevtimestamp > $gCfg{revtimerange}) || + ($itemtype == 1 && $actiontype ne 'ADD')) { @{ $self }{qw( timestamp author comment)} = ($timestamp, $author, $comment); diff --git a/script/vss2svn.pl b/script/vss2svn.pl index be7fe33..a37857a 100755 --- a/script/vss2svn.pl +++ b/script/vss2svn.pl @@ -8,6 +8,7 @@ use DBI; use DBD::SQLite2; use XML::Simple; use File::Find; +use File::Path; use Time::CTime; use Data::Dumper; @@ -15,6 +16,7 @@ use lib '.'; use Vss2Svn::ActionHandler; use Vss2Svn::DataCache; use Vss2Svn::SvnRevHandler; +use Vss2svn::Dumpfile; our(%gCfg, %gSth, @gErr, %gFh, $gSysOut, %gActionType, %gNameLookup, %gId); @@ -65,10 +67,6 @@ sub RunConversion { # Take the history of physical actions and convert them to VSS # file actions BUILDACTIONHIST => {handler => \&BuildVssActionHistory, - next => 'BUILDREVS'}, - - # Combine these individual actions into atomic actions a' la SVN - BUILDREVS => {handler => \&BuildRevs, next => 'IMPORTSVN'}, # Create a dumpfile or import to repository @@ -248,7 +246,7 @@ sub GetVssItemVersions { my($parentdata, $version, $vernum, $action, $name, $actionid, $actiontype, $tphysname, $itemname, $itemtype, $parent, $user, $timestamp, $comment, - $info, $priority, $sortkey, $cachename); + $is_binary, $info, $priority, $sortkey, $cachename); VERSION: foreach $version (@{ $xml->{Version} }) { @@ -276,6 +274,7 @@ VERSION: } $comment = undef; + $is_binary = 0; $info = undef; $parentdata = 0; $priority = 5; @@ -290,7 +289,7 @@ VERSION: $tphysname = $physname; $itemname = ''; } elsif ($physname ne $tphysname) { - # If version's physical name and file physical name are different, + # If version's physical name and file's physical name are different, # this is a project describing an action on a child item. Most of # the time, this very same data will be in the child's physical # file and with more detail (such as check-in comment). @@ -316,6 +315,11 @@ VERSION: if ($itemtype == 1) { $itemname .= '/'; + } elsif (defined($xml->{ItemInfo}) && + defined($xml->{ItemInfo}->{Binary}) && + $xml->{ItemInfo}->{Binary}) { + + $is_binary = 1; } if ($actiontype eq 'RENAME') { @@ -341,8 +345,8 @@ VERSION: $sortkey = reverse($tphysname); $cache->add($tphysname, $vernum, $parentphys, $actiontype, $itemname, - $itemtype, $timestamp, $user, $info, $priority, $sortkey, - $parentdata, $comment); + $itemtype, $timestamp, $user, $is_binary, $info, $priority, + $sortkey, $parentdata, $comment); } @@ -371,8 +375,6 @@ sub GetItemName { } else { print "Found name '$cachename' in namecache, but kept original " . "'$itemname'\n" if $gCfg{debug}; - - 1; } } @@ -482,20 +484,31 @@ sub UpdateParentRec { my($row, $child) = @_; # The child record has the "correct" version number (relative to the child - # and not the parent), as well as the comment info + # and not the parent), as well as the comment info and whether the file is + # binary + + my $comment; + + { + no warnings 'uninitialized'; + $comment = "$row->{comment}\n$child->{comment}"; + $comment =~ s/\n$//; + } my $sql = <<"EOSQL"; UPDATE PhysicalAction SET version = ?, + is_binary = ?, comment = ? WHERE action_id = ? EOSQL my $sth = $gCfg{dbh}->prepare($sql); - $sth->execute( $child->{version}, $child->{comment}, $row->{action_id} ); + $sth->execute( $child->{version}, $child->{is_binary}, $comment, + $row->{action_id} ); } # End UpdateParentRec @@ -529,7 +542,7 @@ sub BuildVssActionHistory { || &ThrowError("Could not create SVN revision handler"); $svnrevs->{verbose} = $gCfg{verbose}; - my($sth, $row, $action, $handler, $physinfo, $itempaths, $itempath); + my($sth, $row, $action, $handler, $physinfo, $itempaths, $allitempaths); my $sql = 'SELECT * FROM PhysicalAction ORDER BY timestamp ASC, ' . 'priority ASC, sortkey ASC'; @@ -582,13 +595,12 @@ ROW: # MOVE: the new path $row->{info} = $handler->{info}; - foreach $itempath (@$itempaths) { - $row->{itempath} = $itempath; + $allitempaths = join("\t", @$itempaths); + $row->{itempaths} = $allitempaths; - $vsscache->add(@$row{ qw(physname version actiontype itempath - itemtype info) }); - $joincache->add( $svnrevs->{revnum}, $vsscache->{pkey} ); - } + $vsscache->add(@$row{ qw(physname version actiontype itempaths + itemtype is_binary info) }); + $joincache->add( $svnrevs->{revnum}, $vsscache->{pkey} ); } @@ -599,49 +611,120 @@ ROW: } # End BuildVssActionHistory ############################################################################### -# NewSvnRevision -############################################################################### -sub NewSvnRevision { - my($seen, $author, $comment, $timestamp) = @_; -} # End NewSvnRevision - -############################################################################### # ImportToSvn ############################################################################### sub ImportToSvn { - defined($gCfg{svnurl})? &CheckinToSvn : &CreateSvnDumpfile; + # For the time being, we support only creating a dumpfile and not directly + # importing to SVN. We could perhaps add this functionality by making the + # CreateSvnDumpfile logic more generic and using polymorphism to switch out + # the Vss2Svn::Dumpfile object with one that handles imports. + + &CreateSvnDumpfile; } # End ImportToSvn ############################################################################### -# CheckinToSvn +# CreateSvnDumpfile ############################################################################### -sub CheckinToSvn { +sub CreateSvnDumpfile { + my $fh; + + my $file = 'vss2svn-dumpfile.txt'; + open $fh, ">$file" + or &ThrowError("Could not create dumpfile '$file'"); + + my($sql, $sth, $row, $revision, $actions, $action, $physname, $itemtype); + + my %exported = (); + + $sql = 'SELECT * FROM SvnRevision ORDER BY revision_id ASC'; + + $sth = $gCfg{dbh}->prepare($sql); + $sth->execute(); + + my $dumpfile = Vss2Svn::Dumpfile->new($fh); + +REVISION: + while(defined($row = $sth->fetchrow_hashref() )) { + $revision = $row->{revision_id}; + $dumpfile->begin_revision($row); -} # End CheckinToSvn + next REVISION if $revision == 0; + + $actions = &GetRevVssActions($revision); + +ACTION: + foreach $action(@$actions) { + $physname = $action->{physname}; + $itemtype = $action->{itemtype}; + + if (!defined $exported{$physname}) { + if ($itemtype == 2) { + $exported{$physname} = &ExportVssPhysFile($physname); + } else { + $exported{$physname} = undef; + } + } + + $dumpfile->do_action($action, $exported{$physname}); + } + } + + my @err = @{ $dumpfile->{errors} }; + + if (scalar @err > 0) { + print "\nERRORS during dumpfile creation:\n "; + print join("\n ", @err); + } + + $dumpfile->finish(); + close $fh; + +} # End CreateSvnDumpfile ############################################################################### -# CreateSvnDumpfile +# GetRevVssActions ############################################################################### -sub CreateSvnDumpfile { +sub GetRevVssActions { + my($revision) = @_; -} # End CreateSvnDumpfile + my($sql, $sth); + $sql = <<"EOSQL"; +SELECT * FROM + VssAction v +INNER JOIN + SvnRevisionVssAction sv ON sv.action_id = v.action_id +WHERE + sv.revision_id = ? +EOSQL + + $sth = $gCfg{dbh}->prepare($sql); + $sth->execute($revision); + + return $sth->fetchall_arrayref( {} ); +} # End GetRevVssActions + +############################################################################### +# ExportVssPhysFile +############################################################################### +sub ExportVssPhysFile { + my($physname) = @_; + + $physname =~ m/^((.).)/; + + my $exportdir = "$gCfg{vssdata}\\$1"; + my $physdir = "$gCfg{vssdir}\\data\\$2"; + + mkpath($exportdir); + + &DoSsCmd("get -b -v1 --force-overwrite $physdir\\$physname $exportdir\\$physname"); + + return $exportdir; +} # End ExportVssPhysFile ############################################################################### # ShowHeader ############################################################################### sub ShowHeader { - if ($gCfg{log}) { - my $prefix = $gCfg{pvcsproj} || $gCfg{svnurl} || "log-$$"; - $prefix =~ s:.*[\\/]::; - $gCfg{logfile} = "./logs/$prefix.txt"; - print "All output will be logged to $gCfg{logfile}...\n"; - open LOG, ">>$gCfg{logfile}" - or die "Couldn't append to logfile $gCfg{logfile}"; - open STDERR, ">&LOG"; - select STDERR; $| = 1; - select LOG; $| = 1; - } - my $info = $gCfg{task} eq 'INIT'? 'BEGINNING CONVERSION...' : "RESUMING CONVERSION FROM TASK '$gCfg{task}' AT STEP $gCfg{step}..."; my $starttime = ctime($^T); @@ -667,14 +750,6 @@ EOTXT # ShowSummary ############################################################################### sub ShowSummary { - if (@gErr) { - print "\n\n\n====ERROR SUMMARY====\n\n"; - foreach (@gErr) { - print "$_\n"; - } - } else { - print "\n\n\n====NO ERRORS ENCOUNTERED THIS RUN====\n\n"; - } my $starttime = ctime($^T); chomp $starttime; @@ -696,15 +771,12 @@ sub ShowSummary { } print <<"EOTXT"; -SVN rev range : $gCfg{firstrev} - $gCfg{lastrev} Started at : $starttime Ended at : $endtime Elapsed time : $elapsed (H:M:S) EOTXT - &CloseFile('LOG'); - } # End ShowSummary ############################################################################### @@ -771,7 +843,6 @@ sub ThrowWarning { $msg .= "\nat $callinfo->[1] line $callinfo->[2]"; warn "ERROR -- $msg\n"; - print "ERROR -- $msg\n" if $gCfg{log}; push @gErr, $msg; @@ -796,37 +867,10 @@ sub StopConversion { } # End StopConversion ############################################################################### -# OpenFile -############################################################################### -sub OpenFile { - my($fhname, $target) = @_; - - (my $name = $target) =~ s/^>//; - - print "\nOPENING FILE $name\n" if $gCfg{verbose}; - - open $gFh{$fhname}, $target - or &ThrowError("Could not open file $name"); - -} # End OpenFile - -############################################################################### -# CloseFile -############################################################################### -sub CloseFile { - my($fhname) = @_; - - close $gFh{$fhname}; - delete $gFh{$fhname}; - -} # End CloseFile - -############################################################################### # CloseAllFiles ############################################################################### sub CloseAllFiles { - map { &CloseFile($_) } values %gFh; - close LOG; + } # End CloseAllFiles ############################################################################### @@ -987,6 +1031,7 @@ CREATE TABLE itemtype INTEGER, timestamp INTEGER, author VARCHAR, + is_binary INTEGER, info VARCHAR, priority INTEGER, sortkey VARCHAR, @@ -1031,8 +1076,9 @@ CREATE TABLE physname VARCHAR, version INTEGER, action VARCHAR, - itempath VARCHAR, + itempaths VARCHAR, itemtype INTEGER, + is_binary INTEGER, info VARCHAR ) EOSQL @@ -1144,6 +1190,10 @@ sub Initialize { $gCfg{svncomment} = "$gCfg{tempdir}\\svncomment.tmp.txt"; mkdir $gCfg{tempdir} unless (-d $gCfg{tempdir}); + # Directories for holding VSS revisions + $gCfg{vssdata} = "$gCfg{tempdir}\\vssdata"; + mkdir $gCfg{vssdata} unless (-d $gCfg{vssdata}); + if ($gCfg{resume} && !-e $gCfg{sqlitedb}) { warn "WARNING: --resume set but no database exists; starting new " . "conversion..."; @@ -1200,9 +1250,6 @@ OPTIONAL PARAMETERS: --ssphys : Full path to ssphys.exe program; uses PATH otherwise --tempdir : Temp directory to use during conversion; default is .\\_vss2svn - --setsvndate : Set svn:date property to original VSS checkin date - (see SVN:DATE WARNING in readme.txt) - --log : Log all output to \\vss2svn.log.txt --debug : Print lots of debugging info. EOTXT -- 2.11.4.GIT