diff options
Diffstat (limited to 'cvs2svn_lib/symbol.py')
-rw-r--r-- | cvs2svn_lib/symbol.py | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/cvs2svn_lib/symbol.py b/cvs2svn_lib/symbol.py new file mode 100644 index 0000000..e3a6b35 --- /dev/null +++ b/cvs2svn_lib/symbol.py @@ -0,0 +1,246 @@ +# (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 classes that represent trunk, branches, and tags. + +The classes in this module represent several concepts related to +symbols and lines of development in the abstract; that is, not within +a particular file, but across all files in a project. + +The classes in this module are organized into the following class +hierarchy: + +AbstractSymbol + | + +--LineOfDevelopment + | | + | +--Trunk + | | + | +--IncludedSymbol (also inherits from TypedSymbol) + | | + | +--Branch + | | + | +--Tag + | + +--Symbol + | + +--TypedSymbol + | + +--IncludedSymbol (also inherits from LineOfDevelopment) + | | + | +--Branch + | | + | +--Tag + | + +--ExcludedSymbol + +Please note the use of multiple inheritance. + +All AbstractSymbols contain an id that is globally unique across all +AbstractSymbols. Moreover, the id of an AbstractSymbol remains the +same even if the symbol is mutated (as described below), and two +AbstractSymbols are considered equal iff their ids are the same, even +if the two instances have different types. Symbols in different +projects always have different ids and are therefore always distinct. +(Indeed, this is pretty much the defining characteristic of a +project.) Even if, for example, two projects each have branches with +the same name, the Symbols representing the branches are distinct and +have distinct ids. (This is important to avoid having to rewrite +databases with new symbol ids in CollateSymbolsPass.) + +AbstractSymbols are all initially created in CollectRevsPass as either +Trunk or Symbol instances. A Symbol instance is essentially an +undifferentiated Symbol. + +In CollateSymbolsPass, it is decided which symbols will be converted +as branches, which as tags, and which excluded altogether. At the +beginning of this pass, the symbols are all represented by instances +of the non-specific Symbol class. During CollateSymbolsPass, each +Symbol instance is replaced by an instance of Branch, Tag, or +ExcludedSymbol with the same id. (Trunk instances are left +unchanged.) At the end of CollateSymbolsPass, all ExcludedSymbols are +discarded and processing continues with only Trunk, Branch, and Tag +instances. These three classes inherit from LineOfDevelopment; +therefore, in later passes the term LineOfDevelopment (abbreviated to +LOD) is used to refer to such objects.""" + + +from cvs2svn_lib.context import Ctx +from cvs2svn_lib.common import path_join + + +class AbstractSymbol: + """Base class for all other classes in this file.""" + + def __init__(self, id, project): + self.id = id + self.project = project + + def __hash__(self): + return self.id + + def __eq__(self, other): + return self.id == other.id + + +class LineOfDevelopment(AbstractSymbol): + """Base class for Trunk, Branch, and Tag. + + This is basically the abstraction for what will be a root tree in + the Subversion repository.""" + + def __init__(self, id, project): + AbstractSymbol.__init__(self, id, project) + self.base_path = None + + def get_path(self, *components): + """Return the svn path for this LineOfDevelopment.""" + + return path_join(self.base_path, *components) + + +class Trunk(LineOfDevelopment): + """Represent the main line of development.""" + + def __getstate__(self): + return (self.id, self.project.id, self.base_path,) + + def __setstate__(self, state): + (self.id, project_id, self.base_path,) = state + self.project = Ctx()._projects[project_id] + + def __cmp__(self, other): + if isinstance(other, Trunk): + return cmp(self.project, other.project) + elif isinstance(other, Symbol): + # Allow Trunk to compare less than Symbols: + return -1 + else: + raise NotImplementedError() + + def __str__(self): + """For convenience only. The format is subject to change at any time.""" + + return 'Trunk' + + def __repr__(self): + return '%s<%x>' % (self, self.id,) + + +class Symbol(AbstractSymbol): + """Represents a symbol within one project in the CVS repository. + + Instance of the Symbol class itself are used to represent symbols + from the CVS repository. CVS, of course, distinguishes between + normal tags and branch tags, but we allow symbol types to be changed + in CollateSymbolsPass. Therefore, we store all CVS symbols as + Symbol instances at the beginning of the conversion. + + In CollateSymbolsPass, Symbols are replaced by Branches, Tags, and + ExcludedSymbols (the latter being discarded at the end of that + pass).""" + + def __init__(self, id, project, name, preferred_parent_id=None): + AbstractSymbol.__init__(self, id, project) + self.name = name + + # If this symbol has a preferred parent, this member is the id of + # the LineOfDevelopment instance representing it. If the symbol + # never appeared in a CVSTag or CVSBranch (for example, because + # all of the branches on this LOD have been detached from the + # dependency tree), then this field is set to None. This field is + # set during FilterSymbolsPass. + self.preferred_parent_id = preferred_parent_id + + def __getstate__(self): + return (self.id, self.project.id, self.name, self.preferred_parent_id,) + + def __setstate__(self, state): + (self.id, project_id, self.name, self.preferred_parent_id,) = state + self.project = Ctx()._projects[project_id] + + def __cmp__(self, other): + if isinstance(other, Symbol): + return cmp(self.project, other.project) \ + or cmp(self.name, other.name) \ + or cmp(self.id, other.id) + elif isinstance(other, Trunk): + # Allow Symbols to compare greater than Trunk: + return +1 + else: + raise NotImplementedError() + + def __str__(self): + return self.name + + def __repr__(self): + return '%s<%x>' % (self, self.id,) + + +class TypedSymbol(Symbol): + """A Symbol whose type (branch, tag, or excluded) has been decided.""" + + def __init__(self, symbol): + Symbol.__init__( + self, symbol.id, symbol.project, symbol.name, + symbol.preferred_parent_id, + ) + + +class IncludedSymbol(TypedSymbol, LineOfDevelopment): + """A TypedSymbol that will be included in the conversion.""" + + def __init__(self, symbol): + TypedSymbol.__init__(self, symbol) + # We can't call the LineOfDevelopment constructor, so initialize + # its extra member explicitly: + try: + # If the old symbol had a base_path set, then use it: + self.base_path = symbol.base_path + except AttributeError: + self.base_path = None + + def __getstate__(self): + return (TypedSymbol.__getstate__(self), self.base_path,) + + def __setstate__(self, state): + (super_state, self.base_path,) = state + TypedSymbol.__setstate__(self, super_state) + + +class Branch(IncludedSymbol): + """An object that describes a CVS branch.""" + + def __str__(self): + """For convenience only. The format is subject to change at any time.""" + + return 'Branch(%r)' % (self.name,) + + +class Tag(IncludedSymbol): + def __str__(self): + """For convenience only. The format is subject to change at any time.""" + + return 'Tag(%r)' % (self.name,) + + +class ExcludedSymbol(TypedSymbol): + def __str__(self): + """For convenience only. The format is subject to change at any time.""" + + return 'ExcludedSymbol(%r)' % (self.name,) + + |