1 package Vss2Svn
::ActionHandler
;
9 COMMIT
=> \
&_commit_handler
,
10 RENAME
=> \
&_rename_handler
,
11 SHARE
=> \
&_share_handler
,
12 BRANCH
=> \
&_branch_handler
,
13 MOVE
=> \
&_move_handler
,
14 RESTORE
=> \
&_restore_handler
,
15 RESTOREDPROJECT
=> \
&_restore_handler
,
16 DELETE
=> \
&_delete_handler
,
17 RECOVER
=> \
&_recover_handler
,
18 PIN
=> \
&_pin_handler
,
19 LABEL
=> \
&_label_handler
,
25 ###############################################################################
27 ###############################################################################
29 my($class, $row) = @_;
45 return bless($self, $class);
48 ###############################################################################
50 ###############################################################################
52 my($self, $action) = @_;
54 $self->{action
} = $action;
55 my $handler = $gHandlers{$action};
57 if (!defined($handler)) {
58 $self->{errmsg
} .= "Unknown action '$action'";
62 if ($self->{verbose
}) {
63 my $physprint = (defined $self->{row
}->{physname
})?
64 $self->{row
}->{physname
} : '!UNDEF';
65 my $parentprint = (defined $self->{row
}->{parentphys
})?
66 $self->{row
}->{parentphys
} : '!UNDEF';
67 print "$action: $physprint, $parentprint \@ $self->{row}->{timestamp}\n";
70 my $rv = $self->$handler;
72 $self->{errmsg
} =~ s/\n$//;
77 ###############################################################################
79 ###############################################################################
83 return $gPhysInfo{ $self->{row
}->{physname
} };
86 ###############################################################################
88 ###############################################################################
91 my $row = $self->{row
};
93 # For each physical item, we store its "real" physical parent in the
94 # 'parentphys' property, then keep a list of additional shared parents in
95 # the 'sharedphys' array.
97 my $parentphys = $row->{parentphys
};
100 if (!defined $parentphys) {
101 # '_' is used as a magic marker for orphaned files
102 $row->{parentphys
} = '_' . $row->{physname
};
103 # $row->{itemname} = $row->{physname} . '_' . $row->{itemname};
107 # the version number could have been changed by the share handler
108 # or in the branch handler, this is the version we branch.
109 my $version = defined $row->{version
} ?
$row->{version
}
112 # if the item to be added was destroyed, then we don't have a version
113 # number here. So we don't need to add the item anyway.
114 if (!defined $version ) {
115 $self->{errmsg
} .= "Attempt to add entry '$row->{physname}' with "
116 . "unknown version number (probably destroyed) parent: $row->{parentphys} itemtype: $row->{itemtype}\n";
118 $gOrphanedInfo {$row->{physname
} } = 1;
122 $gPhysInfo{ $row->{physname
} } =
124 type
=> $row->{itemtype
},
125 name
=> $row->{itemname
},
126 # parentphys => $row->{parentphys},
129 first_version
=> $version,
130 last_version
=> $version,
131 orphaned
=> $orphaned,
132 was_binary
=> $row->{is_binary
},
135 $self->_add_parent ($row->{physname
}, $row->{parentphys
});
136 $self->_track_item_paths ($version);
138 # File was just created so no need to look for shares
139 $self->{itempaths
} = [$self->_get_current_item_path()];
141 # don't convert orphaned items
142 # return $orphaned ? 0 : 1;
146 ###############################################################################
148 ###############################################################################
149 sub _commit_handler
{
151 my $row = $self->{row
};
153 my $physname = $row->{physname
};
154 my $physinfo = $gPhysInfo{$physname};
156 if (!defined $physinfo) {
157 $self->{errmsg
} .= "Attempt to commit unknown item '$physname':\n"
158 . "$self->{physname_seen}\n";
163 $physinfo->{was_binary
} = $row->{is_binary
};
165 # We need to track at least the version number, even if there is no
166 # active parent. This is necessary, if we later share this item, we need
167 # to share from the latest seen version.
169 # remember the last version, in which the file was modified
170 $physinfo->{last_version
} = $row->{version
};
172 # and track all itempaths for the new version
173 $self->_track_item_paths ($row->{version
});
175 my $itempaths = $self->_get_active_item_paths();
176 if (!defined $itempaths && defined $physinfo->{orphaned
}) {
177 $self->{errmsg
} .= "No more active itempath to commit to orphaned item '$physname':\n"
178 . "$self->{physname_seen}\n";
183 $self->{itempaths
} = $itempaths;
185 if (!defined $self->{itempaths
}) {
186 $self->{errmsg
} .= "No more active itempath to commit to '$physname':\n"
187 . "$self->{physname_seen}\n";
193 } # End _commit_handler
195 ###############################################################################
197 ###############################################################################
198 sub _rename_handler
{
200 my $row = $self->{row
};
202 my $physname = $row->{physname
};
203 my $physinfo = $gPhysInfo{$physname};
205 if (!defined $physinfo) {
206 # only report an error, if the file wasn't detected as orphaned.
207 if (!defined $gOrphanedInfo {$physname}) {
208 $self->{errmsg
} .= "Attempt to rename unknown item '$physname':\n"
209 . "$self->{physname_seen}\n";
215 # Get the existing paths before the rename; info will contain the new name
216 my $itempaths = $self->_get_vivid_item_paths();
218 # Renames on shares may show up once for each share, which we don't want
219 # since one rename takes care of all locations. If the "new" name is
220 # already the same as the old, just ignore it.
221 if ($physinfo->{name
} eq $row->{info
}) {
225 # A rename of an item renames it in all its shares
226 $physinfo->{name
} = $row->{info
};
228 # no need to track the itempathes, since a rename doesn't create a new
231 $self->{itempaths
} = $itempaths;
232 $self->{info
} = $row->{info
};
235 } # End _rename_handler
237 ###############################################################################
239 ###############################################################################
242 my $row = $self->{row
};
244 my $physname = $row->{physname
};
245 my $physinfo = $gPhysInfo{$physname};
247 if (!defined $physinfo) {
248 $self->{errmsg
} .= "Attempt to share unknown item '$physname':\n"
249 . "$self->{physname_seen}\n";
254 my $version = $row->{version
};
255 $version = $physinfo->{last_version
} if (!defined $version);
257 $row->{is_binary
} = $physinfo->{was_binary
};
259 # 'itempath' is the path for this new location (the share target);
260 # note: since we can share from a orphaned item, we use the itemname that
261 # is provided in the row information for the share target and not the
262 # current name of the item. The orphaned name is mangeled to make it unique
263 my $parentpath = $self->_get_current_parent_path ();
264 my $itempath = $parentpath . $row->{itemname
};
266 # a SHARE *can* rename a file if the parent is no longer present.
267 $row->{info
} = $row->{itemname
};
268 $self->_rename_handler();
270 # 'sourceinfo' contains the source path
271 my $sourceinfo = $self->_get_valid_path ($physname, $row->{parentphys
}, $version);
273 if (!defined($sourceinfo)) {
274 # We can't figure out the path for the parent that this share came from,
275 # so it was either destroyed or corrupted. That means that this isn't
276 # a share anymore; it's a new add.
278 $self->{action
} = 'ADD';
281 # track the addition of the new parent
282 $self->_add_parent ($physname, $row->{parentphys
});
284 # if this is a share+pin action, then remember the pin version
285 if (defined $row->{version
}) {
286 $physinfo->{parents
}->{$row->{parentphys
}}->{pinned
} = $row->{version
};
289 $self->{itempaths
} = [$itempath];
290 $self->{info
} = $sourceinfo;
291 $self->{version
} = $version;
293 # the share target is now also a valid "copy from" itempath
294 $self->_track_item_path ($physname, $row->{parentphys
}, $version, $itempath);
297 } # End _share_handler
299 ###############################################################################
301 ###############################################################################
302 sub _branch_handler
{
304 my $row = $self->{row
};
306 # Branching a file is actually a null action in SVN; it simply means we
307 # stop duplicating checkins. Return the existing path, but internally
308 # we'll remove this parent from the list of shared physical parents from
309 # the old location, then create a new one with the pertinent info. The row's
310 # 'physname' is that of the new file; 'info' is the formerly shared file.
312 my $physname = $row->{physname
};
313 my $oldphysname = $row->{info
};
315 my $oldphysinfo = $gPhysInfo{$oldphysname};
317 if (!defined $oldphysinfo) {
318 $self->{errmsg
} .= "Attempt to branch unknown item '$oldphysname':\n"
319 . "$self->{physname_seen}\n";
324 my $version = defined $row->{version
} ?
$row->{version
}
327 # if we branch into a destroyed object, delete is the logical choice
328 if (!defined $version ) {
329 $self->{errmsg
} .= "Attempt to branch '$oldphysname' into "
330 . "'$physname' at an unknown version number "
331 . "('$physname' probably destroyed)\n";
332 $gOrphanedInfo{$physname} = 1;
333 $self->{action
} = 'DELETE';
334 $row->{physname
} = $oldphysname;
335 $row->{info
} = undef;
336 return $self->_delete_handler();
339 # treat the old path as deleted
340 # we can't branch an item, that doesn't have a parent. This happens when the
341 # parent was destroyed.
342 if (defined $row->{parentphys
}) {
343 $oldphysinfo->{parents
}->{$row->{parentphys
}}->{deleted
} = 1;
346 # since we have the "orphaned" handling, we can map this action to an
347 # addition, so that this item will show up in the orphaned cache.
348 # TODO: To keep the history of the item we can try to ShareBranch
349 # from original item if it is also somewhere accessible.
351 # my $copypath = $self->_get_valid_path ($oldphysinfo, $row->{parentphys}, $row->{version});
353 $self->{action
} = 'ADD';
356 # Now treat the new entry as a new addition
357 my $result = $self->_add_handler();
359 # remember the ancestor of this item, we need it, when we later whant to refer to versions prior
360 # to the branch, e.g in PIN situations.
362 $gPhysInfo{ $row->{physname
} }->{ancestor
} = $oldphysname;
364 $self->{info
} = $oldphysname;
368 } # End _branch_handler
370 ###############################################################################
372 ###############################################################################
374 my($self, $oldName) = @_;
375 my $row = $self->{row
};
377 my $physname = $row->{physname
};
378 my $physinfo = $gPhysInfo{$physname};
380 if (!defined $physinfo) {
381 $self->{errmsg
} .= "Attempt to move unknown item '$physname':\n"
382 . "$self->{physname_seen}\n";
387 # row->{info} contains the source parent
388 # row->{parentphys} contains the target parent
390 # check the source path
391 if (!defined $row->{info
}) {
392 # Check if this is an orphaned item
393 if (defined $physinfo->{orphaned
}) {
394 $row->{info
} = '_' . $physname;
395 undef $physinfo->{orphaned
};
397 # Don't know from where to move. Share it there instead
398 $self->{action
} = 'SHARE';
399 return $self->_share_handler();
403 # check the target path
404 if (!defined ($row->{parentphys
})) {
405 # the target directory was destroyed, so there is no apropriate move
406 # target information. Fall back to a move to the orphaned cache
407 $physinfo->{orphaned
} = 1;
408 $row->{parentphys
} = '_' . $row->{physname
};
411 # '$sourceinfo' is the path for the old location (the move source);
412 my $sourceparent = $self->_get_parent_path ($row->{info
});
414 if (defined $oldName)
416 $sourceinfo = $sourceparent . $oldName;
420 $sourceinfo = $sourceparent . $row->{itemname
};
423 # '$itempath' contains the move target path
424 my $parentpath = $self->_get_current_parent_path ();
425 my $itempath = $parentpath . $physinfo->{name
}; # $row->{itemname};
428 if (!defined($sourceparent)) {
429 # We can't figure out the path for the parent that this move came from,
430 # so it was either destroyed or corrupted. That means that this isn't
431 # a move anymore; it's a new add.
433 $self->{action
} = 'ADD';
437 # set the old parent inactive
438 $physinfo->{parents
}->{$row->{info
}}->{deleted
} = 1;
441 # if the item mysteriously changed name during the move
442 $physinfo->{name
} = $row->{itemname
};
444 # track the addition of the new parent
445 $self->_add_parent ($physname, $row->{parentphys
});
447 $self->{itempaths
} = [$itempath];
448 $self->{info
} = $sourceinfo;
450 # the move target is now also a valid "copy from" itempath
451 $self->_track_item_path ($physname, $row->{parentphys
}, $physinfo->{last_version
}, $itempath);
454 } # End _move_handler
456 ###############################################################################
458 ###############################################################################
459 sub _restore_handler
{
461 my $row = $self->{row
};
463 $self->{action
} = 'MOVE';
464 $row->{actiontype
} = 'MOVE';
465 # $row->{info} = $row->{parentphys};
466 # $row->{parentphys} = '_' . $row->{physname};
468 $gPhysInfo{ $row->{physname
} } =
470 type
=> $row->{itemtype
},
471 name
=> $row->{itemname
},
476 was_binary
=> $row->{is_binary
},
479 my $newName = $row->{info
};
483 return $self->_move_handler ($newName);
486 ###############################################################################
488 ###############################################################################
489 sub _delete_handler
{
491 my $row = $self->{row
};
493 # For a delete operation we return the path of the item to be deleted
495 my $physname = $row->{physname
};
496 my $physinfo = $gPhysInfo{$physname};
498 if (!defined $physinfo) {
499 # only report an error, if the file wasn't detected as orphaned.
500 if (!defined $gOrphanedInfo {$physname}) {
501 $self->{errmsg
} .= "Attempt to delete unknown item '$physname':\n"
502 . "$self->{physname_seen}\n";
507 my $parentpath = $self->_get_current_parent_path ();
508 my $itempaths = [$parentpath . $physinfo->{name
}];
510 # protect for delete/purge cycles: if the parentphys isn't in the shares
511 # anymore, the file was already deleted from the parent and is now purged
512 if (defined $physinfo->{parents
}->{$row->{parentphys
}}->{deleted
}) {
516 # set the parent inactive
517 $physinfo->{parents
}->{$row->{parentphys
}}->{deleted
} = 1;
519 $self->{itempaths
} = $itempaths;
523 } # End _delete_handler
525 ###############################################################################
527 ###############################################################################
528 sub _recover_handler
{
530 my $row = $self->{row
};
532 my $physname = $row->{physname
};
533 my $physinfo = $gPhysInfo{$physname};
535 if (!defined $physinfo) {
536 # only report an error, if the file wasn't detected as orphaned.
537 if (!defined $gOrphanedInfo {$physname}) {
538 $self->{errmsg
} .= "Attempt to recover unknown item '$physname':\n"
539 . "$self->{physname_seen}\n";
545 # recover this item within the current parent
546 my $parentinfo = $physinfo->{parents
}->{$row->{parentphys
}};
547 if (!defined $parentinfo->{deleted
}) {
548 $self->{errmsg
} .= "Attempt to recover an active item '$physname':\n"
549 . "$self->{physname_seen}\n";
553 undef $parentinfo->{deleted
};
555 # We only recover the path explicitly set in this row, so build the path
556 # ourself by taking the path of this parent and appending the name
557 my $parentpath = $self->_get_current_parent_path();
558 my $itempath = $parentpath . $physinfo->{name
};
560 # Since the item could be modified between the delete and the recovery,
561 # we need to find a valid source for the recover
563 $self->_get_valid_path ($physname, $row->{parentphys
}, $row->{version
});
564 $self->{itempaths
} = [$itempath];
566 # We only set the version number, if this item is a file item. If it is a
567 # project item, we must recover from the last known revision, which is
568 # determined in the dumpfile handler
569 if ($row->{itemtype
} == 2) {
570 $self->{version
} = $physinfo->{last_version
};
574 } # End _recover_handler
576 ###############################################################################
578 ###############################################################################
581 my $row = $self->{row
};
583 my $physname = $row->{physname
};
584 my $physinfo = $gPhysInfo{$physname};
586 if (!defined $physinfo) {
587 $self->{errmsg
} .= "Attempt to pin unknown item '$physname':\n"
588 . "$self->{physname_seen}\n";
593 my $parentpath = $self->_get_current_parent_path();
594 my $itempath = $parentpath . $physinfo->{name
};
596 my $parentinfo = \
%{$physinfo->{parents
}->{$row->{parentphys
}}};
598 # depending on the version number of the PIN/UNPIN action, we don't have
599 # to convert this action into a real commit. In this case we only have to
601 my $change_action = 1;
603 my $version = $row->{version
};
604 if (!defined $row->{version
}) {
605 # this is the unpin handler
607 # is this the unpin version and the last version identically?
608 $change_action = 0 if (defined $parentinfo->{pinned
}
609 && $parentinfo->{pinned
} == $physinfo->{last_version
} );
611 undef $parentinfo->{pinned
};
612 $version = $physinfo->{last_version
};
615 # is this the pin version and the last version identically?
616 # since the UNPIN/PIN merge, it is possible, that the item can still be
618 $change_action = 0 if ($row->{version
} == $physinfo->{last_version
}
619 && !defined $parentinfo->{pinned
});
621 $parentinfo->{pinned
} = $row->{version
};
624 $self->{itempaths
} = [$itempath];
626 $self->_get_valid_path ($physname, $row->{parentphys
}, $row->{version
});
627 $self->{version
} = $version;
629 # the unpinned target is now also a valid "copy from" itempath
630 $self->_track_item_path ($physname, $row->{parentphys
}, $version, $itempath);
632 return $change_action;
635 ###############################################################################
637 ###############################################################################
639 # currently the handler only tracks labels that where assigned to files
640 # we need this for the item name tracking
642 my $row = $self->{row
};
644 my $itempaths = $self->_get_active_item_paths();
646 $self->_track_item_paths ($row->{version
});
648 $self->{itempaths
} = $itempaths;
649 $self->{info
} = $row->{label
};
652 } # End _label_handler
654 ###############################################################################
655 # _get_current_parent_path
656 ###############################################################################
657 sub _get_current_parent_path
{
660 return $self->_get_parent_path($self->{row
}->{parentphys
});
661 } # End _get_current_parent_path
664 ###############################################################################
666 ###############################################################################
667 sub _get_parent_path
{
668 my($self, $physname) = @_;
670 # Uses recursion to determine the current full paths for an item based on
671 # the name of its physical file. We can't cache this information because
672 # a rename in a parent folder would not immediately trigger a rename in
673 # all of the child items.
675 # By default, we return an anonymous array of all paths in which the item
676 # is shared, unless $mainonly is true. Luckily, only files can be shared,
677 # not projects, so once we start recursing we can set $mainonly to true.
679 if ($self->{verbose
}) {
680 my $physprint = (defined $physname)?
$physname : '!UNDEF';
681 my $space = ($self->{recursed
})?
' ' : '';
682 print "${space}_get_parent_path($physprint)\n";
685 if (++($self->{recursed
}) >= 1000) {
686 $self->{errmsg
} .= "Infinite recursion detected while looking up "
687 . "parent for '$physname':\n$self->{physname_seen}\n";
692 if (!defined($physname)) {
696 if ($physname eq '') {
700 if ($physname eq 'AAAAAAAA') {
701 # End of recursion; all items must go back to 'AAAAAAAA', which was so
702 # named because that's what most VSS users yell after using it much. :-)
703 return $self->{trunkdir
} . '/';
706 if ($physname =~ m/^_.*/) {
707 # End of recursion; this is the orphaned node
708 # return the name of the orphaned directory + the name of the orphaned
709 # file in order to make the path unique
710 return '/orphaned/' . $physname . '/';
713 my $physinfo = $gPhysInfo{$physname};
715 if (!defined $physinfo) {
716 $self->{errmsg
} .= "Could not determine real path for '$physname':\n"
717 . "$self->{physname_seen}\n";
722 $self->{physname_seen
} .= "$physname, ";
724 # In a move szenario, we can have one deleted and one active parent. We
725 # are only interested in the active ones here.
726 my @pathstoget = $self->_get_active_parents ($physname);
728 # TODO: For projects there should be only one active parent
729 my $parent = $pathstoget[0];
731 # if we don't have any active parents, the item path itself is deleted
732 if (!defined ($parent)) {
738 $result = $self->_get_parent_path($pathstoget[0], 1);
740 if(!defined($result)) {
744 return $result . $physinfo->{name
};
746 } # End _get_parent_path
748 ###############################################################################
749 # _get_current_item_paths
750 ###############################################################################
751 sub _get_current_item_paths
{
752 my($self, $mainonly) = @_;
754 my @parents = $self->_get_parents ($self->{row
}->{physname
});
755 return $self->_get_item_paths($self->{row
}->{physname
}, @parents);
756 } # End _get_current_item_paths
758 ###############################################################################
759 # _get_vivid_item_paths
760 ###############################################################################
761 sub _get_vivid_item_paths
{
762 my($self, $mainonly) = @_;
764 my @parents = $self->_get_vivid_parents ($self->{row
}->{physname
});
765 return $self->_get_item_paths($self->{row
}->{physname
}, @parents);
766 } # End _get_vivid_item_paths
768 ###############################################################################
769 # _get_active_item_paths
770 ###############################################################################
771 sub _get_active_item_paths
{
772 my($self, $mainonly) = @_;
774 my @parents = $self->_get_active_parents ($self->{row
}->{physname
});
775 return $self->_get_item_paths($self->{row
}->{physname
}, @parents);
776 } # End _get_active_item_paths
778 ###############################################################################
779 # _get_current_item_path
780 ###############################################################################
781 sub _get_current_item_path
{
784 my @parents = $self->_get_parents ($self->{row
}->{physname
});
786 if (scalar @parents == 0) {
790 my $physname = $self->{row
}->{physname
};
791 my $paths = $self->_get_item_paths($physname, $parents[0]);
793 if (!defined $paths) {
794 $self->{errmsg
} .= "Could not retrieve item path for '$physname': "
795 . "(probably bogous timestamp in parent and child action)\n";
800 } # End _get_current_item_path
802 ###############################################################################
804 ###############################################################################
805 sub _get_item_paths
{
806 my($self, $physname, @parents) = @_;
808 # Uses recursion to determine the current full paths for an item based on
809 # the name of its physical file. We can't cache this information because
810 # a rename in a parent folder would not immediately trigger a rename in
811 # all of the child items.
813 # By default, we return an anonymous array of all paths in which the item
814 # is shared, unless $mainonly is true. Luckily, only files can be shared,
815 # not projects, so once we start recursing we can set $mainonly to true.
817 if ($self->{verbose
}) {
818 my $physprint = (defined $physname)?
$physname : '!UNDEF';
819 my $space = ($self->{recursed
})?
' ' : '';
820 print "${space}_get_item_paths($physprint)\n";
824 if (!defined($physname)) {
828 if ($physname eq 'AAAAAAAA') {
829 # End of recursion; all items must go back to 'AAAAAAAA', which was so
830 # named because that's what most VSS users yell after using it much. :-)
831 return [$self->{trunkdir
} . '/'];
834 if ($physname =~ m/^_.*/) {
835 # End of recursion; this is the orphaned node
836 # return the name of the orphaned directory + the name of the orphaned
837 # file in order to make the path unique
838 return '/orphaned/' . $physname . '/';
841 my $physinfo = $gPhysInfo{$physname};
843 if (!defined $physinfo) {
844 $self->{errmsg
} .= "Could not determine real path for '$physname':\n"
845 . "$self->{physname_seen}\n";
850 $self->{physname_seen
} .= "$physname, ";
852 my @pathstoget = @parents;
858 foreach my $parent (@pathstoget) {
859 if (!defined $parent) {
862 $result = $self->_get_parent_path($parent);
864 if(!defined($result)) {
868 push @
$paths, $result . $physinfo->{name
};
873 } # End _get_item_paths
877 ###############################################################################
879 # This function maintains a map that records the itempath that was valid for
880 # each version of the physical file in the context of the different parents.
881 # This map is needed, e.g. during pinning when a file is pinned to a previous
882 # version. Since the file, could have renamed in between, we need to know the
883 # previous itempath that was valid in the previous version.
885 # This map does not replace the recursive lookup of the itempath in teh function
886 # _get_item_paths. The itempathes stored here are "historic" item pathes.
887 # A rename e.g. is not reflectected in the version history of the physical file
888 # and therefor does not have a distinct version as in subversion.
889 ###############################################################################
890 sub _track_item_paths
{
891 my($self, $version) = @_;
893 my $row = $self->{row
};
895 # we only need to track the path for actions that deal with a specific
897 if (defined $version) {
899 my $physinfo = $gPhysInfo{ $row->{physname
} };
901 my @parents = $self->_get_active_parents ($row->{physname
});
905 foreach my $parent (@parents) {
907 my $parentpath = $self->_get_parent_path ($parent);
908 if (!defined $parentpath) {
911 $result = $parentpath . $physinfo->{name
};
913 $self->_track_item_path ($row->{physname
}, $parent, $row->{version
}, $result);
917 } # End _track_item_paths
920 ###############################################################################
922 ###############################################################################
923 sub _track_item_path
{
924 my($self, $physname, $parent, $version, $itempath) = @_;
926 if (defined $version && defined $itempath) {
928 my $physinfo = $gPhysInfo{ $physname };
930 my $versions = \@
{$physinfo->{parents
}->{$parent}->{versions
}};
932 # in the case of pinning and sharing with pinning, the version number
933 # denotes a version in the past. So if there is already an entry for
934 # this version number skip this parent.
935 if (exists $versions->[$version]) {
939 $versions->[$version] = $itempath;
941 } # End _track_item_path
944 ###############################################################################
946 # This function returns all parents where the physical file is not deleted,
947 # r all active projects. If a file is deleted, the file
948 # does nor take place in any further rename activity, so it is
950 ###############################################################################
951 sub _get_vivid_parents
{
952 my($self, $physname) = @_;
954 my $physinfo = $gPhysInfo{$physname};
957 if (defined $physinfo) {
960 foreach my $parentphys (@
{$physinfo->{order
}}) {
962 # skip orphaned parents
963 # if ($parentphys eq '99999999' ) {
967 my $parent = $physinfo->{parents
}->{$parentphys};
968 if (!defined $parent)
973 # skip deleted parents, since these parents do not
974 # participate in specific vss action
975 if (defined $parent->{deleted
} ) {
979 push @parents, $parentphys;
984 } # End _get_vivid_parents
986 ###############################################################################
987 # _get_active_parents
988 # This function returns all parents where the physical file is not deleted
989 # or pinned, or all active projects. If a file is pinned or deleted, the file
990 # does nor take place in any further checkin or rename activity, so it is
992 ###############################################################################
993 sub _get_active_parents
{
994 my($self, $physname) = @_;
996 my $physinfo = $gPhysInfo{$physname};
999 if (defined $physinfo) {
1002 foreach my $parentphys (@
{$physinfo->{order
}}) {
1004 # skip orphaned parents
1005 # if ($parentphys eq '99999999' ) {
1009 my $parent = $physinfo->{parents
}->{$parentphys};
1010 if (!defined $parent)
1015 # skip deleted or pinned parents, since these parents do not
1016 # participate in any vss action
1017 if (defined $parent->{deleted
} || defined $parent->{pinned
} ) {
1021 push @parents, $parentphys;
1026 } # End _get_active_parents
1028 ###############################################################################
1030 # This function returns all parents for the physical file
1031 ###############################################################################
1033 my($self, $physname) = @_;
1035 my $physinfo = $gPhysInfo{$physname};
1038 if (defined $physinfo) {
1041 foreach my $parentphys (@
{$physinfo->{order
}}) {
1043 # skip orphaned parents
1044 # if ($parentphys eq '99999999' ) {
1048 my $parent = $physinfo->{parents
}->{$parentphys};
1049 if (!defined $parent)
1054 push @parents, $parentphys;
1059 } # End _get_active_parents
1061 ###############################################################################
1063 # This function returns an itempath for the physical file, that was valid in
1064 # the previous version. Since all activities that create new versions of a file
1065 # must be done on an active path, there should be at least one valid item path
1067 # If we can't find any valid itempath, we can not perform a "copy from" revision
1068 # In this case, we need to recheckin the current content of the item
1069 ###############################################################################
1070 sub _get_valid_path
{
1071 my($self, $physname, $parentphys, $version) = @_;
1073 my $physinfo = $gPhysInfo{$physname};
1074 if (!defined $physinfo) {
1078 if (!defined $version) {
1079 $version = $physinfo->{last_version
};
1082 # 0.) If the version we are looking for is prior to the first version of this
1083 # item (e.g in a branch / Pin situation), we need to check the ancestor
1084 if (defined $physinfo &&
1085 $version < $physinfo->{first_version
}) {
1086 return $self->_get_valid_path ($physinfo->{ancestor
}, $parentphys, $version);
1089 # 1.) We first check for non-deleted contexts, even though these item pathes
1090 # are valid. A deleted context means, that an item existed in this context
1091 # in a specific version, but was later deleted from that context. So it is
1092 # normally not possible to perform any further action on this item. Therefore
1093 # we prefer non-deleted contexts.
1094 my $path = $self->_get_valid_path2 ($physname, $parentphys, $version, 0);
1095 return $path if defined $path;
1097 # 2.) now we also check for deleted contexts
1098 $path = $self->_get_valid_path2 ($physname, $parentphys, $version, 1);
1100 } # End _get_valid_path
1102 ###############################################################################
1104 # This function is an internal helper: It will check for active and inactive,
1105 # but valid item pathes, depending on the $deleted flag, see also _get_valid_path
1106 ###############################################################################
1107 sub _get_valid_path2
{
1108 my($self, $physname, $parentphys, $version, $deleted) = @_;
1110 my $physinfo = $gPhysInfo{$physname};
1112 # 1. check the parent requested, if there was an item name for this version
1113 # we can use this item name, since it was valid in that time
1114 my $parent = $physinfo->{parents
}->{$parentphys};
1115 if (defined $parent &&
1116 (!defined $parent->{deleted
} || $deleted == 1) &&
1117 $parent->{versions
}->[$version]) {
1118 return $parent->{versions
}->[$version];
1121 # 2. check all other parents in the order, the where added
1125 foreach $parentphys (@
{$physinfo->{order
}}) {
1127 $parent = $physinfo->{parents
}->{$parentphys};
1128 if (defined $parent &&
1129 (!defined $parent->{deleted
} || $deleted == 1) &&
1130 $parent->{versions
}->[$version]) {
1131 return $parent->{versions
}->[$version];
1136 } # End _get_valid_path2
1138 ###############################################################################
1140 # Track the addition of a new parent to this itempath. This will also track the
1141 # order, in which the parents where added to the physical file. The valid
1142 # itempath lookup will search for valid pathes in the order the parents where
1143 # added to the project.
1144 ###############################################################################
1146 my($self, $physname, $parentphys) = @_;
1148 my $physinfo = $gPhysInfo{$physname};
1149 if (defined $physinfo) {
1150 # check wether this parent was previously deleted
1151 if (defined $physinfo->{parents
}->{$parentphys} &&
1152 defined $physinfo->{parents
}->{$parentphys}->{deleted
}) {
1153 undef $physinfo->{parents
}->{$parentphys}->{deleted
};
1156 $physinfo->{parents
}->{$parentphys} = {};
1157 push @
{ $physinfo->{order
} }, $parentphys;