Extract functions generate_edits_from_blocks() and write_edits().
[cvs2svn.git] / cvs2svn_lib / cvs_item.py
blob93f0bc54ab0923ac898cf8a584ab71e1b38b81a6
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 to store atomic CVS events.
19 A CVSItem is a single event, pertaining to a single file, that can be
20 determined to have occured based on the information in the CVS
21 repository.
23 The inheritance tree is as follows:
25 CVSItem
27 +--CVSRevision
28 | |
29 | +--CVSRevisionModification (* -> 'Exp')
30 | | |
31 | | +--CVSRevisionAdd ('dead' -> 'Exp')
32 | | |
33 | | +--CVSRevisionChange ('Exp' -> 'Exp')
34 | |
35 | +--CVSRevisionAbsent (* -> 'dead')
36 | |
37 | +--CVSRevisionDelete ('Exp' -> 'dead')
38 | |
39 | +--CVSRevisionNoop ('dead' -> 'dead')
41 +--CVSSymbol
43 +--CVSBranch
44 | |
45 | +--CVSBranchNoop
47 +--CVSTag
49 +--CVSTagNoop
51 """
54 from cvs2svn_lib.context import Ctx
57 class CVSItem(object):
58 __slots__ = [
59 'id',
60 'cvs_file',
61 'revision_reader_token',
64 def __init__(self, id, cvs_file, revision_reader_token):
65 self.id = id
66 self.cvs_file = cvs_file
67 self.revision_reader_token = revision_reader_token
69 def __eq__(self, other):
70 return self.id == other.id
72 def __cmp__(self, other):
73 return cmp(self.id, other.id)
75 def __hash__(self):
76 return self.id
78 def __getstate__(self):
79 raise NotImplementedError()
81 def __setstate__(self, data):
82 raise NotImplementedError()
84 def get_svn_path(self):
85 """Return the SVN path associated with this CVSItem."""
87 raise NotImplementedError()
89 def get_pred_ids(self):
90 """Return the CVSItem.ids of direct predecessors of SELF.
92 A predecessor is defined to be a CVSItem that has to have been
93 committed before this one."""
95 raise NotImplementedError()
97 def get_succ_ids(self):
98 """Return the CVSItem.ids of direct successors of SELF.
100 A direct successor is defined to be a CVSItem that has this one as
101 a direct predecessor."""
103 raise NotImplementedError()
105 def get_cvs_symbol_ids_opened(self):
106 """Return an iterable over the ids of CVSSymbols that this item opens.
108 The definition of 'open' is that the path corresponding to this
109 CVSItem will have to be copied when filling the corresponding
110 symbol."""
112 raise NotImplementedError()
114 def get_ids_closed(self):
115 """Return an iterable over the CVSItem.ids of CVSItems closed by this one.
117 A CVSItem A is said to close a CVSItem B if committing A causes B
118 to be overwritten or deleted (no longer available) in the SVN
119 repository. This is interesting because it sets the last SVN
120 revision number from which the contents of B can be copied (for
121 example, to fill a symbol). See the concrete implementations of
122 this method for the exact rules about what closes what."""
124 raise NotImplementedError()
126 def check_links(self, cvs_file_items):
127 """Check for consistency of links to other CVSItems.
129 Other items can be looked up in CVS_FILE_ITEMS, which is an
130 instance of CVSFileItems. Raise an AssertionError if there is a
131 problem."""
133 raise NotImplementedError()
135 def __repr__(self):
136 return '%s(%s)' % (self.__class__.__name__, self,)
139 class CVSRevision(CVSItem):
140 """Information about a single CVS revision.
142 A CVSRevision holds the information known about a single version of
143 a single file.
145 Members:
147 id -- (int) unique ID for this revision.
149 cvs_file -- (CVSFile) CVSFile affected by this revision.
151 timestamp -- (int) date stamp for this revision.
153 metadata_id -- (int) id of metadata instance record in
154 metadata_db.
156 prev_id -- (int) id of the logically previous CVSRevision, either
157 on the same or the source branch (or None).
159 next_id -- (int) id of the logically next CVSRevision (or None).
161 rev -- (string) the CVS revision number, e.g., '1.3'.
163 deltatext_exists -- (bool) true iff this revision's deltatext is
164 not empty.
166 lod -- (LineOfDevelopment) LOD on which this revision occurred.
168 first_on_branch_id -- (int or None) if this revision is the first
169 on its branch, the cvs_branch_id of that branch; else, None.
171 ntdbr -- (bool) true iff this is a non-trunk default branch
172 revision.
174 ntdbr_prev_id -- (int or None) Iff this is the 1.2 revision after
175 the end of a default branch, the id of the last rev on the
176 default branch; else, None.
178 ntdbr_next_id -- (int or None) Iff this is the last revision on a
179 default branch preceding a 1.2 rev, the id of the 1.2
180 revision; else, None.
182 tag_ids -- (list of int) ids of all CVSTags rooted at this
183 CVSRevision.
185 branch_ids -- (list of int) ids of all CVSBranches rooted at this
186 CVSRevision.
188 branch_commit_ids -- (list of int) ids of first CVSRevision
189 committed on each branch rooted in this revision (for branches
190 with commits).
192 opened_symbols -- (None or list of (symbol_id, cvs_symbol_id)
193 tuples) information about all CVSSymbols opened by this
194 revision. This member is set in FilterSymbolsPass; before
195 then, it is None.
197 closed_symbols -- (None or list of (symbol_id, cvs_symbol_id)
198 tuples) information about all CVSSymbols closed by this
199 revision. This member is set in FilterSymbolsPass; before
200 then, it is None.
202 revision_reader_token -- (arbitrary) a token that can be set by
203 RevisionCollector for the later use of RevisionReader.
207 __slots__ = [
208 'timestamp',
209 'metadata_id',
210 'prev_id',
211 'next_id',
212 'rev',
213 'deltatext_exists',
214 'lod',
215 'first_on_branch_id',
216 'ntdbr',
217 'ntdbr_prev_id',
218 'ntdbr_next_id',
219 'tag_ids',
220 'branch_ids',
221 'branch_commit_ids',
222 'opened_symbols',
223 'closed_symbols',
226 def __init__(
227 self,
228 id, cvs_file,
229 timestamp, metadata_id,
230 prev_id, next_id,
231 rev, deltatext_exists,
232 lod, first_on_branch_id, ntdbr,
233 ntdbr_prev_id, ntdbr_next_id,
234 tag_ids, branch_ids, branch_commit_ids,
235 revision_reader_token,
237 """Initialize a new CVSRevision object."""
239 CVSItem.__init__(self, id, cvs_file, revision_reader_token)
241 self.timestamp = timestamp
242 self.metadata_id = metadata_id
243 self.prev_id = prev_id
244 self.next_id = next_id
245 self.rev = rev
246 self.deltatext_exists = deltatext_exists
247 self.lod = lod
248 self.first_on_branch_id = first_on_branch_id
249 self.ntdbr = ntdbr
250 self.ntdbr_prev_id = ntdbr_prev_id
251 self.ntdbr_next_id = ntdbr_next_id
252 self.tag_ids = tag_ids
253 self.branch_ids = branch_ids
254 self.branch_commit_ids = branch_commit_ids
255 self.opened_symbols = None
256 self.closed_symbols = None
258 def _get_cvs_path(self):
259 return self.cvs_file.cvs_path
261 cvs_path = property(_get_cvs_path)
263 def get_svn_path(self):
264 return self.lod.get_path(self.cvs_file.cvs_path)
266 def __getstate__(self):
267 """Return the contents of this instance, for pickling.
269 The presence of this method improves the space efficiency of
270 pickling CVSRevision instances."""
272 return (
273 self.id, self.cvs_file.id,
274 self.timestamp, self.metadata_id,
275 self.prev_id, self.next_id,
276 self.rev,
277 self.deltatext_exists,
278 self.lod.id,
279 self.first_on_branch_id,
280 self.ntdbr,
281 self.ntdbr_prev_id, self.ntdbr_next_id,
282 self.tag_ids, self.branch_ids, self.branch_commit_ids,
283 self.opened_symbols, self.closed_symbols,
284 self.revision_reader_token,
287 def __setstate__(self, data):
288 (self.id, cvs_file_id,
289 self.timestamp, self.metadata_id,
290 self.prev_id, self.next_id,
291 self.rev,
292 self.deltatext_exists,
293 lod_id,
294 self.first_on_branch_id,
295 self.ntdbr,
296 self.ntdbr_prev_id, self.ntdbr_next_id,
297 self.tag_ids, self.branch_ids, self.branch_commit_ids,
298 self.opened_symbols, self.closed_symbols,
299 self.revision_reader_token) = data
300 self.cvs_file = Ctx()._cvs_path_db.get_path(cvs_file_id)
301 self.lod = Ctx()._symbol_db.get_symbol(lod_id)
303 def get_effective_prev_id(self):
304 """Return the ID of the effective predecessor of this item.
306 This is the ID of the item that determines whether the object
307 existed before this CVSRevision."""
309 if self.ntdbr_prev_id is not None:
310 return self.ntdbr_prev_id
311 else:
312 return self.prev_id
314 def get_symbol_pred_ids(self):
315 """Return the pred_ids for symbol predecessors."""
317 retval = set()
318 if self.first_on_branch_id is not None:
319 retval.add(self.first_on_branch_id)
320 return retval
322 def get_pred_ids(self):
323 retval = self.get_symbol_pred_ids()
324 if self.prev_id is not None:
325 retval.add(self.prev_id)
326 if self.ntdbr_prev_id is not None:
327 retval.add(self.ntdbr_prev_id)
328 return retval
330 def get_symbol_succ_ids(self):
331 """Return the succ_ids for symbol successors."""
333 retval = set()
334 for id in self.branch_ids + self.tag_ids:
335 retval.add(id)
336 return retval
338 def get_succ_ids(self):
339 retval = self.get_symbol_succ_ids()
340 if self.next_id is not None:
341 retval.add(self.next_id)
342 if self.ntdbr_next_id is not None:
343 retval.add(self.ntdbr_next_id)
344 for id in self.branch_commit_ids:
345 retval.add(id)
346 return retval
348 def get_ids_closed(self):
349 # Special handling is needed in the case of non-trunk default
350 # branches. The following cases have to be handled:
352 # Case 1: Revision 1.1 not deleted; revision 1.2 exists:
354 # 1.1 -----------------> 1.2
355 # \ ^ ^ /
356 # \ | | /
357 # 1.1.1.1 -> 1.1.1.2
359 # * 1.1.1.1 closes 1.1 (because its post-commit overwrites 1.1
360 # on trunk)
362 # * 1.1.1.2 closes 1.1.1.1
364 # * 1.2 doesn't close anything (the post-commit from 1.1.1.1
365 # already closed 1.1, and no symbols can sprout from the
366 # post-commit of 1.1.1.2)
368 # Case 2: Revision 1.1 not deleted; revision 1.2 does not exist:
370 # 1.1 ..................
371 # \ ^ ^
372 # \ | |
373 # 1.1.1.1 -> 1.1.1.2
375 # * 1.1.1.1 closes 1.1 (because its post-commit overwrites 1.1
376 # on trunk)
378 # * 1.1.1.2 closes 1.1.1.1
380 # Case 3: Revision 1.1 deleted; revision 1.2 exists:
382 # ............... 1.2
383 # ^ ^ /
384 # | | /
385 # 1.1.1.1 -> 1.1.1.2
387 # * 1.1.1.1 doesn't close anything
389 # * 1.1.1.2 closes 1.1.1.1
391 # * 1.2 doesn't close anything (no symbols can sprout from the
392 # post-commit of 1.1.1.2)
394 # Case 4: Revision 1.1 deleted; revision 1.2 doesn't exist:
396 # ...............
397 # ^ ^
398 # | |
399 # 1.1.1.1 -> 1.1.1.2
401 # * 1.1.1.1 doesn't close anything
403 # * 1.1.1.2 closes 1.1.1.1
405 if self.first_on_branch_id is not None:
406 # The first CVSRevision on a branch is considered to close the
407 # branch:
408 yield self.first_on_branch_id
409 if self.ntdbr:
410 # If the 1.1 revision was not deleted, the 1.1.1.1 revision is
411 # considered to close it:
412 yield self.prev_id
413 elif self.ntdbr_prev_id is not None:
414 # This is the special case of a 1.2 revision that follows a
415 # non-trunk default branch. Either 1.1 was deleted or the first
416 # default branch revision closed 1.1, so we don't have to close
417 # 1.1. Technically, we close the revision on trunk that was
418 # copied from the last non-trunk default branch revision in a
419 # post-commit, but for now no symbols can sprout from that
420 # revision so we ignore that one, too.
421 pass
422 elif self.prev_id is not None:
423 # Since this CVSRevision is not the first on a branch, its
424 # prev_id is on the same LOD and this item closes that one:
425 yield self.prev_id
427 def _get_branch_ids_recursively(self, cvs_file_items):
428 """Return the set of all CVSBranches that sprout from this CVSRevision.
430 After parent adjustment in FilterSymbolsPass, it is possible for
431 branches to sprout directly from a CVSRevision, or from those
432 branches, etc. Return all branches that sprout from this
433 CVSRevision, directly or indirectly."""
435 retval = set()
436 branch_ids_to_process = list(self.branch_ids)
437 while branch_ids_to_process:
438 branch = cvs_file_items[branch_ids_to_process.pop()]
439 retval.add(branch)
440 branch_ids_to_process.extend(branch.branch_ids)
442 return retval
444 def check_links(self, cvs_file_items):
445 assert self.cvs_file == cvs_file_items.cvs_file
447 prev = cvs_file_items.get(self.prev_id)
448 next = cvs_file_items.get(self.next_id)
449 first_on_branch = cvs_file_items.get(self.first_on_branch_id)
450 ntdbr_next = cvs_file_items.get(self.ntdbr_next_id)
451 ntdbr_prev = cvs_file_items.get(self.ntdbr_prev_id)
452 effective_prev = cvs_file_items.get(self.get_effective_prev_id())
454 if prev is None:
455 # This is the first CVSRevision on trunk or a detached branch:
456 assert self.id in cvs_file_items.root_ids
457 elif first_on_branch is not None:
458 # This is the first CVSRevision on an existing branch:
459 assert isinstance(first_on_branch, CVSBranch)
460 assert first_on_branch.symbol == self.lod
461 assert first_on_branch.next_id == self.id
462 cvs_revision_source = first_on_branch.get_cvs_revision_source(
463 cvs_file_items
465 assert cvs_revision_source.id == prev.id
466 assert self.id in prev.branch_commit_ids
467 else:
468 # This revision follows another revision on the same LOD:
469 assert prev.next_id == self.id
470 assert prev.lod == self.lod
472 if next is not None:
473 assert next.prev_id == self.id
474 assert next.lod == self.lod
476 if ntdbr_next is not None:
477 assert self.ntdbr
478 assert ntdbr_next.ntdbr_prev_id == self.id
480 if ntdbr_prev is not None:
481 assert ntdbr_prev.ntdbr_next_id == self.id
483 for tag_id in self.tag_ids:
484 tag = cvs_file_items[tag_id]
485 assert isinstance(tag, CVSTag)
486 assert tag.source_id == self.id
487 assert tag.source_lod == self.lod
489 for branch_id in self.branch_ids:
490 branch = cvs_file_items[branch_id]
491 assert isinstance(branch, CVSBranch)
492 assert branch.source_id == self.id
493 assert branch.source_lod == self.lod
495 branch_commit_ids = list(self.branch_commit_ids)
497 for branch in self._get_branch_ids_recursively(cvs_file_items):
498 assert isinstance(branch, CVSBranch)
499 if branch.next_id is not None:
500 assert branch.next_id in branch_commit_ids
501 branch_commit_ids.remove(branch.next_id)
503 assert not branch_commit_ids
505 assert self.__class__ == cvs_revision_type_map[(
506 isinstance(self, CVSRevisionModification),
507 effective_prev is not None
508 and isinstance(effective_prev, CVSRevisionModification),
511 def __str__(self):
512 """For convenience only. The format is subject to change at any time."""
514 return '%s:%s<%x>' % (self.cvs_file, self.rev, self.id,)
517 class CVSRevisionModification(CVSRevision):
518 """Base class for CVSRevisionAdd or CVSRevisionChange."""
520 __slots__ = []
522 def get_cvs_symbol_ids_opened(self):
523 return self.tag_ids + self.branch_ids
526 class CVSRevisionAdd(CVSRevisionModification):
527 """A CVSRevision that creates a file that previously didn't exist.
529 The file might have never existed on this LOD, or it might have
530 existed previously but been deleted by a CVSRevisionDelete."""
532 __slots__ = []
535 class CVSRevisionChange(CVSRevisionModification):
536 """A CVSRevision that modifies a file that already existed on this LOD."""
538 __slots__ = []
541 class CVSRevisionAbsent(CVSRevision):
542 """A CVSRevision for which the file is nonexistent on this LOD."""
544 __slots__ = []
546 def get_cvs_symbol_ids_opened(self):
547 return []
550 class CVSRevisionDelete(CVSRevisionAbsent):
551 """A CVSRevision that deletes a file that existed on this LOD."""
553 __slots__ = []
556 class CVSRevisionNoop(CVSRevisionAbsent):
557 """A CVSRevision that doesn't do anything.
559 The revision was 'dead' and the predecessor either didn't exist or
560 was also 'dead'. These revisions can't necessarily be thrown away
561 because (1) they impose ordering constraints on other items; (2)
562 they might have a nontrivial log message that we don't want to throw
563 away."""
565 __slots__ = []
568 # A map
570 # {(nondead(cvs_rev), nondead(prev_cvs_rev)) : cvs_revision_subtype}
572 # , where nondead() means that the cvs revision exists and is not
573 # 'dead', and CVS_REVISION_SUBTYPE is the subtype of CVSRevision that
574 # should be used for CVS_REV.
575 cvs_revision_type_map = {
576 (False, False) : CVSRevisionNoop,
577 (False, True) : CVSRevisionDelete,
578 (True, False) : CVSRevisionAdd,
579 (True, True) : CVSRevisionChange,
583 class CVSSymbol(CVSItem):
584 """Represent a symbol on a particular CVSFile.
586 This is the base class for CVSBranch and CVSTag.
588 Members:
590 id -- (int) unique ID for this item.
592 cvs_file -- (CVSFile) CVSFile affected by this item.
594 symbol -- (Symbol) the symbol affected by this CVSSymbol.
596 source_lod -- (LineOfDevelopment) the LOD that is the source for
597 this CVSSymbol.
599 source_id -- (int) the ID of the CVSRevision or CVSBranch that is
600 the source for this item. This initially points to a
601 CVSRevision, but can be changed to a CVSBranch via parent
602 adjustment in FilterSymbolsPass.
604 revision_reader_token -- (arbitrary) a token that can be set by
605 RevisionCollector for the later use of RevisionReader.
609 __slots__ = [
610 'symbol',
611 'source_lod',
612 'source_id',
615 def __init__(
616 self, id, cvs_file, symbol, source_lod, source_id,
617 revision_reader_token,
619 """Initialize a CVSSymbol object."""
621 CVSItem.__init__(self, id, cvs_file, revision_reader_token)
623 self.symbol = symbol
624 self.source_lod = source_lod
625 self.source_id = source_id
627 def get_cvs_revision_source(self, cvs_file_items):
628 """Return the CVSRevision that is the ultimate source of this symbol."""
630 cvs_source = cvs_file_items[self.source_id]
631 while not isinstance(cvs_source, CVSRevision):
632 cvs_source = cvs_file_items[cvs_source.source_id]
634 return cvs_source
636 def get_svn_path(self):
637 return self.symbol.get_path(self.cvs_file.cvs_path)
639 def get_ids_closed(self):
640 # A Symbol does not close any other CVSItems:
641 return []
644 class CVSBranch(CVSSymbol):
645 """Represent the creation of a branch in a particular CVSFile.
647 Members:
649 id -- (int) unique ID for this item.
651 cvs_file -- (CVSFile) CVSFile affected by this item.
653 symbol -- (Symbol) the symbol affected by this CVSSymbol.
655 branch_number -- (string) the number of this branch (e.g.,
656 '1.3.4'), or None if this is a converted CVSTag.
658 source_lod -- (LineOfDevelopment) the LOD that is the source for
659 this CVSSymbol.
661 source_id -- (int) id of the CVSRevision or CVSBranch from which
662 this branch sprouts. This initially points to a CVSRevision,
663 but can be changed to a CVSBranch via parent adjustment in
664 FilterSymbolsPass.
666 next_id -- (int or None) id of first CVSRevision on this branch,
667 if any; else, None.
669 tag_ids -- (list of int) ids of all CVSTags rooted at this
670 CVSBranch (can be set due to parent adjustment in
671 FilterSymbolsPass).
673 branch_ids -- (list of int) ids of all CVSBranches rooted at this
674 CVSBranch (can be set due to parent adjustment in
675 FilterSymbolsPass).
677 opened_symbols -- (None or list of (symbol_id, cvs_symbol_id)
678 tuples) information about all CVSSymbols opened by this
679 branch. This member is set in FilterSymbolsPass; before then,
680 it is None.
682 revision_reader_token -- (arbitrary) a token that can be set by
683 RevisionCollector for the later use of RevisionReader.
687 __slots__ = [
688 'branch_number',
689 'next_id',
690 'tag_ids',
691 'branch_ids',
692 'opened_symbols',
695 def __init__(
696 self, id, cvs_file, symbol, branch_number,
697 source_lod, source_id, next_id,
698 revision_reader_token,
700 """Initialize a CVSBranch."""
702 CVSSymbol.__init__(
703 self, id, cvs_file, symbol, source_lod, source_id,
704 revision_reader_token,
706 self.branch_number = branch_number
707 self.next_id = next_id
708 self.tag_ids = []
709 self.branch_ids = []
710 self.opened_symbols = None
712 def __getstate__(self):
713 return (
714 self.id, self.cvs_file.id,
715 self.symbol.id, self.branch_number,
716 self.source_lod.id, self.source_id, self.next_id,
717 self.tag_ids, self.branch_ids,
718 self.opened_symbols,
719 self.revision_reader_token,
722 def __setstate__(self, data):
724 self.id, cvs_file_id,
725 symbol_id, self.branch_number,
726 source_lod_id, self.source_id, self.next_id,
727 self.tag_ids, self.branch_ids,
728 self.opened_symbols,
729 self.revision_reader_token,
730 ) = data
731 self.cvs_file = Ctx()._cvs_path_db.get_path(cvs_file_id)
732 self.symbol = Ctx()._symbol_db.get_symbol(symbol_id)
733 self.source_lod = Ctx()._symbol_db.get_symbol(source_lod_id)
735 def get_pred_ids(self):
736 return set([self.source_id])
738 def get_succ_ids(self):
739 retval = set(self.tag_ids + self.branch_ids)
740 if self.next_id is not None:
741 retval.add(self.next_id)
742 return retval
744 def get_cvs_symbol_ids_opened(self):
745 return self.tag_ids + self.branch_ids
747 def check_links(self, cvs_file_items):
748 source = cvs_file_items.get(self.source_id)
749 next = cvs_file_items.get(self.next_id)
751 assert self.id in source.branch_ids
752 if isinstance(source, CVSRevision):
753 assert self.source_lod == source.lod
754 elif isinstance(source, CVSBranch):
755 assert self.source_lod == source.symbol
756 else:
757 assert False
759 if next is not None:
760 assert isinstance(next, CVSRevision)
761 assert next.lod == self.symbol
762 assert next.first_on_branch_id == self.id
764 for tag_id in self.tag_ids:
765 tag = cvs_file_items[tag_id]
766 assert isinstance(tag, CVSTag)
767 assert tag.source_id == self.id
768 assert tag.source_lod == self.symbol
770 for branch_id in self.branch_ids:
771 branch = cvs_file_items[branch_id]
772 assert isinstance(branch, CVSBranch)
773 assert branch.source_id == self.id
774 assert branch.source_lod == self.symbol
776 def __str__(self):
777 """For convenience only. The format is subject to change at any time."""
779 return '%s:%s:%s<%x>' \
780 % (self.cvs_file, self.symbol, self.branch_number, self.id,)
783 class CVSBranchNoop(CVSBranch):
784 """A CVSBranch whose source is a CVSRevisionAbsent."""
786 __slots__ = []
788 def get_cvs_symbol_ids_opened(self):
789 return []
792 # A map
794 # {nondead(source_cvs_rev) : cvs_branch_subtype}
796 # , where nondead() means that the cvs revision exists and is not
797 # 'dead', and CVS_BRANCH_SUBTYPE is the subtype of CVSBranch that
798 # should be used.
799 cvs_branch_type_map = {
800 False : CVSBranchNoop,
801 True : CVSBranch,
805 class CVSTag(CVSSymbol):
806 """Represent the creation of a tag on a particular CVSFile.
808 Members:
810 id -- (int) unique ID for this item.
812 cvs_file -- (CVSFile) CVSFile affected by this item.
814 symbol -- (Symbol) the symbol affected by this CVSSymbol.
816 source_lod -- (LineOfDevelopment) the LOD that is the source for
817 this CVSSymbol.
819 source_id -- (int) the ID of the CVSRevision or CVSBranch that is
820 being tagged. This initially points to a CVSRevision, but can
821 be changed to a CVSBranch via parent adjustment in
822 FilterSymbolsPass.
824 revision_reader_token -- (arbitrary) a token that can be set by
825 RevisionCollector for the later use of RevisionReader.
829 __slots__ = []
831 def __init__(
832 self, id, cvs_file, symbol, source_lod, source_id,
833 revision_reader_token,
835 """Initialize a CVSTag."""
837 CVSSymbol.__init__(
838 self, id, cvs_file, symbol, source_lod, source_id,
839 revision_reader_token,
842 def __getstate__(self):
843 return (
844 self.id, self.cvs_file.id, self.symbol.id,
845 self.source_lod.id, self.source_id,
846 self.revision_reader_token,
849 def __setstate__(self, data):
851 self.id, cvs_file_id, symbol_id, source_lod_id, self.source_id,
852 self.revision_reader_token,
853 ) = data
854 self.cvs_file = Ctx()._cvs_path_db.get_path(cvs_file_id)
855 self.symbol = Ctx()._symbol_db.get_symbol(symbol_id)
856 self.source_lod = Ctx()._symbol_db.get_symbol(source_lod_id)
858 def get_pred_ids(self):
859 return set([self.source_id])
861 def get_succ_ids(self):
862 return set()
864 def get_cvs_symbol_ids_opened(self):
865 return []
867 def check_links(self, cvs_file_items):
868 source = cvs_file_items.get(self.source_id)
870 assert self.id in source.tag_ids
871 if isinstance(source, CVSRevision):
872 assert self.source_lod == source.lod
873 elif isinstance(source, CVSBranch):
874 assert self.source_lod == source.symbol
875 else:
876 assert False
878 def __str__(self):
879 """For convenience only. The format is subject to change at any time."""
881 return '%s:%s<%x>' \
882 % (self.cvs_file, self.symbol, self.id,)
885 class CVSTagNoop(CVSTag):
886 """A CVSTag whose source is a CVSRevisionAbsent."""
888 __slots__ = []
891 # A map
893 # {nondead(source_cvs_rev) : cvs_tag_subtype}
895 # , where nondead() means that the cvs revision exists and is not
896 # 'dead', and CVS_TAG_SUBTYPE is the subtype of CVSTag that should be
897 # used.
898 cvs_tag_type_map = {
899 False : CVSTagNoop,
900 True : CVSTag,