Handle --cvs-revnums via optparse.
[cvs2svn.git] / cvs2svn_lib / run_options.py
blob1533de60a772acc06945641589aa75aa35590e92
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 cvs2svn run options."""
20 import sys
21 import os
22 import re
23 import optparse
24 import time
26 from cvs2svn_lib.version import VERSION
27 from cvs2svn_lib import config
28 from cvs2svn_lib.common import warning_prefix
29 from cvs2svn_lib.common import error_prefix
30 from cvs2svn_lib.common import FatalError
31 from cvs2svn_lib.common import CVSTextDecoder
32 from cvs2svn_lib.log import Log
33 from cvs2svn_lib.context import Ctx
34 from cvs2svn_lib.svn_output_option import DumpfileOutputOption
35 from cvs2svn_lib.svn_output_option import NewRepositoryOutputOption
36 from cvs2svn_lib.svn_output_option import ExistingRepositoryOutputOption
37 from cvs2svn_lib.project import Project
38 from cvs2svn_lib.pass_manager import InvalidPassError
39 from cvs2svn_lib.revision_manager import NullRevisionRecorder
40 from cvs2svn_lib.revision_manager import NullRevisionExcluder
41 from cvs2svn_lib.rcs_revision_manager import RCSRevisionReader
42 from cvs2svn_lib.cvs_revision_manager import CVSRevisionReader
43 from cvs2svn_lib.checkout_internal import InternalRevisionRecorder
44 from cvs2svn_lib.checkout_internal import InternalRevisionExcluder
45 from cvs2svn_lib.checkout_internal import InternalRevisionReader
46 from cvs2svn_lib.symbol_strategy import AllBranchRule
47 from cvs2svn_lib.symbol_strategy import AllTagRule
48 from cvs2svn_lib.symbol_strategy import BranchIfCommitsRule
49 from cvs2svn_lib.symbol_strategy import ExcludeRegexpStrategyRule
50 from cvs2svn_lib.symbol_strategy import ForceBranchRegexpStrategyRule
51 from cvs2svn_lib.symbol_strategy import ForceTagRegexpStrategyRule
52 from cvs2svn_lib.symbol_strategy import ExcludeTrivialImportBranchRule
53 from cvs2svn_lib.symbol_strategy import HeuristicStrategyRule
54 from cvs2svn_lib.symbol_strategy import UnambiguousUsageRule
55 from cvs2svn_lib.symbol_strategy import HeuristicPreferredParentRule
56 from cvs2svn_lib.symbol_strategy import SymbolHintsFileRule
57 from cvs2svn_lib.symbol_strategy import TrunkPathRule
58 from cvs2svn_lib.symbol_strategy import BranchesPathRule
59 from cvs2svn_lib.symbol_strategy import TagsPathRule
60 from cvs2svn_lib.symbol_transform import ReplaceSubstringsSymbolTransform
61 from cvs2svn_lib.symbol_transform import RegexpSymbolTransform
62 from cvs2svn_lib.symbol_transform import NormalizePathsSymbolTransform
63 from cvs2svn_lib.property_setters import AutoPropsPropertySetter
64 from cvs2svn_lib.property_setters import CVSBinaryFileDefaultMimeTypeSetter
65 from cvs2svn_lib.property_setters import CVSBinaryFileEOLStyleSetter
66 from cvs2svn_lib.property_setters import CVSRevisionNumberSetter
67 from cvs2svn_lib.property_setters import DefaultEOLStyleSetter
68 from cvs2svn_lib.property_setters import EOLStyleFromMimeTypeSetter
69 from cvs2svn_lib.property_setters import ExecutablePropertySetter
70 from cvs2svn_lib.property_setters import KeywordsPropertySetter
71 from cvs2svn_lib.property_setters import MimeMapper
72 from cvs2svn_lib.property_setters import SVNBinaryFileKeywordsPropertySetter
75 class GetoptOptions(object):
76 """Backwards compatibility adapter for getopt-style options.
78 optparse-compatible options can be created with the __call__()
79 method. When such an option is seen, it appends (opt, value) tuples
80 to self.opts. These can be processed in a getopt-style option
81 processing loop."""
83 def __init__(self, opts):
84 self.opts = opts
86 def __call__(self, *args, **kw):
87 """Create an optparse-compatible Option object.
89 The arguments are compatible with those of the optparse.Options
90 constructor, except that action is allways set to 'callback' and
91 the callback is always set to self.callback. In particular, if
92 the option should take an argument, then the 'type' keyword
93 argument should be used."""
95 kw['action'] = 'callback'
96 kw['callback'] = self.callback
97 return optparse.Option(*args, **kw)
99 def callback(self, option, opt_str, value, parser):
100 self.opts.append((opt_str, value,))
103 usage = """\
104 Usage: %prog --options OPTIONFILE
105 %prog [OPTION...] OUTPUT-OPTION CVS-REPOS-PATH"""
107 description="""\
108 Convert a CVS repository into a Subversion repository, including history.
112 class IncompatibleOption(optparse.Option):
113 """An optparse.Option that is incompatible with the --options option.
115 Record that the option was used so that error checking can later be
116 done."""
118 def __init__(self, *args, **kw):
119 optparse.Option.__init__(self, *args, **kw)
121 def take_action(self, action, dest, opt, value, values, parser):
122 oio = parser.values.options_incompatible_options
123 if opt not in oio:
124 oio.append(opt)
125 return optparse.Option.take_action(
126 self, action, dest, opt, value, values, parser
130 class ContextOption(optparse.Option):
131 """An optparse.Option that stores its value to Ctx."""
133 def __init__(self, *args, **kw):
134 if kw.get('action') not in self.STORE_ACTIONS:
135 raise ValueError('Invalid action: %s' % (kw['action'],))
137 self.__action = kw.pop('action')
138 try:
139 self.__dest = kw.pop('dest')
140 except KeyError:
141 opt = args[0]
142 if not opt.startswith('--'):
143 raise ValueError
144 self.__dest = opt[2:].replace('-', '_')
145 if 'const' in kw:
146 self.__const = kw.pop('const')
148 kw['action'] = 'callback'
149 kw['callback'] = self.__callback
151 optparse.Option.__init__(self, *args, **kw)
153 def __callback(self, option, opt_str, value, parser):
154 oio = parser.values.options_incompatible_options
155 if opt_str not in oio:
156 oio.append(opt_str)
158 action = self.__action
159 dest = self.__dest
161 if action == "store":
162 setattr(Ctx(), dest, value)
163 elif action == "store_const":
164 setattr(Ctx(), dest, self.__const)
165 elif action == "store_true":
166 setattr(Ctx(), dest, True)
167 elif action == "store_false":
168 setattr(Ctx(), dest, False)
169 elif action == "append":
170 getattr(Ctx(), dest).append(value)
171 elif action == "count":
172 setattr(Ctx(), dest, getattr(Ctx(), dest, 0) + 1)
173 else:
174 raise RuntimeError("unknown action %r" % self.__action)
176 return 1
179 class RunOptions:
180 """A place to store meta-options that are used to start the conversion."""
182 def __init__(self, progname, cmd_args, pass_manager):
183 """Process the command-line options, storing run options to SELF.
185 PROGNAME is the name of the program, used in the usage string.
186 CMD_ARGS is the list of command-line arguments passed to the
187 program. PASS_MANAGER is an instance of PassManager, needed to
188 help process the -p and --help-passes options."""
190 self.pass_manager = pass_manager
191 self.start_pass = 1
192 self.end_pass = self.pass_manager.num_passes
193 self.profiling = False
194 self.progname = progname
196 self.projects = []
198 # A list of one list of SymbolStrategyRules for each project:
199 self.project_symbol_strategy_rules = []
201 self.opts = []
202 go = GetoptOptions(self.opts)
204 parser = self.parser = optparse.OptionParser(
205 usage=usage,
206 description=description,
207 add_help_option=False,
209 # A place to record any options used that are incompatible with
210 # --options:
211 parser.set_default('options_incompatible_options', [])
213 group = parser.add_option_group('Configuration via options file')
214 parser.set_default('options_files', [])
215 group.add_option(
216 '--options', type='string',
217 action='append', dest='options_files',
218 help=(
219 'read the conversion options from PATH. This '
220 'method allows more flexibility than using '
221 'command-line options. See documentation for info'
223 metavar='PATH',
227 group = parser.add_option_group('Output options')
228 group.add_option(IncompatibleOption(
229 '--svnrepos', '-s', type='string',
230 action='store',
231 help='path where SVN repos should be created',
232 metavar='PATH',
234 parser.set_default('existing_svnrepos', False)
235 group.add_option(IncompatibleOption(
236 '--existing-svnrepos',
237 action='store_true',
238 help='load into existing SVN repository (for use with --svnrepos)',
240 group.add_option(IncompatibleOption(
241 '--fs-type', type='string',
242 action='store',
243 help=(
244 'pass --fs-type=TYPE to "svnadmin create" (for use with '
245 '--svnrepos)'
247 metavar='TYPE',
249 parser.set_default('bdb_txn_nosync', False)
250 group.add_option(IncompatibleOption(
251 '--bdb-txn-nosync',
252 action='store_true',
253 help=(
254 'pass --bdb-txn-nosync to "svnadmin create" (for use with '
255 '--svnrepos)'
258 parser.set_default('create_options', [])
259 group.add_option(IncompatibleOption(
260 '--create-option', type='string',
261 action='append', dest='create_options',
262 help='pass OPT to "svnadmin create" (for use with --svnrepos)',
263 metavar='OPT',
265 group.add_option(IncompatibleOption(
266 '--dumpfile', type='string',
267 action='store',
268 help='just produce a dumpfile; don\'t commit to a repos',
269 metavar='PATH',
271 group.add_option(
272 '--dry-run',
273 action='callback', callback=self.callback_dry_run,
274 help=(
275 'do not create a repository or a dumpfile; just print what '
276 'would happen.'
280 # Deprecated options:
281 parser.set_default('dump_only', False)
282 group.add_option(IncompatibleOption(
283 '--dump-only',
284 action='callback', callback=self.callback_dump_only,
285 help=optparse.SUPPRESS_HELP,
287 group.add_option(IncompatibleOption(
288 '--create',
289 action='callback', callback=self.callback_create,
290 help=optparse.SUPPRESS_HELP,
294 group = parser.add_option_group('Conversion options')
295 group.add_option(ContextOption(
296 '--trunk-only',
297 action='store_true',
298 help='convert only trunk commits, not tags nor branches',
300 parser.set_default('trunk_base', config.DEFAULT_TRUNK_BASE)
301 group.add_option(IncompatibleOption(
302 '--trunk', type='string',
303 action='store', dest='trunk_base',
304 help=(
305 'path for trunk (default: %s)'
306 % (config.DEFAULT_TRUNK_BASE,)
308 metavar='PATH',
310 parser.set_default('branches_base', config.DEFAULT_BRANCHES_BASE)
311 group.add_option(IncompatibleOption(
312 '--branches', type='string',
313 action='store', dest='branches_base',
314 help=(
315 'path for branches (default: %s)'
316 % (config.DEFAULT_BRANCHES_BASE,)
318 metavar='PATH',
320 parser.set_default('tags_base', config.DEFAULT_TAGS_BASE)
321 group.add_option(IncompatibleOption(
322 '--tags', type='string',
323 action='store', dest='tags_base',
324 help=(
325 'path for tags (default: %s)'
326 % (config.DEFAULT_TAGS_BASE,)
328 metavar='PATH',
330 group.add_option(ContextOption(
331 '--no-prune',
332 action='store_false', dest='prune',
333 help='don\'t prune empty directories',
335 parser.set_default('encodings', [])
336 group.add_option(IncompatibleOption(
337 '--encoding', type='string',
338 action='append', dest='encodings',
339 help=(
340 'encoding for paths and log messages in CVS repos. '
341 'If option is specified multiple times, encoders '
342 'are tried in order until one succeeds. See '
343 'http://docs.python.org/lib/standard-encodings.html '
344 'for a list of standard Python encodings.'
346 metavar='ENC',
348 group.add_option(IncompatibleOption(
349 '--fallback-encoding', type='string',
350 action='store',
351 help='If all --encodings fail, use lossy encoding with ENC',
352 metavar='ENC',
354 parser.set_default('symbol_transforms', [])
355 group.add_option(IncompatibleOption(
356 '--symbol-transform', type='string',
357 action='callback', callback=self.callback_symbol_transform,
358 help=(
359 'transform symbol names from P to S, where P and S '
360 'use Python regexp and reference syntax '
361 'respectively. P must match the whole symbol name'
363 metavar='P:S',
365 parser.set_default('symbol_strategy_rules', [])
366 group.add_option(IncompatibleOption(
367 '--symbol-hints', type='string',
368 action='callback', callback=self.callback_symbol_hints,
369 help='read symbol conversion hints from PATH',
370 metavar='PATH',
372 parser.set_default('force_branch', False)
373 group.add_option(IncompatibleOption(
374 '--force-branch', type='string',
375 action='callback', callback=self.callback_force_branch,
376 help='force symbols matching REGEXP to be branches',
377 metavar='REGEXP',
379 parser.set_default('force_tag', False)
380 group.add_option(IncompatibleOption(
381 '--force-tag', type='string',
382 action='callback', callback=self.callback_force_tag,
383 help='force symbols matching REGEXP to be tags',
384 metavar='REGEXP',
386 group.add_option(IncompatibleOption(
387 '--exclude', type='string',
388 action='callback', callback=self.callback_exclude,
389 help='exclude branches and tags matching REGEXP',
390 metavar='REGEXP',
392 parser.set_default('keep_trivial_imports', False)
393 group.add_option(IncompatibleOption(
394 '--keep-trivial-imports',
395 action='store_true',
396 help=(
397 'do not exclude branches that were only used for '
398 'a single import (usually these are unneeded)'
401 parser.set_default('symbol_default', 'heuristic')
402 group.add_option(IncompatibleOption(
403 '--symbol-default', type='choice',
404 choices=['heuristic', 'strict', 'branch', 'tag'],
405 help=(
406 'specify how ambiguous symbols are converted. '
407 'OPT is "heuristic" (default), "strict", "branch", '
408 'or "tag"'
410 metavar='OPT',
412 group.add_option(ContextOption(
413 '--keep-cvsignore',
414 action='store_true',
415 help=(
416 'keep .cvsignore files (in addition to creating '
417 'the analogous svn:ignore properties)'
420 group.add_option(ContextOption(
421 '--no-cross-branch-commits',
422 action='store_false', dest='cross_branch_commits',
423 help='prevent the creation of cross-branch commits',
425 group.add_option(ContextOption(
426 '--retain-conflicting-attic-files',
427 action='store_true',
428 help=(
429 'if a file appears both in and out of '
430 'the CVS Attic, then leave the attic version in a '
431 'SVN directory called "Attic"'
434 group.add_option(ContextOption(
435 '--username', type='string',
436 action='store',
437 help='username for cvs2svn-synthesized commits',
438 metavar='NAME',
440 group.add_option(IncompatibleOption(
441 '--cvs-revnums',
442 action='callback', callback=self.callback_cvs_revnums,
443 help='record CVS revision numbers as file properties',
445 parser.set_default('mime_types_files', [])
446 group.add_option(IncompatibleOption(
447 '--mime-types', type='string',
448 action='append', dest='mime_types_files',
449 help=(
450 'specify an apache-style mime.types file for setting '
451 'svn:mime-type'
453 metavar='FILE',
455 parser.set_default('eol_from_mime_type', False)
456 group.add_option(IncompatibleOption(
457 '--eol-from-mime-type',
458 action='store_true',
459 help='set svn:eol-style from mime type if known',
461 parser.set_default('auto_props_files', [])
462 group.add_option(IncompatibleOption(
463 '--auto-props', type='string',
464 action='append', dest='auto_props_files',
465 help=(
466 'set file properties from the auto-props section '
467 'of a file in svn config format'
469 metavar='FILE',
471 group.add_option(IncompatibleOption(
472 '--default-eol', type='choice',
473 choices=['binary', 'native', 'CRLF', 'LF', 'CR'],
474 help=(
475 'default svn:eol-style for non-binary files with '
476 'undetermined mime types. VALUE is "binary" '
477 '(default), "native", "CRLF", "LF", or "CR"'
479 metavar='VALUE',
481 parser.set_default('keywords_off', False)
482 group.add_option(IncompatibleOption(
483 '--keywords-off',
484 action='store_true',
485 help=(
486 'don\'t set svn:keywords on any files (by default, '
487 'cvs2svn sets svn:keywords on non-binary files to "%s")'
488 % (config.SVN_KEYWORDS_VALUE,)
492 # Deprecated options:
493 group.add_option(IncompatibleOption(
494 '--no-default-eol',
495 action='store_const', dest='default_eol', const=None,
496 help=optparse.SUPPRESS_HELP,
498 parser.set_default('auto_props_ignore_case', True)
499 # True is the default now, so this option has no effect:
500 group.add_option(IncompatibleOption(
501 '--auto-props-ignore-case',
502 action='store_true',
503 help=optparse.SUPPRESS_HELP,
507 group = parser.add_option_group('Extraction options')
508 parser.set_default('use_rcs', False)
509 group.add_option(IncompatibleOption(
510 '--use-rcs',
511 action='store_true',
512 help='use RCS to extract revision contents',
514 parser.set_default('use_cvs', False)
515 group.add_option(IncompatibleOption(
516 '--use-cvs',
517 action='store_true',
518 help=(
519 'use CVS to extract revision contents '
520 '(only use this if having problems with RCS)'
523 parser.set_default('use_internal_co', False)
524 group.add_option(IncompatibleOption(
525 '--use-internal-co',
526 action='store_true',
527 help=(
528 'use internal code to extract revision contents '
529 '(very fast but disk space intensive) (default)'
534 group = parser.add_option_group('Environment options')
535 group.add_option(ContextOption(
536 '--tmpdir', type='string',
537 action='store',
538 help=(
539 'directory to use for temporary data files '
540 '(default "cvs2svn-tmp")'
542 metavar='PATH',
544 group.add_option(ContextOption(
545 '--svnadmin', type='string',
546 action='store', dest='svnadmin_executable',
547 help='path to the "svnadmin" program',
548 metavar='PATH',
550 parser.set_default('co_executable', config.CO_EXECUTABLE)
551 group.add_option(IncompatibleOption(
552 '--co', type='string',
553 action='store', dest='co_executable',
554 help='path to the "co" program (required if --use-rcs)',
555 metavar='PATH',
557 parser.set_default('cvs_executable', config.CVS_EXECUTABLE)
558 group.add_option(IncompatibleOption(
559 '--cvs', type='string',
560 action='store', dest='cvs_executable',
561 help='path to the "cvs" program (required if --use-cvs)',
562 metavar='PATH',
564 group.add_option(ContextOption(
565 '--sort', type='string',
566 action='store', dest='sort_executable',
567 help='path to the GNU "sort" program',
568 metavar='PATH',
572 group = parser.add_option_group('Partial conversions')
573 group.add_option(
574 '--pass', type='string',
575 action='callback', callback=self.callback_passes,
576 help='execute only specified PASS of conversion',
577 metavar='PASS',
579 group.add_option(
580 '--passes', '-p', type='string',
581 action='callback', callback=self.callback_passes,
582 help=(
583 'execute passes START through END, inclusive (PASS, '
584 'START, and END can be pass names or numbers)'
586 metavar='[START]:[END]',
590 group = parser.add_option_group('Information options')
591 group.add_option(
592 '--version',
593 action='callback', callback=self.callback_version,
594 help='print the version number',
596 group.add_option(
597 '--help', '-h',
598 action="help",
599 help='print this usage message and exit with success',
601 group.add_option(
602 '--help-passes',
603 action='callback', callback=self.callback_help_passes,
604 help='list the available passes and their numbers',
606 group.add_option(
607 '--verbose', '-v',
608 action='callback', callback=self.callback_verbose,
609 help='verbose (may be specified twice for debug output)',
611 group.add_option(
612 '--quiet', '-q',
613 action='callback', callback=self.callback_quiet,
614 help='quiet (may be specified twice for very quiet)',
616 group.add_option(ContextOption(
617 '--write-symbol-info', type='string',
618 action='store', dest='symbol_info_filename',
619 help='write information and statistics about CVS symbols to PATH.',
620 metavar='PATH',
622 group.add_option(ContextOption(
623 '--skip-cleanup',
624 action='store_true',
625 help='prevent the deletion of intermediate files',
627 group.add_option(
628 '--profile',
629 action='callback', callback=self.callback_profile,
630 help='profile with \'hotshot\' (into file cvs2svn.hotshot)',
633 (self.options, self.args) = parser.parse_args()
635 # Next look for any --options options, process them, and remove
636 # them from the list, as they affect the processing of other
637 # options:
638 options_file_found = False
639 for value in self.options.options_files:
640 self.process_options_file(value)
641 options_file_found = True
643 # Now the log level has been set; log the time when the run started:
644 Log().verbose(
645 time.strftime(
646 'Conversion start time: %Y-%m-%d %I:%M:%S %Z',
647 time.localtime(Log().start_time)
651 if options_file_found:
652 # All of the options that are compatible with --options have
653 # been consumed above. It is an error if any other options or
654 # arguments are left:
655 self.verify_options_consumed()
656 else:
657 # --options was not specified. So we can process other options
658 # that are not compatible with --options.
659 self.process_remaining_options()
661 # Check for problems with the options:
662 self.check_options()
664 def add_project(
665 self,
666 project_cvs_repos_path,
667 trunk_path=None, branches_path=None, tags_path=None,
668 initial_directories=[],
669 symbol_transforms=None,
670 symbol_strategy_rules=[],
672 """Add a project to be converted.
674 Most arguments are passed straight through to the Project
675 constructor. SYMBOL_STRATEGY_RULES is an iterable of
676 SymbolStrategyRules that will be applied to symbols in this
677 project."""
679 initial_directories = [
680 path
681 for path in [trunk_path, branches_path, tags_path]
682 if path
683 ] + list(initial_directories)
685 symbol_strategy_rules = list(symbol_strategy_rules)
687 # Add rules to set the SVN paths for LODs depending on whether
688 # they are the trunk, tags, or branches:
689 if trunk_path is not None:
690 symbol_strategy_rules.append(TrunkPathRule(trunk_path))
691 if branches_path is not None:
692 symbol_strategy_rules.append(BranchesPathRule(branches_path))
693 if tags_path is not None:
694 symbol_strategy_rules.append(TagsPathRule(tags_path))
696 id = len(self.projects)
697 project = Project(
699 project_cvs_repos_path,
700 initial_directories=initial_directories,
701 symbol_transforms=symbol_transforms,
704 self.projects.append(project)
705 self.project_symbol_strategy_rules.append(symbol_strategy_rules)
707 def clear_projects(self):
708 """Clear the list of projects to be converted.
710 This method is for the convenience of options files, which may
711 want to import one another."""
713 del self.projects[:]
714 del self.project_symbol_strategy_rules[:]
716 def callback_help_passes(self, option, opt_str, value, parser):
717 self.pass_manager.help_passes()
718 sys.exit(0)
720 def callback_version(self, option, opt_str, value, parser):
721 sys.stdout.write(
722 '%s version %s\n' % (os.path.basename(self.progname), VERSION)
724 sys.exit(0)
726 def callback_verbose(self, option, opt_str, value, parser):
727 Log().increase_verbosity()
729 def callback_quiet(self, option, opt_str, value, parser):
730 Log().decrease_verbosity()
732 def callback_passes(self, option, opt_str, value, parser):
733 if value.find(':') >= 0:
734 start_pass, end_pass = value.split(':')
735 self.start_pass = self.pass_manager.get_pass_number(start_pass, 1)
736 self.end_pass = self.pass_manager.get_pass_number(
737 end_pass, self.pass_manager.num_passes
739 else:
740 self.end_pass = \
741 self.start_pass = \
742 self.pass_manager.get_pass_number(value)
744 def callback_dry_run(self, option, opt_str, value, parser):
745 Ctx().dry_run = True
747 def callback_profile(self, option, opt_str, value, parser):
748 self.profiling = True
750 def callback_dump_only(self, option, opt_str, value, parser):
751 parser.values.dump_only = True
752 Log().error(
753 warning_prefix +
754 ': The --dump-only option is deprecated (it is implied '
755 'by --dumpfile).\n'
758 def callback_create(self, option, opt_str, value, parser):
759 Log().error(
760 warning_prefix +
761 ': The behaviour produced by the --create option is now the '
762 'default;\n'
763 'passing the option is deprecated.\n'
766 def callback_symbol_hints(self, option, opt_str, value, parser):
767 parser.values.symbol_strategy_rules.append(SymbolHintsFileRule(value))
769 def callback_force_branch(self, option, opt_str, value, parser):
770 parser.values.symbol_strategy_rules.append(
771 ForceBranchRegexpStrategyRule(value)
773 parser.values.force_branch = True
775 def callback_force_tag(self, option, opt_str, value, parser):
776 parser.values.symbol_strategy_rules.append(
777 ForceTagRegexpStrategyRule(value)
779 parser.values.force_tag = True
781 def callback_exclude(self, option, opt_str, value, parser):
782 parser.values.symbol_strategy_rules.append(
783 ExcludeRegexpStrategyRule(value)
786 def callback_cvs_revnums(self, option, opt_str, value, parser):
787 Ctx().svn_property_setters.append(CVSRevisionNumberSetter())
789 def callback_symbol_transform(self, option, opt_str, value, parser):
790 [pattern, replacement] = value.split(":")
791 try:
792 parser.values.symbol_transforms.append(
793 RegexpSymbolTransform(pattern, replacement)
795 except re.error:
796 raise FatalError("'%s' is not a valid regexp." % (pattern,))
798 def process_remaining_options(self):
799 """Process the options that are not compatible with --options."""
801 # Convenience var, so we don't have to keep instantiating this Borg.
802 ctx = Ctx()
804 options = self.options
806 # Consistency check for options and arguments.
807 if len(self.args) == 0:
808 self.usage()
809 sys.exit(1)
811 if len(self.args) > 1:
812 Log().error(error_prefix + ": must pass only one CVS repository.\n")
813 self.usage()
814 sys.exit(1)
816 cvsroot = self.args[0]
818 if options.dump_only and not options.dumpfile:
819 raise FatalError("'--dump-only' requires '--dumpfile' to be specified.")
821 if not options.svnrepos and not options.dumpfile and not ctx.dry_run:
822 raise FatalError("must pass one of '-s' or '--dumpfile'.")
824 def not_both(opt1val, opt1name, opt2val, opt2name):
825 if opt1val and opt2val:
826 raise FatalError("cannot pass both '%s' and '%s'."
827 % (opt1name, opt2name,))
829 not_both(options.svnrepos, '-s',
830 options.dumpfile, '--dumpfile')
832 not_both(options.dumpfile, '--dumpfile',
833 options.existing_svnrepos, '--existing-svnrepos')
835 not_both(options.bdb_txn_nosync, '--bdb-txn-nosync',
836 options.existing_svnrepos, '--existing-svnrepos')
838 not_both(options.dumpfile, '--dumpfile',
839 options.bdb_txn_nosync, '--bdb-txn-nosync')
841 not_both(options.fs_type, '--fs-type',
842 options.existing_svnrepos, '--existing-svnrepos')
844 not_both(options.use_rcs, '--use-rcs',
845 options.use_cvs, '--use-cvs')
847 not_both(options.use_rcs, '--use-rcs',
848 options.use_internal_co, '--use-internal-co')
850 not_both(options.use_cvs, '--use-cvs',
851 options.use_internal_co, '--use-internal-co')
853 not_both(ctx.trunk_only, '--trunk-only',
854 options.force_branch, '--force-branch')
856 not_both(ctx.trunk_only, '--trunk-only',
857 options.force_tag, '--force-tag')
859 if (
860 options.fs_type
861 and options.fs_type != 'bdb'
862 and options.bdb_txn_nosync
864 raise FatalError("cannot pass --bdb-txn-nosync with --fs-type=%s."
865 % options.fs_type)
867 if options.svnrepos:
868 if options.existing_svnrepos:
869 ctx.output_option = ExistingRepositoryOutputOption(options.svnrepos)
870 else:
871 ctx.output_option = NewRepositoryOutputOption(
872 options.svnrepos,
873 fs_type=options.fs_type, bdb_txn_nosync=options.bdb_txn_nosync,
874 create_options=options.create_options)
875 else:
876 ctx.output_option = DumpfileOutputOption(options.dumpfile)
878 if options.use_rcs:
879 ctx.revision_recorder = NullRevisionRecorder()
880 ctx.revision_excluder = NullRevisionExcluder()
881 ctx.revision_reader = RCSRevisionReader(options.co_executable)
882 elif options.use_cvs:
883 ctx.revision_recorder = NullRevisionRecorder()
884 ctx.revision_excluder = NullRevisionExcluder()
885 ctx.revision_reader = CVSRevisionReader(options.cvs_executable)
886 else:
887 # --use-internal-co is the default:
888 ctx.revision_recorder = InternalRevisionRecorder(compress=True)
889 ctx.revision_excluder = InternalRevisionExcluder()
890 ctx.revision_reader = InternalRevisionReader(compress=True)
892 if 'ascii' not in options.encodings:
893 options.encodings.append('ascii')
895 try:
896 ctx.cvs_author_decoder = CVSTextDecoder(
897 options.encodings, options.fallback_encoding
899 ctx.cvs_log_decoder = CVSTextDecoder(
900 options.encodings, options.fallback_encoding
902 # Don't use fallback_encoding for filenames:
903 ctx.cvs_filename_decoder = CVSTextDecoder(options.encodings)
904 except LookupError, e:
905 raise FatalError(str(e))
907 # Add the standard symbol name cleanup rules:
908 options.symbol_transforms.extend([
909 ReplaceSubstringsSymbolTransform('\\','/'),
910 # Remove leading, trailing, and repeated slashes:
911 NormalizePathsSymbolTransform(),
914 if not options.keep_trivial_imports:
915 options.symbol_strategy_rules.append(ExcludeTrivialImportBranchRule())
917 options.symbol_strategy_rules.append(UnambiguousUsageRule())
918 if options.symbol_default == 'strict':
919 pass
920 elif options.symbol_default == 'branch':
921 options.symbol_strategy_rules.append(AllBranchRule())
922 elif options.symbol_default == 'tag':
923 options.symbol_strategy_rules.append(AllTagRule())
924 elif options.symbol_default == 'heuristic':
925 options.symbol_strategy_rules.append(BranchIfCommitsRule())
926 options.symbol_strategy_rules.append(HeuristicStrategyRule())
927 else:
928 assert False
930 # Now add a rule whose job it is to pick the preferred parents of
931 # branches and tags:
932 options.symbol_strategy_rules.append(HeuristicPreferredParentRule())
934 for value in options.auto_props_files:
935 ctx.svn_property_setters.append(
936 AutoPropsPropertySetter(value, options.auto_props_ignore_case)
939 for value in options.mime_types_files:
940 ctx.svn_property_setters.append(MimeMapper(value))
942 ctx.svn_property_setters.append(CVSBinaryFileEOLStyleSetter())
944 ctx.svn_property_setters.append(CVSBinaryFileDefaultMimeTypeSetter())
946 if options.eol_from_mime_type:
947 ctx.svn_property_setters.append(EOLStyleFromMimeTypeSetter())
949 ctx.svn_property_setters.append(
950 DefaultEOLStyleSetter(options.default_eol)
953 ctx.svn_property_setters.append(SVNBinaryFileKeywordsPropertySetter())
955 if not options.keywords_off:
956 ctx.svn_property_setters.append(
957 KeywordsPropertySetter(config.SVN_KEYWORDS_VALUE))
959 ctx.svn_property_setters.append(ExecutablePropertySetter())
961 # Create the default project (using ctx.trunk, ctx.branches, and
962 # ctx.tags):
963 self.add_project(
964 cvsroot,
965 trunk_path=options.trunk_base,
966 branches_path=options.branches_base,
967 tags_path=options.tags_base,
968 symbol_transforms=options.symbol_transforms,
969 symbol_strategy_rules=options.symbol_strategy_rules,
972 def check_options(self):
973 """Check the the run options are OK.
975 This should only be called after all options have been processed."""
977 # Convenience var, so we don't have to keep instantiating this Borg.
978 ctx = Ctx()
980 if not self.start_pass <= self.end_pass:
981 raise InvalidPassError(
982 'Ending pass must not come before starting pass.')
984 if not ctx.dry_run and ctx.output_option is None:
985 raise FatalError('No output option specified.')
987 if ctx.output_option is not None:
988 ctx.output_option.check()
990 if not self.projects:
991 raise FatalError('No project specified.')
993 def get_options(self, *names):
994 """Return a list of (option,value) pairs for options in NAMES.
996 Return a list containing any (opt, value) pairs from self.opts
997 where opt is in NAMES. The matching options are removed from
998 self.opts."""
1000 retval = []
1001 i = 0
1002 while i < len(self.opts):
1003 (opt, value) = self.opts[i]
1004 if opt in names:
1005 del self.opts[i]
1006 retval.append( (opt, value) )
1007 else:
1008 i += 1
1009 return retval
1011 def verify_options_consumed(self):
1012 """Verify that all command line options and arguments have been used.
1014 The --options option was specified, and all options that are
1015 compatible with that option have already been consumed. Verify
1016 that there are no remaining (i.e., incompatible) options or
1017 arguments."""
1019 if self.options.options_incompatible_options or self.opts or self.args:
1020 if self.options.options_incompatible_options or self.opts:
1021 oio = (
1022 self.options.options_incompatible_options
1023 + [opt for (opt,value) in self.opts]
1025 Log().error(
1026 '%s: The following options cannot be used in combination with '
1027 'the --options\n'
1028 'option:\n'
1029 ' %s\n'
1030 % (error_prefix, '\n '.join(oio))
1032 if self.args:
1033 Log().error(
1034 '%s: No cvs-repos-path arguments are allowed with the --options '
1035 'option.\n'
1036 % (error_prefix,)
1038 sys.exit(1)
1040 def process_options_file(self, options_filename):
1041 """Read options from the file named OPTIONS_FILENAME.
1043 Store the run options to SELF."""
1045 g = {}
1046 l = {
1047 'ctx' : Ctx(),
1048 'run_options' : self,
1050 execfile(options_filename, g, l)
1052 def usage(self):
1053 self.parser.print_help()