Ejemplo n.º 1
0
  def update(self, revnum, id):
    """Indicate that the root node of this LOD changed to ID at REVNUM.

    REVNUM is a revision number that must be the same as that of the
    previous recorded change (in which case the previous change is
    overwritten) or later (in which the new change is appended).

    ID can be a node ID, or it can be None to indicate that this LOD
    ceased to exist in REVNUM."""

    if revnum < self.revnums[-1]:
      raise KeyError()
    elif revnum == self.revnums[-1]:
      # This is an attempt to overwrite an entry that was already
      # updated during this revision.  Don't allow the replacement
      # None -> None or allow one new id to be replaced with another:
      old_id = self.ids[-1]
      if old_id is None and id is None:
        raise InternalError(
            'ID changed from None -> None for %s, r%d' % (self.lod, revnum,)
            )
      elif (old_id is not None and id is not None
            and old_id in self._mirror._new_nodes):
        raise InternalError(
            'ID changed from %x -> %x for %s, r%d'
            % (old_id, id, self.lod, revnum,)
            )
      self.ids[-1] = id
    else:
      self.revnums.append(revnum)
      self.ids.append(id)
Ejemplo n.º 2
0
    def get_range_map(self, svn_symbol_commit):
        """Return the ranges of all CVSSymbols in SVN_SYMBOL_COMMIT.

    Return a map { CVSSymbol : SVNRevisionRange }."""

        # A map { cvs_symbol_id : CVSSymbol }:
        cvs_symbol_map = {}
        for cvs_symbol in svn_symbol_commit.get_cvs_items():
            cvs_symbol_map[cvs_symbol.id] = cvs_symbol

        range_map = {}

        for (revnum, type, cvs_symbol_id) \
                in self._generate_lines(svn_symbol_commit.symbol):
            cvs_symbol = cvs_symbol_map.get(cvs_symbol_id)
            if cvs_symbol is None:
                # This CVSSymbol is not part of SVN_SYMBOL_COMMIT.
                continue
            range = range_map.get(cvs_symbol)
            if type == OPENING:
                if range is not None:
                    raise InternalError('Multiple openings logged for %r' %
                                        (cvs_symbol, ))
                range_map[cvs_symbol] = SVNRevisionRange(
                    cvs_symbol.source_lod, revnum)
            else:
                if range is None:
                    raise InternalError('Closing precedes opening for %r' %
                                        (cvs_symbol, ))
                if range.closing_revnum is not None:
                    raise InternalError('Multiple closings logged for %r' %
                                        (cvs_symbol, ))
                range.add_closing(revnum)

        # Make sure that all CVSSymbols are accounted for, and adjust the
        # closings to be not later than svn_symbol_commit.revnum.
        for cvs_symbol in cvs_symbol_map.itervalues():
            try:
                range = range_map[cvs_symbol]
            except KeyError:
                raise InternalError('No opening for %s' % (cvs_symbol, ))

            if range.opening_revnum >= svn_symbol_commit.revnum:
                raise InternalError('Opening in r%d not ready for %s in r%d' %
                                    (
                                        range.opening_revnum,
                                        cvs_symbol,
                                        svn_symbol_commit.revnum,
                                    ))

            if range.closing_revnum is not None \
                   and range.closing_revnum > svn_symbol_commit.revnum:
                range.closing_revnum = None

        return range_map
Ejemplo n.º 3
0
  def process_primary_commit(self, svn_commit):
    author = self._get_author(svn_commit)
    log_msg = self._get_log_msg(svn_commit)

    lods = set()
    for cvs_rev in svn_commit.get_cvs_items():
      lods.add(cvs_rev.lod)
    if len(lods) != 1:
      raise InternalError('Commit affects %d LODs' % (len(lods),))
    lod = lods.pop()

    self._mirror.start_commit(svn_commit.revnum)
    if isinstance(lod, Trunk):
      # FIXME: is this correct?:
      self.f.write('commit refs/heads/master\n')
    else:
      self.f.write('commit refs/heads/%s\n' % (lod.name,))
    mark = self._create_commit_mark(lod, svn_commit.revnum)
    logger.normal(
        'Writing commit r%d on %s (mark :%d)'
        % (svn_commit.revnum, lod, mark,)
        )
    self.f.write('mark :%d\n' % (mark,))
    self.f.write(
        'committer %s %d +0000\n' % (author, svn_commit.date,)
        )
    self.f.write('data %d\n' % (len(log_msg),))
    self.f.write('%s\n' % (log_msg,))
    for cvs_rev in svn_commit.get_cvs_items():
      self.revision_writer.process_revision(cvs_rev, post_commit=False)

    self.f.write('\n')
    self._mirror.end_commit()
Ejemplo n.º 4
0
  def copy_path(self, cvs_path, src_lod, dest_lod, src_revnum):
    if isinstance(cvs_path, CVSFile):
      node_kind = 'file'
      if cvs_path.rcs_basename == '.cvsignore':
        # FIXME: Here we have to adjust the containing directory's
        # svn:ignore property to reflect the addition of the
        # .cvsignore file to the LOD!  This is awkward because we
        # don't have the contents of the .cvsignore file available.
        if not Ctx().keep_cvsignore:
          return
    elif isinstance(cvs_path, CVSDirectory):
      node_kind = 'dir'
    else:
      raise InternalError()

    self._dumpfile.write(
        'Node-path: %s\n'
        'Node-kind: %s\n'
        'Node-action: add\n'
        'Node-copyfrom-rev: %d\n'
        'Node-copyfrom-path: %s\n'
        '\n'
        % (
            self._utf8_path(dest_lod.get_path(cvs_path.cvs_path)),
            node_kind,
            src_revnum,
            self._utf8_path(src_lod.get_path(cvs_path.cvs_path))
            )
        )
Ejemplo n.º 5
0
  def process_post_commit(self, svn_commit):
    author = self._get_author(svn_commit)
    log_msg = self._get_log_msg(svn_commit)

    source_lods = set()
    for cvs_rev in svn_commit.cvs_revs:
      source_lods.add(cvs_rev.lod)
    if len(source_lods) != 1:
      raise InternalError('Commit is from %d LODs' % (len(source_lods),))
    source_lod = source_lods.pop()

    self._mirror.start_commit(svn_commit.revnum)
    # FIXME: is this correct?:
    self.f.write('commit refs/heads/master\n')
    mark = self._create_commit_mark(None, svn_commit.revnum)
    logger.normal(
        'Writing post-commit r%d on Trunk (mark :%d)'
        % (svn_commit.revnum, mark,)
        )
    self.f.write('mark :%d\n' % (mark,))
    self.f.write(
        'committer %s %d +0000\n' % (author, svn_commit.date,)
        )
    self.f.write('data %d\n' % (len(log_msg),))
    self.f.write('%s\n' % (log_msg,))
    self.f.write(
        'merge :%d\n'
        % (self._get_source_mark(source_lod, svn_commit.revnum),)
        )
    for cvs_rev in svn_commit.cvs_revs:
      self.revision_writer.process_revision(cvs_rev, post_commit=True)

    self.f.write('\n')
    self._mirror.end_commit()
Ejemplo n.º 6
0
    def _process_branch_changeset(self, changeset, timestamp):
        """Process BranchChangeset CHANGESET, producing a SVNBranchCommit.

    Filter out CVSBranchNoops.  If no CVSBranches are left, don't
    generate a SVNBranchCommit."""

        if Ctx().trunk_only:
            raise InternalError(
                'BranchChangeset encountered during a --trunk-only conversion')

        cvs_branches = [
            cvs_branch for cvs_branch in changeset.iter_cvs_items()
            if not isinstance(cvs_branch, CVSBranchNoop)
        ]
        if cvs_branches:
            svn_commit = SVNBranchCommit(
                changeset.symbol,
                [cvs_branch.id for cvs_branch in cvs_branches],
                timestamp,
                self.revnum_generator.gen_id(),
            )
            yield svn_commit
            for cvs_branch in cvs_branches:
                Ctx()._symbolings_logger.log_branch_revision(
                    cvs_branch, svn_commit.revnum)
        else:
            logger.debug(
                'Omitting %r because it contains only CVSBranchNoops' %
                (changeset, ))
Ejemplo n.º 7
0
    def iter_lods(self):
        """Iterate over LinesOfDevelopment in this file, in depth-first order.

    For each LOD, yield an LODItems instance.  The traversal starts at
    each root node but returns the LODs in depth-first order.

    It is allowed to modify the CVSFileItems instance while the
    traversal is occurring, but only in ways that don't affect the
    tree structure above (i.e., towards the trunk from) the current
    LOD."""

        # Make a list out of root_ids so that callers can change it:
        for id in list(self.root_ids):
            cvs_item = self[id]
            if isinstance(cvs_item, CVSRevision):
                # This LOD doesn't have a CVSBranch associated with it.
                # Either it is Trunk, or it is a branch whose CVSBranch has
                # been deleted.
                lod = cvs_item.lod
                cvs_branch = None
            elif isinstance(cvs_item, CVSBranch):
                # This is a Branch that has been severed from the rest of the
                # tree.
                lod = cvs_item.symbol
                id = cvs_item.next_id
                cvs_branch = cvs_item
            else:
                raise InternalError('Unexpected root item: %s' % (cvs_item, ))

            for lod_items in self._iter_tree(lod, cvs_branch, id):
                yield lod_items
Ejemplo n.º 8
0
    def _is_simple_copy(self, svn_commit, source_groups):
        """Return True iff SVN_COMMIT can be created as a simple copy.

    SVN_COMMIT is an SVNTagCommit.  Return True iff it can be created
    as a simple copy from an existing revision (i.e., if the fixup
    branch can be avoided for this tag creation)."""

        # The first requirement is that there be exactly one source:
        if len(source_groups) != 1:
            return False

        (svn_revnum, source_lod, cvs_symbols) = source_groups[0]

        # The second requirement is that the destination LOD not already
        # exist:
        try:
            self._mirror.get_current_lod_directory(svn_commit.symbol)
        except KeyError:
            # The LOD doesn't already exist.  This is good.
            pass
        else:
            # The LOD already exists.  It cannot be created by a copy.
            return False

        # The third requirement is that the source LOD contains exactly
        # the same files as we need to add to the symbol:
        try:
            source_node = self._mirror.get_old_lod_directory(
                source_lod, svn_revnum)
        except KeyError:
            raise InternalError('Source %r does not exist' % (source_lod, ))
        return (set([cvs_symbol.cvs_file for cvs_symbol in cvs_symbols
                     ]) == set(self._get_all_files(source_node)))
Ejemplo n.º 9
0
    def discard(self, *ids):
        """The text records with IDS are no longer needed; discard them.

    This involves calling their free() methods and also removing them
    from SELF.

    If SELF.deferred_deletes is not None, then the ids to be deleted
    are added to the list instead of deleted immediately.  This
    mechanism is to prevent a stack overflow from the avalanche of
    deletes that can result from deleting a long chain of revisions."""

        if self.deferred_deletes is None:
            # This is an outer-level delete.
            self.deferred_deletes = list(ids)
            while self.deferred_deletes:
                id = self.deferred_deletes.pop()
                text_record = self[id]
                if text_record.refcount != 0:
                    raise InternalError(
                        'TextRecordDatabase.discard(%s) called with refcount = %d'
                        % (
                            text_record,
                            text_record.refcount,
                        ))
                # This call might cause other text_record ids to be added to
                # self.deferred_deletes:
                text_record.free(self)
                del self[id]
            self.deferred_deletes = None
        else:
            self.deferred_deletes.extend(ids)
Ejemplo n.º 10
0
    def process_post_commit(self, svn_commit):
        author = self._get_author(svn_commit)
        log_msg = self._get_log_msg(svn_commit)

        source_lods = set()
        for cvs_rev in svn_commit.cvs_revs:
            source_lods.add(cvs_rev.lod)
        if len(source_lods) != 1:
            raise InternalError('Commit is from %d LODs' %
                                (len(source_lods), ))
        source_lod = source_lods.pop()
        # FIXME: is this correct?:
        self.f.write('commit refs/heads/master\n')
        self.f.write('mark :%d\n' %
                     (self._create_commit_mark(None, svn_commit.revnum), ))
        self.f.write('committer %s %d +0000\n' % (
            author,
            svn_commit.date,
        ))
        self.f.write('data %d\n' % (len(log_msg), ))
        self.f.write('%s\n' % (log_msg, ))
        self.f.write('merge :%d\n' %
                     (self._get_source_mark(source_lod, svn_commit.revnum), ))
        for cvs_rev in svn_commit.cvs_revs:
            if isinstance(cvs_rev, CVSRevisionNoop):
                pass

            elif isinstance(cvs_rev, CVSRevisionDelete):
                self.f.write('D %s\n' % (cvs_rev.cvs_file.cvs_path, ))

            elif isinstance(cvs_rev, CVSRevisionModification):
                if cvs_rev.cvs_file.executable:
                    mode = '100755'
                else:
                    mode = '100644'

                self.f.write('M %s :%d %s\n' % (
                    mode,
                    cvs_rev.revision_recorder_token,
                    cvs_rev.cvs_file.cvs_path,
                ))

            else:
                raise InternalError('Unexpected CVSRevision type: %s' %
                                    (cvs_rev, ))

        self.f.write('\n')
Ejemplo n.º 11
0
 def _set_symbol(self, symbol, mark):
   if isinstance(symbol, Branch):
     category = 'heads'
   elif isinstance(symbol, Tag):
     category = 'tags'
   else:
     raise InternalError()
   self.f.write('reset refs/%s/%s\n' % (category, symbol.name,))
   self.f.write('from :%d\n' % (mark,))
Ejemplo n.º 12
0
  def _process_symbol_commit(self, svn_commit, git_branch, source_groups):
    author = self._get_author(svn_commit)
    log_msg = self._get_log_msg(svn_commit)

    # There are two distinct cases we need to care for here:
    #  1. initial creation of a LOD
    #  2. fixup of an existing LOD to include more files, because the LOD in
    #     CVS was created piecemeal over time, with intervening commits

    # We look at _marks here, but self._mirror._get_lod_history(lod).exists()
    # might be technically more correct (though _get_lod_history is currently
    # underscore-private)
    is_initial_lod_creation = svn_commit.symbol not in self._marks

    # Create the mark, only after the check above
    mark = self._create_commit_mark(svn_commit.symbol, svn_commit.revnum)

    if is_initial_lod_creation:
      # Get the primary parent
      p_source_revnum, p_source_lod, p_cvs_symbols = source_groups[0]
      try:
        p_source_node = self._mirror.get_old_lod_directory(
            p_source_lod, p_source_revnum
            )
      except KeyError:
        raise InternalError('Source %r does not exist' % (p_source_lod,))
      cvs_files_to_delete = set(self._get_all_files(p_source_node))

      for (source_revnum, source_lod, cvs_symbols,) in source_groups:
        for cvs_symbol in cvs_symbols:
          cvs_files_to_delete.discard(cvs_symbol.cvs_file)

    self.f.write('commit %s\n' % (git_branch,))
    self.f.write('mark :%d\n' % (mark,))
    self.f.write('committer %s %d +0000\n' % (author, svn_commit.date,))
    self.f.write('data %d\n' % (len(log_msg),))
    self.f.write('%s\n' % (log_msg,))

    # Only record actual DVCS ancestry for the primary sprout parent,
    # all the rest are effectively cherrypicks.
    if is_initial_lod_creation:
      self.f.write(
          'from :%d\n'
          % (self._get_source_mark(p_source_lod, p_source_revnum),)
          )

    for (source_revnum, source_lod, cvs_symbols,) in source_groups:
      for cvs_symbol in cvs_symbols:
        self.revision_writer.branch_file(cvs_symbol)

    if is_initial_lod_creation:
      for cvs_file in cvs_files_to_delete:
        self.f.write('D %s\n' % (cvs_file.cvs_path,))

    self.f.write('\n')
    return mark
Ejemplo n.º 13
0
 def process_revision(self, cvs_rev, post_commit):
   if isinstance(cvs_rev, CVSRevisionAdd):
     self.add_file(cvs_rev, post_commit)
   elif isinstance(cvs_rev, CVSRevisionChange):
     self.modify_file(cvs_rev, post_commit)
   elif isinstance(cvs_rev, CVSRevisionDelete):
     self.delete_file(cvs_rev, post_commit)
   elif isinstance(cvs_rev, CVSRevisionNoop):
     pass
   else:
     raise InternalError('Unexpected CVSRevision type: %s' % (cvs_rev,))
Ejemplo n.º 14
0
def create_symbol_changeset(id, symbol, cvs_item_ids):
    """Factory function for SymbolChangesets.

  Return a BranchChangeset or TagChangeset, depending on the type of
  SYMBOL.  SYMBOL must be a Branch or Tag."""

    if isinstance(symbol, Branch):
        return BranchChangeset(id, symbol, cvs_item_ids)
    if isinstance(symbol, Tag):
        return TagChangeset(id, symbol, cvs_item_ids)
    else:
        raise InternalError('Unknown symbol type %s' % (symbol, ))
Ejemplo n.º 15
0
    def _adjust_branch_parents(self, cvs_branch):
        """Adjust the parent of CVS_BRANCH if possible and preferred.

    CVS_BRANCH is an instance of CVSBranch.  This method must be
    called in leaf-to-trunk order."""

        # The Symbol that cvs_branch would like to have as a parent:
        preferred_parent = Ctx()._symbol_db.get_symbol(
            cvs_branch.symbol.preferred_parent_id)

        if cvs_branch.source_lod == preferred_parent:
            # The preferred parent is already the parent.
            return

        # The CVSRevision that is its direct parent:
        source = self[cvs_branch.source_id]
        # This is always a CVSRevision because we haven't adjusted it yet:
        assert isinstance(source, CVSRevision)

        if isinstance(preferred_parent, Trunk):
            # It is not possible to graft *onto* Trunk:
            return

        # Try to find the preferred parent among the possible parents:
        for branch_id in source.branch_ids:
            possible_parent = self[branch_id]
            if possible_parent.symbol == preferred_parent:
                # We found it!
                break
            elif possible_parent.symbol == cvs_branch.symbol:
                # Only branches that precede the branch to be adjusted are
                # considered possible parents.  Leave parentage unchanged:
                return
        else:
            # This point should never be reached.
            raise InternalError(
                'Possible parent search did not terminate as expected')

        parent = possible_parent
        assert isinstance(parent, CVSBranch)

        Log().debug('Grafting %s from %s (on %s) onto %s' % (
            cvs_branch,
            source,
            source.lod,
            parent,
        ))
        # Switch parent:
        source.branch_ids.remove(cvs_branch.id)
        parent.branch_ids.append(cvs_branch.id)
        cvs_branch.source_lod = parent.symbol
        cvs_branch.source_id = parent.id
Ejemplo n.º 16
0
    def _get_metadata(self):
        """Return the Metadata instance for this commit."""

        if self._metadata is None:
            # Set self._metadata for this commit from that of the first cvs
            # revision.
            if not self.cvs_revs:
                raise InternalError(
                    'SVNPrimaryCommit contains no CVS revisions')

            metadata_id = self.cvs_revs[0].metadata_id
            self._metadata = Ctx()._metadata_db[metadata_id]

        return self._metadata
Ejemplo n.º 17
0
  def iter_root_lods(self):
    """Iterate over the LODItems for all root LODs (non-recursively)."""

    for id in list(self.root_ids):
      cvs_item = self[id]
      if isinstance(cvs_item, CVSRevision):
        # This LOD doesn't have a CVSBranch associated with it.
        # Either it is Trunk, or it is a branch whose CVSBranch has
        # been deleted.
        yield self._get_lod(cvs_item.lod, None, id)
      elif isinstance(cvs_item, CVSBranch):
        # This is a Branch that has been severed from the rest of the
        # tree.
        yield self._get_lod(cvs_item.symbol, cvs_item, cvs_item.next_id)
      else:
        raise InternalError('Unexpected root item: %s' % (cvs_item,))
Ejemplo n.º 18
0
    def process_primary_commit(self, svn_commit):
        author = self._get_author(svn_commit)
        log_msg = self._get_log_msg(svn_commit)

        lods = set()
        for cvs_rev in svn_commit.get_cvs_items():
            lods.add(cvs_rev.lod)
        if len(lods) != 1:
            raise InternalError('Commit affects %d LODs' % (len(lods), ))
        lod = lods.pop()
        if isinstance(lod, Trunk):
            # FIXME: is this correct?:
            self.f.write('commit refs/heads/master\n')
        else:
            self.f.write('commit refs/heads/%s\n' % (lod.name, ))
        self.f.write('mark :%d\n' %
                     (self._create_commit_mark(lod, svn_commit.revnum), ))
        self.f.write('committer %s %d +0000\n' % (
            author,
            svn_commit.date,
        ))
        self.f.write('data %d\n' % (len(log_msg), ))
        self.f.write('%s\n' % (log_msg, ))
        for cvs_rev in svn_commit.get_cvs_items():
            if isinstance(cvs_rev, CVSRevisionNoop):
                pass

            elif isinstance(cvs_rev, CVSRevisionDelete):
                self.f.write('D %s\n' % (cvs_rev.cvs_file.cvs_path, ))

            elif isinstance(cvs_rev, CVSRevisionModification):
                if cvs_rev.cvs_file.executable:
                    mode = '100755'
                else:
                    mode = '100644'

                self.f.write('M %s :%d %s\n' % (
                    mode,
                    cvs_rev.revision_recorder_token,
                    cvs_rev.cvs_file.cvs_path,
                ))

        self.f.write('\n')
Ejemplo n.º 19
0
    def _process_tag_changeset(self, changeset, timestamp):
        """Process TagChangeset CHANGESET, producing a SVNTagCommit.

    Filter out CVSTagNoops.  If no CVSTags are left, don't generate a
    SVNTagCommit."""

        if Ctx().trunk_only:
            raise InternalError(
                'TagChangeset encountered during a --trunk-only conversion')

        cvs_tag_ids = [
            cvs_tag.id for cvs_tag in changeset.iter_cvs_items()
            if not isinstance(cvs_tag, CVSTagNoop)
        ]
        if cvs_tag_ids:
            yield SVNTagCommit(
                changeset.symbol,
                cvs_tag_ids,
                timestamp,
                self.revnum_generator.gen_id(),
            )
        else:
            logger.debug('Omitting %r because it contains only CVSTagNoops' %
                         (changeset, ))
Ejemplo n.º 20
0
    def _open_writable_node(self, cvs_directory, lod, create):
        """Open a writable node for CVS_DIRECTORY in LOD.

    Iff CREATE is True, create a directory node at SVN_PATH and any
    missing directories.  Return an instance of _WritableMirrorNode.

    Raise KeyError if CVS_DIRECTORY doesn't exist and CREATE is not
    set."""

        if cvs_directory.parent_directory is None:
            return self._open_writable_lod_node(lod, create)

        parent_node = self._open_writable_node(cvs_directory.parent_directory,
                                               lod, create)

        try:
            node = parent_node[cvs_directory]
        except KeyError:
            if create:
                # The component does not exist, so we create it.
                new_node = self._create_empty_node()
                parent_node[cvs_directory] = new_node
                self._invoke_delegates('mkdir', lod, cvs_directory)
                return new_node
            else:
                raise
        else:
            if isinstance(node, _WritableMirrorNode):
                return node
            elif isinstance(node, _ReadOnlyMirrorNode):
                new_node = self._copy_node(node)
                parent_node[cvs_directory] = new_node
                return new_node
            else:
                raise InternalError('Attempt to modify file at %s in mirror' %
                                    (cvs_directory, ))
Ejemplo n.º 21
0
 def _set_node(self, cvs_file, svn_revision_range):
     parent_node = self._get_node(cvs_file.parent_directory, create=True)
     if cvs_file in parent_node:
         raise InternalError('%s appeared twice in sources for %s' %
                             (cvs_file, self._symbol))
     parent_node[cvs_file] = svn_revision_range
Ejemplo n.º 22
0
    def __init__(self, symbol, cvs_symbol_ids, date, revnum):
        if not isinstance(symbol, Tag):
            raise InternalError('Incorrect symbol type %r' % (symbol, ))

        SVNSymbolCommit.__init__(self, symbol, cvs_symbol_ids, date, revnum)