Allow the --trunk-only option to be combined with the --options option.
[cvs2svn.git] / cvs2svn_lib / run_options.py
blob68bc05d35f059db7232e2db67139dd7e752a1483
1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2000-2007 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."""
19 import sys
20 import re
21 import optparse
22 from optparse import OptionGroup
23 import time
25 from cvs2svn_lib.version import VERSION
26 from cvs2svn_lib import config
27 from cvs2svn_lib.common import warning_prefix
28 from cvs2svn_lib.common import error_prefix
29 from cvs2svn_lib.common import FatalError
30 from cvs2svn_lib.common import CVSTextDecoder
31 from cvs2svn_lib.log import Log
32 from cvs2svn_lib.context import Ctx
33 from cvs2svn_lib.man_writer import ManOption
34 from cvs2svn_lib.pass_manager import InvalidPassError
35 from cvs2svn_lib.symbol_strategy import AllBranchRule
36 from cvs2svn_lib.symbol_strategy import AllTagRule
37 from cvs2svn_lib.symbol_strategy import BranchIfCommitsRule
38 from cvs2svn_lib.symbol_strategy import ExcludeRegexpStrategyRule
39 from cvs2svn_lib.symbol_strategy import ForceBranchRegexpStrategyRule
40 from cvs2svn_lib.symbol_strategy import ForceTagRegexpStrategyRule
41 from cvs2svn_lib.symbol_strategy import ExcludeTrivialImportBranchRule
42 from cvs2svn_lib.symbol_strategy import HeuristicStrategyRule
43 from cvs2svn_lib.symbol_strategy import UnambiguousUsageRule
44 from cvs2svn_lib.symbol_strategy import HeuristicPreferredParentRule
45 from cvs2svn_lib.symbol_strategy import SymbolHintsFileRule
46 from cvs2svn_lib.symbol_transform import ReplaceSubstringsSymbolTransform
47 from cvs2svn_lib.symbol_transform import RegexpSymbolTransform
48 from cvs2svn_lib.symbol_transform import NormalizePathsSymbolTransform
49 from cvs2svn_lib.property_setters import AutoPropsPropertySetter
50 from cvs2svn_lib.property_setters import CVSBinaryFileDefaultMimeTypeSetter
51 from cvs2svn_lib.property_setters import CVSBinaryFileEOLStyleSetter
52 from cvs2svn_lib.property_setters import CVSRevisionNumberSetter
53 from cvs2svn_lib.property_setters import DefaultEOLStyleSetter
54 from cvs2svn_lib.property_setters import EOLStyleFromMimeTypeSetter
55 from cvs2svn_lib.property_setters import ExecutablePropertySetter
56 from cvs2svn_lib.property_setters import KeywordsPropertySetter
57 from cvs2svn_lib.property_setters import MimeMapper
58 from cvs2svn_lib.property_setters import SVNBinaryFileKeywordsPropertySetter
61 usage = """\
62 Usage: %prog --options OPTIONFILE
63 %prog [OPTION...] OUTPUT-OPTION CVS-REPOS-PATH"""
65 description="""\
66 Convert a CVS repository into a Subversion repository, including history.
67 """
69 authors = u"""\
70 Main authors are:
71 .br
72 C. Michael Pilato <cmpilato@collab.net>
73 .br
74 Greg Stein <gstein@lyra.org>
75 .br
76 Branko \u010cibej <brane@xbc.nu>
77 .br
78 Blair Zajac <blair@orcaware.com>
79 .br
80 Max Bowsher <maxb@ukf.net>
81 .br
82 Brian Fitzpatrick <fitz@red-bean.com>
83 .br
84 Tobias Ringstr\u00f6m <tobias@ringstrom.mine.nu>
85 .br
86 Karl Fogel <kfogel@collab.net>
87 .br
88 Erik H\u00fclsmann <e.huelsmann@gmx.net>
89 .br
90 David Summers <david@summersoft.fay.ar.us>
91 .br
92 Michael Haggerty <mhagger@alum.mit.edu>
93 .PP
94 Manpage was written for the Debian GNU/Linux system by
95 Laszlo 'GCS' Boszormenyi <gcs@lsc.hu> (but may be used by others).
96 """
99 class IncompatibleOption(ManOption):
100 """A ManOption that is incompatible with the --options option.
102 Record that the option was used so that error checking can later be
103 done."""
105 def __init__(self, *args, **kw):
106 ManOption.__init__(self, *args, **kw)
108 def take_action(self, action, dest, opt, value, values, parser):
109 oio = parser.values.options_incompatible_options
110 if opt not in oio:
111 oio.append(opt)
112 return ManOption.take_action(
113 self, action, dest, opt, value, values, parser
117 class ContextOption(ManOption):
118 """A ManOption that stores its value to Ctx."""
120 def __init__(self, *args, **kw):
121 if kw.get('action') not in self.STORE_ACTIONS:
122 raise ValueError('Invalid action: %s' % (kw['action'],))
124 self.__compatible_with_option = kw.pop('compatible_with_option', False)
125 self.__action = kw.pop('action')
126 try:
127 self.__dest = kw.pop('dest')
128 except KeyError:
129 opt = args[0]
130 if not opt.startswith('--'):
131 raise ValueError
132 self.__dest = opt[2:].replace('-', '_')
133 if 'const' in kw:
134 self.__const = kw.pop('const')
136 kw['action'] = 'callback'
137 kw['callback'] = self.__callback
139 ManOption.__init__(self, *args, **kw)
141 def __callback(self, option, opt_str, value, parser):
142 if not self.__compatible_with_option:
143 oio = parser.values.options_incompatible_options
144 if opt_str not in oio:
145 oio.append(opt_str)
147 action = self.__action
148 dest = self.__dest
150 if action == "store":
151 setattr(Ctx(), dest, value)
152 elif action == "store_const":
153 setattr(Ctx(), dest, self.__const)
154 elif action == "store_true":
155 setattr(Ctx(), dest, True)
156 elif action == "store_false":
157 setattr(Ctx(), dest, False)
158 elif action == "append":
159 getattr(Ctx(), dest).append(value)
160 elif action == "count":
161 setattr(Ctx(), dest, getattr(Ctx(), dest, 0) + 1)
162 else:
163 raise RuntimeError("unknown action %r" % self.__action)
165 return 1
168 class IncompatibleOptionsException(FatalError):
169 pass
172 # Options that are not allowed to be used with --trunk-only:
173 SYMBOL_OPTIONS = [
174 '--symbol-transform',
175 '--symbol-hints',
176 '--force-branch',
177 '--force-tag',
178 '--exclude',
179 '--keep-trivial-imports',
180 '--symbol-default',
181 '--no-cross-branch-commits',
184 class SymbolOptionsWithTrunkOnlyException(IncompatibleOptionsException):
185 def __init__(self):
186 IncompatibleOptionsException.__init__(
187 self,
188 'The following symbol-related options cannot be used together\n'
189 'with --trunk-only:\n'
190 ' %s'
191 % ('\n '.join(SYMBOL_OPTIONS),)
195 def not_both(opt1val, opt1name, opt2val, opt2name):
196 """Raise an exception if both opt1val and opt2val are set."""
197 if opt1val and opt2val:
198 raise IncompatibleOptionsException(
199 "cannot pass both '%s' and '%s'." % (opt1name, opt2name,)
203 class RunOptions(object):
204 """A place to store meta-options that are used to start the conversion."""
206 def __init__(self, progname, cmd_args, pass_manager):
207 """Process the command-line options, storing run options to SELF.
209 PROGNAME is the name of the program, used in the usage string.
210 CMD_ARGS is the list of command-line arguments passed to the
211 program. PASS_MANAGER is an instance of PassManager, needed to
212 help process the -p and --help-passes options."""
214 self.progname = progname
215 self.cmd_args = cmd_args
216 self.pass_manager = pass_manager
217 self.start_pass = 1
218 self.end_pass = self.pass_manager.num_passes
219 self.profiling = False
221 self.projects = []
223 # A list of one list of SymbolStrategyRules for each project:
224 self.project_symbol_strategy_rules = []
226 parser = self.parser = optparse.OptionParser(
227 usage=usage,
228 description=description,
229 add_help_option=False,
231 # A place to record any options used that are incompatible with
232 # --options:
233 parser.set_default('options_incompatible_options', [])
235 # Populate the options parser with the options, one group at a
236 # time:
237 parser.add_option_group(self._get_options_file_options_group())
238 parser.add_option_group(self._get_output_options_group())
239 parser.add_option_group(self._get_conversion_options_group())
240 parser.add_option_group(self._get_symbol_handling_options_group())
241 parser.add_option_group(self._get_subversion_properties_options_group())
242 parser.add_option_group(self._get_extraction_options_group())
243 parser.add_option_group(self._get_environment_options_group())
244 parser.add_option_group(self._get_partial_conversion_options_group())
245 parser.add_option_group(self._get_information_options_group())
247 (self.options, self.args) = parser.parse_args(args=self.cmd_args)
249 # Now the log level has been set; log the time when the run started:
250 Log().verbose(
251 time.strftime(
252 'Conversion start time: %Y-%m-%d %I:%M:%S %Z',
253 time.localtime(Log().start_time)
257 if self.options.options_file_found:
258 # Check that no options that are incompatible with --options
259 # were used:
260 self.verify_option_compatibility()
261 else:
262 # --options was not specified. So do the main initialization
263 # based on other command-line options:
264 self.process_options()
266 # Check for problems with the options:
267 self.check_options()
269 def _get_options_file_options_group(self):
270 group = OptionGroup(
271 self.parser, 'Configuration via options file'
273 self.parser.set_default('options_file_found', False)
274 group.add_option(ManOption(
275 '--options', type='string',
276 action='callback', callback=self.callback_options,
277 help=(
278 'read the conversion options from PATH. This '
279 'method allows more flexibility than using '
280 'command-line options. See documentation for info'
282 man_help=(
283 'Read the conversion options from \\fIpath\\fR instead of from '
284 'the command line. This option allows far more conversion '
285 'flexibility than can be achieved using the command-line alone. '
286 'See the documentation for more information. Only the following '
287 'command-line options are allowed in combination with '
288 '\\fB--options\\fR: \\fB-h\\fR/\\fB--help\\fR, '
289 '\\fB--help-passes\\fR, \\fB--version\\fR, '
290 '\\fB-v\\fR/\\fB--verbose\\fR, \\fB-q\\fR/\\fB--quiet\\fR, '
291 '\\fB-p\\fR/\\fB--pass\\fR/\\fB--passes\\fR, \\fB--dry-run\\fR, '
292 '\\fB--profile\\fR, \\fB--sort\\fR, and \\fB--trunk-only\\fR. '
293 'Options are processed in the order specified on the command '
294 'line.'
296 metavar='PATH',
298 return group
300 def _get_output_options_group(self):
301 group = OptionGroup(self.parser, 'Output options')
302 return group
304 def _get_conversion_options_group(self):
305 group = OptionGroup(self.parser, 'Conversion options')
306 group.add_option(ContextOption(
307 '--trunk-only',
308 action='store_true',
309 compatible_with_option=True,
310 help='convert only trunk commits, not tags nor branches',
311 man_help=(
312 'Convert only trunk commits, not tags nor branches.'
315 self.parser.set_default('encodings', [])
316 group.add_option(IncompatibleOption(
317 '--encoding', type='string',
318 action='append', dest='encodings',
319 help=(
320 'encoding for paths and log messages in CVS repos. '
321 'If option is specified multiple times, encoders '
322 'are tried in order until one succeeds. See '
323 'http://docs.python.org/lib/standard-encodings.html '
324 'for a list of standard Python encodings.'
326 man_help=(
327 'Use \\fIencoding\\fR as the encoding for filenames, log '
328 'messages, and author names in the CVS repos. This option '
329 'may be specified multiple times, in which case the encodings '
330 'are tried in order until one succeeds. Default: ascii. See '
331 'http://docs.python.org/lib/standard-encodings.html for a list '
332 'of other standard encodings.'
334 metavar='ENC',
336 group.add_option(IncompatibleOption(
337 '--fallback-encoding', type='string',
338 action='store',
339 help='If all --encodings fail, use lossy encoding with ENC',
340 man_help=(
341 'If none of the encodings specified with \\fB--encoding\\fR '
342 'succeed in decoding an author name or log message, then fall '
343 'back to using \\fIencoding\\fR in lossy \'replace\' mode. '
344 'Use of this option may cause information to be lost, but at '
345 'least it allows the conversion to run to completion. This '
346 'option only affects the encoding of log messages and author '
347 'names; there is no fallback encoding for filenames. (By '
348 'using an \\fB--options\\fR file, it is possible to specify '
349 'a fallback encoding for filenames.) Default: disabled.'
351 metavar='ENC',
353 group.add_option(ContextOption(
354 '--retain-conflicting-attic-files',
355 action='store_true',
356 help=(
357 'if a file appears both in and out of '
358 'the CVS Attic, then leave the attic version in a '
359 'SVN directory called "Attic"'
361 man_help=(
362 'If a file appears both inside an outside of the CVS attic, '
363 'retain the attic version in an SVN subdirectory called '
364 '\'Attic\'. (Normally this situation is treated as a fatal '
365 'error.)'
369 return group
371 def _get_symbol_handling_options_group(self):
372 group = OptionGroup(self.parser, 'Symbol handling')
373 self.parser.set_default('symbol_transforms', [])
374 group.add_option(IncompatibleOption(
375 '--symbol-transform', type='string',
376 action='callback', callback=self.callback_symbol_transform,
377 help=(
378 'transform symbol names from P to S, where P and S '
379 'use Python regexp and reference syntax '
380 'respectively. P must match the whole symbol name'
382 man_help=(
383 'Transform RCS/CVS symbol names before entering them into '
384 'Subversion. \\fIpattern\\fR is a Python regexp pattern that '
385 'is matches against the entire symbol name; \\fIreplacement\\fR '
386 'is a replacement using Python\'s regexp reference syntax. '
387 'You may specify any number of these options; they will be '
388 'applied in the order given on the command line.'
390 metavar='P:S',
392 self.parser.set_default('symbol_strategy_rules', [])
393 group.add_option(IncompatibleOption(
394 '--symbol-hints', type='string',
395 action='callback', callback=self.callback_symbol_hints,
396 help='read symbol conversion hints from PATH',
397 man_help=(
398 'Read symbol conversion hints from \\fIpath\\fR. The format of '
399 '\\fIpath\\fR is the same as the format output by '
400 '\\fB--write-symbol-info\\fR, namely a text file with four '
401 'whitespace-separated columns: \\fIproject-id\\fR, '
402 '\\fIsymbol\\fR, \\fIconversion\\fR, and '
403 '\\fIparent-lod-name\\fR. \\fIproject-id\\fR is the numerical '
404 'ID of the project to which the symbol belongs, counting from '
405 '0. \\fIproject-id\\fR can be set to \'.\' if '
406 'project-specificity is not needed. \\fIsymbol-name\\fR is the '
407 'name of the symbol being specified. \\fIconversion\\fR '
408 'specifies how the symbol should be converted, and can be one '
409 'of the values \'branch\', \'tag\', or \'exclude\'. If '
410 '\\fIconversion\\fR is \'.\', then this rule does not affect '
411 'how the symbol is converted. \\fIparent-lod-name\\fR is the '
412 'name of the symbol from which this symbol should sprout, or '
413 '\'.trunk.\' if the symbol should sprout from trunk. If '
414 '\\fIparent-lod-name\\fR is omitted or \'.\', then this rule '
415 'does not affect the preferred parent of this symbol. The file '
416 'may contain blank lines or comment lines (lines whose first '
417 'non-whitespace character is \'#\').'
419 metavar='PATH',
421 self.parser.set_default('symbol_default', 'heuristic')
422 group.add_option(IncompatibleOption(
423 '--symbol-default', type='choice',
424 choices=['heuristic', 'strict', 'branch', 'tag'],
425 action='store',
426 help=(
427 'specify how ambiguous symbols are converted. '
428 'OPT is "heuristic" (default), "strict", "branch", '
429 'or "tag"'
431 man_help=(
432 'Specify how to convert ambiguous symbols (those that appear in '
433 'the CVS archive as both branches and tags). \\fIopt\\fR must '
434 'be \'heuristic\' (decide how to treat each ambiguous symbol '
435 'based on whether it was used more often as a branch/tag in '
436 'CVS), \'strict\' (no default; every ambiguous symbol has to be '
437 'resolved manually using \\fB--force-branch\\fR, '
438 '\\fB--force-tag\\fR, or \\fB--exclude\\fR), \'branch\' (treat '
439 'every ambiguous symbol as a branch), or \'tag\' (treat every '
440 'ambiguous symbol as a tag). The default is \'heuristic\'.'
442 metavar='OPT',
444 group.add_option(IncompatibleOption(
445 '--force-branch', type='string',
446 action='callback', callback=self.callback_force_branch,
447 help='force symbols matching REGEXP to be branches',
448 man_help=(
449 'Force symbols whose names match \\fIregexp\\fR to be branches. '
450 '\\fIregexp\\fR must match the whole symbol name.'
452 metavar='REGEXP',
454 group.add_option(IncompatibleOption(
455 '--force-tag', type='string',
456 action='callback', callback=self.callback_force_tag,
457 help='force symbols matching REGEXP to be tags',
458 man_help=(
459 'Force symbols whose names match \\fIregexp\\fR to be tags. '
460 '\\fIregexp\\fR must match the whole symbol name.'
462 metavar='REGEXP',
464 group.add_option(IncompatibleOption(
465 '--exclude', type='string',
466 action='callback', callback=self.callback_exclude,
467 help='exclude branches and tags matching REGEXP',
468 man_help=(
469 'Exclude branches and tags whose names match \\fIregexp\\fR '
470 'from the conversion. \\fIregexp\\fR must match the whole '
471 'symbol name.'
473 metavar='REGEXP',
475 self.parser.set_default('keep_trivial_imports', False)
476 group.add_option(IncompatibleOption(
477 '--keep-trivial-imports',
478 action='store_true',
479 help=(
480 'do not exclude branches that were only used for '
481 'a single import (usually these are unneeded)'
483 man_help=(
484 'Do not exclude branches that were only used for a single '
485 'import. (By default such branches are excluded because they '
486 'are usually created by the inappropriate use of \\fBcvs '
487 'import\\fR.)'
491 return group
493 def _get_subversion_properties_options_group(self):
494 group = OptionGroup(self.parser, 'Subversion properties')
495 group.add_option(ContextOption(
496 '--username', type='string',
497 action='store',
498 help='username for cvs2svn-synthesized commits',
499 man_help=(
500 'Set the default username to \\fIname\\fR when cvs2svn needs '
501 'to generate a commit for which CVS does not record the '
502 'original username. This happens when a branch or tag is '
503 'created. The default is to use no author at all for such '
504 'commits.'
506 metavar='NAME',
508 self.parser.set_default('auto_props_files', [])
509 group.add_option(IncompatibleOption(
510 '--auto-props', type='string',
511 action='append', dest='auto_props_files',
512 help=(
513 'set file properties from the auto-props section '
514 'of a file in svn config format'
516 man_help=(
517 'Specify a file in the format of Subversion\'s config file, '
518 'whose [auto-props] section can be used to set arbitrary '
519 'properties on files in the Subversion repository based on '
520 'their filenames. (The [auto-props] section header must be '
521 'present; other sections of the config file, including the '
522 'enable-auto-props setting, are ignored.) Filenames are matched '
523 'to the filename patterns case-insensitively.'
526 metavar='FILE',
528 self.parser.set_default('mime_types_files', [])
529 group.add_option(IncompatibleOption(
530 '--mime-types', type='string',
531 action='append', dest='mime_types_files',
532 help=(
533 'specify an apache-style mime.types file for setting '
534 'svn:mime-type'
536 man_help=(
537 'Specify an apache-style mime.types \\fIfile\\fR for setting '
538 'svn:mime-type.'
540 metavar='FILE',
542 self.parser.set_default('eol_from_mime_type', False)
543 group.add_option(IncompatibleOption(
544 '--eol-from-mime-type',
545 action='store_true',
546 help='set svn:eol-style from mime type if known',
547 man_help=(
548 'For files that don\'t have the kb expansion mode but have a '
549 'known mime type, set the eol-style based on the mime type. '
550 'For such files, set svn:eol-style to "native" if the mime type '
551 'begins with "text/", and leave it unset (i.e., no EOL '
552 'translation) otherwise. Files with unknown mime types are '
553 'not affected by this option. This option has no effect '
554 'unless the \\fB--mime-types\\fR option is also specified.'
557 group.add_option(IncompatibleOption(
558 '--default-eol', type='choice',
559 choices=['binary', 'native', 'CRLF', 'LF', 'CR'],
560 action='store',
561 help=(
562 'default svn:eol-style for non-binary files with '
563 'undetermined mime types. STYLE is "binary" '
564 '(default), "native", "CRLF", "LF", or "CR"'
566 man_help=(
567 'Set svn:eol-style to \\fIstyle\\fR for files that don\'t have '
568 'the CVS \'kb\' expansion mode and whose end-of-line '
569 'translation mode hasn\'t been determined by one of the other '
570 'options. \\fIstyle\\fR must be \'binary\' (default), '
571 '\'native\', \'CRLF\', \'LF\', or \'CR\'.'
573 metavar='STYLE',
575 self.parser.set_default('keywords_off', False)
576 group.add_option(IncompatibleOption(
577 '--keywords-off',
578 action='store_true',
579 help=(
580 'don\'t set svn:keywords on any files (by default, '
581 'cvs2svn sets svn:keywords on non-binary files to "%s")'
582 % (config.SVN_KEYWORDS_VALUE,)
584 man_help=(
585 'By default, cvs2svn sets svn:keywords on CVS files to "author '
586 'id date" if the mode of the RCS file in question is either kv, '
587 'kvl or unset. If you use the --keywords-off switch, cvs2svn '
588 'will not set svn:keywords for any file. While this will not '
589 'touch the keywords in the contents of your files, Subversion '
590 'will not expand them.'
593 group.add_option(ContextOption(
594 '--keep-cvsignore',
595 action='store_true',
596 help=(
597 'keep .cvsignore files (in addition to creating '
598 'the analogous svn:ignore properties)'
600 man_help=(
601 'Include \\fI.cvsignore\\fR files in the output. (Normally '
602 'they are unneeded because cvs2svn sets the corresponding '
603 '\\fIsvn:ignore\\fR properties.)'
606 group.add_option(IncompatibleOption(
607 '--cvs-revnums',
608 action='callback', callback=self.callback_cvs_revnums,
609 help='record CVS revision numbers as file properties',
610 man_help=(
611 'Record CVS revision numbers as file properties in the '
612 'Subversion repository. (Note that unless it is removed '
613 'explicitly, the last CVS revision number will remain '
614 'associated with the file even after the file is changed '
615 'within Subversion.)'
619 # Deprecated options:
620 group.add_option(IncompatibleOption(
621 '--no-default-eol',
622 action='store_const', dest='default_eol', const=None,
623 help=optparse.SUPPRESS_HELP,
624 man_help=optparse.SUPPRESS_HELP,
626 self.parser.set_default('auto_props_ignore_case', True)
627 # True is the default now, so this option has no effect:
628 group.add_option(IncompatibleOption(
629 '--auto-props-ignore-case',
630 action='store_true',
631 help=optparse.SUPPRESS_HELP,
632 man_help=optparse.SUPPRESS_HELP,
635 return group
637 def _get_extraction_options_group(self):
638 group = OptionGroup(self.parser, 'Extraction options')
640 return group
642 def _get_environment_options_group(self):
643 group = OptionGroup(self.parser, 'Environment options')
644 group.add_option(ContextOption(
645 '--tmpdir', type='string',
646 action='store',
647 help=(
648 'directory to use for temporary data files '
649 '(default "cvs2svn-tmp")'
651 man_help=(
652 'Set the \\fIpath\\fR to use for temporary data. Default '
653 'is a directory called \\fIcvs2svn-tmp\\fR under the current '
654 'directory.'
656 metavar='PATH',
658 self.parser.set_default('co_executable', config.CO_EXECUTABLE)
659 group.add_option(IncompatibleOption(
660 '--co', type='string',
661 action='store', dest='co_executable',
662 help='path to the "co" program (required if --use-rcs)',
663 man_help=(
664 'Path to the \\fIco\\fR program. (\\fIco\\fR is needed if the '
665 '\\fB--use-rcs\\fR option is used.)'
667 metavar='PATH',
669 self.parser.set_default('cvs_executable', config.CVS_EXECUTABLE)
670 group.add_option(IncompatibleOption(
671 '--cvs', type='string',
672 action='store', dest='cvs_executable',
673 help='path to the "cvs" program (required if --use-cvs)',
674 man_help=(
675 'Path to the \\fIcvs\\fR program. (\\fIcvs\\fR is needed if the '
676 '\\fB--use-cvs\\fR option is used.)'
678 metavar='PATH',
680 group.add_option(ContextOption(
681 '--sort', type='string',
682 action='store', dest='sort_executable',
683 compatible_with_option=True,
684 help='path to the GNU "sort" program',
685 man_help=(
686 'Path to the GNU \\fIsort\\fR program. (cvs2svn requires GNU '
687 'sort.)'
689 metavar='PATH',
692 return group
694 def _get_partial_conversion_options_group(self):
695 group = OptionGroup(self.parser, 'Partial conversions')
696 group.add_option(ManOption(
697 '--pass', type='string',
698 action='callback', callback=self.callback_passes,
699 help='execute only specified PASS of conversion',
700 man_help=(
701 'Execute only pass \\fIpass\\fR of the conversion. '
702 '\\fIpass\\fR can be specified by name or by number (see '
703 '\\fB--help-passes\\fR).'
705 metavar='PASS',
707 group.add_option(ManOption(
708 '--passes', '-p', type='string',
709 action='callback', callback=self.callback_passes,
710 help=(
711 'execute passes START through END, inclusive (PASS, '
712 'START, and END can be pass names or numbers)'
714 man_help=(
715 'Execute passes \\fIstart\\fR through \\fIend\\fR of the '
716 'conversion (inclusive). \\fIstart\\fR and \\fIend\\fR can be '
717 'specified by name or by number (see \\fB--help-passes\\fR). '
718 'If \\fIstart\\fR or \\fIend\\fR is missing, it defaults to '
719 'the first or last pass, respectively. For this to work the '
720 'earlier passes must have been completed before on the '
721 'same CVS repository, and the generated data files must be '
722 'in the temporary directory (see \\fB--tmpdir\\fR).'
724 metavar='[START]:[END]',
727 return group
729 def _get_information_options_group(self):
730 group = OptionGroup(self.parser, 'Information options')
731 group.add_option(ManOption(
732 '--version',
733 action='callback', callback=self.callback_version,
734 help='print the version number',
735 man_help='Print the version number.',
737 group.add_option(ManOption(
738 '--help', '-h',
739 action="help",
740 help='print this usage message and exit with success',
741 man_help='Print the usage message and exit with success.',
743 group.add_option(ManOption(
744 '--help-passes',
745 action='callback', callback=self.callback_help_passes,
746 help='list the available passes and their numbers',
747 man_help=(
748 'Print the numbers and names of the conversion passes and '
749 'exit with success.'
752 group.add_option(ManOption(
753 '--man',
754 action='callback', callback=self.callback_manpage,
755 help='write the manpage for this program to standard output',
756 man_help=(
757 'Output the unix-style manpage for this program to standard '
758 'output.'
761 group.add_option(ManOption(
762 '--verbose', '-v',
763 action='callback', callback=self.callback_verbose,
764 help='verbose (may be specified twice for debug output)',
765 man_help=(
766 'Print more information while running. This option may be '
767 'specified twice to output voluminous debugging information.'
770 group.add_option(ManOption(
771 '--quiet', '-q',
772 action='callback', callback=self.callback_quiet,
773 help='quiet (may be specified twice for very quiet)',
774 man_help=(
775 'Print less information while running. This option may be '
776 'specified twice to suppress all non-error output.'
779 group.add_option(ContextOption(
780 '--write-symbol-info', type='string',
781 action='store', dest='symbol_info_filename',
782 help='write information and statistics about CVS symbols to PATH.',
783 man_help=(
784 'Write to \\fIpath\\fR symbol statistics and information about '
785 'how symbols were converted during CollateSymbolsPass.'
787 metavar='PATH',
789 group.add_option(ContextOption(
790 '--skip-cleanup',
791 action='store_true',
792 help='prevent the deletion of intermediate files',
793 man_help='Prevent the deletion of temporary files.',
795 group.add_option(ManOption(
796 '--profile',
797 action='callback', callback=self.callback_profile,
798 help='profile with \'hotshot\' (into file cvs2svn.hotshot)',
799 man_help=(
800 'Profile with \'hotshot\' (into file \\fIcvs2svn.hotshot\\fR).'
804 return group
806 def callback_options(self, option, opt_str, value, parser):
807 parser.values.options_file_found = True
808 self.process_options_file(value)
810 def callback_help_passes(self, option, opt_str, value, parser):
811 self.pass_manager.help_passes()
812 sys.exit(0)
814 def callback_manpage(self, option, opt_str, value, parser):
815 raise NotImplementedError()
817 def callback_version(self, option, opt_str, value, parser):
818 sys.stdout.write(
819 '%s version %s\n' % (self.progname, VERSION)
821 sys.exit(0)
823 def callback_verbose(self, option, opt_str, value, parser):
824 Log().increase_verbosity()
826 def callback_quiet(self, option, opt_str, value, parser):
827 Log().decrease_verbosity()
829 def callback_passes(self, option, opt_str, value, parser):
830 if value.find(':') >= 0:
831 start_pass, end_pass = value.split(':')
832 self.start_pass = self.pass_manager.get_pass_number(start_pass, 1)
833 self.end_pass = self.pass_manager.get_pass_number(
834 end_pass, self.pass_manager.num_passes
836 else:
837 self.end_pass = \
838 self.start_pass = \
839 self.pass_manager.get_pass_number(value)
841 def callback_profile(self, option, opt_str, value, parser):
842 self.profiling = True
844 def callback_symbol_hints(self, option, opt_str, value, parser):
845 parser.values.symbol_strategy_rules.append(SymbolHintsFileRule(value))
847 def callback_force_branch(self, option, opt_str, value, parser):
848 parser.values.symbol_strategy_rules.append(
849 ForceBranchRegexpStrategyRule(value)
852 def callback_force_tag(self, option, opt_str, value, parser):
853 parser.values.symbol_strategy_rules.append(
854 ForceTagRegexpStrategyRule(value)
857 def callback_exclude(self, option, opt_str, value, parser):
858 parser.values.symbol_strategy_rules.append(
859 ExcludeRegexpStrategyRule(value)
862 def callback_cvs_revnums(self, option, opt_str, value, parser):
863 Ctx().svn_property_setters.append(CVSRevisionNumberSetter())
865 def callback_symbol_transform(self, option, opt_str, value, parser):
866 [pattern, replacement] = value.split(":")
867 try:
868 parser.values.symbol_transforms.append(
869 RegexpSymbolTransform(pattern, replacement)
871 except re.error:
872 raise FatalError("'%s' is not a valid regexp." % (pattern,))
874 def process_encoding_options(self):
875 """Process options related to encoding/decoding character data."""
877 ctx = Ctx()
879 if 'ascii' not in self.options.encodings:
880 self.options.encodings.append('ascii')
882 try:
883 ctx.cvs_author_decoder = CVSTextDecoder(
884 self.options.encodings, self.options.fallback_encoding
886 ctx.cvs_log_decoder = CVSTextDecoder(
887 self.options.encodings, self.options.fallback_encoding
889 # Don't use fallback_encoding for filenames:
890 ctx.cvs_filename_decoder = CVSTextDecoder(self.options.encodings)
891 except LookupError, e:
892 raise FatalError(str(e))
894 def process_symbol_strategy_options(self):
895 """Process symbol strategy-related options."""
897 ctx = Ctx()
898 options = self.options
900 # Add the standard symbol name cleanup rules:
901 self.options.symbol_transforms.extend([
902 ReplaceSubstringsSymbolTransform('\\','/'),
903 # Remove leading, trailing, and repeated slashes:
904 NormalizePathsSymbolTransform(),
907 if ctx.trunk_only:
908 if options.symbol_strategy_rules or options.keep_trivial_imports:
909 raise SymbolOptionsWithTrunkOnlyException()
911 else:
912 if not options.keep_trivial_imports:
913 options.symbol_strategy_rules.append(ExcludeTrivialImportBranchRule())
915 options.symbol_strategy_rules.append(UnambiguousUsageRule())
916 if options.symbol_default == 'strict':
917 pass
918 elif options.symbol_default == 'branch':
919 options.symbol_strategy_rules.append(AllBranchRule())
920 elif options.symbol_default == 'tag':
921 options.symbol_strategy_rules.append(AllTagRule())
922 elif options.symbol_default == 'heuristic':
923 options.symbol_strategy_rules.append(BranchIfCommitsRule())
924 options.symbol_strategy_rules.append(HeuristicStrategyRule())
925 else:
926 assert False
928 # Now add a rule whose job it is to pick the preferred parents of
929 # branches and tags:
930 options.symbol_strategy_rules.append(HeuristicPreferredParentRule())
932 def process_property_setter_options(self):
933 """Process the options that set SVN properties."""
935 ctx = Ctx()
936 options = self.options
938 for value in options.auto_props_files:
939 ctx.svn_property_setters.append(
940 AutoPropsPropertySetter(value, options.auto_props_ignore_case)
943 for value in options.mime_types_files:
944 ctx.svn_property_setters.append(MimeMapper(value))
946 ctx.svn_property_setters.append(CVSBinaryFileEOLStyleSetter())
948 ctx.svn_property_setters.append(CVSBinaryFileDefaultMimeTypeSetter())
950 if options.eol_from_mime_type:
951 ctx.svn_property_setters.append(EOLStyleFromMimeTypeSetter())
953 ctx.svn_property_setters.append(
954 DefaultEOLStyleSetter(options.default_eol)
957 ctx.svn_property_setters.append(SVNBinaryFileKeywordsPropertySetter())
959 if not options.keywords_off:
960 ctx.svn_property_setters.append(
961 KeywordsPropertySetter(config.SVN_KEYWORDS_VALUE))
963 ctx.svn_property_setters.append(ExecutablePropertySetter())
965 def process_options(self):
966 """Do the main configuration based on command-line options.
968 This method is only called if the --options option was not
969 specified."""
971 raise NotImplementedError()
973 def check_options(self):
974 """Check the the run options are OK.
976 This should only be called after all options have been processed."""
978 # Convenience var, so we don't have to keep instantiating this Borg.
979 ctx = Ctx()
981 if not self.start_pass <= self.end_pass:
982 raise InvalidPassError(
983 'Ending pass must not come before starting pass.')
985 if not ctx.dry_run and ctx.output_option is None:
986 raise FatalError('No output option specified.')
988 if ctx.output_option is not None:
989 ctx.output_option.check()
991 if not self.projects:
992 raise FatalError('No project specified.')
994 def verify_option_compatibility(self):
995 """Verify that no options incompatible with --options were used.
997 The --options option was specified. Verify that no incompatible
998 options or arguments were specified."""
1000 if self.options.options_incompatible_options or self.args:
1001 if self.options.options_incompatible_options:
1002 oio = self.options.options_incompatible_options
1003 Log().error(
1004 '%s: The following options cannot be used in combination with '
1005 'the --options\n'
1006 'option:\n'
1007 ' %s\n'
1008 % (error_prefix, '\n '.join(oio))
1010 if self.args:
1011 Log().error(
1012 '%s: No cvs-repos-path arguments are allowed with the --options '
1013 'option.\n'
1014 % (error_prefix,)
1016 sys.exit(1)
1018 def process_options_file(self, options_filename):
1019 """Read options from the file named OPTIONS_FILENAME.
1021 Store the run options to SELF."""
1023 g = {}
1024 l = {
1025 'ctx' : Ctx(),
1026 'run_options' : self,
1028 execfile(options_filename, g, l)
1030 def usage(self):
1031 self.parser.print_help()