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."""
24 from cvs2svn_lib
import config
25 from cvs2svn_lib
.common
import warning_prefix
26 from cvs2svn_lib
.common
import error_prefix
27 from cvs2svn_lib
.common
import FatalError
28 from cvs2svn_lib
.common
import normalize_svn_path
29 from cvs2svn_lib
.log
import logger
30 from cvs2svn_lib
.context
import Ctx
31 from cvs2svn_lib
.run_options
import not_both
32 from cvs2svn_lib
.run_options
import RunOptions
33 from cvs2svn_lib
.run_options
import ContextOption
34 from cvs2svn_lib
.run_options
import IncompatibleOption
35 from cvs2svn_lib
.project
import Project
36 from cvs2svn_lib
.svn_output_option
import DumpfileOutputOption
37 from cvs2svn_lib
.svn_output_option
import ExistingRepositoryOutputOption
38 from cvs2svn_lib
.svn_output_option
import NewRepositoryOutputOption
39 from cvs2svn_lib
.symbol_strategy
import TrunkPathRule
40 from cvs2svn_lib
.symbol_strategy
import BranchesPathRule
41 from cvs2svn_lib
.symbol_strategy
import TagsPathRule
42 from cvs2svn_lib
.property_setters
import FilePropertySetter
45 class SVNEOLFixPropertySetter(FilePropertySetter
):
46 """Set _eol_fix property.
48 This keyword is used to tell the RevisionReader how to munge EOLs
49 when generating the fulltext, based on how svn:eol-style is set. If
50 svn:eol-style is not set, it does _eol_style to None, thereby
51 disabling any EOL munging."""
53 # A mapping from the value of the svn:eol-style property to the EOL
54 # string that should appear in a dumpfile:
62 def set_properties(self
, cvs_file
):
63 # Fix EOLs if necessary:
64 eol_style
= cvs_file
.properties
.get('svn:eol-style', None)
66 self
.maybe_set_property(
67 cvs_file
, '_eol_fix', self
.EOL_REPLACEMENTS
[eol_style
]
70 self
.maybe_set_property(
71 cvs_file
, '_eol_fix', None
75 class SVNKeywordHandlingPropertySetter(FilePropertySetter
):
76 """Set _keyword_handling property based on the file mode and svn:keywords.
78 This setting tells the RevisionReader that it has to collapse RCS
79 keywords when generating the fulltext."""
81 def set_properties(self
, cvs_file
):
82 if cvs_file
.mode
== 'b' or cvs_file
.mode
== 'o':
83 # Leave keywords in the form that they were checked in.
85 elif cvs_file
.mode
== 'k':
86 # This mode causes CVS to collapse keywords on checkout, so we
89 elif cvs_file
.properties
.get('svn:keywords'):
90 # Subversion is going to expand the keywords, so they have to be
91 # collapsed in the dumpfile:
94 # CVS expands keywords, so we will too.
97 self
.maybe_set_property(cvs_file
, '_keyword_handling', value
)
100 class SVNRunOptions(RunOptions
):
101 short_desc
= 'convert a CVS repository into a Subversion repository'
105 [\\fIOPTION\\fR]... \\fIOUTPUT-OPTION CVS-REPOS-PATH\\fR
108 [\\fIOPTION\\fR]... \\fI--options=PATH\\fR
112 Create a new Subversion repository based on the version history stored in a
113 CVS repository. Each CVS commit will be mirrored in the Subversion
114 repository, including such information as date of commit and id of the
117 \\fICVS-REPOS-PATH\\fR is the filesystem path of the part of the CVS
118 repository that you want to convert. It is not possible to convert a
119 CVS repository to which you only have remote access; see the FAQ for
120 more information. This path doesn't have to be the top level
121 directory of a CVS repository; it can point at a project within a
122 repository, in which case only that project will be converted. This
123 path or one of its parent directories has to contain a subdirectory
124 called CVSROOT (though the CVSROOT directory can be empty).
126 Multiple CVS repositories can be converted into a single Subversion
127 repository in a single run of cvs2svn, but only by using an
128 \\fB--options\\fR file.
132 A directory under \\fI%s\\fR (or the directory specified by
133 \\fB--tmpdir\\fR) is used as scratch space for temporary data files.
134 """ % (tempfile
.gettempdir(),)
142 DEFAULT_USERNAME
= None
144 def _get_output_options_group(self
):
145 group
= super(SVNRunOptions
, self
)._get
_output
_options
_group
()
147 group
.add_option(IncompatibleOption(
148 '--svnrepos', '-s', type='string',
150 help='path where SVN repos should be created',
152 'Write the output of the conversion into a Subversion repository '
153 'located at \\fIpath\\fR. This option causes a new Subversion '
154 'repository to be created at \\fIpath\\fR unless the '
155 '\\fB--existing-svnrepos\\fR option is also used.'
159 self
.parser
.set_default('existing_svnrepos', False)
160 group
.add_option(IncompatibleOption(
161 '--existing-svnrepos',
163 help='load into existing SVN repository (for use with --svnrepos)',
165 'Load the converted CVS repository into an existing Subversion '
166 'repository, instead of creating a new repository. (This option '
167 'should be used in combination with '
168 '\\fB-s\\fR/\\fB--svnrepos\\fR.) The repository must either be '
169 'empty or contain no paths that overlap with those that will '
170 'result from the conversion. Please note that you need write '
171 'permission for the repository files.'
174 group
.add_option(IncompatibleOption(
175 '--fs-type', type='string',
178 'pass --fs-type=TYPE to "svnadmin create" (for use with '
182 'Pass \\fI--fs-type\\fR=\\fItype\\fR to "svnadmin create" when '
183 'creating a new repository.'
187 self
.parser
.set_default('bdb_txn_nosync', False)
188 group
.add_option(IncompatibleOption(
192 'pass --bdb-txn-nosync to "svnadmin create" (for use with '
196 'Pass \\fI--bdb-txn-nosync\\fR to "svnadmin create" when '
197 'creating a new BDB-style Subversion repository.'
200 self
.parser
.set_default('create_options', [])
201 group
.add_option(IncompatibleOption(
202 '--create-option', type='string',
203 action
='append', dest
='create_options',
204 help='pass OPT to "svnadmin create" (for use with --svnrepos)',
206 'Pass \\fIopt\\fR to "svnadmin create" when creating a new '
207 'Subversion repository (can be specified multiple times to '
208 'pass multiple options).'
212 group
.add_option(IncompatibleOption(
213 '--dumpfile', type='string',
215 help='just produce a dumpfile; don\'t commit to a repos',
217 'Just produce a dumpfile; don\'t commit to an SVN repository. '
218 'Write the dumpfile to \\fIpath\\fR.'
223 group
.add_option(ContextOption(
227 'do not create a repository or a dumpfile; just print what '
231 'Do not create a repository or a dumpfile; just print the '
232 'details of what cvs2svn would do if it were really converting '
237 # Deprecated options:
238 self
.parser
.set_default('dump_only', False)
239 group
.add_option(IncompatibleOption(
241 action
='callback', callback
=self
.callback_dump_only
,
242 help=optparse
.SUPPRESS_HELP
,
243 man_help
=optparse
.SUPPRESS_HELP
,
245 group
.add_option(IncompatibleOption(
247 action
='callback', callback
=self
.callback_create
,
248 help=optparse
.SUPPRESS_HELP
,
249 man_help
=optparse
.SUPPRESS_HELP
,
254 def _get_conversion_options_group(self
):
255 group
= super(SVNRunOptions
, self
)._get
_conversion
_options
_group
()
257 self
.parser
.set_default('trunk_base', config
.DEFAULT_TRUNK_BASE
)
258 group
.add_option(IncompatibleOption(
259 '--trunk', type='string',
260 action
='store', dest
='trunk_base',
262 'path for trunk (default: %s)'
263 % (config
.DEFAULT_TRUNK_BASE
,)
266 'Set the top-level path to use for trunk in the Subversion '
267 'repository. The default is \\fI%s\\fR.'
268 % (config
.DEFAULT_TRUNK_BASE
,)
272 self
.parser
.set_default('branches_base', config
.DEFAULT_BRANCHES_BASE
)
273 group
.add_option(IncompatibleOption(
274 '--branches', type='string',
275 action
='store', dest
='branches_base',
277 'path for branches (default: %s)'
278 % (config
.DEFAULT_BRANCHES_BASE
,)
281 'Set the top-level path to use for branches in the Subversion '
282 'repository. The default is \\fI%s\\fR.'
283 % (config
.DEFAULT_BRANCHES_BASE
,)
287 self
.parser
.set_default('tags_base', config
.DEFAULT_TAGS_BASE
)
288 group
.add_option(IncompatibleOption(
289 '--tags', type='string',
290 action
='store', dest
='tags_base',
292 'path for tags (default: %s)'
293 % (config
.DEFAULT_TAGS_BASE
,)
296 'Set the top-level path to use for tags in the Subversion '
297 'repository. The default is \\fI%s\\fR.'
298 % (config
.DEFAULT_TAGS_BASE
,)
302 group
.add_option(ContextOption(
303 '--include-empty-directories',
304 action
='store_true', dest
='include_empty_directories',
306 'include empty directories within the CVS repository '
310 'Treat empty subdirectories within the CVS repository as actual '
311 'directories, creating them when the parent directory is created '
312 'and removing them if and when the parent directory is pruned.'
315 group
.add_option(ContextOption(
317 action
='store_false', dest
='prune',
318 help='don\'t prune empty directories',
320 'When all files are deleted from a directory in the Subversion '
321 'repository, don\'t delete the empty directory (the default is '
322 'to delete any empty directories).'
325 group
.add_option(ContextOption(
326 '--no-cross-branch-commits',
327 action
='store_false', dest
='cross_branch_commits',
328 help='prevent the creation of cross-branch commits',
330 'Prevent the creation of commits that affect files on multiple '
337 def _get_extraction_options_group(self
):
338 group
= super(SVNRunOptions
, self
)._get
_extraction
_options
_group
()
339 self
._add
_use
_internal
_co
_option
(group
)
340 self
._add
_use
_cvs
_option
(group
)
341 self
._add
_use
_rcs
_option
(group
)
344 def _get_environment_options_group(self
):
345 group
= super(SVNRunOptions
, self
)._get
_environment
_options
_group
()
347 group
.add_option(ContextOption(
348 '--svnadmin', type='string',
349 action
='store', dest
='svnadmin_executable',
350 help='path to the "svnadmin" program',
352 'Path to the \\fIsvnadmin\\fR program. (\\fIsvnadmin\\fR is '
353 'needed when the \\fB-s\\fR/\\fB--svnrepos\\fR output option is '
357 compatible_with_option
=True,
362 def callback_dump_only(self
, option
, opt_str
, value
, parser
):
363 parser
.values
.dump_only
= True
366 ': The --dump-only option is deprecated (it is implied '
370 def callback_create(self
, option
, opt_str
, value
, parser
):
373 ': The behaviour produced by the --create option is now the '
375 'passing the option is deprecated.\n'
378 def process_extraction_options(self
):
379 """Process options related to extracting data from the CVS repository."""
380 self
.process_all_extraction_options()
382 def process_output_options(self
):
383 """Process the options related to SVN output."""
386 options
= self
.options
388 if options
.dump_only
and not options
.dumpfile
:
389 raise FatalError("'--dump-only' requires '--dumpfile' to be specified.")
391 if not options
.svnrepos
and not options
.dumpfile
and not ctx
.dry_run
:
392 raise FatalError("must pass one of '-s' or '--dumpfile'.")
394 not_both(options
.svnrepos
, '-s',
395 options
.dumpfile
, '--dumpfile')
397 not_both(options
.dumpfile
, '--dumpfile',
398 options
.existing_svnrepos
, '--existing-svnrepos')
400 not_both(options
.bdb_txn_nosync
, '--bdb-txn-nosync',
401 options
.existing_svnrepos
, '--existing-svnrepos')
403 not_both(options
.dumpfile
, '--dumpfile',
404 options
.bdb_txn_nosync
, '--bdb-txn-nosync')
406 not_both(options
.fs_type
, '--fs-type',
407 options
.existing_svnrepos
, '--existing-svnrepos')
411 and options
.fs_type
!= 'bdb'
412 and options
.bdb_txn_nosync
414 raise FatalError("cannot pass --bdb-txn-nosync with --fs-type=%s."
418 if options
.existing_svnrepos
:
419 ctx
.output_option
= ExistingRepositoryOutputOption(options
.svnrepos
)
421 ctx
.output_option
= NewRepositoryOutputOption(
423 fs_type
=options
.fs_type
, bdb_txn_nosync
=options
.bdb_txn_nosync
,
424 create_options
=options
.create_options
)
426 ctx
.output_option
= DumpfileOutputOption(options
.dumpfile
)
430 project_cvs_repos_path
,
431 trunk_path
=None, branches_path
=None, tags_path
=None,
432 initial_directories
=[],
433 symbol_transforms
=None,
434 symbol_strategy_rules
=[],
437 """Add a project to be converted.
439 Most arguments are passed straight through to the Project
440 constructor. SYMBOL_STRATEGY_RULES is an iterable of
441 SymbolStrategyRules that will be applied to symbols in this
444 if trunk_path
is not None:
445 trunk_path
= normalize_svn_path(trunk_path
, allow_empty
=True)
446 if branches_path
is not None:
447 branches_path
= normalize_svn_path(branches_path
, allow_empty
=False)
448 if tags_path
is not None:
449 tags_path
= normalize_svn_path(tags_path
, allow_empty
=False)
451 initial_directories
= [
453 for path
in [trunk_path
, branches_path
, tags_path
]
456 normalize_svn_path(path
)
457 for path
in initial_directories
460 symbol_strategy_rules
= list(symbol_strategy_rules
)
462 # Add rules to set the SVN paths for LODs depending on whether
463 # they are the trunk, tags, or branches:
464 if trunk_path
is not None:
465 symbol_strategy_rules
.append(TrunkPathRule(trunk_path
))
466 if branches_path
is not None:
467 symbol_strategy_rules
.append(BranchesPathRule(branches_path
))
468 if tags_path
is not None:
469 symbol_strategy_rules
.append(TagsPathRule(tags_path
))
471 id = len(self
.projects
)
474 project_cvs_repos_path
,
475 initial_directories
=initial_directories
,
476 symbol_transforms
=symbol_transforms
,
477 exclude_paths
=exclude_paths
,
480 self
.projects
.append(project
)
481 self
.project_symbol_strategy_rules
.append(symbol_strategy_rules
)
483 def clear_projects(self
):
484 """Clear the list of projects to be converted.
486 This method is for the convenience of options files, which may
487 want to import one another."""
490 del self
.project_symbol_strategy_rules
[:]
492 def process_property_setter_options(self
):
493 super(SVNRunOptions
, self
).process_property_setter_options()
495 # Property setters for internal use:
496 Ctx().file_property_setters
.append(SVNEOLFixPropertySetter())
497 Ctx().file_property_setters
.append(SVNKeywordHandlingPropertySetter())
499 def process_options(self
):
500 # Consistency check for options and arguments.
501 if len(self
.args
) == 0:
505 if len(self
.args
) > 1:
506 logger
.error(error_prefix
+ ": must pass only one CVS repository.\n")
510 cvsroot
= self
.args
[0]
512 self
.process_extraction_options()
513 self
.process_output_options()
514 self
.process_symbol_strategy_options()
515 self
.process_property_setter_options()
517 # Create the default project (using ctx.trunk, ctx.branches, and
521 trunk_path
=self
.options
.trunk_base
,
522 branches_path
=self
.options
.branches_base
,
523 tags_path
=self
.options
.tags_base
,
524 symbol_transforms
=self
.options
.symbol_transforms
,
525 symbol_strategy_rules
=self
.options
.symbol_strategy_rules
,