diff options
Diffstat (limited to 'cvs2svn_lib/cvs_item.py')
-rw-r--r-- | cvs2svn_lib/cvs_item.py | 901 |
1 files changed, 901 insertions, 0 deletions
diff --git a/cvs2svn_lib/cvs_item.py b/cvs2svn_lib/cvs_item.py new file mode 100644 index 0000000..5c01a24 --- /dev/null +++ b/cvs2svn_lib/cvs_item.py @@ -0,0 +1,901 @@ +# (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 to store atomic CVS events. + +A CVSItem is a single event, pertaining to a single file, that can be +determined to have occured based on the information in the CVS +repository. + +The inheritance tree is as follows: + +CVSItem +| ++--CVSRevision +| | +| +--CVSRevisionModification (* -> 'Exp') +| | | +| | +--CVSRevisionAdd ('dead' -> 'Exp') +| | | +| | +--CVSRevisionChange ('Exp' -> 'Exp') +| | +| +--CVSRevisionAbsent (* -> 'dead') +| | +| +--CVSRevisionDelete ('Exp' -> 'dead') +| | +| +--CVSRevisionNoop ('dead' -> 'dead') +| ++--CVSSymbol + | + +--CVSBranch + | | + | +--CVSBranchNoop + | + +--CVSTag + | + +--CVSTagNoop + +""" + + +from cvs2svn_lib.context import Ctx + + +class CVSItem(object): + __slots__ = [ + 'id', + 'cvs_file', + 'revision_recorder_token', + ] + + def __init__(self, id, cvs_file, revision_recorder_token): + self.id = id + self.cvs_file = cvs_file + self.revision_recorder_token = revision_recorder_token + + def __eq__(self, other): + return self.id == other.id + + def __cmp__(self, other): + return cmp(self.id, other.id) + + def __hash__(self): + return self.id + + def __getstate__(self): + raise NotImplementedError() + + def __setstate__(self, data): + raise NotImplementedError() + + def get_svn_path(self): + """Return the SVN path associated with this CVSItem.""" + + raise NotImplementedError() + + def get_pred_ids(self): + """Return the CVSItem.ids of direct predecessors of SELF. + + A predecessor is defined to be a CVSItem that has to have been + committed before this one.""" + + raise NotImplementedError() + + def get_succ_ids(self): + """Return the CVSItem.ids of direct successors of SELF. + + A direct successor is defined to be a CVSItem that has this one as + a direct predecessor.""" + + raise NotImplementedError() + + def get_cvs_symbol_ids_opened(self): + """Return an iterable over the ids of CVSSymbols that this item opens. + + The definition of 'open' is that the path corresponding to this + CVSItem will have to be copied when filling the corresponding + symbol.""" + + raise NotImplementedError() + + def get_ids_closed(self): + """Return an iterable over the CVSItem.ids of CVSItems closed by this one. + + A CVSItem A is said to close a CVSItem B if committing A causes B + to be overwritten or deleted (no longer available) in the SVN + repository. This is interesting because it sets the last SVN + revision number from which the contents of B can be copied (for + example, to fill a symbol). See the concrete implementations of + this method for the exact rules about what closes what.""" + + raise NotImplementedError() + + def check_links(self, cvs_file_items): + """Check for consistency of links to other CVSItems. + + Other items can be looked up in CVS_FILE_ITEMS, which is an + instance of CVSFileItems. Raise an AssertionError if there is a + problem.""" + + raise NotImplementedError() + + def __repr__(self): + return '%s(%s)' % (self.__class__.__name__, self,) + + +class CVSRevision(CVSItem): + """Information about a single CVS revision. + + A CVSRevision holds the information known about a single version of + a single file. + + Members: + + id -- (int) unique ID for this revision. + + cvs_file -- (CVSFile) CVSFile affected by this revision. + + timestamp -- (int) date stamp for this revision. + + metadata_id -- (int) id of metadata instance record in + metadata_db. + + prev_id -- (int) id of the logically previous CVSRevision, either + on the same or the source branch (or None). + + next_id -- (int) id of the logically next CVSRevision (or None). + + rev -- (string) the CVS revision number, e.g., '1.3'. + + deltatext_exists -- (bool) true iff this revision's deltatext is + not empty. + + lod -- (LineOfDevelopment) LOD on which this revision occurred. + + first_on_branch_id -- (int or None) if this revision is the first + on its branch, the cvs_branch_id of that branch; else, None. + + ntdbr -- (bool) true iff this is a non-trunk default branch + revision. + + ntdbr_prev_id -- (int or None) Iff this is the 1.2 revision after + the end of a default branch, the id of the last rev on the + default branch; else, None. + + ntdbr_next_id -- (int or None) Iff this is the last revision on a + default branch preceding a 1.2 rev, the id of the 1.2 + revision; else, None. + + tag_ids -- (list of int) ids of all CVSTags rooted at this + CVSRevision. + + branch_ids -- (list of int) ids of all CVSBranches rooted at this + CVSRevision. + + branch_commit_ids -- (list of int) ids of first CVSRevision + committed on each branch rooted in this revision (for branches + with commits). + + opened_symbols -- (None or list of (symbol_id, cvs_symbol_id) + tuples) information about all CVSSymbols opened by this + revision. This member is set in FilterSymbolsPass; before + then, it is None. + + closed_symbols -- (None or list of (symbol_id, cvs_symbol_id) + tuples) information about all CVSSymbols closed by this + revision. This member is set in FilterSymbolsPass; before + then, it is None. + + revision_recorder_token -- (arbitrary) a token that can be set by + RevisionRecorder for the later use of RevisionReader. + + """ + + __slots__ = [ + 'timestamp', + 'metadata_id', + 'prev_id', + 'next_id', + 'rev', + 'deltatext_exists', + 'lod', + 'first_on_branch_id', + 'ntdbr', + 'ntdbr_prev_id', + 'ntdbr_next_id', + 'tag_ids', + 'branch_ids', + 'branch_commit_ids', + 'opened_symbols', + 'closed_symbols', + ] + + def __init__(self, + id, cvs_file, + timestamp, metadata_id, + prev_id, next_id, + rev, deltatext_exists, + lod, first_on_branch_id, ntdbr, + ntdbr_prev_id, ntdbr_next_id, + tag_ids, branch_ids, branch_commit_ids, + revision_recorder_token): + """Initialize a new CVSRevision object.""" + + CVSItem.__init__(self, id, cvs_file, revision_recorder_token) + + self.timestamp = timestamp + self.metadata_id = metadata_id + self.prev_id = prev_id + self.next_id = next_id + self.rev = rev + self.deltatext_exists = deltatext_exists + self.lod = lod + self.first_on_branch_id = first_on_branch_id + self.ntdbr = ntdbr + self.ntdbr_prev_id = ntdbr_prev_id + self.ntdbr_next_id = ntdbr_next_id + self.tag_ids = tag_ids + self.branch_ids = branch_ids + self.branch_commit_ids = branch_commit_ids + self.opened_symbols = None + self.closed_symbols = None + + def _get_cvs_path(self): + return self.cvs_file.cvs_path + + cvs_path = property(_get_cvs_path) + + def get_svn_path(self): + return self.lod.get_path(self.cvs_file.cvs_path) + + def __getstate__(self): + """Return the contents of this instance, for pickling. + + The presence of this method improves the space efficiency of + pickling CVSRevision instances.""" + + return ( + self.id, self.cvs_file.id, + self.timestamp, self.metadata_id, + self.prev_id, self.next_id, + self.rev, + self.deltatext_exists, + self.lod.id, + self.first_on_branch_id, + self.ntdbr, + self.ntdbr_prev_id, self.ntdbr_next_id, + self.tag_ids, self.branch_ids, self.branch_commit_ids, + self.opened_symbols, self.closed_symbols, + self.revision_recorder_token, + ) + + def __setstate__(self, data): + (self.id, cvs_file_id, + self.timestamp, self.metadata_id, + self.prev_id, self.next_id, + self.rev, + self.deltatext_exists, + lod_id, + self.first_on_branch_id, + self.ntdbr, + self.ntdbr_prev_id, self.ntdbr_next_id, + self.tag_ids, self.branch_ids, self.branch_commit_ids, + self.opened_symbols, self.closed_symbols, + self.revision_recorder_token) = data + self.cvs_file = Ctx()._cvs_file_db.get_file(cvs_file_id) + self.lod = Ctx()._symbol_db.get_symbol(lod_id) + + def get_effective_prev_id(self): + """Return the ID of the effective predecessor of this item. + + This is the ID of the item that determines whether the object + existed before this CVSRevision.""" + + if self.ntdbr_prev_id is not None: + return self.ntdbr_prev_id + else: + return self.prev_id + + def get_symbol_pred_ids(self): + """Return the pred_ids for symbol predecessors.""" + + retval = set() + if self.first_on_branch_id is not None: + retval.add(self.first_on_branch_id) + return retval + + def get_pred_ids(self): + retval = self.get_symbol_pred_ids() + if self.prev_id is not None: + retval.add(self.prev_id) + if self.ntdbr_prev_id is not None: + retval.add(self.ntdbr_prev_id) + return retval + + def get_symbol_succ_ids(self): + """Return the succ_ids for symbol successors.""" + + retval = set() + for id in self.branch_ids + self.tag_ids: + retval.add(id) + return retval + + def get_succ_ids(self): + retval = self.get_symbol_succ_ids() + if self.next_id is not None: + retval.add(self.next_id) + if self.ntdbr_next_id is not None: + retval.add(self.ntdbr_next_id) + for id in self.branch_commit_ids: + retval.add(id) + return retval + + def get_ids_closed(self): + # Special handling is needed in the case of non-trunk default + # branches. The following cases have to be handled: + # + # Case 1: Revision 1.1 not deleted; revision 1.2 exists: + # + # 1.1 -----------------> 1.2 + # \ ^ ^ / + # \ | | / + # 1.1.1.1 -> 1.1.1.2 + # + # * 1.1.1.1 closes 1.1 (because its post-commit overwrites 1.1 + # on trunk) + # + # * 1.1.1.2 closes 1.1.1.1 + # + # * 1.2 doesn't close anything (the post-commit from 1.1.1.1 + # already closed 1.1, and no symbols can sprout from the + # post-commit of 1.1.1.2) + # + # Case 2: Revision 1.1 not deleted; revision 1.2 does not exist: + # + # 1.1 .................. + # \ ^ ^ + # \ | | + # 1.1.1.1 -> 1.1.1.2 + # + # * 1.1.1.1 closes 1.1 (because its post-commit overwrites 1.1 + # on trunk) + # + # * 1.1.1.2 closes 1.1.1.1 + # + # Case 3: Revision 1.1 deleted; revision 1.2 exists: + # + # ............... 1.2 + # ^ ^ / + # | | / + # 1.1.1.1 -> 1.1.1.2 + # + # * 1.1.1.1 doesn't close anything + # + # * 1.1.1.2 closes 1.1.1.1 + # + # * 1.2 doesn't close anything (no symbols can sprout from the + # post-commit of 1.1.1.2) + # + # Case 4: Revision 1.1 deleted; revision 1.2 doesn't exist: + # + # ............... + # ^ ^ + # | | + # 1.1.1.1 -> 1.1.1.2 + # + # * 1.1.1.1 doesn't close anything + # + # * 1.1.1.2 closes 1.1.1.1 + + if self.first_on_branch_id is not None: + # The first CVSRevision on a branch is considered to close the + # branch: + yield self.first_on_branch_id + if self.ntdbr: + # If the 1.1 revision was not deleted, the 1.1.1.1 revision is + # considered to close it: + yield self.prev_id + elif self.ntdbr_prev_id is not None: + # This is the special case of a 1.2 revision that follows a + # non-trunk default branch. Either 1.1 was deleted or the first + # default branch revision closed 1.1, so we don't have to close + # 1.1. Technically, we close the revision on trunk that was + # copied from the last non-trunk default branch revision in a + # post-commit, but for now no symbols can sprout from that + # revision so we ignore that one, too. + pass + elif self.prev_id is not None: + # Since this CVSRevision is not the first on a branch, its + # prev_id is on the same LOD and this item closes that one: + yield self.prev_id + + def _get_branch_ids_recursively(self, cvs_file_items): + """Return the set of all CVSBranches that sprout from this CVSRevision. + + After parent adjustment in FilterSymbolsPass, it is possible for + branches to sprout directly from a CVSRevision, or from those + branches, etc. Return all branches that sprout from this + CVSRevision, directly or indirectly.""" + + retval = set() + branch_ids_to_process = list(self.branch_ids) + while branch_ids_to_process: + branch = cvs_file_items[branch_ids_to_process.pop()] + retval.add(branch) + branch_ids_to_process.extend(branch.branch_ids) + + return retval + + def check_links(self, cvs_file_items): + assert self.cvs_file == cvs_file_items.cvs_file + + prev = cvs_file_items.get(self.prev_id) + next = cvs_file_items.get(self.next_id) + first_on_branch = cvs_file_items.get(self.first_on_branch_id) + ntdbr_next = cvs_file_items.get(self.ntdbr_next_id) + ntdbr_prev = cvs_file_items.get(self.ntdbr_prev_id) + effective_prev = cvs_file_items.get(self.get_effective_prev_id()) + + if prev is None: + # This is the first CVSRevision on trunk or a detached branch: + assert self.id in cvs_file_items.root_ids + elif first_on_branch is not None: + # This is the first CVSRevision on an existing branch: + assert isinstance(first_on_branch, CVSBranch) + assert first_on_branch.symbol == self.lod + assert first_on_branch.next_id == self.id + cvs_revision_source = first_on_branch.get_cvs_revision_source( + cvs_file_items + ) + assert cvs_revision_source.id == prev.id + assert self.id in prev.branch_commit_ids + else: + # This revision follows another revision on the same LOD: + assert prev.next_id == self.id + assert prev.lod == self.lod + + if next is not None: + assert next.prev_id == self.id + assert next.lod == self.lod + + if ntdbr_next is not None: + assert self.ntdbr + assert ntdbr_next.ntdbr_prev_id == self.id + + if ntdbr_prev is not None: + assert ntdbr_prev.ntdbr_next_id == self.id + + for tag_id in self.tag_ids: + tag = cvs_file_items[tag_id] + assert isinstance(tag, CVSTag) + assert tag.source_id == self.id + assert tag.source_lod == self.lod + + for branch_id in self.branch_ids: + branch = cvs_file_items[branch_id] + assert isinstance(branch, CVSBranch) + assert branch.source_id == self.id + assert branch.source_lod == self.lod + + branch_commit_ids = list(self.branch_commit_ids) + + for branch in self._get_branch_ids_recursively(cvs_file_items): + assert isinstance(branch, CVSBranch) + if branch.next_id is not None: + assert branch.next_id in branch_commit_ids + branch_commit_ids.remove(branch.next_id) + + assert not branch_commit_ids + + assert self.__class__ == cvs_revision_type_map[( + isinstance(self, CVSRevisionModification), + effective_prev is not None + and isinstance(effective_prev, CVSRevisionModification), + )] + + def __str__(self): + """For convenience only. The format is subject to change at any time.""" + + return '%s:%s<%x>' % (self.cvs_file, self.rev, self.id,) + + +class CVSRevisionModification(CVSRevision): + """Base class for CVSRevisionAdd or CVSRevisionChange.""" + + __slots__ = [] + + def get_cvs_symbol_ids_opened(self): + return self.tag_ids + self.branch_ids + + +class CVSRevisionAdd(CVSRevisionModification): + """A CVSRevision that creates a file that previously didn't exist. + + The file might have never existed on this LOD, or it might have + existed previously but been deleted by a CVSRevisionDelete.""" + + __slots__ = [] + + +class CVSRevisionChange(CVSRevisionModification): + """A CVSRevision that modifies a file that already existed on this LOD.""" + + __slots__ = [] + + +class CVSRevisionAbsent(CVSRevision): + """A CVSRevision for which the file is nonexistent on this LOD.""" + + __slots__ = [] + + def get_cvs_symbol_ids_opened(self): + return [] + + +class CVSRevisionDelete(CVSRevisionAbsent): + """A CVSRevision that deletes a file that existed on this LOD.""" + + __slots__ = [] + + +class CVSRevisionNoop(CVSRevisionAbsent): + """A CVSRevision that doesn't do anything. + + The revision was 'dead' and the predecessor either didn't exist or + was also 'dead'. These revisions can't necessarily be thrown away + because (1) they impose ordering constraints on other items; (2) + they might have a nontrivial log message that we don't want to throw + away.""" + + __slots__ = [] + + +# A map +# +# {(nondead(cvs_rev), nondead(prev_cvs_rev)) : cvs_revision_subtype} +# +# , where nondead() means that the cvs revision exists and is not +# 'dead', and CVS_REVISION_SUBTYPE is the subtype of CVSRevision that +# should be used for CVS_REV. +cvs_revision_type_map = { + (False, False) : CVSRevisionNoop, + (False, True) : CVSRevisionDelete, + (True, False) : CVSRevisionAdd, + (True, True) : CVSRevisionChange, + } + + +class CVSSymbol(CVSItem): + """Represent a symbol on a particular CVSFile. + + This is the base class for CVSBranch and CVSTag. + + Members: + + id -- (int) unique ID for this item. + + cvs_file -- (CVSFile) CVSFile affected by this item. + + symbol -- (Symbol) the symbol affected by this CVSSymbol. + + source_lod -- (LineOfDevelopment) the LOD that is the source for + this CVSSymbol. + + source_id -- (int) the ID of the CVSRevision or CVSBranch that is + the source for this item. This initially points to a + CVSRevision, but can be changed to a CVSBranch via parent + adjustment in FilterSymbolsPass. + + revision_recorder_token -- (arbitrary) a token that can be set by + RevisionRecorder for the later use of RevisionReader. + + """ + + __slots__ = [ + 'symbol', + 'source_lod', + 'source_id', + ] + + def __init__( + self, id, cvs_file, symbol, source_lod, source_id, + revision_recorder_token + ): + """Initialize a CVSSymbol object.""" + + CVSItem.__init__(self, id, cvs_file, revision_recorder_token) + + self.symbol = symbol + self.source_lod = source_lod + self.source_id = source_id + + def get_cvs_revision_source(self, cvs_file_items): + """Return the CVSRevision that is the ultimate source of this symbol.""" + + cvs_source = cvs_file_items[self.source_id] + while not isinstance(cvs_source, CVSRevision): + cvs_source = cvs_file_items[cvs_source.source_id] + + return cvs_source + + def get_svn_path(self): + return self.symbol.get_path(self.cvs_file.cvs_path) + + def get_ids_closed(self): + # A Symbol does not close any other CVSItems: + return [] + + +class CVSBranch(CVSSymbol): + """Represent the creation of a branch in a particular CVSFile. + + Members: + + id -- (int) unique ID for this item. + + cvs_file -- (CVSFile) CVSFile affected by this item. + + symbol -- (Symbol) the symbol affected by this CVSSymbol. + + branch_number -- (string) the number of this branch (e.g., + '1.3.4'), or None if this is a converted CVSTag. + + source_lod -- (LineOfDevelopment) the LOD that is the source for + this CVSSymbol. + + source_id -- (int) id of the CVSRevision or CVSBranch from which + this branch sprouts. This initially points to a CVSRevision, + but can be changed to a CVSBranch via parent adjustment in + FilterSymbolsPass. + + next_id -- (int or None) id of first CVSRevision on this branch, + if any; else, None. + + tag_ids -- (list of int) ids of all CVSTags rooted at this + CVSBranch (can be set due to parent adjustment in + FilterSymbolsPass). + + branch_ids -- (list of int) ids of all CVSBranches rooted at this + CVSBranch (can be set due to parent adjustment in + FilterSymbolsPass). + + opened_symbols -- (None or list of (symbol_id, cvs_symbol_id) + tuples) information about all CVSSymbols opened by this + branch. This member is set in FilterSymbolsPass; before then, + it is None. + + revision_recorder_token -- (arbitrary) a token that can be set by + RevisionRecorder for the later use of RevisionReader. + + """ + + __slots__ = [ + 'branch_number', + 'next_id', + 'tag_ids', + 'branch_ids', + 'opened_symbols', + ] + + def __init__( + self, id, cvs_file, symbol, branch_number, + source_lod, source_id, next_id, + revision_recorder_token, + ): + """Initialize a CVSBranch.""" + + CVSSymbol.__init__( + self, id, cvs_file, symbol, source_lod, source_id, + revision_recorder_token + ) + self.branch_number = branch_number + self.next_id = next_id + self.tag_ids = [] + self.branch_ids = [] + self.opened_symbols = None + + def __getstate__(self): + return ( + self.id, self.cvs_file.id, + self.symbol.id, self.branch_number, + self.source_lod.id, self.source_id, self.next_id, + self.tag_ids, self.branch_ids, + self.opened_symbols, + self.revision_recorder_token, + ) + + def __setstate__(self, data): + ( + self.id, cvs_file_id, + symbol_id, self.branch_number, + source_lod_id, self.source_id, self.next_id, + self.tag_ids, self.branch_ids, + self.opened_symbols, + self.revision_recorder_token, + ) = data + self.cvs_file = Ctx()._cvs_file_db.get_file(cvs_file_id) + self.symbol = Ctx()._symbol_db.get_symbol(symbol_id) + self.source_lod = Ctx()._symbol_db.get_symbol(source_lod_id) + + def get_pred_ids(self): + return set([self.source_id]) + + def get_succ_ids(self): + retval = set(self.tag_ids + self.branch_ids) + if self.next_id is not None: + retval.add(self.next_id) + return retval + + def get_cvs_symbol_ids_opened(self): + return self.tag_ids + self.branch_ids + + def check_links(self, cvs_file_items): + source = cvs_file_items.get(self.source_id) + next = cvs_file_items.get(self.next_id) + + assert self.id in source.branch_ids + if isinstance(source, CVSRevision): + assert self.source_lod == source.lod + elif isinstance(source, CVSBranch): + assert self.source_lod == source.symbol + else: + assert False + + if next is not None: + assert isinstance(next, CVSRevision) + assert next.lod == self.symbol + assert next.first_on_branch_id == self.id + + for tag_id in self.tag_ids: + tag = cvs_file_items[tag_id] + assert isinstance(tag, CVSTag) + assert tag.source_id == self.id + assert tag.source_lod == self.symbol + + for branch_id in self.branch_ids: + branch = cvs_file_items[branch_id] + assert isinstance(branch, CVSBranch) + assert branch.source_id == self.id + assert branch.source_lod == self.symbol + + def __str__(self): + """For convenience only. The format is subject to change at any time.""" + + return '%s:%s:%s<%x>' \ + % (self.cvs_file, self.symbol, self.branch_number, self.id,) + + +class CVSBranchNoop(CVSBranch): + """A CVSBranch whose source is a CVSRevisionAbsent.""" + + __slots__ = [] + + def get_cvs_symbol_ids_opened(self): + return [] + + +# A map +# +# {nondead(source_cvs_rev) : cvs_branch_subtype} +# +# , where nondead() means that the cvs revision exists and is not +# 'dead', and CVS_BRANCH_SUBTYPE is the subtype of CVSBranch that +# should be used. +cvs_branch_type_map = { + False : CVSBranchNoop, + True : CVSBranch, + } + + +class CVSTag(CVSSymbol): + """Represent the creation of a tag on a particular CVSFile. + + Members: + + id -- (int) unique ID for this item. + + cvs_file -- (CVSFile) CVSFile affected by this item. + + symbol -- (Symbol) the symbol affected by this CVSSymbol. + + source_lod -- (LineOfDevelopment) the LOD that is the source for + this CVSSymbol. + + source_id -- (int) the ID of the CVSRevision or CVSBranch that is + being tagged. This initially points to a CVSRevision, but can + be changed to a CVSBranch via parent adjustment in + FilterSymbolsPass. + + revision_recorder_token -- (arbitrary) a token that can be set by + RevisionRecorder for the later use of RevisionReader. + + """ + + __slots__ = [] + + def __init__( + self, id, cvs_file, symbol, source_lod, source_id, + revision_recorder_token, + ): + """Initialize a CVSTag.""" + + CVSSymbol.__init__( + self, id, cvs_file, symbol, source_lod, source_id, + revision_recorder_token, + ) + + def __getstate__(self): + return ( + self.id, self.cvs_file.id, self.symbol.id, + self.source_lod.id, self.source_id, + self.revision_recorder_token, + ) + + def __setstate__(self, data): + ( + self.id, cvs_file_id, symbol_id, source_lod_id, self.source_id, + self.revision_recorder_token, + ) = data + self.cvs_file = Ctx()._cvs_file_db.get_file(cvs_file_id) + self.symbol = Ctx()._symbol_db.get_symbol(symbol_id) + self.source_lod = Ctx()._symbol_db.get_symbol(source_lod_id) + + def get_pred_ids(self): + return set([self.source_id]) + + def get_succ_ids(self): + return set() + + def get_cvs_symbol_ids_opened(self): + return [] + + def check_links(self, cvs_file_items): + source = cvs_file_items.get(self.source_id) + + assert self.id in source.tag_ids + if isinstance(source, CVSRevision): + assert self.source_lod == source.lod + elif isinstance(source, CVSBranch): + assert self.source_lod == source.symbol + else: + assert False + + def __str__(self): + """For convenience only. The format is subject to change at any time.""" + + return '%s:%s<%x>' \ + % (self.cvs_file, self.symbol, self.id,) + + +class CVSTagNoop(CVSTag): + """A CVSTag whose source is a CVSRevisionAbsent.""" + + __slots__ = [] + + +# A map +# +# {nondead(source_cvs_rev) : cvs_tag_subtype} +# +# , where nondead() means that the cvs revision exists and is not +# 'dead', and CVS_TAG_SUBTYPE is the subtype of CVSTag that should be +# used. +cvs_tag_type_map = { + False : CVSTagNoop, + True : CVSTag, + } + + |