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 contains classes to set common cvs2xxx run options."""
22 from optparse
import OptionGroup
28 from cvs2svn_lib
.version
import VERSION
29 from cvs2svn_lib
import config
30 from cvs2svn_lib
.common
import error_prefix
31 from cvs2svn_lib
.common
import FatalError
32 from cvs2svn_lib
.man_writer
import ManWriter
33 from cvs2svn_lib
.log
import logger
34 from cvs2svn_lib
.context
import Ctx
35 from cvs2svn_lib
.man_writer
import ManOption
36 from cvs2svn_lib
.pass_manager
import InvalidPassError
37 from cvs2svn_lib
.revision_manager
import NullRevisionCollector
38 from cvs2svn_lib
.rcs_revision_manager
import RCSRevisionReader
39 from cvs2svn_lib
.cvs_revision_manager
import CVSRevisionReader
40 from cvs2svn_lib
.checkout_internal
import InternalRevisionCollector
41 from cvs2svn_lib
.checkout_internal
import InternalRevisionReader
42 from cvs2svn_lib
.symbol_strategy
import AllBranchRule
43 from cvs2svn_lib
.symbol_strategy
import AllExcludedRule
44 from cvs2svn_lib
.symbol_strategy
import AllTagRule
45 from cvs2svn_lib
.symbol_strategy
import BranchIfCommitsRule
46 from cvs2svn_lib
.symbol_strategy
import ExcludeRegexpStrategyRule
47 from cvs2svn_lib
.symbol_strategy
import ForceBranchRegexpStrategyRule
48 from cvs2svn_lib
.symbol_strategy
import ForceTagRegexpStrategyRule
49 from cvs2svn_lib
.symbol_strategy
import ExcludeTrivialImportBranchRule
50 from cvs2svn_lib
.symbol_strategy
import HeuristicStrategyRule
51 from cvs2svn_lib
.symbol_strategy
import UnambiguousUsageRule
52 from cvs2svn_lib
.symbol_strategy
import HeuristicPreferredParentRule
53 from cvs2svn_lib
.symbol_strategy
import SymbolHintsFileRule
54 from cvs2svn_lib
.symbol_transform
import ReplaceSubstringsSymbolTransform
55 from cvs2svn_lib
.symbol_transform
import RegexpSymbolTransform
56 from cvs2svn_lib
.symbol_transform
import NormalizePathsSymbolTransform
57 from cvs2svn_lib
.property_setters
import AutoPropsPropertySetter
58 from cvs2svn_lib
.property_setters
import CVSBinaryFileDefaultMimeTypeSetter
59 from cvs2svn_lib
.property_setters
import CVSBinaryFileEOLStyleSetter
60 from cvs2svn_lib
.property_setters
import CVSRevisionNumberSetter
61 from cvs2svn_lib
.property_setters
import DefaultEOLStyleSetter
62 from cvs2svn_lib
.property_setters
import EOLStyleFromMimeTypeSetter
63 from cvs2svn_lib
.property_setters
import ExecutablePropertySetter
64 from cvs2svn_lib
.property_setters
import DescriptionPropertySetter
65 from cvs2svn_lib
.property_setters
import KeywordsPropertySetter
66 from cvs2svn_lib
.property_setters
import MimeMapper
67 from cvs2svn_lib
.property_setters
import SVNBinaryFileKeywordsPropertySetter
71 Usage: %prog --options OPTIONFILE
72 %prog [OPTION...] OUTPUT-OPTION CVS-REPOS-PATH"""
75 Convert a CVS repository into a Subversion repository, including history.
79 class IncompatibleOption(ManOption
):
80 """A ManOption that is incompatible with the --options option.
82 Record that the option was used so that error checking can later be
85 def __init__(self
, *args
, **kw
):
86 ManOption
.__init
__(self
, *args
, **kw
)
88 def take_action(self
, action
, dest
, opt
, value
, values
, parser
):
89 oio
= parser
.values
.options_incompatible_options
92 return ManOption
.take_action(
93 self
, action
, dest
, opt
, value
, values
, parser
97 class ContextOption(ManOption
):
98 """A ManOption that stores its value to Ctx."""
100 def __init__(self
, *args
, **kw
):
101 if kw
.get('action') not in self
.STORE_ACTIONS
:
102 raise ValueError('Invalid action: %s' % (kw
['action'],))
104 self
.__compatible
_with
_option
= kw
.pop('compatible_with_option', False)
105 self
.__action
= kw
.pop('action')
107 self
.__dest
= kw
.pop('dest')
110 if not opt
.startswith('--'):
112 self
.__dest
= opt
[2:].replace('-', '_')
114 self
.__const
= kw
.pop('const')
116 kw
['action'] = 'callback'
117 kw
['callback'] = self
.__callback
119 ManOption
.__init
__(self
, *args
, **kw
)
121 def __callback(self
, option
, opt_str
, value
, parser
):
122 if not self
.__compatible
_with
_option
:
123 oio
= parser
.values
.options_incompatible_options
124 if opt_str
not in oio
:
127 action
= self
.__action
130 if action
== "store":
131 setattr(Ctx(), dest
, value
)
132 elif action
== "store_const":
133 setattr(Ctx(), dest
, self
.__const
)
134 elif action
== "store_true":
135 setattr(Ctx(), dest
, True)
136 elif action
== "store_false":
137 setattr(Ctx(), dest
, False)
138 elif action
== "append":
139 getattr(Ctx(), dest
).append(value
)
140 elif action
== "count":
141 setattr(Ctx(), dest
, getattr(Ctx(), dest
, 0) + 1)
143 raise RuntimeError("unknown action %r" % self
.__action
)
148 class IncompatibleOptionsException(FatalError
):
152 # Options that are not allowed to be used with --trunk-only:
154 '--symbol-transform',
159 '--keep-trivial-imports',
161 '--no-cross-branch-commits',
164 class SymbolOptionsWithTrunkOnlyException(IncompatibleOptionsException
):
166 IncompatibleOptionsException
.__init
__(
168 'The following symbol-related options cannot be used together\n'
169 'with --trunk-only:\n'
171 % ('\n '.join(SYMBOL_OPTIONS
),)
175 def not_both(opt1val
, opt1name
, opt2val
, opt2name
):
176 """Raise an exception if both opt1val and opt2val are set."""
177 if opt1val
and opt2val
:
178 raise IncompatibleOptionsException(
179 "cannot pass both '%s' and '%s'." % (opt1name
, opt2name
,)
183 class RunOptions(object):
184 """A place to store meta-options that are used to start the conversion."""
186 # Components of the man page. Attributes set to None here must be set
187 # by subclasses; others may be overridden/augmented by subclasses if
194 u
"C. Michael Pilato <cmpilato@collab.net>",
195 u
"Greg Stein <gstein@lyra.org>",
196 u
"Branko \u010cibej <brane@xbc.nu>",
197 u
"Blair Zajac <blair@orcaware.com>",
198 u
"Max Bowsher <maxb@ukf.net>",
199 u
"Brian Fitzpatrick <fitz@red-bean.com>",
200 u
"Tobias Ringstr\u00f6m <tobias@ringstrom.mine.nu>",
201 u
"Karl Fogel <kfogel@collab.net>",
202 u
"Erik H\u00fclsmann <e.huelsmann@gmx.net>",
203 u
"David Summers <david@summersoft.fay.ar.us>",
204 u
"Michael Haggerty <mhagger@alum.mit.edu>",
208 def __init__(self
, progname
, cmd_args
, pass_manager
):
209 """Process the command-line options, storing run options to SELF.
211 PROGNAME is the name of the program, used in the usage string.
212 CMD_ARGS is the list of command-line arguments passed to the
213 program. PASS_MANAGER is an instance of PassManager, needed to
214 help process the -p and --help-passes options."""
216 self
.progname
= progname
217 self
.cmd_args
= cmd_args
218 self
.pass_manager
= pass_manager
220 self
.end_pass
= self
.pass_manager
.num_passes
221 self
.profiling
= False
225 # A list of one list of SymbolStrategyRules for each project:
226 self
.project_symbol_strategy_rules
= []
228 parser
= self
.parser
= optparse
.OptionParser(
230 description
=self
.get_description(),
231 add_help_option
=False,
233 # A place to record any options used that are incompatible with
235 parser
.set_default('options_incompatible_options', [])
237 # Populate the options parser with the options, one group at a
239 parser
.add_option_group(self
._get
_options
_file
_options
_group
())
240 parser
.add_option_group(self
._get
_output
_options
_group
())
241 parser
.add_option_group(self
._get
_conversion
_options
_group
())
242 parser
.add_option_group(self
._get
_symbol
_handling
_options
_group
())
243 parser
.add_option_group(self
._get
_subversion
_properties
_options
_group
())
244 parser
.add_option_group(self
._get
_extraction
_options
_group
())
245 parser
.add_option_group(self
._get
_environment
_options
_group
())
246 parser
.add_option_group(self
._get
_partial
_conversion
_options
_group
())
247 parser
.add_option_group(self
._get
_information
_options
_group
())
249 (self
.options
, self
.args
) = parser
.parse_args(args
=self
.cmd_args
)
251 # Now the log level has been set; log the time when the run started:
254 'Conversion start time: %Y-%m-%d %I:%M:%S %Z',
255 time
.localtime(logger
.start_time
)
259 if self
.options
.options_file_found
:
260 # Check that no options that are incompatible with --options
262 self
.verify_option_compatibility()
264 # --options was not specified. So do the main initialization
265 # based on other command-line options:
266 self
.process_options()
268 # Check for problems with the options:
271 def get_description(self
):
274 def _get_options_file_options_group(self
):
276 self
.parser
, 'Configuration via options file'
278 self
.parser
.set_default('options_file_found', False)
279 group
.add_option(ManOption(
280 '--options', type='string',
281 action
='callback', callback
=self
.callback_options
,
283 'read the conversion options from PATH. This '
284 'method allows more flexibility than using '
285 'command-line options. See documentation for info'
288 'Read the conversion options from \\fIpath\\fR instead of from '
289 'the command line. This option allows far more conversion '
290 'flexibility than can be achieved using the command-line alone. '
291 'See the documentation for more information. Only the following '
292 'command-line options are allowed in combination with '
293 '\\fB--options\\fR: \\fB-h\\fR/\\fB--help\\fR, '
294 '\\fB--help-passes\\fR, \\fB--version\\fR, '
295 '\\fB-v\\fR/\\fB--verbose\\fR, \\fB-q\\fR/\\fB--quiet\\fR, '
296 '\\fB-p\\fR/\\fB--pass\\fR/\\fB--passes\\fR, \\fB--dry-run\\fR, '
297 '\\fB--profile\\fR, \\fB--trunk-only\\fR, \\fB--encoding\\fR, '
298 'and \\fB--fallback-encoding\\fR. '
299 'Options are processed in the order specified on the command '
306 def _get_output_options_group(self
):
307 group
= OptionGroup(self
.parser
, 'Output options')
310 def _get_conversion_options_group(self
):
311 group
= OptionGroup(self
.parser
, 'Conversion options')
312 group
.add_option(ContextOption(
315 compatible_with_option
=True,
316 help='convert only trunk commits, not tags nor branches',
318 'Convert only trunk commits, not tags nor branches.'
321 group
.add_option(ManOption(
322 '--encoding', type='string',
323 action
='callback', callback
=self
.callback_encoding
,
325 'encoding for paths and log messages in CVS repos. '
326 'If option is specified multiple times, encoders '
327 'are tried in order until one succeeds. See '
328 'http://docs.python.org/lib/standard-encodings.html '
329 'for a list of standard Python encodings.'
332 'Use \\fIencoding\\fR as the encoding for filenames, log '
333 'messages, and author names in the CVS repos. This option '
334 'may be specified multiple times, in which case the encodings '
335 'are tried in order until one succeeds. Default: ascii. See '
336 'http://docs.python.org/lib/standard-encodings.html for a list '
337 'of other standard encodings.'
341 group
.add_option(ManOption(
342 '--fallback-encoding', type='string',
343 action
='callback', callback
=self
.callback_fallback_encoding
,
344 help='If all --encodings fail, use lossy encoding with ENC',
346 'If none of the encodings specified with \\fB--encoding\\fR '
347 'succeed in decoding an author name or log message, then fall '
348 'back to using \\fIencoding\\fR in lossy \'replace\' mode. '
349 'Use of this option may cause information to be lost, but at '
350 'least it allows the conversion to run to completion. This '
351 'option only affects the encoding of log messages and author '
352 'names; there is no fallback encoding for filenames. (By '
353 'using an \\fB--options\\fR file, it is possible to specify '
354 'a fallback encoding for filenames.) Default: disabled.'
358 group
.add_option(ContextOption(
359 '--retain-conflicting-attic-files',
362 'if a file appears both in and out of '
363 'the CVS Attic, then leave the attic version in a '
364 'subdirectory called "Attic"'
367 'If a file appears both inside and outside of the CVS attic, '
368 'retain the attic version in a subdirectory called '
369 '\'Attic\'. (Normally this situation is treated as a fatal '
376 def _get_symbol_handling_options_group(self
):
377 group
= OptionGroup(self
.parser
, 'Symbol handling')
378 self
.parser
.set_default('symbol_transforms', [])
379 group
.add_option(IncompatibleOption(
380 '--symbol-transform', type='string',
381 action
='callback', callback
=self
.callback_symbol_transform
,
383 'transform symbol names from P to S, where P and S '
384 'use Python regexp and reference syntax '
385 'respectively. P must match the whole symbol name'
388 'Transform RCS/CVS symbol names before entering them into the '
389 'output history. \\fIpattern\\fR is a Python regexp pattern that '
390 'is matches against the entire symbol name; \\fIreplacement\\fR '
391 'is a replacement using Python\'s regexp reference syntax. '
392 'You may specify any number of these options; they will be '
393 'applied in the order given on the command line.'
397 self
.parser
.set_default('symbol_strategy_rules', [])
398 group
.add_option(IncompatibleOption(
399 '--symbol-hints', type='string',
400 action
='callback', callback
=self
.callback_symbol_hints
,
401 help='read symbol conversion hints from PATH',
403 'Read symbol conversion hints from \\fIpath\\fR. The format of '
404 '\\fIpath\\fR is the same as the format output by '
405 '\\fB--write-symbol-info\\fR, namely a text file with four '
406 'whitespace-separated columns: \\fIproject-id\\fR, '
407 '\\fIsymbol\\fR, \\fIconversion\\fR, and '
408 '\\fIparent-lod-name\\fR. \\fIproject-id\\fR is the numerical '
409 'ID of the project to which the symbol belongs, counting from '
410 '0. \\fIproject-id\\fR can be set to \'.\' if '
411 'project-specificity is not needed. \\fIsymbol-name\\fR is the '
412 'name of the symbol being specified. \\fIconversion\\fR '
413 'specifies how the symbol should be converted, and can be one '
414 'of the values \'branch\', \'tag\', or \'exclude\'. If '
415 '\\fIconversion\\fR is \'.\', then this rule does not affect '
416 'how the symbol is converted. \\fIparent-lod-name\\fR is the '
417 'name of the symbol from which this symbol should sprout, or '
418 '\'.trunk.\' if the symbol should sprout from trunk. If '
419 '\\fIparent-lod-name\\fR is omitted or \'.\', then this rule '
420 'does not affect the preferred parent of this symbol. The file '
421 'may contain blank lines or comment lines (lines whose first '
422 'non-whitespace character is \'#\').'
426 self
.parser
.set_default('symbol_default', 'heuristic')
427 group
.add_option(IncompatibleOption(
428 '--symbol-default', type='choice',
429 choices
=['heuristic', 'strict', 'branch', 'tag', 'exclude'],
432 'specify how ambiguous symbols are converted. '
433 'OPT is "heuristic" (default), "strict", "branch", '
437 'Specify how to convert ambiguous symbols (those that appear in '
438 'the CVS archive as both branches and tags). \\fIopt\\fR must '
439 'be \'heuristic\' (decide how to treat each ambiguous symbol '
440 'based on whether it was used more often as a branch/tag in '
441 'CVS), \'strict\' (no default; every ambiguous symbol has to be '
442 'resolved manually using \\fB--force-branch\\fR, '
443 '\\fB--force-tag\\fR, or \\fB--exclude\\fR), \'branch\' (treat '
444 'every ambiguous symbol as a branch), \'tag\' (treat every '
445 'ambiguous symbol as a tag), or \'exclude\' (do not convert '
446 'ambiguous symbols). The default is \'heuristic\'.'
450 group
.add_option(IncompatibleOption(
451 '--force-branch', type='string',
452 action
='callback', callback
=self
.callback_force_branch
,
453 help='force symbols matching REGEXP to be branches',
455 'Force symbols whose names match \\fIregexp\\fR to be branches. '
456 '\\fIregexp\\fR must match the whole symbol name.'
460 group
.add_option(IncompatibleOption(
461 '--force-tag', type='string',
462 action
='callback', callback
=self
.callback_force_tag
,
463 help='force symbols matching REGEXP to be tags',
465 'Force symbols whose names match \\fIregexp\\fR to be tags. '
466 '\\fIregexp\\fR must match the whole symbol name.'
470 group
.add_option(IncompatibleOption(
471 '--exclude', type='string',
472 action
='callback', callback
=self
.callback_exclude
,
473 help='exclude branches and tags matching REGEXP',
475 'Exclude branches and tags whose names match \\fIregexp\\fR '
476 'from the conversion. \\fIregexp\\fR must match the whole '
481 self
.parser
.set_default('keep_trivial_imports', False)
482 group
.add_option(IncompatibleOption(
483 '--keep-trivial-imports',
486 'do not exclude branches that were only used for '
487 'a single import (usually these are unneeded)'
490 'Do not exclude branches that were only used for a single '
491 'import. (By default such branches are excluded because they '
492 'are usually created by the inappropriate use of \\fBcvs '
499 def _get_subversion_properties_options_group(self
):
500 group
= OptionGroup(self
.parser
, 'Subversion properties')
502 if self
.DEFAULT_USERNAME
is None:
503 default
= 'The default is to use no author at all for such commits.'
505 default
= 'Default: "%s".' % (self
.DEFAULT_USERNAME
,)
507 group
.add_option(ContextOption(
508 '--username', type='string', default
=self
.DEFAULT_USERNAME
,
510 help='username for synthesized commits. ' + default
,
512 'Set the default username to \\fIname\\fR when this program needs '
513 'to generate a commit for which CVS does not record the '
514 'original username. This happens when a branch or tag is '
520 self
.parser
.set_default('auto_props_files', [])
521 group
.add_option(IncompatibleOption(
522 '--auto-props', type='string',
523 action
='append', dest
='auto_props_files',
525 'set file properties from the auto-props section '
526 'of a file in svn config format'
529 'Specify a file in the format of Subversion\'s config file, '
530 'whose [auto-props] section can be used to set arbitrary '
531 'properties on files in the Subversion repository based on '
532 'their filenames. (The [auto-props] section header must be '
533 'present; other sections of the config file, including the '
534 'enable-auto-props setting, are ignored.) Filenames are matched '
535 'to the filename patterns case-insensitively.'
540 self
.parser
.set_default('mime_types_files', [])
541 group
.add_option(IncompatibleOption(
542 '--mime-types', type='string',
543 action
='append', dest
='mime_types_files',
545 'specify an apache-style mime.types file for setting '
549 'Specify an apache-style mime.types \\fIfile\\fR for setting '
554 self
.parser
.set_default('eol_from_mime_type', False)
555 group
.add_option(IncompatibleOption(
556 '--eol-from-mime-type',
558 help='set svn:eol-style from mime type if known',
560 'For files that don\'t have the kb expansion mode but have a '
561 'known mime type, set the eol-style based on the mime type. '
562 'For such files, set svn:eol-style to "native" if the mime type '
563 'begins with "text/", and leave it unset (i.e., no EOL '
564 'translation) otherwise. Files with unknown mime types are '
565 'not affected by this option. This option has no effect '
566 'unless the \\fB--mime-types\\fR option is also specified.'
569 self
.parser
.set_default('default_eol', 'binary')
570 group
.add_option(IncompatibleOption(
571 '--default-eol', type='choice',
572 choices
=['binary', 'native', 'CRLF', 'LF', 'CR'],
575 'default svn:eol-style for non-binary files with '
576 'undetermined mime types. STYLE is "binary" '
577 '(default), "native", "CRLF", "LF", or "CR"'
580 'Set svn:eol-style to \\fIstyle\\fR for files that don\'t have '
581 'the CVS \'kb\' expansion mode and whose end-of-line '
582 'translation mode hasn\'t been determined by one of the other '
583 'options. \\fIstyle\\fR must be \'binary\' (default), '
584 '\'native\', \'CRLF\', \'LF\', or \'CR\'.'
588 self
.parser
.set_default('keywords_off', False)
589 group
.add_option(IncompatibleOption(
593 'don\'t set svn:keywords on any files (by default, '
594 'cvs2svn sets svn:keywords on non-binary files to "%s")'
595 % (config
.SVN_KEYWORDS_VALUE
,)
598 'By default, cvs2svn sets svn:keywords on CVS files to "author '
599 'id date" if the mode of the RCS file in question is either kv, '
600 'kvl or unset. If you use the --keywords-off switch, cvs2svn '
601 'will not set svn:keywords for any file. While this will not '
602 'touch the keywords in the contents of your files, Subversion '
603 'will not expand them.'
606 group
.add_option(ContextOption(
610 'keep .cvsignore files (in addition to creating '
611 'the analogous svn:ignore properties)'
614 'Include \\fI.cvsignore\\fR files in the output. (Normally '
615 'they are unneeded because cvs2svn sets the corresponding '
616 '\\fIsvn:ignore\\fR properties.)'
619 group
.add_option(IncompatibleOption(
621 action
='callback', callback
=self
.callback_cvs_revnums
,
622 help='record CVS revision numbers as file properties',
624 'Record CVS revision numbers as file properties in the '
625 'Subversion repository. (Note that unless it is removed '
626 'explicitly, the last CVS revision number will remain '
627 'associated with the file even after the file is changed '
628 'within Subversion.)'
632 # Deprecated options:
633 group
.add_option(IncompatibleOption(
635 action
='store_const', dest
='default_eol', const
=None,
636 help=optparse
.SUPPRESS_HELP
,
637 man_help
=optparse
.SUPPRESS_HELP
,
639 self
.parser
.set_default('auto_props_ignore_case', True)
640 # True is the default now, so this option has no effect:
641 group
.add_option(IncompatibleOption(
642 '--auto-props-ignore-case',
644 help=optparse
.SUPPRESS_HELP
,
645 man_help
=optparse
.SUPPRESS_HELP
,
650 def _get_extraction_options_group(self
):
651 group
= OptionGroup(self
.parser
, 'Extraction options')
655 def _add_use_internal_co_option(self
, group
):
656 self
.parser
.set_default('use_internal_co', False)
657 group
.add_option(IncompatibleOption(
661 'use internal code to extract revision contents '
662 '(fastest but disk space intensive) (default)'
665 'Use internal code to extract revision contents. This '
666 'is up to 50% faster than using \\fB--use-rcs\\fR, but needs '
667 'a lot of disk space: roughly the size of your CVS repository '
668 'plus the peak size of a complete checkout of the repository '
669 'with all branches that existed and still had commits pending '
670 'at a given time. This option is the default.'
674 def _add_use_cvs_option(self
, group
):
675 self
.parser
.set_default('use_cvs', False)
676 group
.add_option(IncompatibleOption(
680 'use CVS to extract revision contents (slower than '
681 '--use-internal-co or --use-rcs)'
684 'Use CVS to extract revision contents. This option is slower '
685 'than \\fB--use-internal-co\\fR or \\fB--use-rcs\\fR.'
689 def _add_use_rcs_option(self
, group
):
690 self
.parser
.set_default('use_rcs', False)
691 group
.add_option(IncompatibleOption(
695 'use RCS to extract revision contents (faster than '
696 '--use-cvs but fails in some cases)'
699 'Use RCS \'co\' to extract revision contents. This option is '
700 'faster than \\fB--use-cvs\\fR but fails in some cases.'
704 def _get_environment_options_group(self
):
705 group
= OptionGroup(self
.parser
, 'Environment options')
706 group
.add_option(ContextOption(
707 '--tmpdir', type='string',
710 'directory to use for temporary data files '
711 '(default is to create a temporary subdirectory under %r)'
712 ) % (tempfile
.gettempdir(),),
714 'Set the \\fIpath\\fR to use for temporary data. The default '
715 'is to create a temporary subdirectory under \\fI%s\\fR.'
716 ) % (tempfile
.gettempdir(),),
719 self
.parser
.set_default('co_executable', config
.CO_EXECUTABLE
)
720 group
.add_option(IncompatibleOption(
721 '--co', type='string',
722 action
='store', dest
='co_executable',
723 help='path to the "co" program (required if --use-rcs)',
725 'Path to the \\fIco\\fR program. (\\fIco\\fR is needed if the '
726 '\\fB--use-rcs\\fR option is used.)'
730 self
.parser
.set_default('cvs_executable', config
.CVS_EXECUTABLE
)
731 group
.add_option(IncompatibleOption(
732 '--cvs', type='string',
733 action
='store', dest
='cvs_executable',
734 help='path to the "cvs" program (required if --use-cvs)',
736 'Path to the \\fIcvs\\fR program. (\\fIcvs\\fR is needed if the '
737 '\\fB--use-cvs\\fR option is used.)'
744 def _get_partial_conversion_options_group(self
):
745 group
= OptionGroup(self
.parser
, 'Partial conversions')
746 group
.add_option(ManOption(
747 '--pass', type='string',
748 action
='callback', callback
=self
.callback_passes
,
749 help='execute only specified PASS of conversion',
751 'Execute only pass \\fIpass\\fR of the conversion. '
752 '\\fIpass\\fR can be specified by name or by number (see '
753 '\\fB--help-passes\\fR).'
757 group
.add_option(ManOption(
758 '--passes', '-p', type='string',
759 action
='callback', callback
=self
.callback_passes
,
761 'execute passes START through END, inclusive (PASS, '
762 'START, and END can be pass names or numbers)'
765 'Execute passes \\fIstart\\fR through \\fIend\\fR of the '
766 'conversion (inclusive). \\fIstart\\fR and \\fIend\\fR can be '
767 'specified by name or by number (see \\fB--help-passes\\fR). '
768 'If \\fIstart\\fR or \\fIend\\fR is missing, it defaults to '
769 'the first or last pass, respectively. For this to work the '
770 'earlier passes must have been completed before on the '
771 'same CVS repository, and the generated data files must be '
772 'in the temporary directory (see \\fB--tmpdir\\fR).'
774 metavar
='[START]:[END]',
779 def _get_information_options_group(self
):
780 group
= OptionGroup(self
.parser
, 'Information options')
781 group
.add_option(ManOption(
783 action
='callback', callback
=self
.callback_version
,
784 help='print the version number',
785 man_help
='Print the version number.',
787 group
.add_option(ManOption(
790 help='print this usage message and exit with success',
791 man_help
='Print the usage message and exit with success.',
793 group
.add_option(ManOption(
795 action
='callback', callback
=self
.callback_help_passes
,
796 help='list the available passes and their numbers',
798 'Print the numbers and names of the conversion passes and '
802 group
.add_option(ManOption(
804 action
='callback', callback
=self
.callback_manpage
,
805 help='write the manpage for this program to standard output',
807 'Output the unix-style manpage for this program to standard '
811 group
.add_option(ManOption(
813 action
='callback', callback
=self
.callback_verbose
,
814 help='verbose (may be specified twice for debug output)',
816 'Print more information while running. This option may be '
817 'specified twice to output voluminous debugging information.'
820 group
.add_option(ManOption(
822 action
='callback', callback
=self
.callback_quiet
,
823 help='quiet (may be specified twice for very quiet)',
825 'Print less information while running. This option may be '
826 'specified twice to suppress all non-error output.'
829 group
.add_option(ContextOption(
830 '--write-symbol-info', type='string',
831 action
='store', dest
='symbol_info_filename',
832 help='write information and statistics about CVS symbols to PATH.',
834 'Write to \\fIpath\\fR symbol statistics and information about '
835 'how symbols were converted during CollateSymbolsPass.'
839 group
.add_option(ContextOption(
842 help='prevent the deletion of intermediate files',
843 man_help
='Prevent the deletion of temporary files.',
850 group
.add_option(ManOption(
852 action
='callback', callback
=self
.callback_profile
,
853 help='profile with \'' + prof
+ '\' (into file cvs2svn.' + prof
+ ')',
855 'Profile with \'' + prof
+ '\' (into file \\fIcvs2svn.' + prof
+ '\\fR).'
861 def callback_options(self
, option
, opt_str
, value
, parser
):
862 parser
.values
.options_file_found
= True
863 self
.process_options_file(value
)
865 def callback_encoding(self
, option
, opt_str
, value
, parser
):
869 ctx
.cvs_author_decoder
.add_encoding(value
)
870 ctx
.cvs_log_decoder
.add_encoding(value
)
871 ctx
.cvs_filename_decoder
.add_encoding(value
)
872 except LookupError, e
:
873 raise FatalError(str(e
))
875 def callback_fallback_encoding(self
, option
, opt_str
, value
, parser
):
879 ctx
.cvs_author_decoder
.set_fallback_encoding(value
)
880 ctx
.cvs_log_decoder
.set_fallback_encoding(value
)
881 # Don't use fallback_encoding for filenames.
882 except LookupError, e
:
883 raise FatalError(str(e
))
885 def callback_help_passes(self
, option
, opt_str
, value
, parser
):
886 self
.pass_manager
.help_passes()
889 def callback_manpage(self
, option
, opt_str
, value
, parser
):
890 f
= codecs
.getwriter('utf_8')(sys
.stdout
)
891 writer
= ManWriter(parser
,
893 date
=datetime
.date
.today(),
894 source
='Version %s' % (VERSION
,),
895 manual
='User Commands',
896 short_desc
=self
.short_desc
,
897 synopsis
=self
.synopsis
,
898 long_desc
=self
.long_desc
,
900 authors
=self
.authors
,
901 see_also
=self
.see_also
)
902 writer
.write_manpage(f
)
905 def callback_version(self
, option
, opt_str
, value
, parser
):
907 '%s version %s\n' % (self
.progname
, VERSION
)
911 def callback_verbose(self
, option
, opt_str
, value
, parser
):
912 logger
.increase_verbosity()
914 def callback_quiet(self
, option
, opt_str
, value
, parser
):
915 logger
.decrease_verbosity()
917 def callback_passes(self
, option
, opt_str
, value
, parser
):
918 if value
.find(':') >= 0:
919 start_pass
, end_pass
= value
.split(':')
920 self
.start_pass
= self
.pass_manager
.get_pass_number(start_pass
, 1)
921 self
.end_pass
= self
.pass_manager
.get_pass_number(
922 end_pass
, self
.pass_manager
.num_passes
927 self
.pass_manager
.get_pass_number(value
)
929 def callback_profile(self
, option
, opt_str
, value
, parser
):
930 self
.profiling
= True
932 def callback_symbol_hints(self
, option
, opt_str
, value
, parser
):
933 parser
.values
.symbol_strategy_rules
.append(SymbolHintsFileRule(value
))
935 def callback_force_branch(self
, option
, opt_str
, value
, parser
):
936 parser
.values
.symbol_strategy_rules
.append(
937 ForceBranchRegexpStrategyRule(value
)
940 def callback_force_tag(self
, option
, opt_str
, value
, parser
):
941 parser
.values
.symbol_strategy_rules
.append(
942 ForceTagRegexpStrategyRule(value
)
945 def callback_exclude(self
, option
, opt_str
, value
, parser
):
946 parser
.values
.symbol_strategy_rules
.append(
947 ExcludeRegexpStrategyRule(value
)
950 def callback_cvs_revnums(self
, option
, opt_str
, value
, parser
):
951 Ctx().revision_property_setters
.append(CVSRevisionNumberSetter())
953 def callback_symbol_transform(self
, option
, opt_str
, value
, parser
):
954 [pattern
, replacement
] = value
.split(":")
956 parser
.values
.symbol_transforms
.append(
957 RegexpSymbolTransform(pattern
, replacement
)
960 raise FatalError("'%s' is not a valid regexp." % (pattern
,))
962 # Common to SVNRunOptions, HgRunOptions (GitRunOptions and
963 # BzrRunOptions do not support --use-internal-co, so cannot use this).
964 def process_all_extraction_options(self
):
966 options
= self
.options
968 not_both(options
.use_rcs
, '--use-rcs',
969 options
.use_cvs
, '--use-cvs')
971 not_both(options
.use_rcs
, '--use-rcs',
972 options
.use_internal_co
, '--use-internal-co')
974 not_both(options
.use_cvs
, '--use-cvs',
975 options
.use_internal_co
, '--use-internal-co')
978 ctx
.revision_collector
= NullRevisionCollector()
979 ctx
.revision_reader
= RCSRevisionReader(options
.co_executable
)
980 elif options
.use_cvs
:
981 ctx
.revision_collector
= NullRevisionCollector()
982 ctx
.revision_reader
= CVSRevisionReader(options
.cvs_executable
)
984 # --use-internal-co is the default:
985 ctx
.revision_collector
= InternalRevisionCollector(compress
=True)
986 ctx
.revision_reader
= InternalRevisionReader(compress
=True)
988 def process_symbol_strategy_options(self
):
989 """Process symbol strategy-related options."""
992 options
= self
.options
994 # Add the standard symbol name cleanup rules:
995 self
.options
.symbol_transforms
.extend([
996 ReplaceSubstringsSymbolTransform('\\','/'),
997 # Remove leading, trailing, and repeated slashes:
998 NormalizePathsSymbolTransform(),
1002 if options
.symbol_strategy_rules
or options
.keep_trivial_imports
:
1003 raise SymbolOptionsWithTrunkOnlyException()
1006 if not options
.keep_trivial_imports
:
1007 options
.symbol_strategy_rules
.append(ExcludeTrivialImportBranchRule())
1009 options
.symbol_strategy_rules
.append(UnambiguousUsageRule())
1010 if options
.symbol_default
== 'strict':
1012 elif options
.symbol_default
== 'branch':
1013 options
.symbol_strategy_rules
.append(AllBranchRule())
1014 elif options
.symbol_default
== 'tag':
1015 options
.symbol_strategy_rules
.append(AllTagRule())
1016 elif options
.symbol_default
== 'heuristic':
1017 options
.symbol_strategy_rules
.append(BranchIfCommitsRule())
1018 options
.symbol_strategy_rules
.append(HeuristicStrategyRule())
1019 elif options
.symbol_default
== 'exclude':
1020 options
.symbol_strategy_rules
.append(AllExcludedRule())
1024 # Now add a rule whose job it is to pick the preferred parents of
1025 # branches and tags:
1026 options
.symbol_strategy_rules
.append(HeuristicPreferredParentRule())
1028 def process_property_setter_options(self
):
1029 """Process the options that set SVN properties."""
1032 options
= self
.options
1034 for value
in options
.auto_props_files
:
1035 ctx
.file_property_setters
.append(
1036 AutoPropsPropertySetter(value
, options
.auto_props_ignore_case
)
1039 for value
in options
.mime_types_files
:
1040 ctx
.file_property_setters
.append(MimeMapper(value
))
1042 ctx
.file_property_setters
.append(CVSBinaryFileEOLStyleSetter())
1044 ctx
.file_property_setters
.append(CVSBinaryFileDefaultMimeTypeSetter())
1046 if options
.eol_from_mime_type
:
1047 ctx
.file_property_setters
.append(EOLStyleFromMimeTypeSetter())
1049 ctx
.file_property_setters
.append(
1050 DefaultEOLStyleSetter(options
.default_eol
)
1053 ctx
.file_property_setters
.append(SVNBinaryFileKeywordsPropertySetter())
1055 if not options
.keywords_off
:
1056 ctx
.file_property_setters
.append(
1057 KeywordsPropertySetter(config
.SVN_KEYWORDS_VALUE
)
1060 ctx
.file_property_setters
.append(ExecutablePropertySetter())
1062 ctx
.file_property_setters
.append(DescriptionPropertySetter())
1064 def process_options(self
):
1065 """Do the main configuration based on command-line options.
1067 This method is only called if the --options option was not
1070 raise NotImplementedError()
1072 def check_options(self
):
1073 """Check the the run options are OK.
1075 This should only be called after all options have been processed."""
1077 # Convenience var, so we don't have to keep instantiating this Borg.
1080 if not self
.start_pass
<= self
.end_pass
:
1081 raise InvalidPassError(
1082 'Ending pass must not come before starting pass.')
1084 if not ctx
.dry_run
and ctx
.output_option
is None:
1085 raise FatalError('No output option specified.')
1087 if ctx
.output_option
is not None:
1088 ctx
.output_option
.check()
1090 if not self
.projects
:
1091 raise FatalError('No project specified.')
1093 def verify_option_compatibility(self
):
1094 """Verify that no options incompatible with --options were used.
1096 The --options option was specified. Verify that no incompatible
1097 options or arguments were specified."""
1099 if self
.options
.options_incompatible_options
or self
.args
:
1100 if self
.options
.options_incompatible_options
:
1101 oio
= self
.options
.options_incompatible_options
1103 '%s: The following options cannot be used in combination with '
1107 % (error_prefix
, '\n '.join(oio
))
1111 '%s: No cvs-repos-path arguments are allowed with the --options '
1117 def process_options_file(self
, options_filename
):
1118 """Read options from the file named OPTIONS_FILENAME.
1120 Store the run options to SELF."""
1124 'run_options' : self
,
1126 execfile(options_filename
, g
)
1129 self
.parser
.print_help()