1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2000-2009 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 manages cvs2svn run options."""
25 from cvs2svn_lib
.version
import VERSION
26 from cvs2svn_lib
import config
27 from cvs2svn_lib
.common
import warning_prefix
28 from cvs2svn_lib
.common
import error_prefix
29 from cvs2svn_lib
.common
import FatalError
30 from cvs2svn_lib
.common
import normalize_svn_path
31 from cvs2svn_lib
.log
import Log
32 from cvs2svn_lib
.context
import Ctx
33 from cvs2svn_lib
.run_options
import not_both
34 from cvs2svn_lib
.run_options
import RunOptions
35 from cvs2svn_lib
.run_options
import ContextOption
36 from cvs2svn_lib
.run_options
import IncompatibleOption
37 from cvs2svn_lib
.run_options
import authors
38 from cvs2svn_lib
.man_writer
import ManWriter
39 from cvs2svn_lib
.project
import Project
40 from cvs2svn_lib
.svn_output_option
import DumpfileOutputOption
41 from cvs2svn_lib
.svn_output_option
import ExistingRepositoryOutputOption
42 from cvs2svn_lib
.svn_output_option
import NewRepositoryOutputOption
43 from cvs2svn_lib
.revision_manager
import NullRevisionRecorder
44 from cvs2svn_lib
.revision_manager
import NullRevisionExcluder
45 from cvs2svn_lib
.rcs_revision_manager
import RCSRevisionReader
46 from cvs2svn_lib
.cvs_revision_manager
import CVSRevisionReader
47 from cvs2svn_lib
.checkout_internal
import InternalRevisionRecorder
48 from cvs2svn_lib
.checkout_internal
import InternalRevisionExcluder
49 from cvs2svn_lib
.checkout_internal
import InternalRevisionReader
50 from cvs2svn_lib
.symbol_strategy
import TrunkPathRule
51 from cvs2svn_lib
.symbol_strategy
import BranchesPathRule
52 from cvs2svn_lib
.symbol_strategy
import TagsPathRule
55 short_desc
= 'convert a cvs repository into a subversion repository'
59 [\\fIOPTION\\fR]... \\fIOUTPUT-OPTION CVS-REPOS-PATH\\fR
62 [\\fIOPTION\\fR]... \\fI--options=PATH\\fR
66 Create a new Subversion repository based on the version history stored in a
67 CVS repository. Each CVS commit will be mirrored in the Subversion
68 repository, including such information as date of commit and id of the
71 \\fICVS-REPOS-PATH\\fR is the filesystem path of the part of the CVS
72 repository that you want to convert. It is not possible to convert a
73 CVS repository to which you only have remote access; see the FAQ for
74 more information. This path doesn't have to be the top level
75 directory of a CVS repository; it can point at a project within a
76 repository, in which case only that project will be converted. This
77 path or one of its parent directories has to contain a subdirectory
78 called CVSROOT (though the CVSROOT directory can be empty).
80 Multiple CVS repositories can be converted into a single Subversion
81 repository in a single run of cvs2svn, but only by using an
82 \\fB--options\\fR file.
86 A directory called \\fIcvs2svn-tmp\\fR (or the directory specified by
87 \\fB--tmpdir\\fR) is used as scratch space for temporary data files.
97 class SVNRunOptions(RunOptions
):
98 def _get_output_options_group(self
):
99 group
= super(SVNRunOptions
, self
)._get
_output
_options
_group
()
101 group
.add_option(IncompatibleOption(
102 '--svnrepos', '-s', type='string',
104 help='path where SVN repos should be created',
106 'Write the output of the conversion into a Subversion repository '
107 'located at \\fIpath\\fR. This option causes a new Subversion '
108 'repository to be created at \\fIpath\\fR unless the '
109 '\\fB--existing-svnrepos\\fR option is also used.'
113 self
.parser
.set_default('existing_svnrepos', False)
114 group
.add_option(IncompatibleOption(
115 '--existing-svnrepos',
117 help='load into existing SVN repository (for use with --svnrepos)',
119 'Load the converted CVS repository into an existing Subversion '
120 'repository, instead of creating a new repository. (This option '
121 'should be used in combination with '
122 '\\fB-s\\fR/\\fB--svnrepos\\fR.) The repository must either be '
123 'empty or contain no paths that overlap with those that will '
124 'result from the conversion. Please note that you need write '
125 'permission for the repository files.'
128 group
.add_option(IncompatibleOption(
129 '--fs-type', type='string',
132 'pass --fs-type=TYPE to "svnadmin create" (for use with '
136 'Pass \\fI--fs-type\\fR=\\fItype\\fR to "svnadmin create" when '
137 'creating a new repository.'
141 self
.parser
.set_default('bdb_txn_nosync', False)
142 group
.add_option(IncompatibleOption(
146 'pass --bdb-txn-nosync to "svnadmin create" (for use with '
150 'Pass \\fI--bdb-txn-nosync\\fR to "svnadmin create" when '
151 'creating a new BDB-style Subversion repository.'
154 self
.parser
.set_default('create_options', [])
155 group
.add_option(IncompatibleOption(
156 '--create-option', type='string',
157 action
='append', dest
='create_options',
158 help='pass OPT to "svnadmin create" (for use with --svnrepos)',
160 'Pass \\fIopt\\fR to "svnadmin create" when creating a new '
161 'Subversion repository (can be specified multiple times to '
162 'pass multiple options).'
166 group
.add_option(IncompatibleOption(
167 '--dumpfile', type='string',
169 help='just produce a dumpfile; don\'t commit to a repos',
171 'Just produce a dumpfile; don\'t commit to an SVN repository. '
172 'Write the dumpfile to \\fIpath\\fR.'
177 group
.add_option(ContextOption(
181 'do not create a repository or a dumpfile; just print what '
185 'Do not create a repository or a dumpfile; just print the '
186 'details of what cvs2svn would do if it were really converting '
191 # Deprecated options:
192 self
.parser
.set_default('dump_only', False)
193 group
.add_option(IncompatibleOption(
195 action
='callback', callback
=self
.callback_dump_only
,
196 help=optparse
.SUPPRESS_HELP
,
197 man_help
=optparse
.SUPPRESS_HELP
,
199 group
.add_option(IncompatibleOption(
201 action
='callback', callback
=self
.callback_create
,
202 help=optparse
.SUPPRESS_HELP
,
203 man_help
=optparse
.SUPPRESS_HELP
,
208 def _get_conversion_options_group(self
):
209 group
= super(SVNRunOptions
, self
)._get
_conversion
_options
_group
()
211 self
.parser
.set_default('trunk_base', config
.DEFAULT_TRUNK_BASE
)
212 group
.add_option(IncompatibleOption(
213 '--trunk', type='string',
214 action
='store', dest
='trunk_base',
216 'path for trunk (default: %s)'
217 % (config
.DEFAULT_TRUNK_BASE
,)
220 'Set the top-level path to use for trunk in the Subversion '
221 'repository. The default is \\fI%s\\fR.'
222 % (config
.DEFAULT_TRUNK_BASE
,)
226 self
.parser
.set_default('branches_base', config
.DEFAULT_BRANCHES_BASE
)
227 group
.add_option(IncompatibleOption(
228 '--branches', type='string',
229 action
='store', dest
='branches_base',
231 'path for branches (default: %s)'
232 % (config
.DEFAULT_BRANCHES_BASE
,)
235 'Set the top-level path to use for branches in the Subversion '
236 'repository. The default is \\fI%s\\fR.'
237 % (config
.DEFAULT_BRANCHES_BASE
,)
241 self
.parser
.set_default('tags_base', config
.DEFAULT_TAGS_BASE
)
242 group
.add_option(IncompatibleOption(
243 '--tags', type='string',
244 action
='store', dest
='tags_base',
246 'path for tags (default: %s)'
247 % (config
.DEFAULT_TAGS_BASE
,)
250 'Set the top-level path to use for tags in the Subversion '
251 'repository. The default is \\fI%s\\fR.'
252 % (config
.DEFAULT_TAGS_BASE
,)
256 group
.add_option(ContextOption(
258 action
='store_false', dest
='prune',
259 help='don\'t prune empty directories',
261 'When all files are deleted from a directory in the Subversion '
262 'repository, don\'t delete the empty directory (the default is '
263 'to delete any empty directories).'
266 group
.add_option(ContextOption(
267 '--no-cross-branch-commits',
268 action
='store_false', dest
='cross_branch_commits',
269 help='prevent the creation of cross-branch commits',
271 'Prevent the creation of commits that affect files on multiple '
278 def _get_extraction_options_group(self
):
279 group
= super(SVNRunOptions
, self
)._get
_extraction
_options
_group
()
280 self
._add
_use
_internal
_co
_option
(group
)
281 self
._add
_use
_cvs
_option
(group
)
282 self
._add
_use
_rcs
_option
(group
)
285 def _get_environment_options_group(self
):
286 group
= super(SVNRunOptions
, self
)._get
_environment
_options
_group
()
288 group
.add_option(ContextOption(
289 '--svnadmin', type='string',
290 action
='store', dest
='svnadmin_executable',
291 help='path to the "svnadmin" program',
293 'Path to the \\fIsvnadmin\\fR program. (\\fIsvnadmin\\fR is '
294 'needed when the \\fB-s\\fR/\\fB--svnrepos\\fR output option is '
302 def callback_dump_only(self
, option
, opt_str
, value
, parser
):
303 parser
.values
.dump_only
= True
306 ': The --dump-only option is deprecated (it is implied '
310 def callback_create(self
, option
, opt_str
, value
, parser
):
313 ': The behaviour produced by the --create option is now the '
315 'passing the option is deprecated.\n'
318 def callback_manpage(self
, option
, opt_str
, value
, parser
):
319 f
= codecs
.getwriter('utf_8')(sys
.stdout
)
323 date
=datetime
.date
.today(),
324 source
='Version %s' % (VERSION
,),
325 manual
='User Commands',
326 short_desc
=short_desc
,
335 def process_extraction_options(self
):
336 """Process options related to extracting data from the CVS repository."""
339 options
= self
.options
341 not_both(options
.use_rcs
, '--use-rcs',
342 options
.use_cvs
, '--use-cvs')
344 not_both(options
.use_rcs
, '--use-rcs',
345 options
.use_internal_co
, '--use-internal-co')
347 not_both(options
.use_cvs
, '--use-cvs',
348 options
.use_internal_co
, '--use-internal-co')
351 ctx
.revision_recorder
= NullRevisionRecorder()
352 ctx
.revision_excluder
= NullRevisionExcluder()
353 ctx
.revision_reader
= RCSRevisionReader(options
.co_executable
)
354 elif options
.use_cvs
:
355 ctx
.revision_recorder
= NullRevisionRecorder()
356 ctx
.revision_excluder
= NullRevisionExcluder()
357 ctx
.revision_reader
= CVSRevisionReader(options
.cvs_executable
)
359 # --use-internal-co is the default:
360 ctx
.revision_recorder
= InternalRevisionRecorder(compress
=True)
361 ctx
.revision_excluder
= InternalRevisionExcluder()
362 ctx
.revision_reader
= InternalRevisionReader(compress
=True)
364 def process_output_options(self
):
365 """Process the options related to SVN output."""
368 options
= self
.options
370 if options
.dump_only
and not options
.dumpfile
:
371 raise FatalError("'--dump-only' requires '--dumpfile' to be specified.")
373 if not options
.svnrepos
and not options
.dumpfile
and not ctx
.dry_run
:
374 raise FatalError("must pass one of '-s' or '--dumpfile'.")
376 not_both(options
.svnrepos
, '-s',
377 options
.dumpfile
, '--dumpfile')
379 not_both(options
.dumpfile
, '--dumpfile',
380 options
.existing_svnrepos
, '--existing-svnrepos')
382 not_both(options
.bdb_txn_nosync
, '--bdb-txn-nosync',
383 options
.existing_svnrepos
, '--existing-svnrepos')
385 not_both(options
.dumpfile
, '--dumpfile',
386 options
.bdb_txn_nosync
, '--bdb-txn-nosync')
388 not_both(options
.fs_type
, '--fs-type',
389 options
.existing_svnrepos
, '--existing-svnrepos')
393 and options
.fs_type
!= 'bdb'
394 and options
.bdb_txn_nosync
396 raise FatalError("cannot pass --bdb-txn-nosync with --fs-type=%s."
400 if options
.existing_svnrepos
:
401 ctx
.output_option
= ExistingRepositoryOutputOption(options
.svnrepos
)
403 ctx
.output_option
= NewRepositoryOutputOption(
405 fs_type
=options
.fs_type
, bdb_txn_nosync
=options
.bdb_txn_nosync
,
406 create_options
=options
.create_options
)
408 ctx
.output_option
= DumpfileOutputOption(options
.dumpfile
)
412 project_cvs_repos_path
,
413 trunk_path
=None, branches_path
=None, tags_path
=None,
414 initial_directories
=[],
415 symbol_transforms
=None,
416 symbol_strategy_rules
=[],
418 """Add a project to be converted.
420 Most arguments are passed straight through to the Project
421 constructor. SYMBOL_STRATEGY_RULES is an iterable of
422 SymbolStrategyRules that will be applied to symbols in this
425 if trunk_path
is not None:
426 trunk_path
= normalize_svn_path(trunk_path
, allow_empty
=True)
427 if branches_path
is not None:
428 branches_path
= normalize_svn_path(branches_path
, allow_empty
=False)
429 if tags_path
is not None:
430 tags_path
= normalize_svn_path(tags_path
, allow_empty
=False)
432 initial_directories
= [
434 for path
in [trunk_path
, branches_path
, tags_path
]
437 normalize_svn_path(path
)
438 for path
in initial_directories
441 symbol_strategy_rules
= list(symbol_strategy_rules
)
443 # Add rules to set the SVN paths for LODs depending on whether
444 # they are the trunk, tags, or branches:
445 if trunk_path
is not None:
446 symbol_strategy_rules
.append(TrunkPathRule(trunk_path
))
447 if branches_path
is not None:
448 symbol_strategy_rules
.append(BranchesPathRule(branches_path
))
449 if tags_path
is not None:
450 symbol_strategy_rules
.append(TagsPathRule(tags_path
))
452 id = len(self
.projects
)
455 project_cvs_repos_path
,
456 initial_directories
=initial_directories
,
457 symbol_transforms
=symbol_transforms
,
460 self
.projects
.append(project
)
461 self
.project_symbol_strategy_rules
.append(symbol_strategy_rules
)
463 def clear_projects(self
):
464 """Clear the list of projects to be converted.
466 This method is for the convenience of options files, which may
467 want to import one another."""
470 del self
.project_symbol_strategy_rules
[:]
472 def process_options(self
):
473 # Consistency check for options and arguments.
474 if len(self
.args
) == 0:
478 if len(self
.args
) > 1:
479 Log().error(error_prefix
+ ": must pass only one CVS repository.\n")
483 cvsroot
= self
.args
[0]
485 self
.process_extraction_options()
486 self
.process_output_options()
487 self
.process_symbol_strategy_options()
488 self
.process_property_setter_options()
490 # Create the default project (using ctx.trunk, ctx.branches, and
494 trunk_path
=self
.options
.trunk_base
,
495 branches_path
=self
.options
.branches_base
,
496 tags_path
=self
.options
.tags_base
,
497 symbol_transforms
=self
.options
.symbol_transforms
,
498 symbol_strategy_rules
=self
.options
.symbol_strategy_rules
,