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\\fR [\\fICVS-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). If omitted,
125 the repository path defaults to the current directory.
127 Multiple CVS repositories can be converted into a single Subversion
128 repository in a single run of cvs2svn, but only by using an
129 \\fB--options\\fR file.
133 A directory under \\fI%s\\fR (or the directory specified by
134 \\fB--tmpdir\\fR) is used as scratch space for temporary data files.
135 """ % (tempfile
.gettempdir(),)
143 DEFAULT_USERNAME
= None
145 def _get_output_options_group(self
):
146 group
= RunOptions
._get
_output
_options
_group
(self
)
148 group
.add_option(IncompatibleOption(
149 '--svnrepos', '-s', type='string',
151 help='path where SVN repos should be created',
153 'Write the output of the conversion into a Subversion repository '
154 'located at \\fIpath\\fR. This option causes a new Subversion '
155 'repository to be created at \\fIpath\\fR unless the '
156 '\\fB--existing-svnrepos\\fR option is also used.'
160 self
.parser
.set_default('existing_svnrepos', False)
161 group
.add_option(IncompatibleOption(
162 '--existing-svnrepos',
164 help='load into existing SVN repository (for use with --svnrepos)',
166 'Load the converted CVS repository into an existing Subversion '
167 'repository, instead of creating a new repository. (This option '
168 'should be used in combination with '
169 '\\fB-s\\fR/\\fB--svnrepos\\fR.) The repository must either be '
170 'empty or contain no paths that overlap with those that will '
171 'result from the conversion. Please note that you need write '
172 'permission for the repository files.'
175 group
.add_option(IncompatibleOption(
176 '--fs-type', type='string',
179 'pass --fs-type=TYPE to "svnadmin create" (for use with '
183 'Pass \\fI--fs-type\\fR=\\fItype\\fR to "svnadmin create" when '
184 'creating a new repository.'
188 self
.parser
.set_default('bdb_txn_nosync', False)
189 group
.add_option(IncompatibleOption(
193 'pass --bdb-txn-nosync to "svnadmin create" (for use with '
197 'Pass \\fI--bdb-txn-nosync\\fR to "svnadmin create" when '
198 'creating a new BDB-style Subversion repository.'
201 self
.parser
.set_default('create_options', [])
202 group
.add_option(IncompatibleOption(
203 '--create-option', type='string',
204 action
='append', dest
='create_options',
205 help='pass OPT to "svnadmin create" (for use with --svnrepos)',
207 'Pass \\fIopt\\fR to "svnadmin create" when creating a new '
208 'Subversion repository (can be specified multiple times to '
209 'pass multiple options).'
213 group
.add_option(IncompatibleOption(
214 '--dumpfile', type='string',
216 help='just produce a dumpfile; don\'t commit to a repos',
218 'Just produce a dumpfile; don\'t commit to an SVN repository. '
219 'Write the dumpfile to \\fIpath\\fR.'
224 group
.add_option(ContextOption(
228 'do not create a repository or a dumpfile; just print what '
232 'Do not create a repository or a dumpfile; just print the '
233 'details of what cvs2svn would do if it were really converting '
238 # Deprecated options:
239 self
.parser
.set_default('dump_only', False)
240 group
.add_option(IncompatibleOption(
242 action
='callback', callback
=self
.callback_dump_only
,
243 help=optparse
.SUPPRESS_HELP
,
244 man_help
=optparse
.SUPPRESS_HELP
,
246 group
.add_option(IncompatibleOption(
248 action
='callback', callback
=self
.callback_create
,
249 help=optparse
.SUPPRESS_HELP
,
250 man_help
=optparse
.SUPPRESS_HELP
,
255 def _get_conversion_options_group(self
):
256 group
= RunOptions
._get
_conversion
_options
_group
(self
)
258 self
.parser
.set_default('trunk_base', config
.DEFAULT_TRUNK_BASE
)
259 group
.add_option(IncompatibleOption(
260 '--trunk', type='string',
261 action
='store', dest
='trunk_base',
263 'path for trunk (default: %s)'
264 % (config
.DEFAULT_TRUNK_BASE
,)
267 'Set the top-level path to use for trunk in the Subversion '
268 'repository. The default is \\fI%s\\fR.'
269 % (config
.DEFAULT_TRUNK_BASE
,)
273 self
.parser
.set_default('branches_base', config
.DEFAULT_BRANCHES_BASE
)
274 group
.add_option(IncompatibleOption(
275 '--branches', type='string',
276 action
='store', dest
='branches_base',
278 'path for branches (default: %s)'
279 % (config
.DEFAULT_BRANCHES_BASE
,)
282 'Set the top-level path to use for branches in the Subversion '
283 'repository. The default is \\fI%s\\fR.'
284 % (config
.DEFAULT_BRANCHES_BASE
,)
288 self
.parser
.set_default('tags_base', config
.DEFAULT_TAGS_BASE
)
289 group
.add_option(IncompatibleOption(
290 '--tags', type='string',
291 action
='store', dest
='tags_base',
293 'path for tags (default: %s)'
294 % (config
.DEFAULT_TAGS_BASE
,)
297 'Set the top-level path to use for tags in the Subversion '
298 'repository. The default is \\fI%s\\fR.'
299 % (config
.DEFAULT_TAGS_BASE
,)
303 group
.add_option(ContextOption(
304 '--include-empty-directories',
305 action
='store_true', dest
='include_empty_directories',
307 'include empty directories within the CVS repository '
311 'Treat empty subdirectories within the CVS repository as actual '
312 'directories, creating them when the parent directory is created '
313 'and removing them if and when the parent directory is pruned.'
316 group
.add_option(ContextOption(
318 action
='store_false', dest
='prune',
319 help='don\'t prune empty directories',
321 'When all files are deleted from a directory in the Subversion '
322 'repository, don\'t delete the empty directory (the default is '
323 'to delete any empty directories).'
326 group
.add_option(ContextOption(
327 '--no-cross-branch-commits',
328 action
='store_false', dest
='cross_branch_commits',
329 help='prevent the creation of cross-branch commits',
331 'Prevent the creation of commits that affect files on multiple '
338 def _get_extraction_options_group(self
):
339 group
= RunOptions
._get
_extraction
_options
_group
(self
)
340 self
._add
_use
_internal
_co
_option
(group
)
341 self
._add
_use
_cvs
_option
(group
)
342 self
._add
_use
_rcs
_option
(group
)
345 def _get_environment_options_group(self
):
346 group
= RunOptions
._get
_environment
_options
_group
(self
)
348 group
.add_option(ContextOption(
349 '--svnadmin', type='string',
350 action
='store', dest
='svnadmin_executable',
351 help='path to the "svnadmin" program',
353 'Path to the \\fIsvnadmin\\fR program. (\\fIsvnadmin\\fR is '
354 'needed when the \\fB-s\\fR/\\fB--svnrepos\\fR output option is '
358 compatible_with_option
=True,
363 def callback_dump_only(self
, option
, opt_str
, value
, parser
):
364 parser
.values
.dump_only
= True
367 ': The --dump-only option is deprecated (it is implied '
371 def callback_create(self
, option
, opt_str
, value
, parser
):
374 ': The behaviour produced by the --create option is now the '
376 'passing the option is deprecated.\n'
379 def process_extraction_options(self
):
380 """Process options related to extracting data from the CVS repository."""
381 self
.process_all_extraction_options()
383 def process_output_options(self
):
384 """Process the options related to SVN output."""
387 options
= self
.options
389 if options
.dump_only
and not options
.dumpfile
:
390 raise FatalError("'--dump-only' requires '--dumpfile' to be specified.")
392 if not options
.svnrepos
and not options
.dumpfile
and not ctx
.dry_run
:
393 raise FatalError("must pass one of '-s' or '--dumpfile'.")
395 not_both(options
.svnrepos
, '-s',
396 options
.dumpfile
, '--dumpfile')
398 not_both(options
.dumpfile
, '--dumpfile',
399 options
.existing_svnrepos
, '--existing-svnrepos')
401 not_both(options
.bdb_txn_nosync
, '--bdb-txn-nosync',
402 options
.existing_svnrepos
, '--existing-svnrepos')
404 not_both(options
.dumpfile
, '--dumpfile',
405 options
.bdb_txn_nosync
, '--bdb-txn-nosync')
407 not_both(options
.fs_type
, '--fs-type',
408 options
.existing_svnrepos
, '--existing-svnrepos')
412 and options
.fs_type
!= 'bdb'
413 and options
.bdb_txn_nosync
415 raise FatalError("cannot pass --bdb-txn-nosync with --fs-type=%s."
419 if options
.existing_svnrepos
:
420 ctx
.output_option
= ExistingRepositoryOutputOption(options
.svnrepos
)
422 ctx
.output_option
= NewRepositoryOutputOption(
424 fs_type
=options
.fs_type
, bdb_txn_nosync
=options
.bdb_txn_nosync
,
425 create_options
=options
.create_options
)
427 ctx
.output_option
= DumpfileOutputOption(options
.dumpfile
)
431 project_cvs_repos_path
,
432 trunk_path
=None, branches_path
=None, tags_path
=None,
433 initial_directories
=[],
434 symbol_transforms
=None,
435 symbol_strategy_rules
=[],
438 """Add a project to be converted.
440 Most arguments are passed straight through to the Project
441 constructor. SYMBOL_STRATEGY_RULES is an iterable of
442 SymbolStrategyRules that will be applied to symbols in this
445 if trunk_path
is not None:
446 trunk_path
= normalize_svn_path(trunk_path
, allow_empty
=True)
447 if branches_path
is not None:
448 branches_path
= normalize_svn_path(branches_path
, allow_empty
=False)
449 if tags_path
is not None:
450 tags_path
= normalize_svn_path(tags_path
, allow_empty
=False)
452 initial_directories
= [
454 for path
in [trunk_path
, branches_path
, tags_path
]
457 normalize_svn_path(path
)
458 for path
in initial_directories
461 symbol_strategy_rules
= list(symbol_strategy_rules
)
463 # Add rules to set the SVN paths for LODs depending on whether
464 # they are the trunk, tags, or branches:
465 if trunk_path
is not None:
466 symbol_strategy_rules
.append(TrunkPathRule(trunk_path
))
467 if branches_path
is not None:
468 symbol_strategy_rules
.append(BranchesPathRule(branches_path
))
469 if tags_path
is not None:
470 symbol_strategy_rules
.append(TagsPathRule(tags_path
))
472 id = len(self
.projects
)
475 project_cvs_repos_path
,
476 initial_directories
=initial_directories
,
477 symbol_transforms
=symbol_transforms
,
478 exclude_paths
=exclude_paths
,
481 self
.projects
.append(project
)
482 self
.project_symbol_strategy_rules
.append(symbol_strategy_rules
)
484 def clear_projects(self
):
485 """Clear the list of projects to be converted.
487 This method is for the convenience of options files, which may
488 want to import one another."""
491 del self
.project_symbol_strategy_rules
[:]
493 def process_property_setter_options(self
):
494 RunOptions
.process_property_setter_options(self
)
496 # Property setters for internal use:
497 Ctx().file_property_setters
.append(SVNEOLFixPropertySetter())
498 Ctx().file_property_setters
.append(SVNKeywordHandlingPropertySetter())
500 def process_options(self
):
501 # Consistency check for options and arguments.
502 if len(self
.args
) == 0:
506 if len(self
.args
) > 1:
507 logger
.error(error_prefix
+ ": must pass only one CVS repository.\n")
511 cvsroot
= self
.args
[0]
513 self
.process_extraction_options()
514 self
.process_output_options()
515 self
.process_symbol_strategy_options()
516 self
.process_property_setter_options()
518 # Create the default project (using ctx.trunk, ctx.branches, and
522 trunk_path
=self
.options
.trunk_base
,
523 branches_path
=self
.options
.branches_base
,
524 tags_path
=self
.options
.tags_base
,
525 symbol_transforms
=self
.options
.symbol_transforms
,
526 symbol_strategy_rules
=self
.options
.symbol_strategy_rules
,