diff options
Diffstat (limited to 'cvs2svn_lib/cvs_file.py')
-rw-r--r-- | cvs2svn_lib/cvs_file.py | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/cvs2svn_lib/cvs_file.py b/cvs2svn_lib/cvs_file.py new file mode 100644 index 0000000..3a1bb4f --- /dev/null +++ b/cvs2svn_lib/cvs_file.py @@ -0,0 +1,287 @@ +# (Be in -*- python -*- mode.) +# +# ==================================================================== +# Copyright (c) 2000-2008 CollabNet. All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://subversion.tigris.org/license-1.html. +# If newer versions of this license are posted there, you may use a +# newer version instead, at your option. +# +# This software consists of voluntary contributions made by many +# individuals. For exact contribution history, see the revision +# history and logs, available at http://cvs2svn.tigris.org/. +# ==================================================================== + +"""This module contains a class to store information about a CVS file.""" + +import os + +from cvs2svn_lib.common import path_join +from cvs2svn_lib.context import Ctx + + +class CVSPath(object): + """Represent a CVS file or directory. + + Members: + + id -- (int) unique ID for this CVSPath. At any moment, there is + at most one CVSPath instance with a particular ID. (This + means that object identity is the same as object equality, and + objects can be used as map keys even though they don't have a + __hash__() method). + + project -- (Project) the project containing this CVSPath. + + parent_directory -- (CVSDirectory or None) the CVSDirectory + containing this CVSPath. + + basename -- (string) the base name of this CVSPath (no ',v'). The + basename of the root directory of a project is ''. + + ordinal -- (int) the order that this instance should be sorted + relative to other CVSPath instances. This member is set based + on the ordering imposed by slow_compare() by CollectData after + all CVSFiles have been processed. Comparisons of CVSPath + using __cmp__() simply compare the ordinals. + + """ + + __slots__ = [ + 'id', + 'project', + 'parent_directory', + 'basename', + 'ordinal', + ] + + def __init__(self, id, project, parent_directory, basename): + self.id = id + self.project = project + self.parent_directory = parent_directory + self.basename = basename + + def __getstate__(self): + """This method must only be called after ordinal has been set.""" + + return ( + self.id, self.project.id, + self.parent_directory, self.basename, + self.ordinal, + ) + + def __setstate__(self, state): + ( + self.id, project_id, + self.parent_directory, self.basename, + self.ordinal, + ) = state + self.project = Ctx()._projects[project_id] + + def get_ancestry(self): + """Return a list of the CVSPaths leading from the root path to SELF. + + Return the CVSPaths in a list, starting with + self.project.get_root_cvs_directory() and ending with self.""" + + ancestry = [] + p = self + while p is not None: + ancestry.append(p) + p = p.parent_directory + + ancestry.reverse() + return ancestry + + def get_cvs_path(self): + """Return the canonical path within the Project. + + The canonical path: + + - Uses forward slashes + + - Doesn't include ',v' for files + + - This doesn't include the 'Attic' segment of the path unless the + file is to be left in an Attic directory in the SVN repository; + i.e., if a filename exists in and out of Attic and the + --retain-conflicting-attic-files option was specified. + + """ + + return path_join(*[p.basename for p in self.get_ancestry()[1:]]) + + cvs_path = property(get_cvs_path) + + def _get_dir_components(self): + """Return a list containing the components of the path leading to SELF. + + The return value contains the base names of all of the parent + directories (except for the root directory) and SELF.""" + + return [p.basename for p in self.get_ancestry()[1:]] + + def __eq__(a, b): + """Compare two CVSPath instances for equality. + + This method is supplied to avoid using __cmp__() for comparing for + equality.""" + + return a is b + + def slow_compare(a, b): + return ( + # Sort first by project: + cmp(a.project, b.project) + # Then by directory components: + or cmp(a._get_dir_components(), b._get_dir_components()) + ) + + def __cmp__(a, b): + """This method must only be called after ordinal has been set.""" + + return cmp(a.ordinal, b.ordinal) + + +class CVSDirectory(CVSPath): + """Represent a CVS directory. + + Members: + + id -- (int or None) unique id for this file. If None, a new id is + generated. + + project -- (Project) the project containing this file. + + parent_directory -- (CVSDirectory or None) the CVSDirectory + containing this CVSDirectory. + + basename -- (string) the base name of this CVSDirectory (no ',v'). + + """ + + __slots__ = [] + + def __init__(self, id, project, parent_directory, basename): + """Initialize a new CVSDirectory object.""" + + CVSPath.__init__(self, id, project, parent_directory, basename) + + def get_filename(self): + """Return the filesystem path to this CVSPath in the CVS repository.""" + + if self.parent_directory is None: + return self.project.project_cvs_repos_path + else: + return os.path.join( + self.parent_directory.get_filename(), self.basename + ) + + filename = property(get_filename) + + def __getstate__(self): + return CVSPath.__getstate__(self) + + def __setstate__(self, state): + CVSPath.__setstate__(self, state) + + def __str__(self): + """For convenience only. The format is subject to change at any time.""" + + return self.cvs_path + '/' + + def __repr__(self): + return 'CVSDirectory<%x>(%r)' % (self.id, str(self),) + + +class CVSFile(CVSPath): + """Represent a CVS file. + + Members: + + id -- (int) unique id for this file. + + project -- (Project) the project containing this file. + + parent_directory -- (CVSDirectory) the CVSDirectory containing + this CVSFile. + + basename -- (string) the base name of this CVSFile (no ',v'). + + _in_attic -- (bool) True if RCS file is in an Attic subdirectory + that is not considered the parent directory. (If a file is + in-and-out-of-attic and one copy is to be left in Attic after + the conversion, then the Attic directory is that file's + PARENT_DIRECTORY and _IN_ATTIC is False.) + + executable -- (bool) True iff RCS file has executable bit set. + + file_size -- (long) size of the RCS file in bytes. + + mode -- (string or None) 'kkv', 'kb', etc. + + PARENT_DIRECTORY might contain an 'Attic' component if it should be + retained in the SVN repository; i.e., if the same filename exists out + of Attic and the --retain-conflicting-attic-files option was specified. + + """ + + __slots__ = [ + '_in_attic', + 'executable', + 'file_size', + 'mode', + ] + + def __init__( + self, id, project, parent_directory, basename, in_attic, + executable, file_size, mode + ): + """Initialize a new CVSFile object.""" + + CVSPath.__init__(self, id, project, parent_directory, basename) + self._in_attic = in_attic + self.executable = executable + self.file_size = file_size + self.mode = mode + + assert self.parent_directory is not None + + def get_filename(self): + """Return the filesystem path to this CVSPath in the CVS repository.""" + + if self._in_attic: + return os.path.join( + self.parent_directory.filename, 'Attic', self.basename + ',v' + ) + else: + return os.path.join( + self.parent_directory.filename, self.basename + ',v' + ) + + filename = property(get_filename) + + def __getstate__(self): + return ( + CVSPath.__getstate__(self), + self._in_attic, self.executable, self.file_size, self.mode, + ) + + def __setstate__(self, state): + ( + cvs_path_state, + self._in_attic, self.executable, self.file_size, self.mode, + ) = state + CVSPath.__setstate__(self, cvs_path_state) + + def __str__(self): + """For convenience only. The format is subject to change at any time.""" + + return self.cvs_path + + def __repr__(self): + return 'CVSFile<%x>(%r)' % (self.id, str(self),) + + |