Changeset 248

Show
Ignore:
Timestamp:
07/31/2006 11:17:47 AM
Author:
toby
Message:

move branches/Dirk/pin_handler back to trunk

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/script/Vss2Svn/ActionHandler.pm

    r183 r248  
    1212     BRANCH     => \&_branch_handler, 
    1313     MOVE       => \&_move_handler, 
     14     RESTORE    => \&_restore_handler, 
    1415     DELETE     => \&_delete_handler, 
    1516     RECOVER    => \&_recover_handler, 
     17     PIN        => \&_pin_handler, 
     18     LABEL      => \&_label_handler, 
    1619    ); 
    1720 
    1821our(%gPhysInfo); 
     22our(%gOrphanedInfo); 
    1923 
    2024############################################################################### 
     
    2933         action => undef, 
    3034         info => undef, 
     35         version => undef, 
    3136         errmsg => '', 
    3237         itempaths => undef, 
     
    8994 
    9095    my $parentphys = $row->{parentphys}; 
    91  
     96    my ($orphaned); 
     97     
    9298    if (!defined $parentphys) { 
     99        # '_' is used as a magic marker for orphaned files 
     100        $row->{parentphys} = '_' . $row->{physname}; 
     101#        $row->{itemname} = $row->{physname} . '_' . $row->{itemname}; 
     102        $orphaned = 1; 
     103    } 
     104 
     105    # the version number could have been changed by the share handler 
     106    # or in the branch handler, this is the version we branch. 
     107    my $version = defined $row->{version} ? $row->{version} 
     108                    : $self->{version}; 
     109     
     110    # if the item to be added was destroyed, then we don't have a version 
     111    # number here. So we don't need to add the item anyway. 
     112    if (!defined $version ) { 
    93113        $self->{errmsg} .= "Attempt to add entry '$row->{physname}' with " 
    94             . "unknown parent\n"; 
    95         return 0; 
    96     } 
    97  
     114            . "unknown version number (probably destroyed)\n"; 
     115         
     116        $gOrphanedInfo {$row->{physname} } = 1; 
     117        return 0; 
     118    } 
     119     
    98120    $gPhysInfo{ $row->{physname} } = 
    99121        { 
    100122         type       => $row->{itemtype}, 
    101123         name       => $row->{itemname}, 
    102          parentphys => $row->{parentphys}, 
    103          sharedphys => [], 
     124#         parentphys => $row->{parentphys}, 
     125#         sharedphys => [], 
     126         parents    => {}, 
     127         last_version => $version, 
     128         orphaned   => $orphaned, 
    104129        }; 
    105130 
     131    $self->_add_parent ($row->{physname}, $row->{parentphys}); 
     132    $self->_track_item_paths ($version); 
     133 
    106134    # File was just created so no need to look for shares 
    107     $self->{itempaths} = $self->_get_current_item_paths(1); 
    108  
     135    $self->{itempaths} = [$self->_get_current_item_path()]; 
     136 
     137    # don't convert orphaned items 
     138#    return $orphaned ? 0 : 1; 
    109139    return 1; 
    110  
    111140}  #  End _add_handler 
    112141 
     
    118147    my $row = $self->{row}; 
    119148 
    120     $self->{itempaths} = $self->_get_current_item_paths(); 
    121  
     149    my $physname = $row->{physname}; 
     150    my $physinfo = $gPhysInfo{$physname}; 
     151 
     152    if (!defined $physinfo) { 
     153        $self->{errmsg} .= "Attempt to commit unknown item '$physname':\n" 
     154            . "$self->{physname_seen}\n"; 
     155 
     156        return 0; 
     157    } 
     158 
     159    # We need to track at least the version number, even if there is no 
     160    # active parent. This is necessary, if we later share this item, we need 
     161    # to share from the latest seen version. 
     162     
     163    # remember the last version, in which the file was modified 
     164    $physinfo->{last_version} = $row->{version}; 
     165     
     166    # and track all itempaths for the new version 
     167    $self->_track_item_paths ($row->{version}); 
     168     
     169    my $itempaths = $self->_get_active_item_paths(); 
     170    if (!defined $itempaths && defined $physinfo->{orphaned}) { 
     171        $self->{errmsg} .= "No more active itempath to commit to orphaned item '$physname':\n" 
     172            . "$self->{physname_seen}\n"; 
     173 
     174        return 0; 
     175    } 
     176         
     177    $self->{itempaths} = $itempaths; 
     178     
     179    if (!defined $self->{itempaths}) { 
     180        $self->{errmsg} .= "No more active itempath to commit to '$physname':\n" 
     181            . "$self->{physname_seen}\n"; 
     182 
     183        return 0; 
     184    } 
     185     
     186    return 1; 
    122187}  #  End _commit_handler 
    123188 
     
    129194    my $row = $self->{row}; 
    130195 
     196    my $physname = $row->{physname}; 
     197    my $physinfo = $gPhysInfo{$physname}; 
     198 
     199    if (!defined $physinfo) { 
     200        # only report an error, if the file wasn't detected as orphaned.  
     201        if (!defined $gOrphanedInfo {$physname}) { 
     202            $self->{errmsg} .= "Attempt to rename unknown item '$physname':\n" 
     203                . "$self->{physname_seen}\n"; 
     204        } 
     205         
     206        return 0; 
     207    } 
     208 
    131209    # Get the existing paths before the rename; info will contain the new name 
    132     my $physname = $row->{physname}; 
    133     my $itempaths = $self->_get_current_item_paths(); 
    134  
    135     my $physinfo = $gPhysInfo{$physname}; 
    136  
    137     if (!defined $physinfo) { 
    138         $self->{errmsg} .= "Attempt to rename unknown item '$physname':\n" 
    139             . "$self->{nameResolveSeen}\n"; 
    140  
    141         return 0; 
    142     } 
     210    my $itempaths = $self->_get_vivid_item_paths(); 
    143211 
    144212    # Renames on shares may show up once for each share, which we don't want 
     
    152220    $physinfo->{name} = $row->{info}; 
    153221 
     222    # no need to track the itempathes, since a rename doesn't create a new 
     223    # item version 
     224 
    154225    $self->{itempaths} = $itempaths; 
    155226    $self->{info} = $row->{info}; 
     
    175246    } 
    176247 
    177     push @{ $physinfo->{sharedphys} }, $row->{parentphys}; 
    178  
    179     # 'itempaths' is the path for this new location (the share target); 
    180     # 'info' contains the source path 
    181     my $parentpaths = $self->_get_item_paths($row->{parentphys}, 1); 
    182  
    183     $self->{itempaths} = [$parentpaths->[0] . $physinfo->{name}]; 
    184     my $sourceinfo = $self->_get_current_item_paths(1); 
    185  
    186     if (!defined($sourceinfo) || scalar(@$sourceinfo) == 0) { 
     248#    # if this is not a share+pin action, then add this item to the sharedphys 
     249#    # list. Otherwise, this item is pinned to a specific version and does not 
     250#    # participate in shared actions 
     251#    if (!defined $row->{version}) { 
     252#        push @{ $physinfo->{sharedphys} }, $row->{parentphys}; 
     253#    } 
     254     
     255    my $version = $row->{version}; 
     256    $version = $physinfo->{last_version} if (!defined $version); 
     257 
     258    # 'itempath' is the path for this new location (the share target); 
     259    # note: since we can share from a orphaned item, we use the itemname that 
     260    # is provided in the row information for the share target and not the 
     261    # current name of the item. The orphaned name is mangeled to make it unique 
     262    my $parentpath = $self->_get_current_parent_path (); 
     263    my $itempath = $parentpath . $row->{itemname}; 
     264 
     265    # 'sourceinfo' contains the source path 
     266    my $sourceinfo = $self->_get_valid_path ($physname, $row->{parentphys}, $version); 
     267 
     268    if (!defined($sourceinfo)) { 
    187269        # We can't figure out the path for the parent that this share came from, 
    188270        # so it was either destroyed or corrupted. That means that this isn't 
     
    190272 
    191273        $self->{action} = 'ADD'; 
    192         return $self->_add_handler(); 
    193     } 
    194  
    195     $self->{info} = $sourceinfo->[0]; 
     274#        $self->{version} = $version; 
     275#        return $self->_add_handler(); 
     276    } 
     277 
     278    # track the addition of the new parent 
     279    $self->_add_parent ($physname, $row->{parentphys}); 
     280     
     281    # if this is a share+pin action, then remember the pin version 
     282    if (defined $row->{version}) { 
     283        $physinfo->{parents}->{$row->{parentphys}}->{pinned} = $row->{version}; 
     284    }  
     285 
     286    $self->{itempaths} = [$itempath]; 
     287    $self->{info} = $sourceinfo; 
     288    $self->{version} = $version; 
     289 
     290    # the share target is now also a valid "copy from" itempath 
     291    $self->_track_item_path ($physname, $row->{parentphys}, $version, $itempath); 
     292 
    196293    return 1; 
    197  
    198294}  #  End _share_handler 
    199295 
     
    216312    my $oldphysinfo = $gPhysInfo{$oldphysname}; 
    217313 
    218     # First delete this parentphys from the old shared object; see 
    219     # _delete_handler for details 
    220     if ($oldphysinfo->{parentphys} eq $row->{parentphys}) { 
    221         $oldphysinfo->{parentphys} = shift( @{ $oldphysinfo->{sharedphys} } ); 
    222     } else { 
    223         my $sharedphys = []; 
    224  
    225         foreach my $oldparent (@{ $oldphysinfo->{sharedphys} }) { 
    226             push @$sharedphys, $oldparent 
    227                 unless $oldparent eq $row->{parentphys}; 
    228         } 
    229  
    230         $oldphysinfo->{sharedphys} = $sharedphys; 
    231     } 
    232  
    233     # Now create a new entry for this branched item 
    234     $gPhysInfo{$physname} = 
    235         { 
    236          type       => $row->{itemtype}, 
    237          name       => $row->{itemname}, 
    238          parentphys => $row->{parentphys}, 
    239          sharedphys => [], 
    240         }; 
    241  
    242     $self->{itempaths} = $self->_get_current_item_paths(1); 
    243  
    244     return 1; 
     314    if (!defined $oldphysinfo) { 
     315        $self->{errmsg} .= "Attempt to branch unknown item '$oldphysname':\n" 
     316            . "$self->{physname_seen}\n"; 
     317 
     318        return 0; 
     319    } 
     320     
     321#    # First delete this parentphys from the old shared object; see 
     322#    # _delete_handler for details 
     323#    if ($oldphysinfo->{parentphys} eq $row->{parentphys}) { 
     324#        $oldphysinfo->{parentphys} = shift( @{ $oldphysinfo->{sharedphys} } ); 
     325#    } else { 
     326#        my $sharedphys = []; 
     327
     328#        foreach my $oldparent (@{ $oldphysinfo->{sharedphys} }) { 
     329#            push @$sharedphys, $oldparent 
     330#                unless $oldparent eq $row->{parentphys}; 
     331#        } 
     332
     333#        $oldphysinfo->{sharedphys} = $sharedphys; 
     334#    } 
     335 
     336    # treat the old path as deleted 
     337    # we can't branch an item, that doesn't have a parent. This happens when the 
     338    # parent was destroyed.  
     339    if (defined $row->{parentphys}) { 
     340        $oldphysinfo->{parents}->{$row->{parentphys}}->{deleted} = 1; 
     341    } 
     342    else { 
     343        # since we have the "orphaned" handling, we can map this action to an 
     344        # addition, so that this item will show up in the orphaned cache. 
     345        # TODO: To keep the history of the item we can try to ShareBranch 
     346        # from original item if it is also somewhere accessible. 
     347        # something like: 
     348#        my $copypath = $self->_get_valid_path ($oldphysinfo, $row->{parentphys}, $row->{version}); 
     349         
     350        $self->{action} = 'ADD'; 
     351    } 
     352 
     353    # Now treat the new entry as a new addition 
     354    return $self->_add_handler(); 
     355 
     356#    # Now create a new entry for this branched item 
     357#    $gPhysInfo{$physname} = 
     358#        { 
     359#         type       => $row->{itemtype}, 
     360#         name       => $row->{itemname}, 
     361##         parentphys => $row->{parentphys}, 
     362##         sharedphys => [], 
     363#         parents    => {}, 
     364#        }; 
     365 
     366#    $self->_add_parent ($physname, $row->{parentphys}); 
     367#    $self->{itempaths} = $self->_get_current_item_paths(1); 
     368 
     369#    return 1; 
    245370 
    246371}  #  End _branch_handler 
     
    256381    # name 
    257382    my $physname = $row->{physname}; 
    258     my $itempaths = $self->_get_current_item_paths(); 
    259  
    260     my $physinfo = $gPhysInfo{$physname}; 
    261  
     383    my $physinfo = $gPhysInfo{$physname}; 
     384   
    262385    if (!defined $physinfo) { 
    263         $self->{errmsg} .= "Attempt to rename unknown item '$physname':\n" 
     386        $self->{errmsg} .= "Attempt to move unknown item '$physname':\n" 
    264387            . "$self->{physname_seen}\n"; 
    265388 
     
    267390    } 
    268391 
    269     # Only projects can have true "moves", and projects don't have shares, so 
    270     # we don't need to worry about any shared paths 
    271     $physinfo->{parentphys} = $row->{parentphys}; 
    272  
    273     # 'itempaths' has the original path; 'info' has the new 
    274     $self->{itempaths} = $itempaths; 
    275     $self->{info} = $self->_get_current_item_paths(1)->[0]; 
     392    # '$sourceinfo' is the path for the old location (the move source); 
     393    my $parentpath = $self->_get_current_parent_path (); 
     394    my $sourceinfo = $parentpath . $row->{itemname}; 
     395 
     396    # '$itempath' contains the move target path 
     397    my $itempath = $self->_get_parent_path ($row->{info}) . $row->{itemname}; 
     398 
     399    if (!defined($sourceinfo)) { 
     400        # We can't figure out the path for the parent that this move came from, 
     401        # so it was either destroyed or corrupted. That means that this isn't 
     402        # a move anymore; it's a new add. 
     403 
     404        $self->{action} = 'ADD'; 
     405#        $self->{version} = $version; 
     406#        return $self->_add_handler(); 
     407         
     408        # we need to swap the source and the target path 
     409        $sourceinfo = $itempath; 
     410        undef $itempath; 
     411    } 
     412    else { 
     413        # set the old parent inactive 
     414        $physinfo->{parents}->{$row->{parentphys}}->{deleted} = 1; 
     415    } 
     416 
     417    # track the addition of the new parent 
     418    $self->_add_parent ($physname, $row->{info}); 
     419     
     420    $self->{itempaths} = [$sourceinfo]; 
     421    $self->{info} = $itempath; 
     422 
     423    # the move target is now also a valid "copy from" itempath 
     424    $self->_track_item_path ($physname, $row->{parentphys}, $physinfo->{last_version}, $itempath); 
    276425 
    277426    return 1; 
    278  
    279427}  #  End _move_handler 
    280428 
    281429############################################################################### 
     430#  _restore_handler 
     431############################################################################### 
     432sub _restore_handler { 
     433    my($self) = @_; 
     434    my $row = $self->{row}; 
     435     
     436    $self->{action} = 'MOVE'; 
     437    $row->{actiontype} = 'MOVE'; 
     438    $row->{info} = $row->{parentphys}; 
     439    $row->{parentphys} = '_' . $row->{physname}; 
     440    return $self->_move_handler (); 
     441} 
     442 
     443############################################################################### 
    282444#  _delete_handler 
    283445############################################################################### 
     
    286448    my $row = $self->{row}; 
    287449 
    288     # For a delete operation we return only the "main" path, since any deletion 
    289     # of shared paths will have their own entry: 
    290     # WRONG: we need to return the path of the item to be deleted, and not the 
    291     # parent path 
     450    # For a delete operation we return the path of the item to be deleted 
    292451 
    293452    my $physname = $row->{physname}; 
    294  
    295453    my $physinfo = $gPhysInfo{$physname}; 
    296454 
    297455    if (!defined $physinfo) { 
    298         $self->{errmsg} .=  "Attempt to delete unknown item '$physname':\n" 
    299             . "$self->{physname_seen}\n"; 
    300         return 0; 
    301     } 
     456        # only report an error, if the file wasn't detected as orphaned.  
     457        if (!defined $gOrphanedInfo {$physname}) { 
     458            $self->{errmsg} .=  "Attempt to delete unknown item '$physname':\n" 
     459                . "$self->{physname_seen}\n"; 
     460        } 
     461        return 0; 
     462    } 
     463 
     464    my $parentpath = $self->_get_current_parent_path (); 
     465    my $itempaths = [$parentpath . $physinfo->{name}]; 
     466 
     467#    if ($physinfo->{parentphys} eq $row->{parentphys}) { 
     468#        # Deleting from the "main" parent; find a new one by shifting off the 
     469#        # first shared path, if any; if none exists this will leave a null 
     470#        # parent entry. We could probably just delete the whole node at this 
     471#        # point. 
     472
     473#        $physinfo->{parentphys} = shift( @{ $physinfo->{sharedphys} } ); 
     474
     475#    } else { 
     476#        my $sharedphys = []; 
     477
     478#        foreach my $parent (@{ $physinfo->{sharedphys} }) { 
     479#            push @$sharedphys, $parent 
     480#                unless $parent eq $row->{parentphys}; 
     481#        } 
     482
     483#        $physinfo->{sharedphys} = $sharedphys; 
     484#    } 
    302485 
    303486    # protect for delete/purge cycles: if the parentphys isn't in the shares 
    304487    # anymore, the file was already deleted from the parent and is now purged 
    305     my $parentFound = defined $physinfo->{parentphys}; 
    306     foreach my $parent (@{ $physinfo->{sharedphys} }) { 
    307         $parentFound = 1 if ($physinfo->{parentphys} eq $parent); 
    308     } 
    309     return 0 unless $parentFound; 
    310  
    311     my $parentpaths = $self->_get_item_paths($row->{parentphys}, 1); 
    312     my $itempaths = [$parentpaths->[0] . $physinfo->{name}]; 
    313  
    314     if ($physinfo->{parentphys} eq $row->{parentphys}) { 
    315         # Deleting from the "main" parent; find a new one by shifting off the 
    316         # first shared path, if any; if none exists this will leave a null 
    317         # parent entry. We could probably just delete the whole node at this 
    318         # point. 
    319  
    320         $physinfo->{parentphys} = shift( @{ $physinfo->{sharedphys} } ); 
    321  
    322     } else { 
    323         my $sharedphys = []; 
    324  
    325         foreach my $parent (@{ $physinfo->{sharedphys} }) { 
    326             push @$sharedphys, $parent 
    327                 unless $parent eq $row->{parentphys}; 
     488    if (defined $physinfo->{parents}->{$row->{parentphys}}->{deleted}) { 
     489        return 0; 
     490    } 
     491 
     492    # set the parent inactive 
     493    $physinfo->{parents}->{$row->{parentphys}}->{deleted} = 1; 
     494 
     495    $self->{itempaths} = $itempaths; 
     496 
     497    return 1; 
     498 
     499}  #  End _delete_handler 
     500 
     501############################################################################### 
     502#  _recover_handler 
     503############################################################################### 
     504sub _recover_handler { 
     505    my($self) = @_; 
     506    my $row = $self->{row}; 
     507 
     508    my $physname = $row->{physname}; 
     509    my $physinfo = $gPhysInfo{$physname}; 
     510 
     511    if (!defined $physinfo) { 
     512        # only report an error, if the file wasn't detected as orphaned.  
     513        if (!defined $gOrphanedInfo {$physname}) { 
     514            $self->{errmsg} .= "Attempt to recover unknown item '$physname':\n" 
     515                . "$self->{physname_seen}\n"; 
    328516        } 
    329  
    330         $physinfo->{sharedphys} = $sharedphys; 
    331     } 
    332  
    333     $self->{itempaths} = $itempaths; 
    334  
    335     return 1; 
    336  
    337 }  #  End _delete_handler 
    338  
    339 ############################################################################### 
    340 #  _recover_handler 
    341 ############################################################################### 
    342 sub _recover_handler { 
    343     my($self) = @_; 
    344     my $row = $self->{row}; 
    345  
    346     my $physname = $row->{physname}; 
    347  
    348     my $physinfo = $gPhysInfo{$physname}; 
    349  
    350     if (!defined $physinfo) { 
    351         $self->{errmsg} .= "Attempt to recover unknown item '$physname':\n" 
     517         
     518        return 0; 
     519    } 
     520 
     521#    if (defined $physinfo->{parentphys}) { 
     522#        # Item still has other shares, so recover it by pushing this parent 
     523#        # onto its shared list 
     524
     525#        push( @{ $physinfo->{sharedphys} }, $row->{parentphys} ); 
     526
     527#    } else { 
     528#        # Recovering its only location; set the main parent back to this 
     529#        $physinfo->{parentphys} = $row->{parentphys}; 
     530#    } 
     531 
     532    # recover this item within the current parent 
     533    my $parentinfo = $physinfo->{parents}->{$row->{parentphys}}; 
     534    if (undef $parentinfo->{deleted}) { 
     535        $self->{errmsg} .= "Attempt to recover an active item '$physname':\n" 
    352536            . "$self->{physname_seen}\n"; 
    353537 
    354538        return 0; 
    355539    } 
    356  
    357     if (defined $physinfo->{parentphys}) { 
    358         # Item still has other shares, so recover it by pushing this parent 
    359         # onto its shared list 
    360  
    361         push( @{ $physinfo->{sharedphys} }, $row->{parentphys} ); 
    362  
    363     } else { 
    364         # Recovering its only location; set the main parent back to this 
    365         $physinfo->{parentphys} = $row->{parentphys}; 
    366     } 
    367  
     540    undef $parentinfo->{deleted}; 
     541     
    368542    # We only recover the path explicitly set in this row, so build the path 
    369543    # ourself by taking the path of this parent and appending the name 
    370     my $parentpaths = $self->_get_item_paths($row->{parentphys}, 1); 
    371     $self->{itempaths} = [$parentpaths->[0] . $physinfo->{name}]; 
     544    my $parentpath = $self->_get_current_parent_path(); 
     545    my $itempath = $parentpath . $physinfo->{name}; 
     546 
     547    # Since the item could be modified between the delete and the recovery, 
     548    # we need to find a valid source for the recover 
     549    $self->{info} = 
     550        $self->_get_valid_path ($physname, $row->{parentphys}, $row->{version}); 
     551    $self->{itempaths} = [$itempath]; 
     552     
     553    # We only set the version number, if this item is a file item. If it is a 
     554    # project item, we must recover from the last known revision, which is 
     555    # determined in the dumpfile handler 
     556    if ($row->{itemtype} == 2) { 
     557        $self->{version} = $physinfo->{last_version}; 
     558    } 
    372559 
    373560    return 1; 
    374  
    375561}  #  End _recover_handler 
    376562 
    377  
    378 ############################################################################### 
    379 #  _get_current_item_paths 
    380 ############################################################################### 
    381 sub _get_current_item_paths { 
    382     my($self, $mainonly) = @_; 
    383  
    384     return $self->_get_item_paths($self->{row}->{physname}, $mainonly); 
    385 }  #  End _get_current_item_paths 
    386  
    387 ############################################################################### 
    388 #  _get_item_paths 
    389 ############################################################################### 
    390 sub _get_item_paths { 
    391     my($self, $physname, $mainonly) = @_; 
     563############################################################################### 
     564#  _pin_handler 
     565############################################################################### 
     566sub _pin_handler { 
     567    my($self) = @_; 
     568    my $row = $self->{row}; 
     569 
     570    my $physname = $row->{physname}; 
     571    my $physinfo = $gPhysInfo{$physname}; 
     572 
     573    if (!defined $physinfo) { 
     574        $self->{errmsg} .= "Attempt to pin unknown item '$physname':\n" 
     575            . "$self->{physname_seen}\n"; 
     576 
     577        return 0; 
     578    } 
     579 
     580    my $parentpath = $self->_get_current_parent_path(); 
     581    my $itempath = $parentpath . $physinfo->{name}; 
     582 
     583    my $parentinfo = \%{$physinfo->{parents}->{$row->{parentphys}}}; 
     584 
     585    my $version = $row->{version}; 
     586    if (!defined $row->{version}) { 
     587        # this is the unpin handler 
     588        undef $parentinfo->{pinned}; 
     589        $version = $physinfo->{last_version}; 
     590    } 
     591    else { 
     592        $parentinfo->{pinned} = $row->{version}; 
     593    } 
     594     
     595    $self->{itempaths} = [$itempath]; 
     596    $self->{info} = 
     597        $self->_get_valid_path ($physname, $row->{parentphys}, $row->{version}); 
     598    $self->{version} = $version; 
     599     
     600    # the unpinned target is now also a valid "copy from" itempath 
     601    $self->_track_item_path ($physname, $row->{parentphys}, $version, $itempath); 
     602 
     603    return 1; 
     604}  #  End _pin_handler 
     605 
     606############################################################################### 
     607#  _label_handler 
     608############################################################################### 
     609sub _label_handler { 
     610    # currently the handler only tracks labels that where assigned to files 
     611    # we need this for the item name tracking 
     612    my($self) = @_; 
     613    my $row = $self->{row}; 
     614 
     615    my $itempaths = $self->_get_active_item_paths(); 
     616 
     617    $self->_track_item_paths ($row->{version}); 
     618     
     619    $self->{itempaths} = $itempaths; 
     620    $self->{info} = $row->{label}; 
     621     
     622    return 1; 
     623}  #  End _label_handler 
     624 
     625############################################################################### 
     626#  _get_current_parent_path 
     627############################################################################### 
     628sub _get_current_parent_path { 
     629    my($self) = @_; 
     630 
     631    return $self->_get_parent_path($self->{row}->{parentphys}); 
     632}  #  End _get_current_parent_path 
     633 
     634 
     635############################################################################### 
     636#  _get_parent_path 
     637############################################################################### 
     638sub _get_parent_path { 
     639    my($self, $physname) = @_; 
    392640 
    393641    # Uses recursion to determine the current full paths for an item based on 
     
    417665    } 
    418666 
     667    if ($physname eq '') { 
     668        return ''; 
     669    } 
     670     
     671    if ($physname eq 'AAAAAAAA') { 
     672        # End of recursion; all items must go back to 'AAAAAAAA', which was so 
     673        # named because that's what most VSS users yell after using it much. :-) 
     674        return '/'; 
     675    } 
     676 
     677    if ($physname =~ m/^_.*/) { 
     678        # End of recursion; this is the orphaned node 
     679        # return the name of the orphaned directory + the name of the orphaned 
     680        # file in order to make the path unique 
     681        return '/orphaned/' . $physname . '/'; 
     682    } 
     683 
     684    my $physinfo = $gPhysInfo{$physname}; 
     685 
     686    if (!defined $physinfo) { 
     687        $self->{errmsg} .= "Could not determine real path for '$physname':\n" 
     688            . "$self->{physname_seen}\n"; 
     689 
     690        return undef; 
     691    } 
     692 
     693    #todo: make the behavoir of orphaned file tracking configurable 
     694#    if ($physinfo->{orphaned}) { 
     695#        return undef; 
     696#    } 
     697 
     698    $self->{physname_seen} .= "$physname, "; 
     699 
     700    # In a move szenario, we can have one deleted and one active parent. We 
     701    # are only interested in the active ones here. 
     702    my @pathstoget = $self->_get_active_parents ($physname); 
     703 
     704    # TODO: For projects there should be only one active parent 
     705    my $parent = $pathstoget[0]; 
     706     
     707    # if we don't have any active parents, the item path itself is deleted 
     708    if (!defined ($parent)) { 
     709        return undef; 
     710    } 
     711 
     712    my $result; 
     713 
     714    $result = $self->_get_parent_path($pathstoget[0], 1); 
     715 
     716    if(!defined($result)) { 
     717        return undef; 
     718    } 
     719 
     720    return $result . $physinfo->{name}; 
     721 
     722}  #  End _get_parent_path 
     723 
     724############################################################################### 
     725#  _get_current_item_paths 
     726############################################################################### 
     727sub _get_current_item_paths { 
     728    my($self, $mainonly) = @_; 
     729     
     730    my @parents = $self->_get_parents ($self->{row}->{physname}); 
     731    return $self->_get_item_paths($self->{row}->{physname}, @parents); 
     732}  #  End _get_current_item_paths 
     733 
     734############################################################################### 
     735#  _get_vivid_item_paths 
     736############################################################################### 
     737sub _get_vivid_item_paths { 
     738    my($self, $mainonly) = @_; 
     739     
     740    my @parents = $self->_get_vivid_parents ($self->{row}->{physname}); 
     741    return $self->_get_item_paths($self->{row}->{physname}, @parents); 
     742}  #  End _get_vivid_item_paths 
     743 
     744############################################################################### 
     745#  _get_active_item_paths 
     746############################################################################### 
     747sub _get_active_item_paths { 
     748    my($self, $mainonly) = @_; 
     749     
     750    my @parents = $self->_get_active_parents ($self->{row}->{physname}); 
     751    return $self->_get_item_paths($self->{row}->{physname}, @parents); 
     752}  #  End _get_active_item_paths 
     753 
     754############################################################################### 
     755#  _get_current_item_path 
     756############################################################################### 
     757sub _get_current_item_path { 
     758    my($self) = @_; 
     759 
     760    my @parents = $self->_get_parents ($self->{row}->{physname}); 
     761     
     762    if (scalar @parents == 0) { 
     763        return undef; 
     764    } 
     765     
     766    my $physname = $self->{row}->{physname}; 
     767    my $paths = $self->_get_item_paths($physname, $parents[0]); 
     768     
     769    if (!defined $paths) { 
     770        $self->{errmsg} .=  "Could not retrieve item path for '$physname': " 
     771            . "(probably bogous timestamp in parent and child action)\n"; 
     772        return undef; 
     773    } 
     774     
     775    return $paths->[0]; 
     776}  #  End _get_current_item_path 
     777 
     778############################################################################### 
     779#  _get_item_paths 
     780############################################################################### 
     781sub _get_item_paths { 
     782    my($self, $physname, @parents) = @_; 
     783 
     784    # Uses recursion to determine the current full paths for an item based on 
     785    # the name of its physical file. We can't cache this information because 
     786    # a rename in a parent folder would not immediately trigger a rename in 
     787    # all of the child items. 
     788 
     789    # By default, we return an anonymous array of all paths in which the item 
     790    # is shared, unless $mainonly is true. Luckily, only files can be shared, 
     791    # not projects, so once we start recursing we can set $mainonly to true. 
     792 
     793    if ($self->{verbose}) { 
     794        my $physprint = (defined $physname)? $physname : '!UNDEF'; 
     795        my $space = ($self->{recursed})? '   ' : ''; 
     796        print "${space}_get_item_paths($physprint)\n"; 
     797    } 
     798 
     799 
     800    if (!defined($physname)) { 
     801        return undef; 
     802    } 
     803 
    419804    if ($physname eq 'AAAAAAAA') { 
    420805        # End of recursion; all items must go back to 'AAAAAAAA', which was so 
     
    423808    } 
    424809 
     810    if ($physname =~ m/^_.*/) { 
     811        # End of recursion; this is the orphaned node 
     812        # return the name of the orphaned directory + the name of the orphaned 
     813        # file in order to make the path unique 
     814        return '/orphaned/' . $physname . '/'; 
     815    } 
     816 
    425817    my $physinfo = $gPhysInfo{$physname}; 
    426818 
     
    432824    } 
    433825 
     826    #todo: make the behavoir of orphaned file tracking configurable 
     827#    if ($physinfo->{orphaned}) 
     828#    { 
     829#        return undef; 
     830#       } 
     831 
    434832    $self->{physname_seen} .= "$physname, "; 
    435833 
    436     my @pathstoget = 
    437         ($physinfo->{parentphys}, @{ $physinfo->{sharedphys} } ); 
    438  
    439     my $paths = []; 
     834#    my @pathstoget = 
     835#        ($physinfo->{parentphys}, @{ $physinfo->{sharedphys} } ); 
     836    my @pathstoget = @parents; 
     837     
     838    my $paths; 
    440839    my $result; 
     840#    if (defined $physinfo->{parents}->{$row->{parentphys}}->{deleted}) { 
     841#        return 0; 
     842#    } 
    441843 
    442844PARENT: 
     
    445847            next PARENT; 
    446848        } 
    447         $result = $self->_get_item_paths($parent, 1); 
    448  
    449         if(!defined($result) || scalar(@$result) == 0) { 
     849        $result = $self->_get_parent_path($parent); 
     850 
     851        if(!defined($result)) { 
    450852            next PARENT; 
    451853        } 
    452854 
    453         push @$paths, $result->[0] . $physinfo->{name}; 
    454     } 
    455  
    456     # It may seem unnecessary to get all the paths if we knew we were going to 
    457     # return just one, but sometimes one of the paths becomes corrupted so we 
    458     # take the first one we can get. 
    459     if ($mainonly && @$paths) { 
    460         return [ $paths->[0] ]; 
     855        push @$paths, $result . $physinfo->{name}; 
    461856    } 
    462857 
     
    467862 
    468863 
     864############################################################################### 
     865#  _track_item_paths: 
     866# This function maintains a map that records the itempath that was valid for 
     867# each version of the physical file in the context of the different parents. 
     868# This map is needed, e.g. during pinning when a file is pinned to a previous 
     869# version. Since the file, could have renamed in between, we need to know the 
     870# previous itempath that was valid in the previous version. 
     871# 
     872# This map does not replace the recursive lookup of the itempath in teh function 
     873# _get_item_paths. The itempathes stored here are "historic" item pathes. 
     874# A rename e.g. is not reflectected in the version history of the physical file 
     875# and therefor does not have a distinct version as in subversion.  
     876############################################################################### 
     877sub _track_item_paths { 
     878    my($self, $version) = @_; 
     879 
     880    my $row = $self->{row}; 
     881     
     882    # we only need to track the path for actions that deal with a specific 
     883    # version 
     884    if (defined $version) { 
     885 
     886        my $physinfo = $gPhysInfo{ $row->{physname} }; 
     887         
     888        my @parents = $self->_get_active_parents ($row->{physname}); 
     889        my $result; 
     890 
     891PARENT: 
     892        foreach my $parent (@parents) { 
     893 
     894            my $parentpath = $self->_get_parent_path ($parent); 
     895            if (!defined $parentpath) { 
     896                next PARENT; 
     897            } 
     898            $result = $parentpath . $physinfo->{name}; 
     899 
     900            $self->_track_item_path ($row->{physname}, $parent, $row->{version}, $result); 
     901 
     902#            my $versions = \@{$physinfo->{parents}->{$parent}->{versions}}; 
     903#             
     904#            # in the case of pinning and sharing with pinning, the version number 
     905#            # denotes a version in the past. So if there is already an entry for 
     906#            # this version number skip this parent. 
     907#            if (exists $versions->[$row->{version}]) { 
     908#                next PARENT; 
     909#            } 
     910             
     911#            # remember the last version, in which the file was modified 
     912#            $physinfo->{last_version} = $row->{version}; 
     913             
     914#            $result = $self->_get_parent_path ($parent) . $physinfo->{name}; 
     915     
     916#            if(!defined($result)) { 
     917#                next PARENT; 
     918#            } 
     919     
     920#            $versions->[$row->{version}] = $result; 
     921        } 
     922    } 
     923}  #  End _track_item_paths 
     924 
     925 
     926############################################################################### 
     927#  _track_item_path: 
     928############################################################################### 
     929sub _track_item_path { 
     930    my($self, $physname, $parent, $version, $itempath) = @_; 
     931 
     932    if (defined $version && defined $itempath) { 
     933 
     934        my $physinfo = $gPhysInfo{ $physname }; 
     935         
     936        my $versions = \@{$physinfo->{parents}->{$parent}->{versions}}; 
     937         
     938        # in the case of pinning and sharing with pinning, the version number 
     939        # denotes a version in the past. So if there is already an entry for 
     940        # this version number skip this parent. 
     941        if (exists $versions->[$version]) { 
     942            return; 
     943        } 
     944 
     945        $versions->[$version] = $itempath; 
     946    } 
     947}  #  End _track_item_path 
     948 
     949 
     950############################################################################### 
     951#  _get_vivid_parents 
     952# This function returns all parents where the physical file is not deleted, 
     953# r all active projects. If a file is deleted, the file 
     954# does nor take place in any further rename activity, so it is 
     955# inactive. 
     956############################################################################### 
     957sub _get_vivid_parents { 
     958    my($self, $physname) = @_; 
     959 
     960    my $physinfo = $gPhysInfo{$physname}; 
     961 
     962    my @parents; 
     963    if (defined $physinfo) { 
     964 
     965PARENT: 
     966        foreach my $parentphys (@{$physinfo->{order}}) { 
     967 
     968            # skip orphaned parents 
     969#            if ($parentphys eq '99999999' ) { 
     970#                next PARENT; 
     971#            } 
     972 
     973            my $parent = $physinfo->{parents}->{$parentphys}; 
     974            if (!defined $parent) 
     975            { 
     976                next PARENT; 
     977            } 
     978             
     979            # skip deleted parents, since these parents do not 
     980            # participate in specific vss action 
     981            if (defined $parent->{deleted} ) { 
     982                next PARENT; 
     983            } 
     984 
     985            push @parents, $parentphys; 
     986        } 
     987    } 
     988     
     989    return @parents 
     990}  # End _get_vivid_parents 
     991 
     992############################################################################### 
     993#  _get_active_parents 
     994# This function returns all parents where the physical file is not deleted 
     995# or pinned, or all active projects. If a file is pinned or deleted, the file 
     996# does nor take place in any further checkin or rename activity, so it is 
     997# inactive. 
     998############################################################################### 
     999sub _get_active_parents { 
     1000    my($self, $physname) = @_; 
     1001 
     1002    my $physinfo = $gPhysInfo{$physname}; 
     1003 
     1004    my @parents; 
     1005    if (defined $physinfo) { 
     1006 
     1007PARENT: 
     1008        foreach my $parentphys (@{$physinfo->{order}}) { 
     1009 
     1010            # skip orphaned parents 
     1011#            if ($parentphys eq '99999999' ) { 
     1012#                next PARENT; 
     1013#            } 
     1014 
     1015            my $parent = $physinfo->{parents}->{$parentphys}; 
     1016            if (!defined $parent) 
     1017            { 
     1018                next PARENT; 
     1019            } 
     1020             
     1021            # skip deleted or pinned parents, since these parents do not 
     1022            # participate in any vss action 
     1023            if (defined $parent->{deleted} || defined $parent->{pinned} ) { 
     1024                next PARENT; 
     1025            } 
     1026 
     1027            push @parents, $parentphys; 
     1028        } 
     1029    } 
     1030     
     1031    return @parents 
     1032}  # End _get_active_parents 
     1033 
     1034############################################################################### 
     1035#  _get_parents 
     1036# This function returns all parents for the physical file 
     1037############################################################################### 
     1038sub _get_parents { 
     1039    my($self, $physname) = @_; 
     1040 
     1041    my $physinfo = $gPhysInfo{$physname}; 
     1042 
     1043    my @parents; 
     1044    if (defined $physinfo) { 
     1045 
     1046PARENT: 
     1047        foreach my $parentphys (@{$physinfo->{order}}) { 
     1048 
     1049            # skip orphaned parents 
     1050#            if ($parentphys eq '99999999' ) { 
     1051#                next PARENT; 
     1052#            } 
     1053 
     1054            my $parent = $physinfo->{parents}->{$parentphys}; 
     1055            if (!defined $parent) 
     1056            { 
     1057                next PARENT; 
     1058            } 
     1059             
     1060            push @parents, $parentphys; 
     1061        } 
     1062    } 
     1063     
     1064    return @parents 
     1065}  # End _get_active_parents 
     1066 
     1067############################################################################### 
     1068#  _get_valid_path 
     1069# This function returns an itempath for the physical file, that was valid in 
     1070# the previous version. Since all activities that create a new version of a file 
     1071# must be done on at least one active path, there should be at least one valid 
     1072# item path for the version. 
     1073# If we can't find any valid itempath, we can not perform a "copy from" revision 
     1074# In this case, we need to recheckin the current content of the item 
     1075############################################################################### 
     1076sub _get_valid_path { 
     1077    my($self, $physname, $parentphys, $version) = @_; 
     1078 
     1079    my $physinfo = $gPhysInfo{$physname}; 
     1080    if (!defined $physinfo) { 
     1081        return undef; 
     1082    } 
     1083 
     1084    if (!defined $version) { 
     1085        $version = $physinfo->{last_version}; 
     1086    } 
     1087     
     1088    # 1. check the parent requested, if there was an item name for this version 
     1089    # we can use this item name, since it was valid in that time 
     1090    my $parent = $physinfo->{parents}->{$parentphys}; 
     1091    if (defined $parent && 
     1092#        $parentphys ne '99999999' && 
     1093        $parent->{versions}->[$version]) { 
     1094        return $parent->{versions}->[$version]; 
     1095    } 
     1096     
     1097    # 2. check all other parents in the order, the where added 
     1098    my @parents; 
     1099 
     1100PARENT: 
     1101    foreach $parentphys (@{$physinfo->{order}}) { 
     1102 
     1103        $parent = $physinfo->{parents}->{$parentphys}; 
     1104        if (defined $parent && 
     1105#            $parentphys ne '99999999' && 
     1106            $parent->{versions}->[$version]) { 
     1107            return $parent->{versions}->[$version]; 
     1108        } 
     1109    } 
     1110     
     1111    return undef; 
     1112}  #  End _get_valid_path 
     1113 
     1114############################################################################### 
     1115#  _add_parent 
     1116# Track the addition of a new parent to this itempath. This will also track the 
     1117# order, in which the parents where added to the physical file. The valid 
     1118# itempath lookup will search for valid pathes in the order the parents where 
     1119# added to the project. 
     1120############################################################################### 
     1121sub _add_parent { 
     1122    my($self, $physname, $parentphys) = @_; 
     1123 
     1124    my $physinfo = $gPhysInfo{$physname}; 
     1125    if (defined $physinfo) { 
     1126        $physinfo->{parents}->{$parentphys} = {}; 
     1127        push @{ $physinfo->{order} }, $parentphys; 
     1128    } 
     1129}  #  End _add_parent 
     1130 
    46911311; 
  • trunk/script/Vss2Svn/DataCache.pm

    r164 r248  
    2525         verbose => $gCfg{verbose}, 
    2626         fh => undef, 
    27          file => "$gCfg{cachedir}\\datachache.$table.tmp.txt", 
     27         file => "$gCfg{cachedir}/datachache.$table.tmp.txt", 
    2828        }; 
    2929 
  • trunk/script/Vss2Svn/Dumpfile.pm

    r190 r248  
    22 
    33use Vss2Svn::Dumpfile::Node; 
     4use Vss2Svn::Dumpfile::SanityChecker; 
    45use Encode qw(from_to); 
    56 
     
    1718     DELETE     => \&_delete_handler, 
    1819     RECOVER    => \&_recover_handler, 
     20     PIN        => \&_pin_handler, 
     21     LABEL      => \&_label_handler, 
    1922    ); 
    2023 
     
    2225# or recovers. 
    2326 
    24 our %gModified = (); 
     27#our %gModified = (); 
    2528our %gDeleted = (); 
     29our %gVersion = (); 
    2630 
    2731############################################################################### 
     
    3640         revision => 0, 
    3741         errors => [], 
    38          modified_cache => {}, 
    3942         deleted_cache => {}, 
    40          svn_items => {}, 
    41          junk_itempaths => {}, 
    42          need_junkdir => 0, 
    43          need_missing_dirs => [], 
     43         version_cache => [], 
     44         repository => Vss2Svn::Dumpfile::SanityChecker->new(), 
    4445        }; 
    4546 
     
    118119 
    119120    $self->{is_primary} = 1; 
    120     $self->{modified_cache} = {}; 
    121121    $self->{deleted_cache} = {}; 
     122    $self->{version_cache} = []; 
    122123 
    123124    my($handler, $this_action); 
     
    126127        $this_action = $action; 
    127128 
    128         if(defined($itempath)) { 
    129             ($this_action, $itempath) = 
    130                 $self->_action_path_sanity_check($this_action, $itempath, $data); 
    131  
    132             return 0 unless defined($itempath); 
    133  
    134         } else { 
    135             # if the item's path isn't defined, its real name was corrupted in 
    136             # vss, so we'll check it in to the junk drawer as an add 
    137             if (defined $main::gCfg{junkdir}) { 
    138                 $itempath = $self->_get_junk_itempath($main::gCfg{junkdir}, 
    139                     join('.', @$data{ qw(physname version revision_id) })); 
    140  
    141                 $self->add_error("Using filename '$itempath' for item with " 
    142                     . "unrecoverable name at revision $data->{revision_id}"); 
    143  
    144                 $this_action = 'ADD'; 
    145             } else { 
    146                 return 0; 
    147             } 
     129#        $this_action = $self->sanity_checker->check ($data, $itempath, $nodes); 
     130#        if (!defined ($this_action)) { 
     131#            return 0; 
     132#        } 
     133             
     134        $handler = $gHandlers{$this_action}; 
     135 
     136        my $thisnodes = []; 
     137        $self->$handler($itempath, $thisnodes, $data, $expdir); 
     138 
     139        # we need to apply all local changes to our repository directly: if we 
     140        # have an action that operates on multiple items, e.g labeling, the 
     141        # necessary missing directories are created for the first item 
     142        foreach my $node (@$thisnodes) { 
     143            $self->{repository}->load($node); 
     144            push @$nodes, $node; 
    148145        } 
    149  
    150         # if need_junkdir = 1, the first item is just about to be added to the 
    151         # junk drawer, so create the dumpfile node to add this directory 
    152         if ($self->{need_junkdir} == 1) { 
    153             $self->_add_svn_dir($nodes, $main::gCfg{junkdir}); 
    154             $self->{need_junkdir} = -1; 
    155         } 
    156  
    157         foreach my $dir (@{ $self->{need_missing_dirs} }) { 
    158             $self->_add_svn_dir($nodes, $dir); 
    159             $self->add_error("Creating missing directory '$dir' for item " 
    160                 . "'$itempath' at revision $data->{revision_id}"); 
    161         } 
    162  
    163         $handler = $gHandlers{$this_action}; 
    164  
    165         $self->$handler($itempath, $nodes, $data, $expdir); 
     146         
    166147        $self->{is_primary} = 0; 
    167148    } 
     
    172153 
    173154    my($physname, $cache); 
    174     while(($physname, $cache) = each %{ $self->{modified_cache} }) { 
    175         $gModified{$physname} = $cache; 
    176     } 
    177  
    178     while(($physname, $cache) = each %{ $self->{deleted_cache} }) { 
    179         $gDeleted{$physname} = $cache; 
     155  
     156    my ($parentphys, $physnames); 
     157    while(($parentphys, $physnames) = each %{ $self->{deleted_cache} }) { 
     158        while(($physname, $cache) = each %{ $physnames }) { 
     159            $gDeleted{$parentphys}->{$physname} = $cache; 
     160        } 
     161    } 
     162 
     163    # track the version -> revision mapping for the file 
     164    foreach my $record (@{$self->{version_cache}}) { 
     165        my $version = \%{$gVersion{$record->{physname}}->[$record->{version}]}; 
     166        $version->{$record->{itempath}} = $record->{revision}; 
    180167    } 
    181168 
    182169}  #  End do_action 
    183170 
    184 ############################################################################### 
    185 #  _get_junk_itempath 
    186 ############################################################################### 
    187 sub _get_junk_itempath { 
    188     my($self, $dir, $base) = @_; 
    189  
    190     $base =~ s:.*/::; 
    191     my $itempath = "$dir/$base"; 
    192     my $count = 1; 
    193  
    194     if($self->{need_junkdir} == 0) { 
    195         $self->{need_junkdir} = 1; 
    196     } 
    197  
    198     if(!defined($self->{junk_itempaths}->{$itempath})) { 
    199         $self->{junk_itempaths}->{$itempath} = 1; 
    200         return $itempath; 
    201     } 
    202  
    203     my($file, $ext); 
    204  
    205     if($base =~ m/^(.*)\.(.*)/) { 
    206         ($file, $ext) = ($1, ".$2"); 
    207     } else { 
    208         ($file, $ext) = ($base, ''); 
    209     } 
    210  
    211     while(defined($self->{junk_itempaths}->{$itempath})) { 
    212         $itempath = "$dir/$file.$count$ext"; 
    213         $count++; 
    214     } 
    215  
    216     return $itempath; 
    217 }  #  End _get_junk_itempath 
    218  
    219 ############################################################################### 
    220 #  _action_path_sanity_check 
    221 ############################################################################### 
    222 sub _action_path_sanity_check { 
    223     my($self, $action, $itempath, $data) = @_; 
    224  
    225     my($itemtype, $revision_id) = @{ $data }{qw(itemtype revision_id)}; 
    226  
    227     return($action, $itempath) if ($itempath eq '' || $itempath eq '/'); 
    228  
    229     my($newaction, $newpath) = ($action, $itempath); 
    230     my $success; 
    231  
    232     $self->{need_missing_dirs} = []; 
    233  
    234     if($action eq 'ADD' || $action eq 'SHARE' || $action eq 'RECOVER') { 
    235         $success = $self->_add_svn_struct_item($itempath, $itemtype); 
    236  
    237         if(!defined($success)) { 
    238             $newpath = undef; 
    239             $self->add_error("Path consistency failure while trying to add " 
    240                 . "item '$itempath' at revision $revision_id; skipping"); 
    241  
    242         } elsif($success == 0) { 
    243             # trying to re-add existing item; if file, change it to a commit 
    244             if ($itemtype == 1) { 
    245  
    246                 $newpath = undef; 
    247                 $self->add_error("Attempt to re-add directory '$itempath' at " 
    248                 . "revision $revision_id; possibly missing delete"); 
    249  
    250             } else { 
    251  
    252                 $newaction = 'COMMIT'; 
    253                 $self->add_error("Attempt to re-add file '$itempath' at " 
    254                     . "revision $revision_id, changing to modify; possibly " 
    255                     . "missing delete"); 
    256  
    257             } 
     171 
     172############################################################################### 
     173#  _add_handler 
     174############################################################################### 
     175sub _add_handler { 
     176    my($self, $itempath, $nodes, $data, $expdir) = @_; 
     177 
     178    if ($self->{repository}->exists ($itempath)) { 
     179        if ($data->{itemtype} == 2) { 
     180            $self->add_error("Attempt to re-add file '$itempath' at " 
     181                . "revision $data->{revision_id}, changing to modify; possibly " 
     182                . "missing delete"); 
     183            return $self->_commit_handler ($itempath, $nodes, $data, $expdir); 
    258184        } 
    259  
    260     } elsif ($action eq 'DELETE') { 
    261         $success = $self->_delete_svn_struct_item($itempath, $itemtype); 
    262  
    263         if(!$success) { 
    264             $newpath = undef; 
    265             $self->add_error("Attempt to delete non-existent item '$itempath' " 
    266                 . "at revision $revision_id; skipping..."); 
     185        else { 
     186            $self->add_error("Attempt to re-add directory '$itempath' at " 
     187                . "revision $data->{revision_id}, skipping action: possibly " 
     188                . "missing delete"); 
     189            return 0; 
    267190        } 
    268  
    269     } elsif ($action eq 'RENAME') { 
    270         $success = $self->_rename_svn_struct_item($itempath, $itemtype, 
    271             $data->{info}); 
    272  
    273         if(!$success) { 
    274             $newpath = undef; 
    275             $self->add_error("Attempt to rename non-existent item '$itempath' " 
    276                 . "at revision $revision_id; skipping..."); 
    277         } 
    278     } elsif ($action eq 'MOVE') { 
    279         my ($ref, $item) = $self->_get_svn_struct_ref_for_move($itempath); 
    280  
    281         if(!$ref) { 
    282             $newpath = undef; 
    283             $self->add_error("Attempt to move non-existent directory '$itempath' " 
    284                 . "at revision $revision_id; skipping..."); 
    285         } 
    286  
    287         $success = $self->_add_svn_struct_item($data->{info}, 1, $ref->{$item}); 
    288  
    289         if(!$success) { 
    290             $newpath = undef; 
    291             $self->add_error("Error while attempting to move directory '$itempath' " 
    292                 . "at revision $revision_id; skipping..."); 
    293         } 
    294  
    295         delete $ref->{$item}; 
    296     } 
    297  
    298     return($newaction, $newpath); 
    299  
    300 }  #  End _action_path_sanity_check 
    301  
    302 ############################################################################### 
    303 #  _add_svn_struct_item 
    304 ############################################################################### 
    305 sub _add_svn_struct_item { 
    306     my($self, $itempath, $itemtype, $newref) = @_; 
    307  
    308     $itempath =~ s:^/::; 
    309     my @subdirs = split '/', $itempath; 
    310  
    311     my $item = pop(@subdirs); 
    312     my $ref = $self->{svn_items}; 
    313  
    314     my $thispath = ''; 
    315  
    316     foreach my $subdir (@subdirs) { 
    317         $thispath .= "$subdir/"; 
    318  
    319         if(ref($ref) ne 'HASH') { 
    320             return undef; 
    321         } 
    322         if(!defined($ref->{$subdir})) { 
    323             # parent directory doesn't exist; add it to list of missing dirs 
    324             # to build up 
    325             push @{ $self->{need_missing_dirs} }, $thispath; 
    326  
    327             $ref->{$subdir} = {}; 
    328         } 
    329  
    330         $ref = $ref->{$subdir}; 
    331     } 
    332  
    333     if(ref($ref) ne 'HASH') { 
    334         # parent "directory" is actually a file 
    335         return undef; 
    336     } 
    337  
    338     if(defined($ref->{$item})) { 
    339         # item already exists; can't add it 
    340         return 0; 
    341     } 
    342  
    343     if(defined($newref)) { 
    344         $ref->{$item} = $newref; 
    345     } else { 
    346         $ref->{$item} = ($itemtype == 1)? {} : 1; 
    347     } 
    348  
    349     return 1; 
    350  
    351 }  #  End _add_svn_struct_item 
    352  
    353 ############################################################################### 
    354 #  _delete_svn_struct_item 
    355 ############################################################################### 
    356 sub _delete_svn_struct_item { 
    357     my($self, $itempath, $itemtype) = @_; 
    358  
    359     return $self->_delete_rename_svn_struct_item($itempath, $itemtype); 
    360 }  #  End _delete_svn_struct_item 
    361  
    362 ############################################################################### 
    363 #  _rename_svn_struct_item 
    364 ############################################################################### 
    365 sub _rename_svn_struct_item { 
    366     my($self, $itempath, $itemtype, $newname) = @_; 
    367  
    368     return $self->_delete_rename_svn_struct_item($itempath, $itemtype, $newname); 
    369 }  #  End _rename_svn_struct_item 
    370  
    371 ############################################################################### 
    372 #  _delete_rename_svn_struct_item 
    373 ############################################################################### 
    374 sub _delete_rename_svn_struct_item { 
    375     my($self, $itempath, $itemtype, $newname, $movedref) = @_; 
    376  
    377     $itempath =~ s:^/::; 
    378     $newname =~ s:/$:: if defined($newname); 
    379     my @subdirs = split '/', $itempath; 
    380  
    381     my $item = pop(@subdirs); 
    382     my $ref = $self->{svn_items}; 
    383  
    384     foreach my $subdir (@subdirs) { 
    385         if(!(ref($ref) eq 'HASH') || !defined($ref->{$subdir})) { 
    386             # can't get to item because a parent directory doesn't exist; give up 
    387             return undef; 
    388         } 
    389  
    390         $ref = $ref->{$subdir}; 
    391     } 
    392  
    393     if((ref($ref) ne 'HASH') || !defined($ref->{$item})) { 
    394         # item doesn't exist; can't delete/rename it 
    395         return 0; 
    396     } 
    397  
    398     if(defined $newname) { 
    399         $ref->{$newname} = $ref->{$item}; 
    400     } 
    401  
    402     delete $ref->{$item}; 
    403  
    404     return 1; 
    405  
    406 }  #  End _delete_rename_svn_struct_item 
    407  
    408 ############################################################################### 
    409 #  _get_svn_struct_ref_for_move 
    410 ############################################################################### 
    411 sub _get_svn_struct_ref_for_move { 
    412     my($self, $itempath) = @_; 
    413  
    414     $itempath =~ s:^/::; 
    415     my @subdirs = split '/', $itempath; 
    416  
    417     my $item = pop(@subdirs); 
    418     my $ref = $self->{svn_items}; 
    419  
    420     my $thispath = ''; 
    421  
    422     foreach my $subdir (@subdirs) { 
    423         $thispath .= "$subdir/"; 
    424  
    425         if(ref($ref) ne 'HASH') { 
    426             return undef; 
    427         } 
    428         if(!defined($ref->{$subdir})) { 
    429             return undef; 
    430         } 
    431  
    432         $ref = $ref->{$subdir}; 
    433     } 
    434  
    435     if((ref($ref) ne 'HASH') || !defined($ref->{$item}) || 
    436        (ref($ref->{$item} ne 'HASH'))) { 
    437         return undef; 
    438     } 
    439  
    440     return ($ref, $item); 
    441  
    442 }  #  End _get_svn_struct_ref_for_move 
    443  
    444 ############################################################################### 
    445 #  _add_svn_dir 
    446 ############################################################################### 
    447 sub _add_svn_dir { 
    448     my($self, $nodes, $dir) = @_; 
    449  
    450     my $node = Vss2Svn::Dumpfile::Node->new(); 
    451     my $data = { itemtype => 1, is_binary => 0 }; 
    452  
    453     $node->set_initial_props($dir, $data); 
    454     $node->{action} = 'add'; 
    455  
    456     push @$nodes, $node; 
    457     $self->_add_svn_struct_item($dir, 1); 
    458  
    459 }  #  End _add_svn_dir 
    460  
    461 ############################################################################### 
    462 #  _add_handler 
    463 ############################################################################### 
    464 sub _add_handler { 
    465     my($self, $itempath, $nodes, $data, $expdir) = @_; 
    466  
     191    } 
     192 
     193    my $success = $self->{repository}->exists_parent ($itempath); 
     194    if(!defined($success)) { 
     195        $self->add_error("Path consistency failure while trying to add " 
     196            . "item '$itempath' at revision $data->{revision_id}; skipping"); 
     197        return 0; 
     198    } 
     199    elsif ($success == 0) { 
     200        $self->add_error("Parent path missing while trying to add " 
     201            . "item '$itempath' at revision $data->{revision_id}: adding missing " 
     202            . "parents"); 
     203        $self->_create_svn_path ($nodes, $itempath); 
     204    } 
     205     
    467206    my $node = Vss2Svn::Dumpfile::Node->new(); 
    468207    $node->set_initial_props($itempath, $data); 
     
    473212    } 
    474213 
    475     $self->track_modified($data->{physname}, $data->{revision_id}, $itempath); 
     214#    $self->track_modified($data->{physname}, $data->{revision_id}, $itempath); 
     215    $self->track_version ($data->{physname}, $data->{version}, $itempath); 
    476216 
    477217    push @$nodes, $node; 
     
    485225    my($self, $itempath, $nodes, $data, $expdir) = @_; 
    486226 
     227    if (!$self->{repository}->exists ($itempath)) { 
     228        $self->add_error("Attempt to commit to non-existant file '$itempath' at " 
     229            . "revision $data->{revision_id}, changing to add; possibly " 
     230            . "missing recover"); 
     231        return $self->_add_handler ($itempath, $nodes, $data, $expdir); 
     232    } 
     233     
    487234    my $node = Vss2Svn::Dumpfile::Node->new(); 
    488235    $node->set_initial_props($itempath, $data); 
     
    493240    } 
    494241 
    495     $self->track_modified($data->{physname}, $data->{revision_id}, $itempath); 
     242#    $self->track_modified($data->{physname}, $data->{revision_id}, $itempath); 
     243    $self->track_version ($data->{physname}, $data->{version}, $itempath); 
    496244 
    497245    push @$nodes, $node; 
     
    508256 
    509257    my $newname = $data->{info}; 
    510  
    511258    my $newpath = $itempath; 
    512259 
     
    517264    } 
    518265 
     266    if ($self->{repository}->exists ($newpath)) { 
     267        $self->add_error("Attempt to rename item '$itempath' to '$newpath' at " 
     268            . "revision $data->{revision_id}, but destination already exists: possibly " 
     269            . "missing delete; skipping"); 
     270        return 0; 
     271    } 
     272 
     273    if (!$self->{repository}->exists ($itempath)) { 
     274        $self->add_error("Attempt to rename item '$itempath' to '$newpath' at " 
     275            . "revision $data->{revision_id}, but source doesn't exists: possibly " 
     276            . "missing recover; skipping"); 
     277        return 0; 
     278    } 
     279 
    519280    my $node = Vss2Svn::Dumpfile::Node->new(); 
    520281    $node->set_initial_props($newpath, $data); 
     
    533294    push @$nodes, $node; 
    534295 
    535     $self->track_modified($data->{physname}, $data->{revision_id}, $newpath); 
     296#    $self->track_modified($data->{physname}, $data->{revision_id}, $newpath); 
     297#    $self->track_version ($data->{physname}, $data->{version}, $newpath); 
    536298 
    537299    $node = Vss2Svn::Dumpfile::Node->new(); 
    538     $node->{path} = $itempath
     300    $node->set_initial_props($itempath, $data)
    539301    $node->{action} = 'delete'; 
    540302    $node->{hideprops} = 1; 
     
    553315    my($self, $itempath, $nodes, $data, $expdir) = @_; 
    554316 
     317    if ($self->{repository}->exists ($itempath)) { 
     318        $self->add_error("Attempt to share item '$data->{info}' to '$itempath' at " 
     319            . "revision $data->{revision_id}, but destination already exists: possibly " 
     320            . "missing delete; skipping"); 
     321        return 0; 
     322    } 
     323 
     324#   It could be possible that we share from a historically renamed item, so we don't check the source 
     325#    if ($self->{repository}->exists ($data->{info})) { 
     326#        $self->add_error("Attempt to share item '$itempath' to '$newpath' at " 
     327#            . "revision $data->{revision_id}, but destination already exists: possibly " 
     328#            . "missing delete; skipping"); 
     329#        return 0; 
     330#    } 
     331 
    555332    my $node = Vss2Svn::Dumpfile::Node->new(); 
    556333    $node->set_initial_props($itempath, $data); 
    557334    $node->{action} = 'add'; 
    558335 
    559     @{ $node }{ qw(copyrev copypath) } 
    560         = $self->last_modified_rev_path($data->{physname}); 
    561  
    562     return unless defined($node->{copyrev}); 
     336#    @{ $node }{ qw(copyrev copypath) } 
     337#        = $self->last_modified_rev_path($data->{physname}); 
     338    $node->{copyrev} = 
     339        $self->get_revision ($data->{physname}, $data->{version}, $data->{info}); 
     340    $node->{copypath} = $data->{info}; 
     341 
     342    if (!defined $node->{copyrev} || !defined $node->{copypath}) { 
     343        return $self->_commit_handler ($itempath, $nodes, $data, $expdir); 
     344    } 
     345 
     346    $self->track_version ($data->{physname}, $data->{version}, $itempath); 
    563347 
    564348    push @$nodes, $node; 
     
    574358    # branching is a no-op in SVN 
    575359 
    576     # if the file is copied later, we need to track, the revision of this branch 
    577     # see the shareBranchShareModify Test 
    578     $self->track_modified($data->{physname}, $data->{revision_id}, $itempath); 
     360#    # if the file is copied later, we need to track, the revision of this branch 
     361#    # see the shareBranchShareModify Test 
     362#    $self->track_modified($data->{physname}, $data->{revision_id}, $itempath); 
     363    $self->track_version ($data->{physname}, $data->{version}, $itempath); 
    579364 
    580365}  #  End _branch_handler 
     
    590375    my $newpath = $data->{info}; 
    591376 
     377    if ($self->{repository}->exists ($newpath)) { 
     378        $self->add_error("Attempt to move item '$itempath' to '$newpath' at " 
     379            . "revision $data->{revision_id}, but destination already exists: possibly " 
     380            . "missing delete; skipping"); 
     381        return 0; 
     382    } 
     383 
     384    if (!$self->{repository}->exists ($itempath)) { 
     385        $self->add_error("Attempt to move item '$itempath' to '$newpath' at " 
     386            . "revision $data->{revision_id}, but source doesn't exists: possibly " 
     387            . "missing recover; skipping"); 
     388        return 0; 
     389    } 
     390     
    592391    my $node = Vss2Svn::Dumpfile::Node->new(); 
    593392    $node->set_initial_props($newpath, $data); 
     
    604403    push @$nodes, $node; 
    605404 
    606     $self->track_modified($data->{physname}, $data->{revision_id}, $newpath); 
     405#    $self->track_modified($data->{physname}, $data->{revision_id}, $newpath); 
     406#    $self->track_version ($data->{physname}, $data->{version}, $newpath); 
    607407 
    608408    $node = Vss2Svn::Dumpfile::Node->new(); 
    609     $node->{path} = $itempath
     409    $node->set_initial_props($itempath, $data)
    610410    $node->{action} = 'delete'; 
    611411    $node->{hideprops} = 1; 
     
    621421    my($self, $itempath, $nodes, $data, $expdir) = @_; 
    622422 
    623     my $node = Vss2Svn::Dumpfile::Node->new(); 
    624     $node->{path} = $itempath; 
     423    if (!$self->{repository}->exists ($itempath)) { 
     424        $self->add_error("Attempt to delete non-existent item '$itempath' at " 
     425            . "revision $data->{revision_id}: possibly " 
     426            . "missing recover/add/share; skipping"); 
     427        return 0; 
     428    } 
     429 
     430    my $node = Vss2Svn::Dumpfile::Node->new(); 
     431    $node->set_initial_props($itempath, $data); 
    625432    $node->{action} = 'delete'; 
    626433    $node->{hideprops} = 1; 
     
    628435    push @$nodes, $node; 
    629436 
    630     $self->track_deleted($data->{physname}, $data->{revision_id}, 
    631                          $itempath); 
     437    $self->track_deleted($data->{parentphys}, $data->{physname}, 
     438                         $data->{revision_id}, $itempath); 
    632439 
    633440}  #  End _delete_handler 
     
    638445sub _recover_handler { 
    639446    my($self, $itempath, $nodes, $data, $expdir) = @_; 
     447 
     448    if ($self->{repository}->exists ($itempath)) { 
     449        $self->add_error("Attempt to recover existing item '$itempath' at " 
     450            . "revision $data->{revision_id}: possibly " 
     451            . "missing delete; change to commit"); 
     452        return $self->_commit_handler ($itempath, $nodes, $data, $expdir); 
     453    } 
    640454 
    641455    my $node = Vss2Svn::Dumpfile::Node->new(); 
     
    643457    $node->{action} = 'add'; 
    644458 
    645     my($copyrev, $copypath) = $self->last_deleted_rev_path($data->{physname}); 
    646  
    647     if (!defined $copyrev) { 
     459    # for projects we want to go back to the revision just one before the deleted 
     460    # revision. For files, we need to go back to the specified revision, since 
     461    # the file could have been modified via a share. 
     462    my($copyrev, $copypath); 
     463    if (!defined ($data->{version})) { 
     464        ($copyrev, $copypath)= $self->last_deleted_rev_path($data->{parentphys}, 
     465                                                            $data->{physname}); 
     466        $copyrev -= 1; 
     467    } 
     468    else { 
     469        $copyrev = 
     470            $self->get_revision ($data->{physname}, $data->{version}, $data->{info}); 
     471        $copypath = $data->{info}; 
     472    } 
     473     
     474    if (!defined $copyrev || !defined $copypath) { 
    648475        $self->add_error( 
    649476            "Could not recover path $itempath at revision $data->{revision_id};" 
    650             . " unable to determine deleted revision"); 
    651         return 0; 
    652     } 
    653  
    654     $node->{copyrev} = $copyrev - 1
     477            . " unable to determine deleted revision or path"); 
     478        return 0; 
     479    } 
     480 
     481    $node->{copyrev} = $copyrev
    655482    $node->{copypath} = $copypath; 
    656483 
     484    if (defined ($data->{version})) { 
     485        $self->track_version ($data->{physname}, $data->{version}, $itempath); 
     486    } 
     487 
    657488    push @$nodes, $node; 
    658489 
     
    660491 
    661492############################################################################### 
    662 #  track_modified 
    663 ############################################################################### 
    664 sub track_modified { 
    665     my($self, $physname, $revision, $path) = @_; 
    666  
    667     return unless $self->{is_primary}; 
    668  
    669     $self->{modified_cache}->{$physname} = 
     493#  _pin_handler 
     494############################################################################### 
     495sub _pin_handler { 
     496    my($self, $itempath, $nodes, $data, $expdir) = @_; 
     497 
     498    if (!$self->{repository}->exists ($itempath)) { 
     499        $self->add_error("Attempt to pin non-existing item '$itempath' at " 
     500            . "revision $data->{revision_id}: possibly " 
     501            . "missing recover; skipping"); 
     502        return 0; 
     503    } 
     504 
     505    my $copyrev =  
     506        $self->get_revision ($data->{physname}, $data->{version}, $data->{info}); 
     507    my $copypath = $data->{info}; 
     508     
     509    # if one of the necessary copy from attributes are unavailable we fall back 
     510    # to a complete checkin 
     511    if (!defined $copyrev || !defined $copypath) { 
     512        return $self->_commit_handler ($itempath, $nodes, $data, $expdir); 
     513    } 
     514     
     515    my $node = Vss2Svn::Dumpfile::Node->new(); 
     516    $node->set_initial_props($itempath, $data); 
     517    $node->{action} = 'add'; 
     518 
     519    $node->{copyrev} = $copyrev; 
     520    $node->{copypath} = $copypath; 
     521 
     522    $self->track_version ($data->{physname}, $data->{version}, $itempath); 
     523 
     524    push @$nodes, $node; 
     525 
     526}  #  End _pin_handler 
     527 
     528############################################################################### 
     529#  _label_handler 
     530############################################################################### 
     531sub _label_handler { 
     532    my($self, $itempath, $nodes, $data, $expdir) = @_; 
     533 
     534    if (!$self->{repository}->exists ($itempath)) { 
     535        $self->add_error("Attempt to label non-existing item '$itempath' at " 
     536            . "revision $data->{revision_id}: possibly " 
     537            . "missing recover; skipping"); 
     538        return 0; 
     539    } 
     540 
     541    my $label = $data->{info}; 
     542     
     543    # It is possible that the label was deleted later, so we see here a label 
     544    # action, but no label was assigned. In this case, we only need to track 
     545    # the version->revision mapping, since the version could have been used 
     546    # as a valid share source. 
     547    if (defined ($label)) { 
     548        my $uniquepath = join('.', @$data{ qw(physname version) }); 
     549        my $labelpath = "$main::gCfg{labeldir}/$data->{info}$itempath"; 
     550 
     551        $self->_create_svn_path ($nodes, $labelpath); 
     552 
     553        my $node = Vss2Svn::Dumpfile::Node->new(); 
     554        $node->set_initial_props($labelpath, $data); 
     555        $node->{action} = 'add'; 
     556     
     557        my $copyrev = $data->{revision_id} - 1; 
     558        my $copypath = $itempath; 
     559 
     560        $node->{copyrev} = $copyrev; 
     561        $node->{copypath} = $copypath; 
     562 
     563        push @$nodes, $node; 
     564         
     565    } 
     566 
     567    $self->track_version ($data->{physname}, $data->{version}, $itempath); 
     568}  #  End _label_handler 
     569 
     570############################################################################### 
     571#  _add_svn_dir 
     572############################################################################### 
     573sub _add_svn_dir { 
     574    my($self, $nodes, $dir) = @_; 
     575 
     576    my $node = Vss2Svn::Dumpfile::Node->new(); 
     577    my $data = { itemtype => 1, is_binary => 0 }; 
     578 
     579    $node->set_initial_props($dir, $data); 
     580    $node->{action} = 'add'; 
     581 
     582    push @$nodes, $node; 
     583}  #  End _add_svn_dir 
     584 
     585 
     586############################################################################### 
     587#  _create_svn_path 
     588############################################################################### 
     589sub _create_svn_path { 
     590    my($self, $nodes, $itempath) = @_; 
     591 
     592    my $missing_dirs = $self->{repository}->get_missing_dirs($itempath); 
     593 
     594    foreach my $dir (@$missing_dirs) { 
     595        $self->_add_svn_dir($nodes, $dir); 
     596    } 
     597}  #  End _create_svn_path 
     598 
     599############################################################################### 
     600#  track_version 
     601############################################################################### 
     602sub track_version { 
     603    my($self, $physname, $version, $itempath) = @_; 
     604 
     605    my $record =  
     606        { 
     607         physname => $physname, 
     608         version => $version, 
     609         revision => $self->{revision}, 
     610         itempath => $itempath, 
     611        }; 
     612    push @{$self->{version_cache}}, $record; 
     613 
     614}  #  End track_version 
     615 
     616 
     617############################################################################### 
     618#  get_revision 
     619############################################################################### 
     620sub get_revision { 
     621    my($self, $physname, $version, $itempath) = @_; 
     622 
     623    if (!defined($gVersion{$physname})) { 
     624        return (undef); 
     625    } 
     626 
     627    if (!exists($gVersion{$physname}->[$version])) { 
     628        return (undef); 
     629    } 
     630     
     631    return $gVersion{$physname}->[$version]->{$itempath}; 
     632 
     633}  #  End get_revision 
     634 
     635############################################################################### 
     636#  track_deleted 
     637############################################################################### 
     638sub track_deleted { 
     639    my($self, $parentphys, $physname, $revision, $path) = @_; 
     640 
     641    $self->{deleted_cache}->{$parentphys}->{$physname} = 
    670642        { 
    671643         revision => $revision, 
     
    673645        }; 
    674646 
    675 }  #  End track_modified 
    676  
    677 ############################################################################### 
    678 #  track_deleted 
    679 ############################################################################### 
    680 sub track_deleted { 
    681     my($self, $physname, $revision, $path) = @_; 
    682  
    683     $self->{deleted_cache}->{$physname} = 
    684         { 
    685          revision => $revision, 
    686          path => $path, 
    687         }; 
    688  
    689647}  #  End track_deleted 
    690648 
    691649############################################################################### 
    692 #  last_modified_rev_path 
    693 ############################################################################### 
    694 sub last_modified_rev_path { 
    695     my($self, $physname) = @_; 
    696  
    697     if (!defined($gModified{$physname})) { 
     650#  last_deleted_rev_path 
     651############################################################################### 
     652sub last_deleted_rev_path { 
     653    my($self, $parentphys, $physname) = @_; 
     654 
     655    if (!defined($gDeleted{$parentphys})) { 
    698656        return (undef, undef); 
    699657    } 
    700658 
    701     return @{ $gModified{$physname} }{ qw(revision path) }; 
    702 }  #  End last_modified_rev_path 
    703  
    704 ############################################################################### 
    705 #  last_deleted_rev_path 
    706 ############################################################################### 
    707 sub last_deleted_rev_path { 
    708     my($self, $physname) = @_; 
    709  
    710     if (!defined($gDeleted{$physname})) { 
     659    if (!defined($gDeleted{$parentphys}->{$physname})) { 
    711660        return (undef, undef); 
    712661    } 
    713662 
    714     return @{ $gDeleted{$physname} }{ qw(revision path) }; 
     663    return @{ $gDeleted{$parentphys}->{$physname} }{ qw(revision path) }; 
    715664}  #  End last_deleted_rev_path 
    716665 
     
    729678    } 
    730679 
    731     my $file = "$expdir\\$data->{physname}.$data->{version}"; 
     680    my $file = "$expdir/$data->{physname}.$data->{version}"; 
    732681 
    733682    if (!open EXP, "$file") { 
  • trunk/script/Vss2Svn/SvnRevHandler.pm

    r164 r248  
    1 package Vss2Svn::SvnRevHandler; 
     1 package Vss2Svn::SvnRevHandler; 
    22 
    33use warnings; 
     
    1515 
    1616    my $svncache = Vss2Svn::DataCache->new('SvnRevision', 1); 
    17  
     17   
     18    # we need to start at revision 1 and not 0 
     19    ++$svncache->{pkey}; 
     20   
    1821    if (!defined($svncache)) { 
    1922        print "\nERROR: Could not create cache 'SvnRevision'\n"; 
  • trunk/script/vss2svn.pl

    r219 r248  
    6464            # Merge data from parent records into child records where possible 
    6565            MERGEPARENTDATA => {handler => \&MergeParentData, 
     66                                next    => 'MERGEMOVEDATA'}, 
     67 
     68            # Merge data from move actions  
     69            MERGEMOVEDATA => {handler => \&MergeMoveData, 
    6670                                next    => 'BUILDACTIONHIST'}, 
    6771 
     
    137141 
    138142    $cache->commit(); 
    139  
    140143}  #  End LoadVssNames 
    141  
    142144 
    143145############################################################################### 
     
    292294    my($parentdata, $version, $vernum, $action, $name, $actionid, $actiontype, 
    293295       $tphysname, $itemname, $itemtype, $parent, $user, $timestamp, $comment, 
    294        $is_binary, $info, $priority, $sortkey, $cachename); 
     296       $is_binary, $info, $priority, $sortkey, $label, $cachename); 
    295297 
    296298VERSION: 
     
    324326        $parentdata = 0; 
    325327        $priority = 5; 
    326  
     328        $label = undef; 
     329         
    327330        if ($version->{Comment} && !ref($version->{Comment})) { 
    328331            $comment = $version->{Comment} || undef; 
     332        } 
     333 
     334        # In case of Label the itemtype is the type of the item currently 
     335        # under investigation 
     336        if ($actiontype eq 'LABEL') { 
     337            my $iteminfo = $xml->{ItemInfo}; 
     338            $itemtype = $iteminfo->{Type}; 
     339 
     340        } 
     341 
     342        # we can have label actions and labes attached to versions 
     343        if (defined $action->{Label} && !ref($action->{Label})) { 
     344            $label = $action->{Label}; 
     345             
     346            # append the label comment to a possible version comment 
     347            if ($action->{LabelComment} && !ref($action->{LabelComment})) { 
     348                if (defined $comment) { 
     349                    print "Merging LabelComment and Comment for " 
     350                        . "'$tphysname;$version->{VersionNumber}'"; # if $gCfg{verbose}; 
     351                    $comment .= "\n"; 
     352                } 
     353   
     354                $comment .= $action->{LabelComment} || undef; 
     355            } 
    329356        } 
    330357 
     
    383410        } elsif ($actiontype eq 'BRANCH') { 
    384411            $info = $action->{Parent}; 
    385         } elsif ($actiontype eq 'PIN') { 
    386             $info = $action->{PinnedToVersion}; 
    387         } 
     412        }  
    388413 
    389414        $vernum = ($parentdata)? undef : $version->{VersionNumber}; 
     415 
     416        # since there is no corresponding client action for PIN, we need to 
     417        # enter the concrete version number here manually 
     418        # In a share action the pinnedToVersion attribute can also be set 
     419#        if ($actiontype eq 'PIN') { 
     420            $vernum = $action->{PinnedToVersion} if (defined $action->{PinnedToVersion}); 
     421#        } 
    390422 
    391423        $priority -= 4 if $actiontype eq 'ADD'; # Adds are always first 
    392424        $priority -= 3 if $actiontype eq 'SHARE'; 
     425        $priority -= 3 if $actiontype eq 'PIN'; 
    393426        $priority -= 2 if $actiontype eq 'BRANCH'; 
    394427 
     
    399432        $cache->add($tphysname, $vernum, $parentphys, $actiontype, $itemname, 
    400433                    $itemtype, $timestamp, $user, $is_binary, $info, $priority, 
    401                     $sortkey, $parentdata, $comment); 
     434                    $sortkey, $parentdata, $label, $comment); 
    402435 
    403436    } 
     
    493526############################################################################### 
    494527sub GetChildRecs { 
    495     my($parentrec) = @_; 
     528    my($parentrec, $parentdata) = @_; 
    496529 
    497530    # Here we need to find any child rows which give us additional info on the 
     
    502535    # we don't get the wrong row. 
    503536 
     537    $parentdata = 0 unless defined $parentdata; 
     538     
    504539    my $sql = <<"EOSQL"; 
    505540SELECT 
     
    508543    PhysicalAction 
    509544WHERE 
    510     parentdata = 0 
     545    parentdata = ? 
    511546    AND physname = ? 
    512547    AND actiontype = ? 
    513     AND (? - timestamp IN (0, 1, 2, 3, 4)) 
     548    AND (? - timestamp IN (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)) 
    514549    AND author = ? 
    515550ORDER BY 
     
    518553 
    519554    my $sth = $gCfg{dbh}->prepare($sql); 
    520     $sth->execute( @{ $parentrec }{qw(physname actiontype timestamp author)} ); 
     555    $sth->execute( $parentdata, @{ $parentrec }{qw(physname actiontype timestamp author)} ); 
    521556 
    522557    return $sth->fetchall_arrayref( {} ); 
     
    559594 
    560595############################################################################### 
     596#  MergeMoveData 
     597############################################################################### 
     598sub MergeMoveData { 
     599    # Similar to the MergeParentData, the MergeMove Data combines two the src 
     600    # and target move actions into one move action. Since both items are parents 
     601    # the MergeParentData function can not deal with this specific problem 
     602 
     603    my($sth, $rows, $row); 
     604    $sth = $gCfg{dbh}->prepare('SELECT * FROM PhysicalAction ' 
     605                               . 'WHERE actiontype = "MOVE_FROM"'); 
     606    $sth->execute(); 
     607 
     608    # need to pull in all recs at once, since we'll be updating/deleting data 
     609    $rows = $sth->fetchall_arrayref( {} ); 
     610 
     611    my($childrecs, $child, $id); 
     612    my @delchild = (); 
     613 
     614    foreach $row (@$rows) { 
     615        $row->{actiontype} = 'MOVE'; 
     616        $childrecs = &GetChildRecs($row, 1); 
     617 
     618        if (scalar @$childrecs > 1) { 
     619            &ThrowWarning("Multiple chidl recs for parent MOVE rec " 
     620                          . "'$row->{action_id}'"); 
     621        } 
     622 
     623        foreach $child (@$childrecs) { 
     624            my $update; 
     625            $update = $gCfg{dbh}->prepare('UPDATE PhysicalAction SET info = ?' 
     626                                          . 'WHERE action_id = ?'); 
     627             
     628            $update->execute( $row->{parentphys}, $child->{action_id} ); 
     629        } 
     630         
     631        push(@delchild, $row->{action_id}); 
     632    } 
     633 
     634    foreach $id (@delchild) { 
     635        &DeleteChildRec($id); 
     636    } 
     637 
     638    1; 
     639 
     640}  #  End MergeMoveData 
     641 
     642############################################################################### 
    561643#  DeleteChildRec 
    562644############################################################################### 
     
    580662        || &ThrowError("Could not create cache 'SvnRevisionVssAction'"); 
    581663 
     664    my $labelcache = Vss2Svn::DataCache->new('Label') 
     665        || &ThrowError("Could not create cache 'Label'"); 
     666 
    582667    # This will keep track of the current SVN revision, and increment it when 
    583668    # the author or comment changes, the timestamps span more than an hour 
     
    591676 
    592677    my $sql = 'SELECT * FROM PhysicalAction ORDER BY timestamp ASC, ' 
    593                     . 'priority ASC, sortkey ASC'; 
     678            . 'itemtype ASC, priority ASC, sortkey ASC'; 
    594679 
    595680    $sth = $gCfg{dbh}->prepare($sql); 
     
    598683ROW: 
    599684    while(defined($row = $sth->fetchrow_hashref() )) { 
    600         $svnrevs->check($row); 
    601685        $action = $row->{actiontype}; 
    602686 
     
    647731        } 
    648732 
     733        # we need to check for the next rev number, after all pathes that can 
     734        # prematurally call the next row. Otherwise, we get an empty revision. 
     735        $svnrevs->check($row); 
     736         
    649737        # May contain add'l info for the action depending on type: 
    650738        # RENAME: the new name (without path) 
    651739        # SHARE: the source path which was shared 
    652740        # MOVE: the new path 
    653         # PIN: the version that was pinned 
     741        # PIN: the path of the version that was pinned       
     742        # LABEL: the name of the label 
    654743        $row->{info} = $handler->{info}; 
    655744 
     745        # The version may have changed 
     746        if (defined $handler->{version}) { 
     747            $row->{version} = $handler->{version}; 
     748        } 
     749         
    656750        $allitempaths = join("\t", @$itempaths); 
    657751        $row->{itempaths} = $allitempaths; 
    658752 
    659         $vsscache->add(@$row{ qw(physname version actiontype itempaths 
     753        $vsscache->add(@$row{ qw(parentphys physname version actiontype itempaths 
    660754                             itemtype is_binary info) }); 
    661755        $joincache->add( $svnrevs->{revnum}, $vsscache->{pkey} ); 
     756         
     757        if (defined $row->{label}) { 
     758            $labelcache->add(@$row{ qw(physname version label itempaths) }); 
     759        } 
    662760 
    663761    } 
     
    666764    $svnrevs->commit(); 
    667765    $joincache->commit(); 
    668  
     766    $labelcache->commit(); 
     767     
    669768}  #  End BuildVssActionHistory 
    670769 
     
    720819        $dumpfile->begin_revision($row); 
    721820 
    722         next REVISION if $revision == 0; 
     821#        next REVISION if $revision == 0; 
    723822 
    724823        $action_sth->execute($revision); 
     
    11571256    } 
    11581257 
    1159     $gCfg{ssphys} = 'SSPHYS.exe' if !defined($gCfg{ssphys}); 
     1258    $gCfg{ssphys} = 'ssphys' if !defined($gCfg{ssphys}); 
    11601259    $gCfg{vssdatadir} = "$gCfg{vssdir}/data"; 
    11611260 
     
    11871286        AddedProject => {type => 1, action => 'ADD'}, 
    11881287        RenamedProject => {type => 1, action => 'RENAME'}, 
    1189         MovedProjectTo => {type => 1, action => 'IGNORE'}, 
    1190         MovedProjectFrom => {type => 1, action => 'MOVE'}, 
     1288        MovedProjectTo => {type => 1, action => 'MOVE'}, 
     1289        MovedProjectFrom => {type => 1, action => 'MOVE_FROM'}, 
    11911290        DeletedProject => {type => 1, action => 'DELETE'}, 
    11921291        DestroyedProject => {type => 1, action => 'DELETE'}, 
    11931292        RecoveredProject => {type => 1, action => 'RECOVER'}, 
     1293        Restore => {type => 1, action => 'RESTORE'}, 
    11941294        CheckedIn => {type => 2, action => 'COMMIT'}, 
    11951295        CreatedFile => {type => 2, action => 'ADD'}, 
     
    12011301        SharedFile => {type => 2, action => 'SHARE'}, 
    12021302        BranchFile => {type => 2, action => 'BRANCH'}, 
    1203         PinnedFile => {type => 2, action => 'IGNORE'}, 
     1303        PinnedFile => {type => 2, action => 'PIN'}, 
    12041304        RollBack => {type => 2, action => 'BRANCH'}, 
    1205         UnpinnedFile => {type => 2, action => 'IGNORE'}, 
    1206         Labeled => {type => 2, action => 'IGNORE'}, 
     1305        UnpinnedFile => {type => 2, action => 'PIN'}, 
     1306        Labeled => {type => 2, action => 'LABEL'}, 
    12071307    ); 
    12081308 
     
    12531353        sortkey     VARCHAR, 
    12541354        parentdata  INTEGER, 
     1355        label       VARCHAR, 
    12551356        comment     TEXT 
    12561357    ) 
     
    12901391    VssAction ( 
    12911392        action_id   INTEGER PRIMARY KEY, 
     1393        parentphys  VARCHAR, 
    12921394        physname    VARCHAR, 
    12931395        version     INTEGER, 
     
    13481450    $sth->execute; 
    13491451 
     1452    $sql = <<"EOSQL"; 
     1453CREATE TABLE 
     1454    Label ( 
     1455        physical VARCHAR, 
     1456        version  INTEGER, 
     1457        label    VARCHAR, 
     1458        imtempaths  VARCHAR 
     1459    ) 
     1460EOSQL 
     1461 
     1462    $sth = $gCfg{dbh}->prepare($sql); 
     1463    $sth->execute; 
     1464 
    13501465    my @cfgitems = qw(task step vssdir svnurl svnuser svnpwd ssphys tempdir 
    13511466        setsvndate starttime); 
     
    14471562    $gCfg{junkdir} = '/lost+found'; 
    14481563 
     1564    $gCfg{labeldir} = '/labels'; 
     1565 
    14491566    $gCfg{errortasks} = []; 
    14501567 
     
    14591576    mkdir $gCfg{vssdata}; 
    14601577 
    1461     $gCfg{ssphys} ||= 'SSPHYS.exe'; 
     1578    $gCfg{ssphys} ||= 'ssphys'; 
    14621579    $gCfg{svn} ||= 'SVN.exe'; 
    14631580 

These ads are automatically generated by Google based on the content of this page. Revenue from these ads helps to pay for hosting fees of this site; however, these ads do not constitute an endorsement by PumaCode.org.