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
= RunOptions
._get
_output
_options
_group
(self
)
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
= RunOptions
._get
_conversion
_options
_group
(self
)
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
= RunOptions
._get
_extraction
_options
_group
(self
)
281 self
.parser
.set_default('use_internal_co', False)
282 group
.add_option(IncompatibleOption(
286 'use internal code to extract revision contents '
287 '(fastest but disk space intensive) (default)'
290 'Use internal code to extract revision contents. This '
291 'is up to 50% faster than using \\fB--use-rcs\\fR, but needs '
292 'a lot of disk space: roughly the size of your CVS repository '
293 'plus the peak size of a complete checkout of the repository '
294 'with all branches that existed and still had commits pending '
295 'at a given time. This option is the default.'
298 self
.parser
.set_default('use_cvs', False)
299 group
.add_option(IncompatibleOption(
303 'use CVS to extract revision contents (slower than '
304 '--use-internal-co or --use-rcs)'
307 'Use CVS to extract revision contents. This option is slower '
308 'than \\fB--use-internal-co\\fR or \\fB--use-rcs\\fR.'
311 self
.parser
.set_default('use_rcs', False)
312 group
.add_option(IncompatibleOption(
316 'use RCS to extract revision contents (faster than '
317 '--use-cvs but fails in some cases)'
320 'Use RCS \'co\' to extract revision contents. This option is '
321 'faster than \\fB--use-cvs\\fR but fails in some cases.'
327 def _get_environment_options_group(self
):
328 group
= RunOptions
._get
_environment
_options
_group
(self
)
330 group
.add_option(ContextOption(
331 '--svnadmin', type='string',
332 action
='store', dest
='svnadmin_executable',
333 help='path to the "svnadmin" program',
335 'Path to the \\fIsvnadmin\\fR program. (\\fIsvnadmin\\fR is '
336 'needed when the \\fB-s\\fR/\\fB--svnrepos\\fR output option is '
344 def callback_dump_only(self
, option
, opt_str
, value
, parser
):
345 parser
.values
.dump_only
= True
348 ': The --dump-only option is deprecated (it is implied '
352 def callback_create(self
, option
, opt_str
, value
, parser
):
355 ': The behaviour produced by the --create option is now the '
357 'passing the option is deprecated.\n'
360 def callback_manpage(self
, option
, opt_str
, value
, parser
):
361 f
= codecs
.getwriter('utf_8')(sys
.stdout
)
365 date
=datetime
.date
.today(),
366 source
='Version %s' % (VERSION
,),
367 manual
='User Commands',
368 short_desc
=short_desc
,
377 def process_extraction_options(self
):
378 """Process options related to extracting data from the CVS repository."""
381 options
= self
.options
383 not_both(options
.use_rcs
, '--use-rcs',
384 options
.use_cvs
, '--use-cvs')
386 not_both(options
.use_rcs
, '--use-rcs',
387 options
.use_internal_co
, '--use-internal-co')
389 not_both(options
.use_cvs
, '--use-cvs',
390 options
.use_internal_co
, '--use-internal-co')
393 ctx
.revision_recorder
= NullRevisionRecorder()
394 ctx
.revision_excluder
= NullRevisionExcluder()
395 ctx
.revision_reader
= RCSRevisionReader(options
.co_executable
)
396 elif options
.use_cvs
:
397 ctx
.revision_recorder
= NullRevisionRecorder()
398 ctx
.revision_excluder
= NullRevisionExcluder()
399 ctx
.revision_reader
= CVSRevisionReader(options
.cvs_executable
)
401 # --use-internal-co is the default:
402 ctx
.revision_recorder
= InternalRevisionRecorder(compress
=True)
403 ctx
.revision_excluder
= InternalRevisionExcluder()
404 ctx
.revision_reader
= InternalRevisionReader(compress
=True)
406 def process_output_options(self
):
407 """Process the options related to SVN output."""
410 options
= self
.options
412 if options
.dump_only
and not options
.dumpfile
:
413 raise FatalError("'--dump-only' requires '--dumpfile' to be specified.")
415 if not options
.svnrepos
and not options
.dumpfile
and not ctx
.dry_run
:
416 raise FatalError("must pass one of '-s' or '--dumpfile'.")
418 not_both(options
.svnrepos
, '-s',
419 options
.dumpfile
, '--dumpfile')
421 not_both(options
.dumpfile
, '--dumpfile',
422 options
.existing_svnrepos
, '--existing-svnrepos')
424 not_both(options
.bdb_txn_nosync
, '--bdb-txn-nosync',
425 options
.existing_svnrepos
, '--existing-svnrepos')
427 not_both(options
.dumpfile
, '--dumpfile',
428 options
.bdb_txn_nosync
, '--bdb-txn-nosync')
430 not_both(options
.fs_type
, '--fs-type',
431 options
.existing_svnrepos
, '--existing-svnrepos')
435 and options
.fs_type
!= 'bdb'
436 and options
.bdb_txn_nosync
438 raise FatalError("cannot pass --bdb-txn-nosync with --fs-type=%s."
442 if options
.existing_svnrepos
:
443 ctx
.output_option
= ExistingRepositoryOutputOption(options
.svnrepos
)
445 ctx
.output_option
= NewRepositoryOutputOption(
447 fs_type
=options
.fs_type
, bdb_txn_nosync
=options
.bdb_txn_nosync
,
448 create_options
=options
.create_options
)
450 ctx
.output_option
= DumpfileOutputOption(options
.dumpfile
)
454 project_cvs_repos_path
,
455 trunk_path
=None, branches_path
=None, tags_path
=None,
456 initial_directories
=[],
457 symbol_transforms
=None,
458 symbol_strategy_rules
=[],
460 """Add a project to be converted.
462 Most arguments are passed straight through to the Project
463 constructor. SYMBOL_STRATEGY_RULES is an iterable of
464 SymbolStrategyRules that will be applied to symbols in this
467 if trunk_path
is not None:
468 trunk_path
= normalize_svn_path(trunk_path
, allow_empty
=True)
469 if branches_path
is not None:
470 branches_path
= normalize_svn_path(branches_path
, allow_empty
=False)
471 if tags_path
is not None:
472 tags_path
= normalize_svn_path(tags_path
, allow_empty
=False)
474 initial_directories
= [
476 for path
in [trunk_path
, branches_path
, tags_path
]
479 normalize_svn_path(path
)
480 for path
in initial_directories
483 symbol_strategy_rules
= list(symbol_strategy_rules
)
485 # Add rules to set the SVN paths for LODs depending on whether
486 # they are the trunk, tags, or branches:
487 if trunk_path
is not None:
488 symbol_strategy_rules
.append(TrunkPathRule(trunk_path
))
489 if branches_path
is not None:
490 symbol_strategy_rules
.append(BranchesPathRule(branches_path
))
491 if tags_path
is not None:
492 symbol_strategy_rules
.append(TagsPathRule(tags_path
))
494 id = len(self
.projects
)
497 project_cvs_repos_path
,
498 initial_directories
=initial_directories
,
499 symbol_transforms
=symbol_transforms
,
502 self
.projects
.append(project
)
503 self
.project_symbol_strategy_rules
.append(symbol_strategy_rules
)
505 def clear_projects(self
):
506 """Clear the list of projects to be converted.
508 This method is for the convenience of options files, which may
509 want to import one another."""
512 del self
.project_symbol_strategy_rules
[:]
514 def process_options(self
):
515 # Consistency check for options and arguments.
516 if len(self
.args
) == 0:
520 if len(self
.args
) > 1:
521 Log().error(error_prefix
+ ": must pass only one CVS repository.\n")
525 cvsroot
= self
.args
[0]
527 self
.process_extraction_options()
528 self
.process_output_options()
529 self
.process_symbol_strategy_options()
530 self
.process_property_setter_options()
532 # Create the default project (using ctx.trunk, ctx.branches, and
536 trunk_path
=self
.options
.trunk_base
,
537 branches_path
=self
.options
.branches_base
,
538 tags_path
=self
.options
.tags_base
,
539 symbol_transforms
=self
.options
.symbol_transforms
,
540 symbol_strategy_rules
=self
.options
.symbol_strategy_rules
,