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."""
23 from cvs2svn_lib
import config
24 from cvs2svn_lib
.common
import warning_prefix
25 from cvs2svn_lib
.common
import error_prefix
26 from cvs2svn_lib
.common
import FatalError
27 from cvs2svn_lib
.common
import normalize_svn_path
28 from cvs2svn_lib
.log
import logger
29 from cvs2svn_lib
.context
import Ctx
30 from cvs2svn_lib
.run_options
import not_both
31 from cvs2svn_lib
.run_options
import RunOptions
32 from cvs2svn_lib
.run_options
import ContextOption
33 from cvs2svn_lib
.run_options
import IncompatibleOption
34 from cvs2svn_lib
.project
import Project
35 from cvs2svn_lib
.svn_output_option
import DumpfileOutputOption
36 from cvs2svn_lib
.svn_output_option
import ExistingRepositoryOutputOption
37 from cvs2svn_lib
.svn_output_option
import NewRepositoryOutputOption
38 from cvs2svn_lib
.symbol_strategy
import TrunkPathRule
39 from cvs2svn_lib
.symbol_strategy
import BranchesPathRule
40 from cvs2svn_lib
.symbol_strategy
import TagsPathRule
41 from cvs2svn_lib
.property_setters
import FilePropertySetter
44 class SVNEOLFixPropertySetter(FilePropertySetter
):
45 """Set _eol_fix property.
47 This keyword is used to tell the RevisionReader how to munge EOLs
48 when generating the fulltext, based on how svn:eol-style is set. If
49 svn:eol-style is not set, it does _eol_style to None, thereby
50 disabling any EOL munging."""
52 # A mapping from the value of the svn:eol-style property to the EOL
53 # string that should appear in a dumpfile:
61 def set_properties(self
, cvs_file
):
62 # Fix EOLs if necessary:
63 eol_style
= cvs_file
.properties
.get('svn:eol-style', None)
65 self
.maybe_set_property(
66 cvs_file
, '_eol_fix', self
.EOL_REPLACEMENTS
[eol_style
]
69 self
.maybe_set_property(
70 cvs_file
, '_eol_fix', None
74 class SVNKeywordHandlingPropertySetter(FilePropertySetter
):
75 """Set _keyword_handling property based on the file mode and svn:keywords.
77 This setting tells the RevisionReader that it has to collapse RCS
78 keywords when generating the fulltext."""
80 def set_properties(self
, cvs_file
):
81 if cvs_file
.mode
== 'b' or cvs_file
.mode
== 'o':
82 # Leave keywords in the form that they were checked in.
84 elif cvs_file
.mode
== 'k':
85 # This mode causes CVS to collapse keywords on checkout, so we
88 elif cvs_file
.properties
.get('svn:keywords'):
89 # Subversion is going to expand the keywords, so they have to be
90 # collapsed in the dumpfile:
93 # CVS expands keywords, so we will too.
96 self
.maybe_set_property(cvs_file
, '_keyword_handling', value
)
99 class SVNRunOptions(RunOptions
):
100 short_desc
= 'convert a CVS repository into a Subversion repository'
104 [\\fIOPTION\\fR]... \\fIOUTPUT-OPTION CVS-REPOS-PATH\\fR
107 [\\fIOPTION\\fR]... \\fI--options=PATH\\fR
111 Create a new Subversion repository based on the version history stored in a
112 CVS repository. Each CVS commit will be mirrored in the Subversion
113 repository, including such information as date of commit and id of the
116 \\fICVS-REPOS-PATH\\fR is the filesystem path of the part of the CVS
117 repository that you want to convert. It is not possible to convert a
118 CVS repository to which you only have remote access; see the FAQ for
119 more information. This path doesn't have to be the top level
120 directory of a CVS repository; it can point at a project within a
121 repository, in which case only that project will be converted. This
122 path or one of its parent directories has to contain a subdirectory
123 called CVSROOT (though the CVSROOT directory can be empty).
125 Multiple CVS repositories can be converted into a single Subversion
126 repository in a single run of cvs2svn, but only by using an
127 \\fB--options\\fR file.
131 A directory called \\fIcvs2svn-tmp\\fR (or the directory specified by
132 \\fB--tmpdir\\fR) is used as scratch space for temporary data files.
141 def _get_output_options_group(self
):
142 group
= super(SVNRunOptions
, self
)._get
_output
_options
_group
()
144 group
.add_option(IncompatibleOption(
145 '--svnrepos', '-s', type='string',
147 help='path where SVN repos should be created',
149 'Write the output of the conversion into a Subversion repository '
150 'located at \\fIpath\\fR. This option causes a new Subversion '
151 'repository to be created at \\fIpath\\fR unless the '
152 '\\fB--existing-svnrepos\\fR option is also used.'
156 self
.parser
.set_default('existing_svnrepos', False)
157 group
.add_option(IncompatibleOption(
158 '--existing-svnrepos',
160 help='load into existing SVN repository (for use with --svnrepos)',
162 'Load the converted CVS repository into an existing Subversion '
163 'repository, instead of creating a new repository. (This option '
164 'should be used in combination with '
165 '\\fB-s\\fR/\\fB--svnrepos\\fR.) The repository must either be '
166 'empty or contain no paths that overlap with those that will '
167 'result from the conversion. Please note that you need write '
168 'permission for the repository files.'
171 group
.add_option(IncompatibleOption(
172 '--fs-type', type='string',
175 'pass --fs-type=TYPE to "svnadmin create" (for use with '
179 'Pass \\fI--fs-type\\fR=\\fItype\\fR to "svnadmin create" when '
180 'creating a new repository.'
184 self
.parser
.set_default('bdb_txn_nosync', False)
185 group
.add_option(IncompatibleOption(
189 'pass --bdb-txn-nosync to "svnadmin create" (for use with '
193 'Pass \\fI--bdb-txn-nosync\\fR to "svnadmin create" when '
194 'creating a new BDB-style Subversion repository.'
197 self
.parser
.set_default('create_options', [])
198 group
.add_option(IncompatibleOption(
199 '--create-option', type='string',
200 action
='append', dest
='create_options',
201 help='pass OPT to "svnadmin create" (for use with --svnrepos)',
203 'Pass \\fIopt\\fR to "svnadmin create" when creating a new '
204 'Subversion repository (can be specified multiple times to '
205 'pass multiple options).'
209 group
.add_option(IncompatibleOption(
210 '--dumpfile', type='string',
212 help='just produce a dumpfile; don\'t commit to a repos',
214 'Just produce a dumpfile; don\'t commit to an SVN repository. '
215 'Write the dumpfile to \\fIpath\\fR.'
220 group
.add_option(ContextOption(
224 'do not create a repository or a dumpfile; just print what '
228 'Do not create a repository or a dumpfile; just print the '
229 'details of what cvs2svn would do if it were really converting '
234 # Deprecated options:
235 self
.parser
.set_default('dump_only', False)
236 group
.add_option(IncompatibleOption(
238 action
='callback', callback
=self
.callback_dump_only
,
239 help=optparse
.SUPPRESS_HELP
,
240 man_help
=optparse
.SUPPRESS_HELP
,
242 group
.add_option(IncompatibleOption(
244 action
='callback', callback
=self
.callback_create
,
245 help=optparse
.SUPPRESS_HELP
,
246 man_help
=optparse
.SUPPRESS_HELP
,
251 def _get_conversion_options_group(self
):
252 group
= super(SVNRunOptions
, self
)._get
_conversion
_options
_group
()
254 self
.parser
.set_default('trunk_base', config
.DEFAULT_TRUNK_BASE
)
255 group
.add_option(IncompatibleOption(
256 '--trunk', type='string',
257 action
='store', dest
='trunk_base',
259 'path for trunk (default: %s)'
260 % (config
.DEFAULT_TRUNK_BASE
,)
263 'Set the top-level path to use for trunk in the Subversion '
264 'repository. The default is \\fI%s\\fR.'
265 % (config
.DEFAULT_TRUNK_BASE
,)
269 self
.parser
.set_default('branches_base', config
.DEFAULT_BRANCHES_BASE
)
270 group
.add_option(IncompatibleOption(
271 '--branches', type='string',
272 action
='store', dest
='branches_base',
274 'path for branches (default: %s)'
275 % (config
.DEFAULT_BRANCHES_BASE
,)
278 'Set the top-level path to use for branches in the Subversion '
279 'repository. The default is \\fI%s\\fR.'
280 % (config
.DEFAULT_BRANCHES_BASE
,)
284 self
.parser
.set_default('tags_base', config
.DEFAULT_TAGS_BASE
)
285 group
.add_option(IncompatibleOption(
286 '--tags', type='string',
287 action
='store', dest
='tags_base',
289 'path for tags (default: %s)'
290 % (config
.DEFAULT_TAGS_BASE
,)
293 'Set the top-level path to use for tags in the Subversion '
294 'repository. The default is \\fI%s\\fR.'
295 % (config
.DEFAULT_TAGS_BASE
,)
299 group
.add_option(ContextOption(
300 '--include-empty-directories',
301 action
='store_true', dest
='include_empty_directories',
303 'include empty directories within the CVS repository '
307 'Treat empty subdirectories within the CVS repository as actual '
308 'directories, creating them when the parent directory is created '
309 'and removing them if and when the parent directory is pruned.'
312 group
.add_option(ContextOption(
314 action
='store_false', dest
='prune',
315 help='don\'t prune empty directories',
317 'When all files are deleted from a directory in the Subversion '
318 'repository, don\'t delete the empty directory (the default is '
319 'to delete any empty directories).'
322 group
.add_option(ContextOption(
323 '--no-cross-branch-commits',
324 action
='store_false', dest
='cross_branch_commits',
325 help='prevent the creation of cross-branch commits',
327 'Prevent the creation of commits that affect files on multiple '
334 def _get_extraction_options_group(self
):
335 group
= super(SVNRunOptions
, self
)._get
_extraction
_options
_group
()
336 self
._add
_use
_internal
_co
_option
(group
)
337 self
._add
_use
_cvs
_option
(group
)
338 self
._add
_use
_rcs
_option
(group
)
341 def _get_environment_options_group(self
):
342 group
= super(SVNRunOptions
, self
)._get
_environment
_options
_group
()
344 group
.add_option(ContextOption(
345 '--svnadmin', type='string',
346 action
='store', dest
='svnadmin_executable',
347 help='path to the "svnadmin" program',
349 'Path to the \\fIsvnadmin\\fR program. (\\fIsvnadmin\\fR is '
350 'needed when the \\fB-s\\fR/\\fB--svnrepos\\fR output option is '
354 compatible_with_option
=True,
359 def callback_dump_only(self
, option
, opt_str
, value
, parser
):
360 parser
.values
.dump_only
= True
363 ': The --dump-only option is deprecated (it is implied '
367 def callback_create(self
, option
, opt_str
, value
, parser
):
370 ': The behaviour produced by the --create option is now the '
372 'passing the option is deprecated.\n'
375 def process_extraction_options(self
):
376 """Process options related to extracting data from the CVS repository."""
377 self
.process_all_extraction_options()
379 def process_output_options(self
):
380 """Process the options related to SVN output."""
383 options
= self
.options
385 if options
.dump_only
and not options
.dumpfile
:
386 raise FatalError("'--dump-only' requires '--dumpfile' to be specified.")
388 if not options
.svnrepos
and not options
.dumpfile
and not ctx
.dry_run
:
389 raise FatalError("must pass one of '-s' or '--dumpfile'.")
391 not_both(options
.svnrepos
, '-s',
392 options
.dumpfile
, '--dumpfile')
394 not_both(options
.dumpfile
, '--dumpfile',
395 options
.existing_svnrepos
, '--existing-svnrepos')
397 not_both(options
.bdb_txn_nosync
, '--bdb-txn-nosync',
398 options
.existing_svnrepos
, '--existing-svnrepos')
400 not_both(options
.dumpfile
, '--dumpfile',
401 options
.bdb_txn_nosync
, '--bdb-txn-nosync')
403 not_both(options
.fs_type
, '--fs-type',
404 options
.existing_svnrepos
, '--existing-svnrepos')
408 and options
.fs_type
!= 'bdb'
409 and options
.bdb_txn_nosync
411 raise FatalError("cannot pass --bdb-txn-nosync with --fs-type=%s."
415 if options
.existing_svnrepos
:
416 ctx
.output_option
= ExistingRepositoryOutputOption(options
.svnrepos
)
418 ctx
.output_option
= NewRepositoryOutputOption(
420 fs_type
=options
.fs_type
, bdb_txn_nosync
=options
.bdb_txn_nosync
,
421 create_options
=options
.create_options
)
423 ctx
.output_option
= DumpfileOutputOption(options
.dumpfile
)
427 project_cvs_repos_path
,
428 trunk_path
=None, branches_path
=None, tags_path
=None,
429 initial_directories
=[],
430 symbol_transforms
=None,
431 symbol_strategy_rules
=[],
434 """Add a project to be converted.
436 Most arguments are passed straight through to the Project
437 constructor. SYMBOL_STRATEGY_RULES is an iterable of
438 SymbolStrategyRules that will be applied to symbols in this
441 if trunk_path
is not None:
442 trunk_path
= normalize_svn_path(trunk_path
, allow_empty
=True)
443 if branches_path
is not None:
444 branches_path
= normalize_svn_path(branches_path
, allow_empty
=False)
445 if tags_path
is not None:
446 tags_path
= normalize_svn_path(tags_path
, allow_empty
=False)
448 initial_directories
= [
450 for path
in [trunk_path
, branches_path
, tags_path
]
453 normalize_svn_path(path
)
454 for path
in initial_directories
457 symbol_strategy_rules
= list(symbol_strategy_rules
)
459 # Add rules to set the SVN paths for LODs depending on whether
460 # they are the trunk, tags, or branches:
461 if trunk_path
is not None:
462 symbol_strategy_rules
.append(TrunkPathRule(trunk_path
))
463 if branches_path
is not None:
464 symbol_strategy_rules
.append(BranchesPathRule(branches_path
))
465 if tags_path
is not None:
466 symbol_strategy_rules
.append(TagsPathRule(tags_path
))
468 id = len(self
.projects
)
471 project_cvs_repos_path
,
472 initial_directories
=initial_directories
,
473 symbol_transforms
=symbol_transforms
,
474 exclude_paths
=exclude_paths
,
477 self
.projects
.append(project
)
478 self
.project_symbol_strategy_rules
.append(symbol_strategy_rules
)
480 def clear_projects(self
):
481 """Clear the list of projects to be converted.
483 This method is for the convenience of options files, which may
484 want to import one another."""
487 del self
.project_symbol_strategy_rules
[:]
489 def process_property_setter_options(self
):
490 super(SVNRunOptions
, self
).process_property_setter_options()
492 # Property setters for internal use:
493 Ctx().file_property_setters
.append(SVNEOLFixPropertySetter())
494 Ctx().file_property_setters
.append(SVNKeywordHandlingPropertySetter())
496 def process_options(self
):
497 # Consistency check for options and arguments.
498 if len(self
.args
) == 0:
502 if len(self
.args
) > 1:
503 logger
.error(error_prefix
+ ": must pass only one CVS repository.\n")
507 cvsroot
= self
.args
[0]
509 self
.process_extraction_options()
510 self
.process_output_options()
511 self
.process_symbol_strategy_options()
512 self
.process_property_setter_options()
514 # Create the default project (using ctx.trunk, ctx.branches, and
518 trunk_path
=self
.options
.trunk_base
,
519 branches_path
=self
.options
.branches_base
,
520 tags_path
=self
.options
.tags_base
,
521 symbol_transforms
=self
.options
.symbol_transforms
,
522 symbol_strategy_rules
=self
.options
.symbol_strategy_rules
,