cvs2git: Make the --blobfile argument optional.
[cvs2svn.git] / cvs2svn_lib / cvs_item.py
blob469f4348d30b713bf6cb92a346f5fce760419218
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 properties -- (dict) the file properties that vary from revision
203 to revision. The get_properties() method combines the
204 properties found in SELF.cvs_file.properties with those here;
205 the latter take precedence. Keys are strings. Values are
206 strings (indicating the property value) or None (indicating
207 that the property should be left unset, even if it is set in
208 SELF.cvs_file.properties). Different backends can use
209 properties for different purposes; for cvs2svn these become
210 SVN versioned properties. Properties whose names start with
211 underscore are reserved for internal cvs2svn purposes.
213 properties_changed -- (None or bool) Will this CVSRevision's
214 get_properties() method return a different value than the same
215 call of the predecessor revision?
217 revision_reader_token -- (arbitrary) a token that can be set by
218 RevisionCollector for the later use of RevisionReader.
222 __slots__ = [
223 'timestamp',
224 'metadata_id',
225 'prev_id',
226 'next_id',
227 'rev',
228 'deltatext_exists',
229 'lod',
230 'first_on_branch_id',
231 'ntdbr',
232 'ntdbr_prev_id',
233 'ntdbr_next_id',
234 'tag_ids',
235 'branch_ids',
236 'branch_commit_ids',
237 'opened_symbols',
238 'closed_symbols',
239 'properties',
240 'properties_changed',
243 def __init__(
244 self,
245 id, cvs_file,
246 timestamp, metadata_id,
247 prev_id, next_id,
248 rev, deltatext_exists,
249 lod, first_on_branch_id, ntdbr,
250 ntdbr_prev_id, ntdbr_next_id,
251 tag_ids, branch_ids, branch_commit_ids,
252 revision_reader_token,
254 """Initialize a new CVSRevision object."""
256 CVSItem.__init__(self, id, cvs_file, revision_reader_token)
258 self.timestamp = timestamp
259 self.metadata_id = metadata_id
260 self.prev_id = prev_id
261 self.next_id = next_id
262 self.rev = rev
263 self.deltatext_exists = deltatext_exists
264 self.lod = lod
265 self.first_on_branch_id = first_on_branch_id
266 self.ntdbr = ntdbr
267 self.ntdbr_prev_id = ntdbr_prev_id
268 self.ntdbr_next_id = ntdbr_next_id
269 self.tag_ids = tag_ids
270 self.branch_ids = branch_ids
271 self.branch_commit_ids = branch_commit_ids
272 self.opened_symbols = None
273 self.closed_symbols = None
274 self.properties = None
275 self.properties_changed = None
277 def _get_cvs_path(self):
278 return self.cvs_file.cvs_path
280 cvs_path = property(_get_cvs_path)
282 def get_svn_path(self):
283 return self.lod.get_path(self.cvs_file.cvs_path)
285 def __getstate__(self):
286 """Return the contents of this instance, for pickling.
288 The presence of this method improves the space efficiency of
289 pickling CVSRevision instances."""
291 return (
292 self.id, self.cvs_file.id,
293 self.timestamp, self.metadata_id,
294 self.prev_id, self.next_id,
295 self.rev,
296 self.deltatext_exists,
297 self.lod.id,
298 self.first_on_branch_id,
299 self.ntdbr,
300 self.ntdbr_prev_id, self.ntdbr_next_id,
301 self.tag_ids, self.branch_ids, self.branch_commit_ids,
302 self.opened_symbols, self.closed_symbols,
303 self.properties, self.properties_changed,
304 self.revision_reader_token,
307 def __setstate__(self, data):
308 (self.id, cvs_file_id,
309 self.timestamp, self.metadata_id,
310 self.prev_id, self.next_id,
311 self.rev,
312 self.deltatext_exists,
313 lod_id,
314 self.first_on_branch_id,
315 self.ntdbr,
316 self.ntdbr_prev_id, self.ntdbr_next_id,
317 self.tag_ids, self.branch_ids, self.branch_commit_ids,
318 self.opened_symbols, self.closed_symbols,
319 self.properties, self.properties_changed,
320 self.revision_reader_token) = data
321 self.cvs_file = Ctx()._cvs_path_db.get_path(cvs_file_id)
322 self.lod = Ctx()._symbol_db.get_symbol(lod_id)
324 def get_properties(self):
325 """Return all of the properties needed for this CVSRevision.
327 Combine SELF.cvs_file.properties and SELF.properties to get the
328 final properties needed for this CVSRevision. (The properties in
329 SELF have precedence.) Return the properties as a map {key :
330 value}, where keys and values are both strings. (Entries with
331 value == None are omitted.) Different backends can use properties
332 for different purposes; for cvs2svn these become SVN versioned
333 properties. Properties whose names start with underscore are
334 reserved for internal cvs2svn purposes."""
336 properties = self.cvs_file.properties.copy()
337 properties.update(self.properties)
339 for (k,v) in properties.items():
340 if v is None:
341 del properties[k]
343 return properties
345 def get_property(self, name, default=None):
346 """Return a particular property for this CVSRevision.
348 This is logically the same as SELF.get_properties().get(name,
349 default) but implemented more efficiently."""
351 if name in self.properties:
352 return self.properties[name]
353 else:
354 return self.cvs_file.properties.get(name, default)
356 def get_effective_prev_id(self):
357 """Return the ID of the effective predecessor of this item.
359 This is the ID of the item that determines whether the object
360 existed before this CVSRevision."""
362 if self.ntdbr_prev_id is not None:
363 return self.ntdbr_prev_id
364 else:
365 return self.prev_id
367 def get_symbol_pred_ids(self):
368 """Return the pred_ids for symbol predecessors."""
370 retval = set()
371 if self.first_on_branch_id is not None:
372 retval.add(self.first_on_branch_id)
373 return retval
375 def get_pred_ids(self):
376 retval = self.get_symbol_pred_ids()
377 if self.prev_id is not None:
378 retval.add(self.prev_id)
379 if self.ntdbr_prev_id is not None:
380 retval.add(self.ntdbr_prev_id)
381 return retval
383 def get_symbol_succ_ids(self):
384 """Return the succ_ids for symbol successors."""
386 retval = set()
387 for id in self.branch_ids + self.tag_ids:
388 retval.add(id)
389 return retval
391 def get_succ_ids(self):
392 retval = self.get_symbol_succ_ids()
393 if self.next_id is not None:
394 retval.add(self.next_id)
395 if self.ntdbr_next_id is not None:
396 retval.add(self.ntdbr_next_id)
397 for id in self.branch_commit_ids:
398 retval.add(id)
399 return retval
401 def get_ids_closed(self):
402 # Special handling is needed in the case of non-trunk default
403 # branches. The following cases have to be handled:
405 # Case 1: Revision 1.1 not deleted; revision 1.2 exists:
407 # 1.1 -----------------> 1.2
408 # \ ^ ^ /
409 # \ | | /
410 # 1.1.1.1 -> 1.1.1.2
412 # * 1.1.1.1 closes 1.1 (because its post-commit overwrites 1.1
413 # on trunk)
415 # * 1.1.1.2 closes 1.1.1.1
417 # * 1.2 doesn't close anything (the post-commit from 1.1.1.1
418 # already closed 1.1, and no symbols can sprout from the
419 # post-commit of 1.1.1.2)
421 # Case 2: Revision 1.1 not deleted; revision 1.2 does not exist:
423 # 1.1 ..................
424 # \ ^ ^
425 # \ | |
426 # 1.1.1.1 -> 1.1.1.2
428 # * 1.1.1.1 closes 1.1 (because its post-commit overwrites 1.1
429 # on trunk)
431 # * 1.1.1.2 closes 1.1.1.1
433 # Case 3: Revision 1.1 deleted; revision 1.2 exists:
435 # ............... 1.2
436 # ^ ^ /
437 # | | /
438 # 1.1.1.1 -> 1.1.1.2
440 # * 1.1.1.1 doesn't close anything
442 # * 1.1.1.2 closes 1.1.1.1
444 # * 1.2 doesn't close anything (no symbols can sprout from the
445 # post-commit of 1.1.1.2)
447 # Case 4: Revision 1.1 deleted; revision 1.2 doesn't exist:
449 # ...............
450 # ^ ^
451 # | |
452 # 1.1.1.1 -> 1.1.1.2
454 # * 1.1.1.1 doesn't close anything
456 # * 1.1.1.2 closes 1.1.1.1
458 if self.first_on_branch_id is not None:
459 # The first CVSRevision on a branch is considered to close the
460 # branch:
461 yield self.first_on_branch_id
462 if self.ntdbr:
463 # If the 1.1 revision was not deleted, the 1.1.1.1 revision is
464 # considered to close it:
465 yield self.prev_id
466 elif self.ntdbr_prev_id is not None:
467 # This is the special case of a 1.2 revision that follows a
468 # non-trunk default branch. Either 1.1 was deleted or the first
469 # default branch revision closed 1.1, so we don't have to close
470 # 1.1. Technically, we close the revision on trunk that was
471 # copied from the last non-trunk default branch revision in a
472 # post-commit, but for now no symbols can sprout from that
473 # revision so we ignore that one, too.
474 pass
475 elif self.prev_id is not None:
476 # Since this CVSRevision is not the first on a branch, its
477 # prev_id is on the same LOD and this item closes that one:
478 yield self.prev_id
480 def _get_branch_ids_recursively(self, cvs_file_items):
481 """Return the set of all CVSBranches that sprout from this CVSRevision.
483 After parent adjustment in FilterSymbolsPass, it is possible for
484 branches to sprout directly from a CVSRevision, or from those
485 branches, etc. Return all branches that sprout from this
486 CVSRevision, directly or indirectly."""
488 retval = set()
489 branch_ids_to_process = list(self.branch_ids)
490 while branch_ids_to_process:
491 branch = cvs_file_items[branch_ids_to_process.pop()]
492 retval.add(branch)
493 branch_ids_to_process.extend(branch.branch_ids)
495 return retval
497 def check_links(self, cvs_file_items):
498 assert self.cvs_file == cvs_file_items.cvs_file
500 prev = cvs_file_items.get(self.prev_id)
501 next = cvs_file_items.get(self.next_id)
502 first_on_branch = cvs_file_items.get(self.first_on_branch_id)
503 ntdbr_next = cvs_file_items.get(self.ntdbr_next_id)
504 ntdbr_prev = cvs_file_items.get(self.ntdbr_prev_id)
505 effective_prev = cvs_file_items.get(self.get_effective_prev_id())
507 if prev is None:
508 # This is the first CVSRevision on trunk or a detached branch:
509 assert self.id in cvs_file_items.root_ids
510 elif first_on_branch is not None:
511 # This is the first CVSRevision on an existing branch:
512 assert isinstance(first_on_branch, CVSBranch)
513 assert first_on_branch.symbol == self.lod
514 assert first_on_branch.next_id == self.id
515 cvs_revision_source = first_on_branch.get_cvs_revision_source(
516 cvs_file_items
518 assert cvs_revision_source.id == prev.id
519 assert self.id in prev.branch_commit_ids
520 else:
521 # This revision follows another revision on the same LOD:
522 assert prev.next_id == self.id
523 assert prev.lod == self.lod
525 if next is not None:
526 assert next.prev_id == self.id
527 assert next.lod == self.lod
529 if ntdbr_next is not None:
530 assert self.ntdbr
531 assert ntdbr_next.ntdbr_prev_id == self.id
533 if ntdbr_prev is not None:
534 assert ntdbr_prev.ntdbr_next_id == self.id
536 for tag_id in self.tag_ids:
537 tag = cvs_file_items[tag_id]
538 assert isinstance(tag, CVSTag)
539 assert tag.source_id == self.id
540 assert tag.source_lod == self.lod
542 for branch_id in self.branch_ids:
543 branch = cvs_file_items[branch_id]
544 assert isinstance(branch, CVSBranch)
545 assert branch.source_id == self.id
546 assert branch.source_lod == self.lod
548 branch_commit_ids = list(self.branch_commit_ids)
550 for branch in self._get_branch_ids_recursively(cvs_file_items):
551 assert isinstance(branch, CVSBranch)
552 if branch.next_id is not None:
553 assert branch.next_id in branch_commit_ids
554 branch_commit_ids.remove(branch.next_id)
556 assert not branch_commit_ids
558 assert self.__class__ == cvs_revision_type_map[(
559 isinstance(self, CVSRevisionModification),
560 effective_prev is not None
561 and isinstance(effective_prev, CVSRevisionModification),
564 def __str__(self):
565 """For convenience only. The format is subject to change at any time."""
567 return '%s:%s<%x>' % (self.cvs_file, self.rev, self.id,)
570 class CVSRevisionModification(CVSRevision):
571 """Base class for CVSRevisionAdd or CVSRevisionChange."""
573 __slots__ = []
575 def get_cvs_symbol_ids_opened(self):
576 return self.tag_ids + self.branch_ids
579 class CVSRevisionAdd(CVSRevisionModification):
580 """A CVSRevision that creates a file that previously didn't exist.
582 The file might have never existed on this LOD, or it might have
583 existed previously but been deleted by a CVSRevisionDelete."""
585 __slots__ = []
588 class CVSRevisionChange(CVSRevisionModification):
589 """A CVSRevision that modifies a file that already existed on this LOD."""
591 __slots__ = []
594 class CVSRevisionAbsent(CVSRevision):
595 """A CVSRevision for which the file is nonexistent on this LOD."""
597 __slots__ = []
599 def get_cvs_symbol_ids_opened(self):
600 return []
603 class CVSRevisionDelete(CVSRevisionAbsent):
604 """A CVSRevision that deletes a file that existed on this LOD."""
606 __slots__ = []
609 class CVSRevisionNoop(CVSRevisionAbsent):
610 """A CVSRevision that doesn't do anything.
612 The revision was 'dead' and the predecessor either didn't exist or
613 was also 'dead'. These revisions can't necessarily be thrown away
614 because (1) they impose ordering constraints on other items; (2)
615 they might have a nontrivial log message that we don't want to throw
616 away."""
618 __slots__ = []
621 # A map
623 # {(nondead(cvs_rev), nondead(prev_cvs_rev)) : cvs_revision_subtype}
625 # , where nondead() means that the cvs revision exists and is not
626 # 'dead', and CVS_REVISION_SUBTYPE is the subtype of CVSRevision that
627 # should be used for CVS_REV.
628 cvs_revision_type_map = {
629 (False, False) : CVSRevisionNoop,
630 (False, True) : CVSRevisionDelete,
631 (True, False) : CVSRevisionAdd,
632 (True, True) : CVSRevisionChange,
636 class CVSSymbol(CVSItem):
637 """Represent a symbol on a particular CVSFile.
639 This is the base class for CVSBranch and CVSTag.
641 Members:
643 id -- (int) unique ID for this item.
645 cvs_file -- (CVSFile) CVSFile affected by this item.
647 symbol -- (Symbol) the symbol affected by this CVSSymbol.
649 source_lod -- (LineOfDevelopment) the LOD that is the source for
650 this CVSSymbol.
652 source_id -- (int) the ID of the CVSRevision or CVSBranch that is
653 the source for this item. This initially points to a
654 CVSRevision, but can be changed to a CVSBranch via parent
655 adjustment in FilterSymbolsPass.
657 revision_reader_token -- (arbitrary) a token that can be set by
658 RevisionCollector for the later use of RevisionReader.
662 __slots__ = [
663 'symbol',
664 'source_lod',
665 'source_id',
668 def __init__(
669 self, id, cvs_file, symbol, source_lod, source_id,
670 revision_reader_token,
672 """Initialize a CVSSymbol object."""
674 CVSItem.__init__(self, id, cvs_file, revision_reader_token)
676 self.symbol = symbol
677 self.source_lod = source_lod
678 self.source_id = source_id
680 def get_cvs_revision_source(self, cvs_file_items):
681 """Return the CVSRevision that is the ultimate source of this symbol."""
683 cvs_source = cvs_file_items[self.source_id]
684 while not isinstance(cvs_source, CVSRevision):
685 cvs_source = cvs_file_items[cvs_source.source_id]
687 return cvs_source
689 def get_svn_path(self):
690 return self.symbol.get_path(self.cvs_file.cvs_path)
692 def get_ids_closed(self):
693 # A Symbol does not close any other CVSItems:
694 return []
697 class CVSBranch(CVSSymbol):
698 """Represent the creation of a branch in a particular CVSFile.
700 Members:
702 id -- (int) unique ID for this item.
704 cvs_file -- (CVSFile) CVSFile affected by this item.
706 symbol -- (Symbol) the symbol affected by this CVSSymbol.
708 branch_number -- (string) the number of this branch (e.g.,
709 '1.3.4'), or None if this is a converted CVSTag.
711 source_lod -- (LineOfDevelopment) the LOD that is the source for
712 this CVSSymbol.
714 source_id -- (int) id of the CVSRevision or CVSBranch from which
715 this branch sprouts. This initially points to a CVSRevision,
716 but can be changed to a CVSBranch via parent adjustment in
717 FilterSymbolsPass.
719 next_id -- (int or None) id of first CVSRevision on this branch,
720 if any; else, None.
722 tag_ids -- (list of int) ids of all CVSTags rooted at this
723 CVSBranch (can be set due to parent adjustment in
724 FilterSymbolsPass).
726 branch_ids -- (list of int) ids of all CVSBranches rooted at this
727 CVSBranch (can be set due to parent adjustment in
728 FilterSymbolsPass).
730 opened_symbols -- (None or list of (symbol_id, cvs_symbol_id)
731 tuples) information about all CVSSymbols opened by this
732 branch. This member is set in FilterSymbolsPass; before then,
733 it is None.
735 revision_reader_token -- (arbitrary) a token that can be set by
736 RevisionCollector for the later use of RevisionReader.
740 __slots__ = [
741 'branch_number',
742 'next_id',
743 'tag_ids',
744 'branch_ids',
745 'opened_symbols',
748 def __init__(
749 self, id, cvs_file, symbol, branch_number,
750 source_lod, source_id, next_id,
751 revision_reader_token,
753 """Initialize a CVSBranch."""
755 CVSSymbol.__init__(
756 self, id, cvs_file, symbol, source_lod, source_id,
757 revision_reader_token,
759 self.branch_number = branch_number
760 self.next_id = next_id
761 self.tag_ids = []
762 self.branch_ids = []
763 self.opened_symbols = None
765 def __getstate__(self):
766 return (
767 self.id, self.cvs_file.id,
768 self.symbol.id, self.branch_number,
769 self.source_lod.id, self.source_id, self.next_id,
770 self.tag_ids, self.branch_ids,
771 self.opened_symbols,
772 self.revision_reader_token,
775 def __setstate__(self, data):
777 self.id, cvs_file_id,
778 symbol_id, self.branch_number,
779 source_lod_id, self.source_id, self.next_id,
780 self.tag_ids, self.branch_ids,
781 self.opened_symbols,
782 self.revision_reader_token,
783 ) = data
784 self.cvs_file = Ctx()._cvs_path_db.get_path(cvs_file_id)
785 self.symbol = Ctx()._symbol_db.get_symbol(symbol_id)
786 self.source_lod = Ctx()._symbol_db.get_symbol(source_lod_id)
788 def get_pred_ids(self):
789 return set([self.source_id])
791 def get_succ_ids(self):
792 retval = set(self.tag_ids + self.branch_ids)
793 if self.next_id is not None:
794 retval.add(self.next_id)
795 return retval
797 def get_cvs_symbol_ids_opened(self):
798 return self.tag_ids + self.branch_ids
800 def check_links(self, cvs_file_items):
801 source = cvs_file_items.get(self.source_id)
802 next = cvs_file_items.get(self.next_id)
804 assert self.id in source.branch_ids
805 if isinstance(source, CVSRevision):
806 assert self.source_lod == source.lod
807 elif isinstance(source, CVSBranch):
808 assert self.source_lod == source.symbol
809 else:
810 assert False
812 if next is not None:
813 assert isinstance(next, CVSRevision)
814 assert next.lod == self.symbol
815 assert next.first_on_branch_id == self.id
817 for tag_id in self.tag_ids:
818 tag = cvs_file_items[tag_id]
819 assert isinstance(tag, CVSTag)
820 assert tag.source_id == self.id
821 assert tag.source_lod == self.symbol
823 for branch_id in self.branch_ids:
824 branch = cvs_file_items[branch_id]
825 assert isinstance(branch, CVSBranch)
826 assert branch.source_id == self.id
827 assert branch.source_lod == self.symbol
829 def __str__(self):
830 """For convenience only. The format is subject to change at any time."""
832 return '%s:%s:%s<%x>' \
833 % (self.cvs_file, self.symbol, self.branch_number, self.id,)
836 class CVSBranchNoop(CVSBranch):
837 """A CVSBranch whose source is a CVSRevisionAbsent."""
839 __slots__ = []
841 def get_cvs_symbol_ids_opened(self):
842 return []
845 # A map
847 # {nondead(source_cvs_rev) : cvs_branch_subtype}
849 # , where nondead() means that the cvs revision exists and is not
850 # 'dead', and CVS_BRANCH_SUBTYPE is the subtype of CVSBranch that
851 # should be used.
852 cvs_branch_type_map = {
853 False : CVSBranchNoop,
854 True : CVSBranch,
858 class CVSTag(CVSSymbol):
859 """Represent the creation of a tag on a particular CVSFile.
861 Members:
863 id -- (int) unique ID for this item.
865 cvs_file -- (CVSFile) CVSFile affected by this item.
867 symbol -- (Symbol) the symbol affected by this CVSSymbol.
869 source_lod -- (LineOfDevelopment) the LOD that is the source for
870 this CVSSymbol.
872 source_id -- (int) the ID of the CVSRevision or CVSBranch that is
873 being tagged. This initially points to a CVSRevision, but can
874 be changed to a CVSBranch via parent adjustment in
875 FilterSymbolsPass.
877 revision_reader_token -- (arbitrary) a token that can be set by
878 RevisionCollector for the later use of RevisionReader.
882 __slots__ = []
884 def __init__(
885 self, id, cvs_file, symbol, source_lod, source_id,
886 revision_reader_token,
888 """Initialize a CVSTag."""
890 CVSSymbol.__init__(
891 self, id, cvs_file, symbol, source_lod, source_id,
892 revision_reader_token,
895 def __getstate__(self):
896 return (
897 self.id, self.cvs_file.id, self.symbol.id,
898 self.source_lod.id, self.source_id,
899 self.revision_reader_token,
902 def __setstate__(self, data):
904 self.id, cvs_file_id, symbol_id, source_lod_id, self.source_id,
905 self.revision_reader_token,
906 ) = data
907 self.cvs_file = Ctx()._cvs_path_db.get_path(cvs_file_id)
908 self.symbol = Ctx()._symbol_db.get_symbol(symbol_id)
909 self.source_lod = Ctx()._symbol_db.get_symbol(source_lod_id)
911 def get_pred_ids(self):
912 return set([self.source_id])
914 def get_succ_ids(self):
915 return set()
917 def get_cvs_symbol_ids_opened(self):
918 return []
920 def check_links(self, cvs_file_items):
921 source = cvs_file_items.get(self.source_id)
923 assert self.id in source.tag_ids
924 if isinstance(source, CVSRevision):
925 assert self.source_lod == source.lod
926 elif isinstance(source, CVSBranch):
927 assert self.source_lod == source.symbol
928 else:
929 assert False
931 def __str__(self):
932 """For convenience only. The format is subject to change at any time."""
934 return '%s:%s<%x>' \
935 % (self.cvs_file, self.symbol, self.id,)
938 class CVSTagNoop(CVSTag):
939 """A CVSTag whose source is a CVSRevisionAbsent."""
941 __slots__ = []
944 # A map
946 # {nondead(source_cvs_rev) : cvs_tag_subtype}
948 # , where nondead() means that the cvs revision exists and is not
949 # 'dead', and CVS_TAG_SUBTYPE is the subtype of CVSTag that should be
950 # used.
951 cvs_tag_type_map = {
952 False : CVSTagNoop,
953 True : CVSTag,