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
.version
import VERSION
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 Log
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 SVNKeywordHandlingPropertySetter(FilePropertySetter
):
46 """Set cvs2svn:_keyword_handling=collapsed if svn:keywords is set.
48 This keyword is used to tell the RevisionReader whether it has to
49 collapse RCS keywords when generating the fulltext."""
51 propname
= '_keyword_handling'
53 def set_properties(self
, cvs_file
):
54 if self
.propname
in cvs_file
.properties
:
57 if cvs_file
.mode
== 'k':
58 # The file is set in CVS to have keywords collapsed on checkout,
60 cvs_file
.properties
[self
.propname
] = 'collapsed'
61 elif cvs_file
.properties
.get('svn:keywords'):
62 # Subversion is going to expand the keywords, so they have to be
63 # collapsed in the repository:
64 cvs_file
.properties
[self
.propname
] = 'collapsed'
67 class SVNRunOptions(RunOptions
):
68 short_desc
= 'convert a CVS repository into a Subversion repository'
72 [\\fIOPTION\\fR]... \\fIOUTPUT-OPTION CVS-REPOS-PATH\\fR
75 [\\fIOPTION\\fR]... \\fI--options=PATH\\fR
79 Create a new Subversion repository based on the version history stored in a
80 CVS repository. Each CVS commit will be mirrored in the Subversion
81 repository, including such information as date of commit and id of the
84 \\fICVS-REPOS-PATH\\fR is the filesystem path of the part of the CVS
85 repository that you want to convert. It is not possible to convert a
86 CVS repository to which you only have remote access; see the FAQ for
87 more information. This path doesn't have to be the top level
88 directory of a CVS repository; it can point at a project within a
89 repository, in which case only that project will be converted. This
90 path or one of its parent directories has to contain a subdirectory
91 called CVSROOT (though the CVSROOT directory can be empty).
93 Multiple CVS repositories can be converted into a single Subversion
94 repository in a single run of cvs2svn, but only by using an
95 \\fB--options\\fR file.
99 A directory called \\fIcvs2svn-tmp\\fR (or the directory specified by
100 \\fB--tmpdir\\fR) is used as scratch space for temporary data files.
109 def _get_output_options_group(self
):
110 group
= super(SVNRunOptions
, self
)._get
_output
_options
_group
()
112 group
.add_option(IncompatibleOption(
113 '--svnrepos', '-s', type='string',
115 help='path where SVN repos should be created',
117 'Write the output of the conversion into a Subversion repository '
118 'located at \\fIpath\\fR. This option causes a new Subversion '
119 'repository to be created at \\fIpath\\fR unless the '
120 '\\fB--existing-svnrepos\\fR option is also used.'
124 self
.parser
.set_default('existing_svnrepos', False)
125 group
.add_option(IncompatibleOption(
126 '--existing-svnrepos',
128 help='load into existing SVN repository (for use with --svnrepos)',
130 'Load the converted CVS repository into an existing Subversion '
131 'repository, instead of creating a new repository. (This option '
132 'should be used in combination with '
133 '\\fB-s\\fR/\\fB--svnrepos\\fR.) The repository must either be '
134 'empty or contain no paths that overlap with those that will '
135 'result from the conversion. Please note that you need write '
136 'permission for the repository files.'
139 group
.add_option(IncompatibleOption(
140 '--fs-type', type='string',
143 'pass --fs-type=TYPE to "svnadmin create" (for use with '
147 'Pass \\fI--fs-type\\fR=\\fItype\\fR to "svnadmin create" when '
148 'creating a new repository.'
152 self
.parser
.set_default('bdb_txn_nosync', False)
153 group
.add_option(IncompatibleOption(
157 'pass --bdb-txn-nosync to "svnadmin create" (for use with '
161 'Pass \\fI--bdb-txn-nosync\\fR to "svnadmin create" when '
162 'creating a new BDB-style Subversion repository.'
165 self
.parser
.set_default('create_options', [])
166 group
.add_option(IncompatibleOption(
167 '--create-option', type='string',
168 action
='append', dest
='create_options',
169 help='pass OPT to "svnadmin create" (for use with --svnrepos)',
171 'Pass \\fIopt\\fR to "svnadmin create" when creating a new '
172 'Subversion repository (can be specified multiple times to '
173 'pass multiple options).'
177 group
.add_option(IncompatibleOption(
178 '--dumpfile', type='string',
180 help='just produce a dumpfile; don\'t commit to a repos',
182 'Just produce a dumpfile; don\'t commit to an SVN repository. '
183 'Write the dumpfile to \\fIpath\\fR.'
188 group
.add_option(ContextOption(
192 'do not create a repository or a dumpfile; just print what '
196 'Do not create a repository or a dumpfile; just print the '
197 'details of what cvs2svn would do if it were really converting '
202 # Deprecated options:
203 self
.parser
.set_default('dump_only', False)
204 group
.add_option(IncompatibleOption(
206 action
='callback', callback
=self
.callback_dump_only
,
207 help=optparse
.SUPPRESS_HELP
,
208 man_help
=optparse
.SUPPRESS_HELP
,
210 group
.add_option(IncompatibleOption(
212 action
='callback', callback
=self
.callback_create
,
213 help=optparse
.SUPPRESS_HELP
,
214 man_help
=optparse
.SUPPRESS_HELP
,
219 def _get_conversion_options_group(self
):
220 group
= super(SVNRunOptions
, self
)._get
_conversion
_options
_group
()
222 self
.parser
.set_default('trunk_base', config
.DEFAULT_TRUNK_BASE
)
223 group
.add_option(IncompatibleOption(
224 '--trunk', type='string',
225 action
='store', dest
='trunk_base',
227 'path for trunk (default: %s)'
228 % (config
.DEFAULT_TRUNK_BASE
,)
231 'Set the top-level path to use for trunk in the Subversion '
232 'repository. The default is \\fI%s\\fR.'
233 % (config
.DEFAULT_TRUNK_BASE
,)
237 self
.parser
.set_default('branches_base', config
.DEFAULT_BRANCHES_BASE
)
238 group
.add_option(IncompatibleOption(
239 '--branches', type='string',
240 action
='store', dest
='branches_base',
242 'path for branches (default: %s)'
243 % (config
.DEFAULT_BRANCHES_BASE
,)
246 'Set the top-level path to use for branches in the Subversion '
247 'repository. The default is \\fI%s\\fR.'
248 % (config
.DEFAULT_BRANCHES_BASE
,)
252 self
.parser
.set_default('tags_base', config
.DEFAULT_TAGS_BASE
)
253 group
.add_option(IncompatibleOption(
254 '--tags', type='string',
255 action
='store', dest
='tags_base',
257 'path for tags (default: %s)'
258 % (config
.DEFAULT_TAGS_BASE
,)
261 'Set the top-level path to use for tags in the Subversion '
262 'repository. The default is \\fI%s\\fR.'
263 % (config
.DEFAULT_TAGS_BASE
,)
267 group
.add_option(ContextOption(
268 '--include-empty-directories',
269 action
='store_true', dest
='include_empty_directories',
271 'include empty directories within the CVS repository '
275 'Treat empty subdirectories within the CVS repository as actual '
276 'directories, creating them when the parent directory is created '
277 'and removing them if and when the parent directory is pruned.'
280 group
.add_option(ContextOption(
282 action
='store_false', dest
='prune',
283 help='don\'t prune empty directories',
285 'When all files are deleted from a directory in the Subversion '
286 'repository, don\'t delete the empty directory (the default is '
287 'to delete any empty directories).'
290 group
.add_option(ContextOption(
291 '--no-cross-branch-commits',
292 action
='store_false', dest
='cross_branch_commits',
293 help='prevent the creation of cross-branch commits',
295 'Prevent the creation of commits that affect files on multiple '
302 def _get_extraction_options_group(self
):
303 group
= super(SVNRunOptions
, self
)._get
_extraction
_options
_group
()
304 self
._add
_use
_internal
_co
_option
(group
)
305 self
._add
_use
_cvs
_option
(group
)
306 self
._add
_use
_rcs
_option
(group
)
309 def _get_environment_options_group(self
):
310 group
= super(SVNRunOptions
, self
)._get
_environment
_options
_group
()
312 group
.add_option(ContextOption(
313 '--svnadmin', type='string',
314 action
='store', dest
='svnadmin_executable',
315 help='path to the "svnadmin" program',
317 'Path to the \\fIsvnadmin\\fR program. (\\fIsvnadmin\\fR is '
318 'needed when the \\fB-s\\fR/\\fB--svnrepos\\fR output option is '
326 def callback_dump_only(self
, option
, opt_str
, value
, parser
):
327 parser
.values
.dump_only
= True
330 ': The --dump-only option is deprecated (it is implied '
334 def callback_create(self
, option
, opt_str
, value
, parser
):
337 ': The behaviour produced by the --create option is now the '
339 'passing the option is deprecated.\n'
342 def process_extraction_options(self
):
343 """Process options related to extracting data from the CVS repository."""
344 self
.process_all_extraction_options()
346 def process_output_options(self
):
347 """Process the options related to SVN output."""
350 options
= self
.options
352 if options
.dump_only
and not options
.dumpfile
:
353 raise FatalError("'--dump-only' requires '--dumpfile' to be specified.")
355 if not options
.svnrepos
and not options
.dumpfile
and not ctx
.dry_run
:
356 raise FatalError("must pass one of '-s' or '--dumpfile'.")
358 not_both(options
.svnrepos
, '-s',
359 options
.dumpfile
, '--dumpfile')
361 not_both(options
.dumpfile
, '--dumpfile',
362 options
.existing_svnrepos
, '--existing-svnrepos')
364 not_both(options
.bdb_txn_nosync
, '--bdb-txn-nosync',
365 options
.existing_svnrepos
, '--existing-svnrepos')
367 not_both(options
.dumpfile
, '--dumpfile',
368 options
.bdb_txn_nosync
, '--bdb-txn-nosync')
370 not_both(options
.fs_type
, '--fs-type',
371 options
.existing_svnrepos
, '--existing-svnrepos')
375 and options
.fs_type
!= 'bdb'
376 and options
.bdb_txn_nosync
378 raise FatalError("cannot pass --bdb-txn-nosync with --fs-type=%s."
382 if options
.existing_svnrepos
:
383 ctx
.output_option
= ExistingRepositoryOutputOption(options
.svnrepos
)
385 ctx
.output_option
= NewRepositoryOutputOption(
387 fs_type
=options
.fs_type
, bdb_txn_nosync
=options
.bdb_txn_nosync
,
388 create_options
=options
.create_options
)
390 ctx
.output_option
= DumpfileOutputOption(options
.dumpfile
)
394 project_cvs_repos_path
,
395 trunk_path
=None, branches_path
=None, tags_path
=None,
396 initial_directories
=[],
397 symbol_transforms
=None,
398 symbol_strategy_rules
=[],
400 """Add a project to be converted.
402 Most arguments are passed straight through to the Project
403 constructor. SYMBOL_STRATEGY_RULES is an iterable of
404 SymbolStrategyRules that will be applied to symbols in this
407 if trunk_path
is not None:
408 trunk_path
= normalize_svn_path(trunk_path
, allow_empty
=True)
409 if branches_path
is not None:
410 branches_path
= normalize_svn_path(branches_path
, allow_empty
=False)
411 if tags_path
is not None:
412 tags_path
= normalize_svn_path(tags_path
, allow_empty
=False)
414 initial_directories
= [
416 for path
in [trunk_path
, branches_path
, tags_path
]
419 normalize_svn_path(path
)
420 for path
in initial_directories
423 symbol_strategy_rules
= list(symbol_strategy_rules
)
425 # Add rules to set the SVN paths for LODs depending on whether
426 # they are the trunk, tags, or branches:
427 if trunk_path
is not None:
428 symbol_strategy_rules
.append(TrunkPathRule(trunk_path
))
429 if branches_path
is not None:
430 symbol_strategy_rules
.append(BranchesPathRule(branches_path
))
431 if tags_path
is not None:
432 symbol_strategy_rules
.append(TagsPathRule(tags_path
))
434 id = len(self
.projects
)
437 project_cvs_repos_path
,
438 initial_directories
=initial_directories
,
439 symbol_transforms
=symbol_transforms
,
442 self
.projects
.append(project
)
443 self
.project_symbol_strategy_rules
.append(symbol_strategy_rules
)
445 def clear_projects(self
):
446 """Clear the list of projects to be converted.
448 This method is for the convenience of options files, which may
449 want to import one another."""
452 del self
.project_symbol_strategy_rules
[:]
454 def process_property_setter_options(self
):
455 super(SVNRunOptions
, self
).process_property_setter_options()
457 # Property setters for internal use:
458 Ctx().file_property_setters
.append(SVNKeywordHandlingPropertySetter())
460 def process_options(self
):
461 # Consistency check for options and arguments.
462 if len(self
.args
) == 0:
466 if len(self
.args
) > 1:
467 Log().error(error_prefix
+ ": must pass only one CVS repository.\n")
471 cvsroot
= self
.args
[0]
473 self
.process_extraction_options()
474 self
.process_output_options()
475 self
.process_symbol_strategy_options()
476 self
.process_property_setter_options()
478 # Create the default project (using ctx.trunk, ctx.branches, and
482 trunk_path
=self
.options
.trunk_base
,
483 branches_path
=self
.options
.branches_base
,
484 tags_path
=self
.options
.tags_base
,
485 symbol_transforms
=self
.options
.symbol_transforms
,
486 symbol_strategy_rules
=self
.options
.symbol_strategy_rules
,