Greetings,
I tried to use the vss2svn 0.10 Beta 2 release to convert an old
repository. Didn't work. Among other things it contained 6 restore
points from older repositories...
Ah well, I couldn't get anything to compile on Windows so I zipped up
the repository and began working on a 450MHz Fedora Core 4 box. After
a week or two of learning Perl and hacking around, I finally got
something produced that actually could be fed to svnadmin load without
errors.
I've attached a patch in case someone else finds it useful.
Cheers,
/Micke
By the way, there seems to be a problem in the build process: On Windows
it looks for an output executable SSPHYS and on linux it searches for
SSPHYS.EXE, neither seem to be appropriate.
------------------------------------------------------------------------
Index: vss2svn.pl
===================================================================
--- vss2svn.pl (revision 223)
+++ vss2svn.pl (working copy)
@@ -23,6 +23,10 @@
our $VERSION = '0.10';
+our $ORPHAN_FOLDER = 0;
+our $ORPHAN_FOLDER_TIMESTAMP = 0;
+our %ORPHANS;
+
&Initialize;
&ConnectDatabase;
@@ -198,9 +202,184 @@
$cache->commit();
+ my $cache2 = $cache->MakeContinuation();
+ AddOrphanAdds($cache2);
+ if ($ORPHAN_FOLDER) {
+ $cache2->commit();
+ $cache2 = $cache2->MakeContinuation();
+ AddOrphanBranches($cache2);
+ $cache2->commit();
+ }
+
} # End GetPhysVssHistory
###############################################################################
+# AddOrphanAdds
+###############################################################################
+sub AddOrphanAdds {
+ my($cache) = @_;
+
+ my @cmds = ( 'ADD' ); #, 'BRANCH' );
+
+ foreach my $cmd (@cmds) {
+ print "Adding extra $cmd entries for orphan objects...\n";
+
+ my $sql1 = "SELECT * FROM Physical";
+ my $sth1 = $gCfg{dbh}->prepare($sql1);
+ $sth1->execute();
+ my $rows = $sth1->fetchall_arrayref( {} );
+
+NID:
+ foreach my $row1 (@$rows) {
+# while (defined(my $row1 = $sth1->fetchrow_hashref() )) {
+ my $physname = $row1->{physname};
+ next NID if (uc($physname) eq 'AAAAAAAA');
+ my $sql2 = <<"EOSQL";
+SELECT
+ *
+FROM
+ PhysicalAction
+WHERE
+ physname = ?
+ AND actiontype = ?
+EOSQL
+
+ my $sth2 = $gCfg{dbh}->prepare($sql2);
+ $sth2->execute($physname, $cmd);
+
+ my $parent_found = 0;
+ my $any_row = 0;
+ my $itemname;
+ my $itemtype;
+ my $timestamp;
+ my $author;
+ my $is_binary;
+ my $info;
+ my $priority;
+ my $sortkey;
+ my $comment;
+
+ROW:
+ while (defined(my $row2 = $sth2->fetchrow_hashref() )) {
+# print "Testing $row2->{physname}\n" if $gCfg{verbose};
+ if (!$any_row) {
+ $itemname = $row2->{itemname};
+ $itemtype = $row2->{itemtype};
+ $timestamp = $row2->{timestamp};
+ $author = $row2->{author};
+ $is_binary = $row2->{is_binary};
+ $info = $row2->{info};
+ $priority = $row2->{priority};
+ $sortkey = $row2->{sortkey};
+ $comment = $row2->{comment};
+ $any_row = 1;
+ }
+ next ROW if (!defined($row2->{parentphys}));
+ $parent_found = 1;
+ last ROW;
+ }
+ if ($any_row && !$parent_found) {
+ # Project dangling in mid-air. Create snake-oil folders...
+ if (!$ORPHAN_FOLDER) {
+ $ORPHAN_FOLDER = 'AAAAAAAZ';
+ } else {
+ if (!$ORPHAN_FOLDER_TIMESTAMP) {
+ $ORPHAN_FOLDER_TIMESTAMP = $timestamp;
+ }
+ if ($timestamp < $ORPHAN_FOLDER_TIMESTAMP) {
+ $ORPHAN_FOLDER_TIMESTAMP = $timestamp;
+ }
+ }
+ print "Creating orphan $cmd for $physname\n" if $gCfg{verbose};
+ my $snakeoil = uc($physname);
+ $snakeoil =~ s/A$/Z/;
+ if (!exists $ORPHANS{$physname}) {
+ $ORPHANS{$physname} = $snakeoil;
+ my $sortkey2 = reverse($snakeoil);
+ $cache->add($snakeoil, undef, $ORPHAN_FOLDER, 'ADD',
+ $physname.'/', 1, $timestamp, 'Admin',
+ 0, undef, 0, $sortkey2,
+ 0, $comment);
+ }
+ $cache->add($physname, undef, $snakeoil,
+ $cmd, $itemname,
+ $itemtype, $timestamp,
+ $author, $is_binary,
+ $info, $priority,
+ $sortkey, 1, $comment);
+ }
+ }
+ }
+
+ if ($ORPHAN_FOLDER) {
+ # Projects dangling in mid-air. Create snake-oil root folder...
+ my $skey = reverse($ORPHAN_FOLDER);
+ $cache->add($ORPHAN_FOLDER, undef, 'AAAAAAAA', 'ADD',
+ 'vss2svn_orphans/', 1, $ORPHAN_FOLDER_TIMESTAMP, 'Admin',
+ 0, undef, 0, $skey,
+ 0, "Folder created for all vss2svn orphans");
+ }
+
+} # End AddOrphanAdds
+
+###############################################################################
+# AddOrphanBranches
+###############################################################################
+sub AddOrphanBranches {
+ my($cache) = @_;
+
+ print "Adding extra BRANCH entries for branched orphan objects...\n";
+
+
+ my %items;
+ my %added = %ORPHANS;
+
+ while (%added) {
+ %items = %added;
+ %added = ();
+ my($iid, $did);
+ while (($iid, $did) = each %items) {
+ my($sth, $row);
+ my $sql = <<"EOSQL";
+SELECT
+ *
+FROM
+ PhysicalAction
+WHERE
+ info = ?
+ AND parentdata = 0
+ AND actiontype = 'BRANCH'
+ AND parentphys IS NULL
+EOSQL
+
+ $sth = $gCfg{dbh}->prepare($sql);
+ $sth->execute($iid);
+ROW:
+ while (defined($row = $sth->fetchrow_hashref() )) {
+ next ROW if (defined($row->{parentphys}));
+ # Found a branch parent record for an orphan item (without
+ # a proper parent).
+ print "Creating orphan BRANCH parent for $iid\n" if
$gCfg{verbose};
+ my $pn = $row->{physname};
+ $cache->add($pn, undef, $did,
+ 'BRANCH', $row->{itemname},
+ $row->{itemtype}, $row->{timestamp},
+ $row->{author}, $row->{is_binary}, $row->{info},
+ $row->{priority}, $row->{sortkey}, 1,
+ $row->{comment});
+ # This branch was done on an orphan item. This item must
+ # also be an orphaned item in the same locaion
+ if (!exists $ORPHANS{$pn}) {
+ $ORPHANS{$pn} = $did;
+ $added{$pn} = $did;
+ }
+ }
+ }
+ }
+
+} # End AddOrphanBranches
+
+###############################################################################
# FindPhysnameFile
###############################################################################
sub FindPhysnameFile {
@@ -322,7 +501,7 @@
$is_binary = 0;
$info = undef;
$parentdata = 0;
- $priority = 5;
+ $priority = 6;
if ($version->{Comment} && !ref($version->{Comment})) {
$comment = $version->{Comment} || undef;
@@ -372,6 +551,12 @@
$is_binary = 1;
}
+ if ($actiontype eq 'RESTORE') {
+ # Restored project...
+ print "Restore of $tphysname in $parentphys\n" if $gCfg{verbose};
+ $actiontype = 'MOVE';
+ }
+
if ($actiontype eq 'RENAME') {
# if a rename, we store the new name in the action's 'info' field
@@ -590,14 +775,13 @@
my($sth, $row, $action, $handler, $physinfo, $itempaths, $allitempaths);
my $sql = 'SELECT * FROM PhysicalAction ORDER BY timestamp ASC, '
- . 'priority ASC, sortkey ASC';
+ . 'itemtype ASC, priority ASC, sortkey ASC';
$sth = $gCfg{dbh}->prepare($sql);
$sth->execute();
ROW:
while(defined($row = $sth->fetchrow_hashref() )) {
- $svnrevs->check($row);
$action = $row->{actiontype};
$handler = Vss2Svn::ActionHandler->new($row);
@@ -646,6 +830,8 @@
}
}
+ $svnrevs->check($row);
+
# May contain add'l info for the action depending on type:
# RENAME: the new name (without path)
# SHARE: the source path which was shared
@@ -1191,6 +1377,7 @@
DeletedProject => {type => 1, action => 'DELETE'},
DestroyedProject => {type => 1, action => 'DELETE'},
RecoveredProject => {type => 1, action => 'RECOVER'},
+ Restore => {type => 1, action => 'RESTORE'},
CheckedIn => {type => 2, action => 'COMMIT'},
CreatedFile => {type => 2, action => 'ADD'},
AddedFile => {type => 2, action => 'ADD'},
Index: Vss2Svn/ActionHandler.pm
===================================================================
--- Vss2Svn/ActionHandler.pm (revision 223)
+++ Vss2Svn/ActionHandler.pm (working copy)
@@ -103,6 +103,16 @@
sharedphys => [],
};
+ if (!defined $row->{itemname} || $row->{itemname} eq '') {
+ $self->{errmsg} .= "Attempt to add unnamed entry '$row->{physname}'\n";
+ return 0;
+ }
+
+ if (uc($row->{physname}) eq 'AAAAAAAA') {
+ # The root is already there in Subversion, right?
+ return 0;
+ }
+
# File was just created so no need to look for shares
$self->{itempaths} = $self->_get_current_item_paths(1);
@@ -180,9 +190,19 @@
# 'info' contains the source path
my $parentpaths = $self->_get_item_paths($row->{parentphys}, 1);
+ # Silly 'the bloody name changed' test
+ if ($physinfo->{name} ne $row->{itemname}) {
+ warn "Shared name not same: $physinfo->{name} ne $row->{itemname}\n";
+ }
+
$self->{itempaths} = [$parentpaths->[0] . $physinfo->{name}];
my $sourceinfo = $self->_get_current_item_paths(1);
+ if (!defined $physinfo->{name} || $physinfo->{name} eq '') {
+ $self->{errmsg} .= "Attempt to share unnamed entry
'$row->{physname}'\n";
+ return 0;
+ }
+
if (!defined($sourceinfo) || scalar(@$sourceinfo) == 0) {
# We can't figure out the path for the parent that this share came
from,
# so it was either destroyed or corrupted. That means that this isn't
@@ -234,11 +254,16 @@
$gPhysInfo{$physname} =
{
type => $row->{itemtype},
- name => $row->{itemname},
+ name => $oldphysinfo->{name}, # $row->{itemname},
parentphys => $row->{parentphys},
sharedphys => [],
};
+ if (!defined $row->{itemname} || $row->{itemname} eq '') {
+ $self->{errmsg} .= "Attempt to branch unnamed entry
'$row->{physname}'\n";
+ return 0;
+ }
+
$self->{itempaths} = $self->_get_current_item_paths(1);
return 1;
@@ -270,6 +295,11 @@
# we don't need to worry about any shared paths
$physinfo->{parentphys} = $row->{parentphys};
+ if (!defined $physinfo->{name} || $physinfo->{name} eq '') {
+ $self->{errmsg} .= "Attempt to move unnamed entry
'$row->{physname}'\n";
+ return 0;
+ }
+
# 'itempaths' has the original path; 'info' has the new
$self->{itempaths} = $itempaths;
$self->{info} = $self->_get_current_item_paths(1)->[0];
@@ -330,6 +360,11 @@
$physinfo->{sharedphys} = $sharedphys;
}
+ if (!defined $physinfo->{name} || $physinfo->{name} eq '') {
+ $self->{errmsg} .= "Attempt to delete unnamed entry
'$row->{physname}'\n";
+ return 0;
+ }
+
$self->{itempaths} = $itempaths;
return 1;
@@ -365,6 +400,11 @@
$physinfo->{parentphys} = $row->{parentphys};
}
+ if (!defined $physinfo->{name} || $physinfo->{name} eq '') {
+ $self->{errmsg} .= "Attempt to recover unnamed entry
'$row->{physname}'\n";
+ return 0;
+ }
+
# We only recover the path explicitly set in this row, so build the path
# ourself by taking the path of this parent and appending the name
my $parentpaths = $self->_get_item_paths($row->{parentphys}, 1);
@@ -457,9 +497,11 @@
# return just one, but sometimes one of the paths becomes corrupted so we
# take the first one we can get.
if ($mainonly && @$paths) {
+# print join(" + ", @$paths) . "\n";
return [ $paths->[0] ];
}
+# print "*" . join(" + ", @$paths) . "\n";
return $paths;
} # End _get_item_paths
Index: Vss2Svn/DataCache.pm
===================================================================
--- Vss2Svn/DataCache.pm (revision 223)
+++ Vss2Svn/DataCache.pm (working copy)
@@ -24,7 +24,7 @@
pkey => -1,
verbose => $gCfg{verbose},
fh => undef,
- file => "$gCfg{cachedir}\\datachache.$table.tmp.txt",
+ file => "$gCfg{cachedir}/datacache.$table.tmp.txt",
};
$self = bless($self, $class);
@@ -138,5 +138,26 @@
$gCfg{verbose} = $verbose;
} # End SetVerbose
+###############################################################################
+# MakeContinuation
+###############################################################################
+sub MakeContinuation {
+ my($self) = @_;
-1;
\ No newline at end of file
+ $self->{file} =
"$gCfg{cachedir}/datacache.$self->{table}.tmp-$self->{pkey}.txt";
+
+ 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";
+ return undef;
+ }
+
+ return $self;
+} # End MakeContinuation
+
+
+1;
Index: Vss2Svn/Dumpfile.pm
===================================================================
--- Vss2Svn/Dumpfile.pm (revision 223)
+++ Vss2Svn/Dumpfile.pm (working copy)
@@ -728,7 +728,7 @@
return 0;
}
- my $file = "$expdir\\$data->{physname}.$data->{version}";
+ my $file = "$expdir/$data->{physname}.$data->{version}";
if (!open EXP, "$file") {
$self->add_error("Could not open export file '$file'");
------------------------------------------------------------------------
_______________________________________________
vss2svn-users mailing list
Project homepage:
http://www.pumacode.org/projects/vss2svn/
Subscribe/Unsubscribe/Admin:
http://lists.pumacode.org/mailman/listinfo/vss2svn-users-lists.pumacode.org
Mailing list web interface (with searchable archives):
http://dir.gmane.org/gmane.comp.version-control.subversion.vss2svn.user