sys.modules['bsddb3'] is already in the local namespace.
[cvs2svn.git] / cvs2svn_lib / symbol.py
blobe3a6b350b8b9e94759a872f68bd8d54216ca504f
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 """This module contains classes that represent trunk, branches, and tags.
19 The classes in this module represent several concepts related to
20 symbols and lines of development in the abstract; that is, not within
21 a particular file, but across all files in a project.
23 The classes in this module are organized into the following class
24 hierarchy:
26 AbstractSymbol
28 +--LineOfDevelopment
29 | |
30 | +--Trunk
31 | |
32 | +--IncludedSymbol (also inherits from TypedSymbol)
33 | |
34 | +--Branch
35 | |
36 | +--Tag
38 +--Symbol
40 +--TypedSymbol
42 +--IncludedSymbol (also inherits from LineOfDevelopment)
43 | |
44 | +--Branch
45 | |
46 | +--Tag
48 +--ExcludedSymbol
50 Please note the use of multiple inheritance.
52 All AbstractSymbols contain an id that is globally unique across all
53 AbstractSymbols. Moreover, the id of an AbstractSymbol remains the
54 same even if the symbol is mutated (as described below), and two
55 AbstractSymbols are considered equal iff their ids are the same, even
56 if the two instances have different types. Symbols in different
57 projects always have different ids and are therefore always distinct.
58 (Indeed, this is pretty much the defining characteristic of a
59 project.) Even if, for example, two projects each have branches with
60 the same name, the Symbols representing the branches are distinct and
61 have distinct ids. (This is important to avoid having to rewrite
62 databases with new symbol ids in CollateSymbolsPass.)
64 AbstractSymbols are all initially created in CollectRevsPass as either
65 Trunk or Symbol instances. A Symbol instance is essentially an
66 undifferentiated Symbol.
68 In CollateSymbolsPass, it is decided which symbols will be converted
69 as branches, which as tags, and which excluded altogether. At the
70 beginning of this pass, the symbols are all represented by instances
71 of the non-specific Symbol class. During CollateSymbolsPass, each
72 Symbol instance is replaced by an instance of Branch, Tag, or
73 ExcludedSymbol with the same id. (Trunk instances are left
74 unchanged.) At the end of CollateSymbolsPass, all ExcludedSymbols are
75 discarded and processing continues with only Trunk, Branch, and Tag
76 instances. These three classes inherit from LineOfDevelopment;
77 therefore, in later passes the term LineOfDevelopment (abbreviated to
78 LOD) is used to refer to such objects."""
81 from cvs2svn_lib.context import Ctx
82 from cvs2svn_lib.common import path_join
85 class AbstractSymbol:
86 """Base class for all other classes in this file."""
88 def __init__(self, id, project):
89 self.id = id
90 self.project = project
92 def __hash__(self):
93 return self.id
95 def __eq__(self, other):
96 return self.id == other.id
99 class LineOfDevelopment(AbstractSymbol):
100 """Base class for Trunk, Branch, and Tag.
102 This is basically the abstraction for what will be a root tree in
103 the Subversion repository."""
105 def __init__(self, id, project):
106 AbstractSymbol.__init__(self, id, project)
107 self.base_path = None
109 def get_path(self, *components):
110 """Return the svn path for this LineOfDevelopment."""
112 return path_join(self.base_path, *components)
115 class Trunk(LineOfDevelopment):
116 """Represent the main line of development."""
118 def __getstate__(self):
119 return (self.id, self.project.id, self.base_path,)
121 def __setstate__(self, state):
122 (self.id, project_id, self.base_path,) = state
123 self.project = Ctx()._projects[project_id]
125 def __cmp__(self, other):
126 if isinstance(other, Trunk):
127 return cmp(self.project, other.project)
128 elif isinstance(other, Symbol):
129 # Allow Trunk to compare less than Symbols:
130 return -1
131 else:
132 raise NotImplementedError()
134 def __str__(self):
135 """For convenience only. The format is subject to change at any time."""
137 return 'Trunk'
139 def __repr__(self):
140 return '%s<%x>' % (self, self.id,)
143 class Symbol(AbstractSymbol):
144 """Represents a symbol within one project in the CVS repository.
146 Instance of the Symbol class itself are used to represent symbols
147 from the CVS repository. CVS, of course, distinguishes between
148 normal tags and branch tags, but we allow symbol types to be changed
149 in CollateSymbolsPass. Therefore, we store all CVS symbols as
150 Symbol instances at the beginning of the conversion.
152 In CollateSymbolsPass, Symbols are replaced by Branches, Tags, and
153 ExcludedSymbols (the latter being discarded at the end of that
154 pass)."""
156 def __init__(self, id, project, name, preferred_parent_id=None):
157 AbstractSymbol.__init__(self, id, project)
158 self.name = name
160 # If this symbol has a preferred parent, this member is the id of
161 # the LineOfDevelopment instance representing it. If the symbol
162 # never appeared in a CVSTag or CVSBranch (for example, because
163 # all of the branches on this LOD have been detached from the
164 # dependency tree), then this field is set to None. This field is
165 # set during FilterSymbolsPass.
166 self.preferred_parent_id = preferred_parent_id
168 def __getstate__(self):
169 return (self.id, self.project.id, self.name, self.preferred_parent_id,)
171 def __setstate__(self, state):
172 (self.id, project_id, self.name, self.preferred_parent_id,) = state
173 self.project = Ctx()._projects[project_id]
175 def __cmp__(self, other):
176 if isinstance(other, Symbol):
177 return cmp(self.project, other.project) \
178 or cmp(self.name, other.name) \
179 or cmp(self.id, other.id)
180 elif isinstance(other, Trunk):
181 # Allow Symbols to compare greater than Trunk:
182 return +1
183 else:
184 raise NotImplementedError()
186 def __str__(self):
187 return self.name
189 def __repr__(self):
190 return '%s<%x>' % (self, self.id,)
193 class TypedSymbol(Symbol):
194 """A Symbol whose type (branch, tag, or excluded) has been decided."""
196 def __init__(self, symbol):
197 Symbol.__init__(
198 self, symbol.id, symbol.project, symbol.name,
199 symbol.preferred_parent_id,
203 class IncludedSymbol(TypedSymbol, LineOfDevelopment):
204 """A TypedSymbol that will be included in the conversion."""
206 def __init__(self, symbol):
207 TypedSymbol.__init__(self, symbol)
208 # We can't call the LineOfDevelopment constructor, so initialize
209 # its extra member explicitly:
210 try:
211 # If the old symbol had a base_path set, then use it:
212 self.base_path = symbol.base_path
213 except AttributeError:
214 self.base_path = None
216 def __getstate__(self):
217 return (TypedSymbol.__getstate__(self), self.base_path,)
219 def __setstate__(self, state):
220 (super_state, self.base_path,) = state
221 TypedSymbol.__setstate__(self, super_state)
224 class Branch(IncludedSymbol):
225 """An object that describes a CVS branch."""
227 def __str__(self):
228 """For convenience only. The format is subject to change at any time."""
230 return 'Branch(%r)' % (self.name,)
233 class Tag(IncludedSymbol):
234 def __str__(self):
235 """For convenience only. The format is subject to change at any time."""
237 return 'Tag(%r)' % (self.name,)
240 class ExcludedSymbol(TypedSymbol):
241 def __str__(self):
242 """For convenience only. The format is subject to change at any time."""
244 return 'ExcludedSymbol(%r)' % (self.name,)