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 the SVNCommitCreator class."""
22 from cvs2svn_lib
.common
import InternalError
23 from cvs2svn_lib
.log
import Log
24 from cvs2svn_lib
.context
import Ctx
25 from cvs2svn_lib
.cvs_item
import CVSRevisionNoop
26 from cvs2svn_lib
.cvs_item
import CVSBranchNoop
27 from cvs2svn_lib
.cvs_item
import CVSTagNoop
28 from cvs2svn_lib
.changeset
import OrderedChangeset
29 from cvs2svn_lib
.changeset
import BranchChangeset
30 from cvs2svn_lib
.changeset
import TagChangeset
31 from cvs2svn_lib
.svn_commit
import SVNInitialProjectCommit
32 from cvs2svn_lib
.svn_commit
import SVNPrimaryCommit
33 from cvs2svn_lib
.svn_commit
import SVNPostCommit
34 from cvs2svn_lib
.svn_commit
import SVNBranchCommit
35 from cvs2svn_lib
.svn_commit
import SVNTagCommit
36 from cvs2svn_lib
.key_generator
import KeyGenerator
39 class SVNCommitCreator
:
40 """This class creates and yields SVNCommits via process_changeset()."""
43 # The revision number to assign to the next new SVNCommit.
44 self
.revnum_generator
= KeyGenerator()
46 # A set containing the Projects that have already been
48 self
._initialized
_projects
= set()
50 def _post_commit(self
, cvs_revs
, motivating_revnum
, timestamp
):
51 """Generate any SVNCommits needed to follow CVS_REVS.
53 That is, handle non-trunk default branches. A revision on a CVS
54 non-trunk default branch is visible in a default CVS checkout of
55 HEAD. So we copy such commits over to Subversion's trunk so that
56 checking out SVN trunk gives the same output as checking out of
57 CVS's default branch."""
61 for cvs_rev
in cvs_revs
62 if cvs_rev
.ntdbr
and not isinstance(cvs_rev
, CVSRevisionNoop
)
67 lambda a
, b
: cmp(a
.cvs_file
.filename
, b
.cvs_file
.filename
)
69 # Generate an SVNCommit for all of our default branch cvs_revs.
71 motivating_revnum
, cvs_revs
, timestamp
,
72 self
.revnum_generator
.gen_id(),
75 def _process_revision_changeset(self
, changeset
, timestamp
):
76 """Process CHANGESET, using TIMESTAMP as the commit time.
78 Create and yield one or more SVNCommits in the process. CHANGESET
79 must be an OrderedChangeset. TIMESTAMP is used as the timestamp
80 for any resulting SVNCommits."""
82 if not changeset
.cvs_item_ids
:
83 Log().warn('Changeset has no items: %r' % changeset
)
86 Log().verbose('-' * 60)
87 Log().verbose('CVS Revision grouping:')
88 Log().verbose(' Time: %s' % time
.ctime(timestamp
))
90 # Generate an SVNCommit unconditionally. Even if the only change in
91 # this group of CVSRevisions is a deletion of an already-deleted
92 # file (that is, a CVS revision in state 'dead' whose predecessor
93 # was also in state 'dead'), the conversion will still generate a
94 # Subversion revision containing the log message for the second dead
95 # revision, because we don't want to lose that information.
97 cvs_revs
= list(changeset
.iter_cvs_items())
99 cvs_revs
.sort(lambda a
, b
: cmp(a
.cvs_file
.filename
, b
.cvs_file
.filename
))
100 svn_commit
= SVNPrimaryCommit(
101 cvs_revs
, timestamp
, self
.revnum_generator
.gen_id()
106 for cvs_rev
in cvs_revs
:
107 Ctx()._symbolings
_logger
.log_revision(cvs_rev
, svn_commit
.revnum
)
109 # Generate an SVNPostCommit if we have default branch revs. If
110 # some of the revisions in this commit happened on a non-trunk
111 # default branch, then those files have to be copied into trunk
112 # manually after being changed on the branch (because the RCS
113 # "default branch" appears as head, i.e., trunk, in practice).
114 # Unfortunately, Subversion doesn't support copies with sources
115 # in the current txn. All copies must be based in committed
116 # revisions. Therefore, we generate the copies in a new
118 for svn_post_commit
in self
._post
_commit
(
119 cvs_revs
, svn_commit
.revnum
, timestamp
121 yield svn_post_commit
123 def _process_tag_changeset(self
, changeset
, timestamp
):
124 """Process TagChangeset CHANGESET, producing a SVNTagCommit.
126 Filter out CVSTagNoops. If no CVSTags are left, don't generate a
131 'TagChangeset encountered during a --trunk-only conversion')
135 for cvs_tag
in changeset
.iter_cvs_items()
136 if not isinstance(cvs_tag
, CVSTagNoop
)
140 changeset
.symbol
, cvs_tag_ids
, timestamp
,
141 self
.revnum_generator
.gen_id(),
145 'Omitting %r because it contains only CVSTagNoops' % (changeset
,)
148 def _process_branch_changeset(self
, changeset
, timestamp
):
149 """Process BranchChangeset CHANGESET, producing a SVNBranchCommit.
151 Filter out CVSBranchNoops. If no CVSBranches are left, don't
152 generate a SVNBranchCommit."""
156 'BranchChangeset encountered during a --trunk-only conversion')
160 for cvs_branch
in changeset
.iter_cvs_items()
161 if not isinstance(cvs_branch
, CVSBranchNoop
)
164 svn_commit
= SVNBranchCommit(
166 [cvs_branch
.id for cvs_branch
in cvs_branches
],
168 self
.revnum_generator
.gen_id(),
171 for cvs_branch
in cvs_branches
:
172 Ctx()._symbolings
_logger
.log_branch_revision(
173 cvs_branch
, svn_commit
.revnum
177 'Omitting %r because it contains only CVSBranchNoops' % (changeset
,)
180 def process_changeset(self
, changeset
, timestamp
):
181 """Process CHANGESET, using TIMESTAMP for all of its entries.
183 Return a generator that generates the resulting SVNCommits.
185 The changesets must be fed to this function in proper dependency
188 # First create any new projects that might be opened by the
191 changeset
.get_projects_opened() - self
._initialized
_projects
193 if Ctx().cross_project_commits
:
194 yield SVNInitialProjectCommit(
195 timestamp
, projects_opened
, self
.revnum_generator
.gen_id()
198 for project
in projects_opened
:
199 yield SVNInitialProjectCommit(
200 timestamp
, [project
], self
.revnum_generator
.gen_id()
202 self
._initialized
_projects
.update(projects_opened
)
204 if isinstance(changeset
, OrderedChangeset
):
206 in self
._process
_revision
_changeset
(changeset
, timestamp
):
208 elif isinstance(changeset
, TagChangeset
):
209 for svn_commit
in self
._process
_tag
_changeset
(changeset
, timestamp
):
211 elif isinstance(changeset
, BranchChangeset
):
212 for svn_commit
in self
._process
_branch
_changeset
(changeset
, timestamp
):
215 raise TypeError('Illegal changeset %r' % changeset
)