1 # Copyright 2004 Ed Price.
3 # This file is part of vss2svn2 (see http://vss2svn2.tigris.org/).
5 # vss2svn2 is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # vss2svn2 is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with vss2svn2; if not, write to the Free Software Foundation,
17 # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 our $VERSION = '0.5.6';
25 VssPath - unambiguous specification of a version of a VSS item.
29 A VssPath is like a regular "path" except that each component of the
30 path has a version number. So it's a "versioned path", consisting of
31 a list of (Version, Name) pairs.
33 A VssPath unambiguously identifies a unique version of a "node" in a
38 * Version is a positive integer.
39 * Name is a string (without any "/" characters).
41 Addtionally, although it's not currently enforced here, the following
42 appear generally true of VSS:
44 * root node has empty Name (zero-length "").
45 * non-root node must have non-empty Name.
46 * only the last path component can have version 1.
48 =head2 Textual representation
50 * each path "component" represented as "<version>:<name>".
51 * multiple components separated by slash ("/").
56 * toplevel dir => 2:/1:foo
57 * toplevel file => 2:/1:foo
59 =head2 Note on uniqueness
61 Although a VssPath is unambiguous, it is I<not> unique. There can be
62 (and generally are) multiple VssPaths which identify the same version
65 For example, consider the following history:
71 Note that there is only one version of "/foo", but it can be accessed
72 through both v2 and v3 of the root.
74 So the following are equivalent:
89 my ($class, $string) = @_;
90 my $self = defined $string ? _parse_string
($string) : [];
97 return VssPath
->new($self->toString);
102 my ($self, $name, $version) = @_;
103 _validate
($name, $version);
104 push @
$self, [$name, $version];
108 # returns [name, version] of the "end" of the path, and removes it from the path.
109 # returns undef (???) if path is root. (huh? doesnt root have name and version??)
117 # returns textual representation
125 my ($name, $version) = @
$_;
126 $ans .= "/" if defined $ans;
127 $ans .= "$version:$name";
132 # returns "normal" unversioned path (as string).
140 my ($name, $version) = @
$_;
141 $ans .= "/" if defined $ans;
147 # parses textual representation
151 my ($class, $string) = @_;
152 bless _parse_string
($string), $class;
155 # returns array of [name, version] pairs
160 return @
{$self->copy};
163 # return 'depth' of path. root is considered depth zero, "/foo" is 1, etc.
170 return scalar @
$self;
173 # this returns true if self matches a PREFIX of another given VssPath.
174 # "matching" means that names are equal, and version numbers in self
175 # are GREATER THAN OR EQUAL to those in the other VssPath.
179 # self other matchesPrefix?
180 # -------- -------- ------------------------------------------
183 # 1: 2: no (version is less than other)
184 # 1:/1:foo 1: no (cant match prefix of shorter path)
185 # 1: 1:/1:foo yes (1: matches 1:)
186 # 2: 1:/1:foo yes (2: matches 1:)
187 # 1: 2:/1:foo no (1: does not match 2:)
188 # 1:/1:foo 1:/1:foo yes (complete match)
189 # 2:/2:foo 1:/1:foo yes (complete match)
190 # 1:/1:foo 1:/2:foo no (1:foo version less than 2:foo)
194 my ($self, $other) = @_;
196 my @self_stack = @
$self;
197 my @other_stack = @
$other;
199 # longer path cant match prefix of shorter path.
200 return 0 if $#self_stack > $#other_stack;
202 for (my $i = 0; $i <= $#self_stack; $i++)
204 my ($self_name, $self_version) = @
{$self_stack[$i]};
205 my ($other_name, $other_version) = @
{$other_stack[$i]};
207 return 0 if $self_name ne $other_name; # XXX case sensitivity?
208 return 0 if $self_version < $other_version;
221 croak
"bad input (trailing slash): $string" if $string =~ m
|/$|;
222 my @pieces = split /\//, $string;
223 croak
"bad input (no pieces?!): $string" unless @pieces;
227 croak
"bad input (piece): $_ of $string" unless m
|(\d
+):(.*)|;
228 my ($version, $name) = ($1, $2);
229 _validate
($name, $version);
230 CORE
::push @
$ans, [$name, $version];
237 my ($name, $version) = @_;
238 _validate_name
($name);
239 _validate_version
($version);
245 croak
"slash ('/') in name: $name" if $name =~ m
|/|;
248 sub _validate_version
251 croak
"non-decimal version: $version" unless $version =~ m
|^\d
+$|;
252 croak
"non-integer version: $version" unless $version eq int $version;
253 croak
"non-positive version: $version" unless $version > 0;