aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'cvs2svn_lib/symbol.py')
-rw-r--r--cvs2svn_lib/symbol.py246
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,)
+
+