Hello Dirk,

I noticed that the RESTORE action had made the MOVE into the pin_handler. I suspect there is a problem in that implementation, since all orphaned files and folders end up in the same folder. At least my load operation broke in svnadmin. If there are two files with identical names in two restored folders, their history will get mixed up, and after the first file is MOVED into position, the second MOVE will break (as there is no longer a file in svn in that folder with that name).

Completely unrelated question: how does SourceSafe handle a repository in a shared folder when two accessing workstations have misaligned clocks? If A's clock is an hour ahead of B's and A commits before B. Is the history correct as commited, or are the entries sorted, or is the error detected before the commit?

Best regards,
/Micke

Dirk wrote:
Hello Dukhat,

thanks for your help in getting the converter into a usable state. I havn't looked in detail into your patch, but it seems, that you added support for orphaned files, since these files can be a source for shares and come to live again, when they are shared into a different project.

You also mapped the RESTORE action to MOVE, which is a quite nice idea, when you have previously added the found items into an orphaned directory.

I have also heavily modified the current 0.10 beta2 in the pin_handler branch. Beneath the orphaned item tracking (only tracking, including the item into the conversion process is fairly simple but not yet done) and it implements also LABEL and correct PIN handling. And thanks to Toby's SanityChecker we also have a possibility to fix possible load errors while creating the dumpfile. The only action missing currently is the RESTORE handling. But I think, that with the "SanityChecker" this issue could be solved also.

I will have a deeper look into your patch later.

Best regards
Dirk

Dukhat schrieb:

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

_______________________________________________
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

_______________________________________________
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

Reply via email to