aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'cvs2svn_lib/svn_commit.py')
-rw-r--r--cvs2svn_lib/svn_commit.py381
1 files changed, 381 insertions, 0 deletions
diff --git a/cvs2svn_lib/svn_commit.py b/cvs2svn_lib/svn_commit.py
new file mode 100644
index 0000000..25dc38e
--- /dev/null
+++ b/cvs2svn_lib/svn_commit.py
@@ -0,0 +1,381 @@
+# (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 the SVNCommit classes.
+
+There are five types of SVNCommits:
+
+ SVNInitialProjectCommit -- Initializes a project (creates its trunk,
+ branches, and tags directories).
+
+ SVNPrimaryCommit -- Commits one or more CVSRevisions on one or more
+ lines of development.
+
+ SVNBranchCommit -- Creates or fills a branch; that is, copies files
+ from a source line of development to a target branch.
+
+ SVNTagCommit -- Creates or fills a tag; that is, copies files from a
+ source line of development to a target tag.
+
+ SVNPostCommit -- Updates trunk to reflect changes on a non-trunk
+ default branch.
+
+"""
+
+
+import textwrap
+
+from cvs2svn_lib.common import InternalError
+from cvs2svn_lib.context import Ctx
+from cvs2svn_lib.symbol import Branch
+from cvs2svn_lib.symbol import Tag
+
+
+class SVNCommit:
+ """This represents one commit to the Subversion Repository."""
+
+ # textwrap.TextWrapper instance to be used for wrapping log messages:
+ text_wrapper = textwrap.TextWrapper(width=76)
+
+ def __init__(self, date, revnum):
+ """Instantiate an SVNCommit.
+
+ REVNUM is the SVN revision number of this commit."""
+
+ # The date of the commit, as an integer. While the SVNCommit is
+ # being built up, this contains the latest date seen so far. This
+ # member is set externally.
+ self.date = date
+
+ # The SVN revision number of this commit, as an integer.
+ self.revnum = revnum
+
+ def __getstate__(self):
+ return (self.date, self.revnum,)
+
+ def __setstate__(self, state):
+ (self.date, self.revnum,) = state
+
+ def get_cvs_items(self):
+ """Return a list containing the CVSItems in this commit."""
+
+ raise NotImplementedError()
+
+ def get_author(self):
+ """Return the author or this commit, or None if none is to be used.
+
+ The return value is exactly as the author appeared in the RCS
+ file, with undefined character encoding."""
+
+ raise NotImplementedError()
+
+ def get_log_msg(self):
+ """Return a log message for this commit.
+
+ The return value is exactly as the log message appeared in the RCS
+ file, with undefined character encoding."""
+
+ raise NotImplementedError()
+
+ def get_warning_summary(self):
+ """Return a summary of this commit that can be used in warnings."""
+
+ return '(subversion rev %s)' % (self.revnum,)
+
+ def get_description(self):
+ """Return a partial description of this SVNCommit, for logging."""
+
+ raise NotImplementedError()
+
+ def output(self, output_option):
+ """Cause this commit to be output to OUTPUT_OPTION.
+
+ This method is used for double-dispatch. Derived classes should
+ call the OutputOption.process_*_commit() method appropriate for
+ the type of SVNCommit."""
+
+ raise NotImplementedError()
+
+ def __str__(self):
+ """ Print a human-readable description of this SVNCommit.
+
+ This description is not intended to be machine-parseable."""
+
+ ret = "SVNCommit #: " + str(self.revnum) + "\n"
+ ret += " debug description: " + self.get_description() + "\n"
+ return ret
+
+
+class SVNInitialProjectCommit(SVNCommit):
+ def __init__(self, date, projects, revnum):
+ SVNCommit.__init__(self, date, revnum)
+ self.projects = list(projects)
+
+ def __getstate__(self):
+ return (
+ SVNCommit.__getstate__(self),
+ [project.id for project in self.projects],
+ )
+
+ def __setstate__(self, state):
+ (svn_commit_state, project_ids,) = state
+ SVNCommit.__setstate__(self, svn_commit_state)
+ self.projects = [
+ Ctx()._projects[project_id] for project_id in project_ids
+ ]
+
+ def get_cvs_items(self):
+ return []
+
+ def get_author(self):
+ return Ctx().username
+
+ def get_log_msg(self):
+ return self.text_wrapper.fill(
+ Ctx().initial_project_commit_message % {}
+ )
+
+ def get_description(self):
+ return 'Project initialization'
+
+ def output(self, output_option):
+ output_option.process_initial_project_commit(self)
+
+
+class SVNRevisionCommit(SVNCommit):
+ """A SVNCommit that includes actual CVS revisions."""
+
+ def __init__(self, cvs_revs, date, revnum):
+ SVNCommit.__init__(self, date, revnum)
+
+ self.cvs_revs = list(cvs_revs)
+
+ # This value is set lazily by _get_metadata():
+ self._metadata = None
+
+ def __getstate__(self):
+ """Return the part of the state represented by this mixin."""
+
+ return (
+ SVNCommit.__getstate__(self),
+ [cvs_rev.id for cvs_rev in self.cvs_revs],
+ )
+
+ def __setstate__(self, state):
+ """Restore the part of the state represented by this mixin."""
+
+ (svn_commit_state, cvs_rev_ids) = state
+ SVNCommit.__setstate__(self, svn_commit_state)
+
+ self.cvs_revs = [
+ cvs_rev
+ for (id, cvs_rev) in Ctx()._cvs_items_db.get_many(cvs_rev_ids)
+ ]
+ self._metadata = None
+
+ def get_cvs_items(self):
+ return self.cvs_revs
+
+ 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
+
+ def get_author(self):
+ return self._get_metadata().author
+
+ def get_warning_summary(self):
+ retval = []
+ retval.append(SVNCommit.get_warning_summary(self) + ' Related files:')
+ for cvs_rev in self.cvs_revs:
+ retval.append(' ' + cvs_rev.cvs_file.filename)
+ return '\n'.join(retval)
+
+ def __str__(self):
+ """Return the revision part of a description of this SVNCommit.
+
+ Derived classes should append the output of this method to the
+ output of SVNCommit.__str__()."""
+
+ ret = []
+ ret.append(SVNCommit.__str__(self))
+ ret.append(' cvs_revs:\n')
+ for cvs_rev in self.cvs_revs:
+ ret.append(' %x\n' % (cvs_rev.id,))
+ return ''.join(ret)
+
+
+class SVNPrimaryCommit(SVNRevisionCommit):
+ def __init__(self, cvs_revs, date, revnum):
+ SVNRevisionCommit.__init__(self, cvs_revs, date, revnum)
+
+ def get_log_msg(self):
+ """Return the actual log message for this commit."""
+
+ return self._get_metadata().log_msg
+
+ def get_description(self):
+ return 'commit'
+
+ def output(self, output_option):
+ output_option.process_primary_commit(self)
+
+
+class SVNPostCommit(SVNRevisionCommit):
+ def __init__(self, motivating_revnum, cvs_revs, date, revnum):
+ SVNRevisionCommit.__init__(self, cvs_revs, date, revnum)
+
+ # The subversion revision number of the *primary* commit where the
+ # default branch changes actually happened. (NOTE: Secondary
+ # commits that fill branches and tags also have a motivating
+ # commit, but we do not record it because it is (currently) not
+ # needed for anything.) motivating_revnum is used when generating
+ # the log message for the commit that synchronizes the default
+ # branch with trunk.
+ #
+ # It is possible for multiple synchronization commits to refer to
+ # the same motivating commit revision number, and it is possible
+ # for a single synchronization commit to contain CVSRevisions on
+ # multiple different default branches.
+ self.motivating_revnum = motivating_revnum
+
+ def __getstate__(self):
+ return (
+ SVNRevisionCommit.__getstate__(self),
+ self.motivating_revnum,
+ )
+
+ def __setstate__(self, state):
+ (rev_state, self.motivating_revnum,) = state
+ SVNRevisionCommit.__setstate__(self, rev_state)
+
+ def get_cvs_items(self):
+ # It might seem that we should return
+ # SVNRevisionCommit.get_cvs_items(self) here, but this commit
+ # doesn't really include those CVSItems, but rather followup
+ # commits to those.
+ return []
+
+ def get_log_msg(self):
+ """Return a manufactured log message for this commit."""
+
+ return self.text_wrapper.fill(
+ Ctx().post_commit_message % {'revnum' : self.motivating_revnum}
+ )
+
+ def get_description(self):
+ return 'post-commit default branch(es)'
+
+ def output(self, output_option):
+ output_option.process_post_commit(self)
+
+
+class SVNSymbolCommit(SVNCommit):
+ def __init__(self, symbol, cvs_symbol_ids, date, revnum):
+ SVNCommit.__init__(self, date, revnum)
+
+ # The TypedSymbol that is filled in this SVNCommit.
+ self.symbol = symbol
+
+ self.cvs_symbol_ids = cvs_symbol_ids
+
+ def __getstate__(self):
+ return (
+ SVNCommit.__getstate__(self),
+ self.symbol.id, self.cvs_symbol_ids,
+ )
+
+ def __setstate__(self, state):
+ (svn_commit_state, symbol_id, self.cvs_symbol_ids) = state
+ SVNCommit.__setstate__(self, svn_commit_state)
+ self.symbol = Ctx()._symbol_db.get_symbol(symbol_id)
+
+ def get_cvs_items(self):
+ return [
+ cvs_symbol
+ for (id, cvs_symbol)
+ in Ctx()._cvs_items_db.get_many(self.cvs_symbol_ids)
+ ]
+
+ def _get_symbol_type(self):
+ """Return the type of the self.symbol ('branch' or 'tag')."""
+
+ raise NotImplementedError()
+
+ def get_author(self):
+ return Ctx().username
+
+ def get_log_msg(self):
+ """Return a manufactured log message for this commit."""
+
+ return self.text_wrapper.fill(
+ Ctx().symbol_commit_message % {
+ 'symbol_type' : self._get_symbol_type(),
+ 'symbol_name' : self.symbol.name,
+ }
+ )
+
+ def get_description(self):
+ return 'copying to %s %r' % (self._get_symbol_type(), self.symbol.name,)
+
+ def __str__(self):
+ """ Print a human-readable description of this SVNCommit.
+
+ This description is not intended to be machine-parseable."""
+
+ return (
+ SVNCommit.__str__(self)
+ + " symbolic name: %s\n" % (self.symbol.name,)
+ )
+
+
+class SVNBranchCommit(SVNSymbolCommit):
+ def __init__(self, symbol, cvs_symbol_ids, date, revnum):
+ if not isinstance(symbol, Branch):
+ raise InternalError('Incorrect symbol type %r' % (symbol,))
+
+ SVNSymbolCommit.__init__(self, symbol, cvs_symbol_ids, date, revnum)
+
+ def _get_symbol_type(self):
+ return 'branch'
+
+ def output(self, output_option):
+ output_option.process_branch_commit(self)
+
+
+class SVNTagCommit(SVNSymbolCommit):
+ 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)
+
+ def _get_symbol_type(self):
+ return 'tag'
+
+ def output(self, output_option):
+ output_option.process_tag_commit(self)
+
+