1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2006-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 """Manage change sets."""
20 from cvs2svn_lib
.common
import InternalError
21 from cvs2svn_lib
.context
import Ctx
22 from cvs2svn_lib
.symbol
import Branch
23 from cvs2svn_lib
.symbol
import Tag
24 from cvs2svn_lib
.time_range
import TimeRange
25 from cvs2svn_lib
.changeset_graph_node
import ChangesetGraphNode
28 class Changeset(object):
29 """A set of cvs_items that might potentially form a single change set."""
31 def __init__(self
, id, cvs_item_ids
):
33 self
.cvs_item_ids
= list(cvs_item_ids
)
35 def iter_cvs_items(self
):
36 """Yield the CVSItems within this Changeset."""
38 for (id, cvs_item
) in Ctx()._cvs
_items
_db
.get_many(self
.cvs_item_ids
):
39 assert cvs_item
is not None
42 def get_projects_opened(self
):
43 """Return the set of projects that might be opened by this changeset."""
45 raise NotImplementedError()
47 def create_graph_node(self
, cvs_item_to_changeset_id
):
48 """Return a ChangesetGraphNode for this Changeset."""
50 raise NotImplementedError()
52 def create_split_changeset(self
, id, cvs_item_ids
):
53 """Return a Changeset with the specified contents.
55 This method is only implemented for changesets that can be split.
56 The type of the new changeset should be the same as that of SELF,
57 and any other information from SELF should also be copied to the
60 raise NotImplementedError()
62 def __getstate__(self
):
63 return (self
.id, self
.cvs_item_ids
,)
65 def __setstate__(self
, state
):
66 (self
.id, self
.cvs_item_ids
,) = state
68 def __cmp__(self
, other
):
69 raise NotImplementedError()
72 raise NotImplementedError()
76 self
, ', '.join(['%x' % id for id in self
.cvs_item_ids
]),)
79 class RevisionChangeset(Changeset
):
80 """A Changeset consisting of CVSRevisions."""
84 def create_graph_node(self
, cvs_item_to_changeset_id
):
85 time_range
= TimeRange()
89 for cvs_item
in self
.iter_cvs_items():
90 time_range
.add(cvs_item
.timestamp
)
92 for pred_id
in cvs_item
.get_pred_ids():
93 changeset_id
= cvs_item_to_changeset_id
.get(pred_id
)
94 if changeset_id
is not None:
95 pred_ids
.add(changeset_id
)
97 for succ_id
in cvs_item
.get_succ_ids():
98 changeset_id
= cvs_item_to_changeset_id
.get(succ_id
)
99 if changeset_id
is not None:
100 succ_ids
.add(changeset_id
)
102 return ChangesetGraphNode(self
, time_range
, pred_ids
, succ_ids
)
104 def create_split_changeset(self
, id, cvs_item_ids
):
105 return RevisionChangeset(id, cvs_item_ids
)
107 def __cmp__(self
, other
):
108 return cmp(self
._sort
_order
, other
._sort
_order
) \
109 or cmp(self
.id, other
.id)
112 return 'RevisionChangeset<%x>' % (self
.id,)
115 class OrderedChangeset(Changeset
):
116 """A Changeset of CVSRevisions whose preliminary order is known.
118 The first changeset ordering involves only RevisionChangesets, and
119 results in a full ordering of RevisionChangesets (i.e., a linear
120 chain of dependencies with the order consistent with the
121 dependencies). These OrderedChangesets form the skeleton for the
122 full topological sort that includes SymbolChangesets as well."""
126 def __init__(self
, id, cvs_item_ids
, ordinal
, prev_id
, next_id
):
127 Changeset
.__init
__(self
, id, cvs_item_ids
)
129 # The order of this changeset among all OrderedChangesets:
130 self
.ordinal
= ordinal
132 # The changeset id of the previous OrderedChangeset, or None if
133 # this is the first OrderedChangeset:
134 self
.prev_id
= prev_id
136 # The changeset id of the next OrderedChangeset, or None if this
137 # is the last OrderedChangeset:
138 self
.next_id
= next_id
140 def get_projects_opened(self
):
142 for cvs_item
in self
.iter_cvs_items():
143 retval
.add(cvs_item
.cvs_file
.project
)
146 def create_graph_node(self
, cvs_item_to_changeset_id
):
147 time_range
= TimeRange()
152 if self
.prev_id
is not None:
153 pred_ids
.add(self
.prev_id
)
155 if self
.next_id
is not None:
156 succ_ids
.add(self
.next_id
)
158 for cvs_item
in self
.iter_cvs_items():
159 time_range
.add(cvs_item
.timestamp
)
161 for pred_id
in cvs_item
.get_symbol_pred_ids():
162 changeset_id
= cvs_item_to_changeset_id
.get(pred_id
)
163 if changeset_id
is not None:
164 pred_ids
.add(changeset_id
)
166 for succ_id
in cvs_item
.get_symbol_succ_ids():
167 changeset_id
= cvs_item_to_changeset_id
.get(succ_id
)
168 if changeset_id
is not None:
169 succ_ids
.add(changeset_id
)
171 return ChangesetGraphNode(self
, time_range
, pred_ids
, succ_ids
)
173 def __getstate__(self
):
175 Changeset
.__getstate
__(self
),
176 self
.ordinal
, self
.prev_id
, self
.next_id
,)
178 def __setstate__(self
, state
):
179 (changeset_state
, self
.ordinal
, self
.prev_id
, self
.next_id
,) = state
180 Changeset
.__setstate
__(self
, changeset_state
)
182 def __cmp__(self
, other
):
183 return cmp(self
._sort
_order
, other
._sort
_order
) \
184 or cmp(self
.id, other
.id)
187 return 'OrderedChangeset<%x(%d)>' % (self
.id, self
.ordinal
,)
190 class SymbolChangeset(Changeset
):
191 """A Changeset consisting of CVSSymbols."""
193 def __init__(self
, id, symbol
, cvs_item_ids
):
194 Changeset
.__init
__(self
, id, cvs_item_ids
)
197 def get_projects_opened(self
):
198 # A SymbolChangeset can never open a project.
201 def create_graph_node(self
, cvs_item_to_changeset_id
):
205 for cvs_item
in self
.iter_cvs_items():
206 for pred_id
in cvs_item
.get_pred_ids():
207 changeset_id
= cvs_item_to_changeset_id
.get(pred_id
)
208 if changeset_id
is not None:
209 pred_ids
.add(changeset_id
)
211 for succ_id
in cvs_item
.get_succ_ids():
212 changeset_id
= cvs_item_to_changeset_id
.get(succ_id
)
213 if changeset_id
is not None:
214 succ_ids
.add(changeset_id
)
216 return ChangesetGraphNode(self
, TimeRange(), pred_ids
, succ_ids
)
218 def __cmp__(self
, other
):
219 return cmp(self
._sort
_order
, other
._sort
_order
) \
220 or cmp(self
.symbol
, other
.symbol
) \
221 or cmp(self
.id, other
.id)
223 def __getstate__(self
):
224 return (Changeset
.__getstate
__(self
), self
.symbol
.id,)
226 def __setstate__(self
, state
):
227 (changeset_state
, symbol_id
) = state
228 Changeset
.__setstate
__(self
, changeset_state
)
229 self
.symbol
= Ctx()._symbol
_db
.get_symbol(symbol_id
)
232 class BranchChangeset(SymbolChangeset
):
233 """A Changeset consisting of CVSBranches."""
237 def create_split_changeset(self
, id, cvs_item_ids
):
238 return BranchChangeset(id, self
.symbol
, cvs_item_ids
)
241 return 'BranchChangeset<%x>("%s")' % (self
.id, self
.symbol
,)
244 class TagChangeset(SymbolChangeset
):
245 """A Changeset consisting of CVSTags."""
249 def create_split_changeset(self
, id, cvs_item_ids
):
250 return TagChangeset(id, self
.symbol
, cvs_item_ids
)
253 return 'TagChangeset<%x>("%s")' % (self
.id, self
.symbol
,)
256 def create_symbol_changeset(id, symbol
, cvs_item_ids
):
257 """Factory function for SymbolChangesets.
259 Return a BranchChangeset or TagChangeset, depending on the type of
260 SYMBOL. SYMBOL must be a Branch or Tag."""
262 if isinstance(symbol
, Branch
):
263 return BranchChangeset(id, symbol
, cvs_item_ids
)
264 if isinstance(symbol
, Tag
):
265 return TagChangeset(id, symbol
, cvs_item_ids
)
267 raise InternalError('Unknown symbol type %s' % (symbol
,))