Remove now-unused function CVSPath.get_filename().
[cvs2svn.git] / cvs2svn_lib / cvs_path.py
blob1ff1a769f4cc4e51f9bd4c36fb60bb393c7db79d
1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2000-2008 CollabNet. All rights reserved.
6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution. The terms
8 # are also available at http://subversion.tigris.org/license-1.html.
9 # If newer versions of this license are posted there, you may use a
10 # newer version instead, at your option.
12 # This software consists of voluntary contributions made by many
13 # individuals. For exact contribution history, see the revision
14 # history and logs, available at http://cvs2svn.tigris.org/.
15 # ====================================================================
17 """Classes that represent files and directories within CVS repositories."""
19 import os
21 from cvs2svn_lib.common import path_join
22 from cvs2svn_lib.context import Ctx
25 class CVSPath(object):
26 """Represent a CVS file or directory.
28 Members:
30 id -- (int) unique ID for this CVSPath. At any moment, there is
31 at most one CVSPath instance with a particular ID. (This
32 means that object identity is the same as object equality, and
33 objects can be used as map keys even though they don't have a
34 __hash__() method).
36 project -- (Project) the project containing this CVSPath.
38 parent_directory -- (CVSDirectory or None) the CVSDirectory
39 containing this CVSPath.
41 basename -- (string) the base name of this CVSPath (no ',v'). The
42 basename of the root directory of a project is ''.
44 filename -- (string) the filesystem path to this CVSPath in the
45 CVS repository. This is in native format, and already
46 normalised the way os.path.normpath() normalises paths. It
47 starts with the repository path passed to
48 run_options.add_project() in the options.py file.
50 ordinal -- (int) the order that this instance should be sorted
51 relative to other CVSPath instances. This member is set based
52 on the ordering imposed by sort_key() by CVSPathDatabase after
53 all CVSFiles have been processed. Comparisons of CVSPath
54 using __cmp__() simply compare the ordinals.
56 """
58 __slots__ = [
59 'id',
60 'project',
61 'parent_directory',
62 'basename',
63 'ordinal',
64 'filename',
67 def __init__(self, id, project, parent_directory, basename):
68 self.id = id
69 self.project = project
70 self.parent_directory = parent_directory
71 self.basename = basename
73 # The filename used to be computed on demand, but it turned out to
74 # be a hot path through the code in some cases. It's used by
75 # SubtreeSymbolTransform and similar transforms, so it's called at
76 # least:
78 # (num_files * num_symbols_per_file * num_subtree_symbol_transforms)
80 # times. On a large repository with several subtree symbol
81 # transforms, that can exceed 100,000,000 calls. And
82 # _calculate_filename() is quite complex, so doing that every time
83 # could add about 10 minutes to the cvs2svn runtime.
85 # So now we precalculate this and just return it.
86 self.filename = os.path.normpath(self._calculate_filename())
88 def __getstate__(self):
89 """This method must only be called after ordinal has been set."""
91 return (
92 self.id, self.project.id,
93 self.parent_directory, self.basename,
94 self.ordinal,
97 def __setstate__(self, state):
99 self.id, project_id,
100 self.parent_directory, self.basename,
101 self.ordinal,
102 ) = state
103 self.project = Ctx()._projects[project_id]
104 self.filename = os.path.normpath(self._calculate_filename())
106 def get_ancestry(self):
107 """Return a list of the CVSPaths leading from the root path to SELF.
109 Return the CVSPaths in a list, starting with
110 self.project.get_root_cvs_directory() and ending with self."""
112 ancestry = []
113 p = self
114 while p is not None:
115 ancestry.append(p)
116 p = p.parent_directory
118 ancestry.reverse()
119 return ancestry
121 def get_cvs_path(self):
122 """Return the canonical path within the Project.
124 The canonical path:
126 - Uses forward slashes
128 - Doesn't include ',v' for files
130 - This doesn't include the 'Attic' segment of the path unless the
131 file is to be left in an Attic directory in the SVN repository;
132 i.e., if a filename exists in and out of Attic and the
133 --retain-conflicting-attic-files option was specified.
137 return path_join(*[p.basename for p in self.get_ancestry()[1:]])
139 cvs_path = property(get_cvs_path)
141 def _get_dir_components(self):
142 """Return a list containing the components of the path leading to SELF.
144 The return value contains the base names of all of the parent
145 directories (except for the root directory) and SELF."""
147 return [p.basename for p in self.get_ancestry()[1:]]
149 def __eq__(a, b):
150 """Compare two CVSPath instances for equality.
152 This method is supplied to avoid using __cmp__() for comparing for
153 equality."""
155 return a is b
157 def sort_key(self):
158 """Return the key that should be used for sorting CVSPath instances.
160 This is a relatively expensive computation, so it is only used
161 once, the the results are used to set the ordinal member."""
163 return (
164 # Sort first by project:
165 self.project,
166 # Then by directory components:
167 self._get_dir_components(),
170 def __cmp__(a, b):
171 """This method must only be called after ordinal has been set."""
173 return cmp(a.ordinal, b.ordinal)
176 class CVSDirectory(CVSPath):
177 """Represent a CVS directory.
179 Members:
181 id -- (int or None) unique id for this file. If None, a new id is
182 generated.
184 project -- (Project) the project containing this file.
186 parent_directory -- (CVSDirectory or None) the CVSDirectory
187 containing this CVSDirectory.
189 basename -- (string) the base name of this CVSDirectory (no ',v').
191 ordinal -- (int) the order that this instance should be sorted
192 relative to other CVSPath instances. See CVSPath.ordinal.
194 empty_subdirectory_ids -- (list of int) a list of the ids of any
195 direct subdirectories that are empty. (An empty directory is
196 defined to be a directory that doesn't contain any RCS files
197 or non-empty subdirectories.
201 __slots__ = ['empty_subdirectory_ids']
203 def __init__(self, id, project, parent_directory, basename):
204 """Initialize a new CVSDirectory object."""
206 CVSPath.__init__(self, id, project, parent_directory, basename)
208 # This member is filled in by CollectData.close():
209 self.empty_subdirectory_ids = []
211 def _calculate_filename(self):
212 """Return the filesystem path to this CVSPath in the CVS repository."""
214 if self.parent_directory is None:
215 return self.project.project_cvs_repos_path
216 else:
217 return os.path.join(
218 self.parent_directory.filename, self.basename
221 def __getstate__(self):
222 return (
223 CVSPath.__getstate__(self),
224 self.empty_subdirectory_ids,
227 def __setstate__(self, state):
229 cvs_path_state,
230 self.empty_subdirectory_ids,
231 ) = state
232 CVSPath.__setstate__(self, cvs_path_state)
234 def __str__(self):
235 """For convenience only. The format is subject to change at any time."""
237 return self.cvs_path + '/'
239 def __repr__(self):
240 return 'CVSDirectory<%x>(%r)' % (self.id, str(self),)
243 class CVSFile(CVSPath):
244 """Represent a CVS file.
246 Members:
248 id -- (int) unique id for this file.
250 project -- (Project) the project containing this file.
252 parent_directory -- (CVSDirectory) the CVSDirectory containing
253 this CVSFile.
255 basename -- (string) the base name of this CVSFile (no ',v').
257 ordinal -- (int) the order that this instance should be sorted
258 relative to other CVSPath instances. See CVSPath.ordinal.
260 _in_attic -- (bool) True if RCS file is in an Attic subdirectory
261 that is not considered the parent directory. (If a file is
262 in-and-out-of-attic and one copy is to be left in Attic after
263 the conversion, then the Attic directory is that file's
264 PARENT_DIRECTORY and _IN_ATTIC is False.)
266 executable -- (bool) True iff RCS file has executable bit set.
268 file_size -- (long) size of the RCS file in bytes.
270 mode -- (string or None) 'kkv', 'kb', etc., as read from the CVS
271 file.
273 description -- (string or None) the file description as read from
274 the RCS file.
276 properties -- (dict) file properties that are preserved across
277 this history of this file. Keys are strings; values are
278 strings (indicating the property value) or None (indicating
279 that the property should be left unset). These properties can
280 be overridden by CVSRevision.properties. Different backends
281 can use these properties for different purposes; for cvs2svn
282 they become SVN versioned properties. Properties whose names
283 start with underscore are reserved for internal cvs2svn
284 purposes.
286 PARENT_DIRECTORY might contain an 'Attic' component if it should be
287 retained in the SVN repository; i.e., if the same filename exists out
288 of Attic and the --retain-conflicting-attic-files option was specified.
292 __slots__ = [
293 '_in_attic',
294 'executable',
295 'file_size',
296 'mode',
297 'description',
298 'properties',
301 def __init__(
302 self, id, project, parent_directory, basename, in_attic,
303 executable, file_size, mode, description
305 """Initialize a new CVSFile object."""
307 assert parent_directory is not None
309 # This member is needed by _calculate_filename(), which is called
310 # by CVSPath.__init__(). So initialize it before calling
311 # CVSPath.__init__().
312 self._in_attic = in_attic
313 CVSPath.__init__(self, id, project, parent_directory, basename)
315 self.executable = executable
316 self.file_size = file_size
317 self.mode = mode
318 self.description = description
319 self.properties = None
321 def determine_file_properties(self, file_property_setters):
322 """Determine the properties for this file from FILE_PROPERTY_SETTERS.
324 This must only be called after SELF.mode and SELF.description have
325 been set by CollectData."""
327 self.properties = {}
329 for file_property_setter in file_property_setters:
330 file_property_setter.set_properties(self)
332 def _calculate_filename(self):
333 """Return the filesystem path to this CVSPath in the CVS repository."""
335 if self._in_attic:
336 return os.path.join(
337 self.parent_directory.filename, 'Attic', self.basename + ',v'
339 else:
340 return os.path.join(
341 self.parent_directory.filename, self.basename + ',v'
344 def __getstate__(self):
345 return (
346 CVSPath.__getstate__(self),
347 self._in_attic, self.executable, self.file_size, self.mode,
348 self.description, self.properties,
351 def __setstate__(self, state):
353 cvs_path_state,
354 self._in_attic, self.executable, self.file_size, self.mode,
355 self.description, self.properties,
356 ) = state
357 CVSPath.__setstate__(self, cvs_path_state)
359 def __str__(self):
360 """For convenience only. The format is subject to change at any time."""
362 return self.cvs_path
364 def __repr__(self):
365 return 'CVSFile<%x>(%r)' % (self.id, str(self),)