def callback_dump_only(self, option, opt_str, value, parser): parser.values.dump_only = True logger.error( warning_prefix + ': The --dump-only option is deprecated (it is implied ' 'by --dumpfile).\n' )
def callback_create(self, option, opt_str, value, parser): logger.error( warning_prefix + ': The behaviour produced by the --create option is now the ' 'default;\n' 'passing the option is deprecated.\n' )
def process_options(self): # Consistency check for options and arguments. if len(self.args) == 0: # Default to using '.' as the source repository path self.args.append(os.getcwd()) if len(self.args) > 1: logger.error(error_prefix + ": must pass only one CVS repository.\n") self.usage() sys.exit(1) cvsroot = self.args[0] self.process_extraction_options() self.process_output_options() self.process_symbol_strategy_options() self.process_property_setter_options() # Create the project: self.set_project( cvsroot, symbol_transforms=self.options.symbol_transforms, symbol_strategy_rules=self.options.symbol_strategy_rules, )
def _check_blocked_excludes(self, symbol_map): """Check for any excluded LODs that are blocked by non-excluded symbols. If any are found, describe the problem to logger.error() and raise a FatalException.""" # A list of (lod,[blocker,...]) tuples for excludes that are # blocked by the specified non-excluded blockers: problems = [] for lod in symbol_map.itervalues(): if isinstance(lod, ExcludedSymbol): # Symbol is excluded; make sure that its blockers are also # excluded: lod_blockers = [] for blocker in self.get_stats(lod).branch_blockers: if isinstance(symbol_map.get(blocker, None), IncludedSymbol): lod_blockers.append(blocker) if lod_blockers: problems.append((lod, lod_blockers)) if problems: s = [] for (lod, lod_blockers) in problems: s.append( '%s: %s cannot be excluded because the following symbols ' 'depend on it:\n' % (error_prefix, lod,) ) for blocker in lod_blockers: s.append(' %s\n' % (blocker,)) s.append('\n') logger.error(''.join(s)) raise FatalException()
def process_options(self): # Consistency check for options and arguments. if len(self.args) == 0: self.usage() sys.exit(1) if len(self.args) > 1: logger.error(error_prefix + ": must pass only one CVS repository.\n") self.usage() sys.exit(1) cvsroot = self.args[0] self.process_extraction_options() self.process_output_options() self.process_symbol_strategy_options() self.process_property_setter_options() # Create the default project (using ctx.trunk, ctx.branches, and # ctx.tags): self.add_project( cvsroot, trunk_path=self.options.trunk_base, branches_path=self.options.branches_base, tags_path=self.options.tags_base, symbol_transforms=self.options.symbol_transforms, symbol_strategy_rules=self.options.symbol_strategy_rules, )
def _check_invalid_tags(self, symbol_map): """Check for commits on any symbols that are to be converted as tags. SYMBOL_MAP is a map {AbstractSymbol : (Trunk|TypedSymbol)} indicating how each AbstractSymbol is to be converted. If there is a commit on a symbol, then it cannot be converted as a tag. If any tags with commits are found, output error messages describing the problems then raise a FatalException.""" logger.quiet("Checking for forced tags with commits...") invalid_tags = [ ] for symbol in symbol_map.itervalues(): if isinstance(symbol, Tag): stats = self.get_stats(symbol) if stats.branch_commit_count > 0: invalid_tags.append(symbol) if not invalid_tags: # No problems found: return s = [] s.append( '%s: The following branches cannot be forced to be tags ' 'because they have commits:\n' % (error_prefix,) ) for tag in invalid_tags: s.append(' %s\n' % (tag.name)) s.append('\n') logger.error(''.join(s)) raise FatalException()
def _check_invalid_tags(self, symbol_map): """Check for commits on any symbols that are to be converted as tags. SYMBOL_MAP is a map {AbstractSymbol : (Trunk|TypedSymbol)} indicating how each AbstractSymbol is to be converted. If there is a commit on a symbol, then it cannot be converted as a tag. If any tags with commits are found, output error messages describing the problems then raise a FatalException.""" logger.quiet("Checking for forced tags with commits...") invalid_tags = [] for symbol in symbol_map.itervalues(): if isinstance(symbol, Tag): stats = self.get_stats(symbol) if stats.branch_commit_count > 0: invalid_tags.append(symbol) if not invalid_tags: # No problems found: return s = [] s.append('%s: The following branches cannot be forced to be tags ' 'because they have commits:\n' % (error_prefix, )) for tag in invalid_tags: s.append(' %s\n' % (tag.name)) s.append('\n') logger.error(''.join(s)) raise FatalException()
def set_revision_info(self, revision, log, text): if revision in self.revisions_seen: # One common form of CVS repository corruption is that the # Deltatext block for revision 1.1 appears twice. CollectData # has already warned about this problem; here we can just ignore # it. return else: self.revisions_seen.add(revision) cvs_rev_id = self.cvs_file_items.original_ids[revision] if is_trunk_revision(revision): # On trunk, revisions are encountered in reverse order (1.<N> # ... 1.1) and deltas are inverted. The first text that we see # is the fulltext for the HEAD revision. After that, the text # corresponding to revision 1.N is the delta (1.<N+1> -> # 1.<N>)). We have to invert the deltas here so that we can # read the revisions out in dependency order; that is, for # revision 1.1 we want the fulltext, and for revision 1.<N> we # want the delta (1.<N-1> -> 1.<N>). This means that we can't # compute the delta for a revision until we see its logical # parent. When we finally see revision 1.1 (which is recognized # because it doesn't have a parent), we can record the diff (1.1 # -> 1.2) for revision 1.2, and also the fulltext for 1.1. if revision == self.head_revision: # This is HEAD, as fulltext. Initialize the RCSStream so # that we can compute deltas backwards in time. self._rcs_stream = RCSStream(text) self._rcs_stream_revision = revision else: # Any other trunk revision is a backward delta. Apply the # delta to the RCSStream to mutate it to the contents of this # revision, and also to get the reverse delta, which we store # as the forward delta of our child revision. try: text = self._rcs_stream.invert_diff(text) except MalformedDeltaException, e: logger.error( 'Malformed RCS delta in %s, revision %s: %s' % (self.cvs_file_items.cvs_file.rcs_path, revision, e)) raise RuntimeError() text_record = DeltaTextRecord( self.cvs_file_items.original_ids[ self._rcs_stream_revision], cvs_rev_id) self.revision_collector._writeout(text_record, text) self._rcs_stream_revision = revision if revision == self.revision_1_1: # This is revision 1.1. Write its fulltext: text_record = FullTextRecord(cvs_rev_id) self.revision_collector._writeout(text_record, self._rcs_stream.get_text()) # There will be no more trunk revisions delivered, so free the # RCSStream. del self._rcs_stream del self._rcs_stream_revision
def define_revision(self, revision, timestamp, author, state, branches, next): """This is a callback method declared in Sink.""" for branch in branches: try: branch_data = self.sdc.rev_to_branch_data(branch) except KeyError: # Normally we learn about the branches from the branch names # and numbers parsed from the symbolic name header. But this # must have been an unlabeled branch that slipped through the # net. Generate a name for it and create a _BranchData record # for it now. branch_data = self.sdc._add_unlabeled_branch( self.sdc.rev_to_branch_number(branch)) assert branch_data.child is None branch_data.child = branch if revision in self._rev_data: # This revision has already been seen. logger.error( 'File %r contains duplicate definitions of revision %s.' % ( self.cvs_file.rcs_path, revision, )) raise RuntimeError() # Record basic information about the revision: rev_data = _RevisionData(self.collect_data.item_key_generator.gen_id(), revision, int(timestamp), author, state) self._rev_data[revision] = rev_data # When on trunk, the RCS 'next' revision number points to what # humans might consider to be the 'previous' revision number. For # example, 1.3's RCS 'next' is 1.2. # # However, on a branch, the RCS 'next' revision number really does # point to what humans would consider to be the 'next' revision # number. For example, 1.1.2.1's RCS 'next' would be 1.1.2.2. # # In other words, in RCS, 'next' always means "where to find the next # deltatext that you need this revision to retrieve. # # That said, we don't *want* RCS's behavior here, so we determine # whether we're on trunk or a branch and set the dependencies # accordingly. if next: if is_trunk_revision(revision): self._primary_dependencies.append(( next, revision, )) else: self._primary_dependencies.append(( revision, next, ))
def record_fatal_error(self, err): """Record that fatal error ERR was found. ERR is a string (without trailing newline) describing the error. Output the error to stderr immediately, and record a copy to be output again in a summary at the end of CollectRevsPass.""" err = '%s: %s' % (error_prefix, err,) logger.error(err + '\n') self.fatal_errors.append(err)
def record_fatal_error(self, err): """Record that fatal error ERR was found. ERR is a string (without trailing newline) describing the error. Output the error to stderr immediately, and record a copy to be output again in a summary at the end of CollectRevsPass.""" err = '%s: %s' % ( error_prefix, err, ) logger.error(err + '\n') self.fatal_errors.append(err)
def check_link_consistency(self): """Check that the CVSItems are linked correctly with each other.""" for cvs_item in self.values(): try: cvs_item.check_links(self) except AssertionError: logger.error( 'Link consistency error in %s\n' 'This is probably a bug internal to cvs2svn. Please file a bug\n' 'report including the following stack trace (see FAQ for more ' 'info).' % (cvs_item, )) raise
def define_revision(self, revision, timestamp, author, state, branches, next): """This is a callback method declared in Sink.""" for branch in branches: try: branch_data = self.sdc.rev_to_branch_data(branch) except KeyError: # Normally we learn about the branches from the branch names # and numbers parsed from the symbolic name header. But this # must have been an unlabeled branch that slipped through the # net. Generate a name for it and create a _BranchData record # for it now. branch_data = self.sdc._add_unlabeled_branch( self.sdc.rev_to_branch_number(branch)) assert branch_data.child is None branch_data.child = branch if revision in self._rev_data: # This revision has already been seen. logger.error('File %r contains duplicate definitions of revision %s.' % (self.cvs_file.rcs_path, revision,)) raise RuntimeError() # Record basic information about the revision: rev_data = _RevisionData( self.collect_data.item_key_generator.gen_id(), revision, int(timestamp), author, state) self._rev_data[revision] = rev_data # When on trunk, the RCS 'next' revision number points to what # humans might consider to be the 'previous' revision number. For # example, 1.3's RCS 'next' is 1.2. # # However, on a branch, the RCS 'next' revision number really does # point to what humans would consider to be the 'next' revision # number. For example, 1.1.2.1's RCS 'next' would be 1.1.2.2. # # In other words, in RCS, 'next' always means "where to find the next # deltatext that you need this revision to retrieve. # # That said, we don't *want* RCS's behavior here, so we determine # whether we're on trunk or a branch and set the dependencies # accordingly. if next: if is_trunk_revision(revision): self._primary_dependencies.append( (next, revision,) ) else: self._primary_dependencies.append( (revision, next,) )
def check_link_consistency(self): """Check that the CVSItems are linked correctly with each other.""" for cvs_item in self.values(): try: cvs_item.check_links(self) except AssertionError: logger.error( 'Link consistency error in %s\n' 'This is probably a bug internal to cvs2svn. Please file a bug\n' 'report including the following stack trace (see FAQ for more ' 'info).' % (cvs_item,)) raise
def verify_option_compatibility(self): """Verify that no options incompatible with --options were used. The --options option was specified. Verify that no incompatible options or arguments were specified.""" if self.options.options_incompatible_options or self.args: if self.options.options_incompatible_options: oio = self.options.options_incompatible_options logger.error( '%s: The following options cannot be used in combination with ' 'the --options\n' 'option:\n' ' %s\n' % (error_prefix, '\n '.join(oio))) if self.args: logger.error( '%s: No cvs-repos-path arguments are allowed with the --options ' 'option.\n' % (error_prefix, )) sys.exit(1)
def verify_option_compatibility(self): """Verify that no options incompatible with --options were used. The --options option was specified. Verify that no incompatible options or arguments were specified.""" if self.options.options_incompatible_options or self.args: if self.options.options_incompatible_options: oio = self.options.options_incompatible_options logger.error( "%s: The following options cannot be used in combination with " "the --options\n" "option:\n" " %s\n" % (error_prefix, "\n ".join(oio)) ) if self.args: logger.error( "%s: No cvs-repos-path arguments are allowed with the --options " "option.\n" % (error_prefix,) ) sys.exit(1)
def __init__( self, mime_types_file=None, mime_mappings=None, ignore_case=False ): """Constructor. Arguments: mime_types_file -- a path to a MIME types file on disk. Each line of the file should contain the MIME type, then a whitespace-separated list of file extensions; e.g., one line might be 'text/plain txt c h cpp hpp'. mime_mappings -- a dictionary mapping a file extension to a MIME type; e.g., {'txt': 'text/plain', 'cpp': 'text/plain'}. ignore_case -- True iff case should be ignored in filename extensions. Setting this option to True can be useful if your CVS repository was used on systems with case-insensitive filenames, in which case you might have a mix of uppercase and lowercase filenames.""" self.mappings = { } if ignore_case: self.transform_case = _squash_case else: self.transform_case = _preserve_case if mime_types_file is None and mime_mappings is None: logger.error('Should specify MIME types file or dict.\n') if mime_types_file is not None: for line in file(mime_types_file): if line.startswith("#"): continue # format of a line is something like # text/plain c h cpp extensions = line.split() if len(extensions) < 2: continue type = extensions.pop(0) for ext in extensions: ext = self.transform_case(ext) if ext in self.mappings and self.mappings[ext] != type: logger.error( "%s: ambiguous MIME mapping for *.%s (%s or %s)\n" % (warning_prefix, ext, self.mappings[ext], type) ) self.mappings[ext] = type if mime_mappings is not None: for ext, type in mime_mappings.iteritems(): ext = self.transform_case(ext) if ext in self.mappings and self.mappings[ext] != type: logger.error( "%s: ambiguous MIME mapping for *.%s (%s or %s)\n" % (warning_prefix, ext, self.mappings[ext], type) ) self.mappings[ext] = type
def __init__(self, mime_types_file=None, mime_mappings=None, ignore_case=False): """Constructor. Arguments: mime_types_file -- a path to a MIME types file on disk. Each line of the file should contain the MIME type, then a whitespace-separated list of file extensions; e.g., one line might be 'text/plain txt c h cpp hpp'. (See http://en.wikipedia.org/wiki/Mime.types for information about mime.types files): mime_mappings -- a dictionary mapping a file extension to a MIME type; e.g., {'txt': 'text/plain', 'cpp': 'text/plain'}. ignore_case -- True iff case should be ignored in filename extensions. Setting this option to True can be useful if your CVS repository was used on systems with case-insensitive filenames, in which case you might have a mix of uppercase and lowercase filenames.""" self.mappings = {} if ignore_case: self.transform_case = _squash_case else: self.transform_case = _preserve_case if mime_types_file is None and mime_mappings is None: logger.error('Should specify MIME types file or dict.\n') if mime_types_file is not None: for line in file(mime_types_file): if line.startswith("#"): continue # format of a line is something like # text/plain c h cpp extensions = line.split() if len(extensions) < 2: continue type = extensions.pop(0) for ext in extensions: ext = self.transform_case(ext) if ext in self.mappings and self.mappings[ext] != type: logger.error( "%s: ambiguous MIME mapping for *.%s (%s or %s)\n" % (warning_prefix, ext, self.mappings[ext], type)) self.mappings[ext] = type if mime_mappings is not None: for ext, type in mime_mappings.iteritems(): ext = self.transform_case(ext) if ext in self.mappings and self.mappings[ext] != type: logger.error( "%s: ambiguous MIME mapping for *.%s (%s or %s)\n" % (warning_prefix, ext, self.mappings[ext], type)) self.mappings[ext] = type
def check_consistency(self, symbol_map): """Check the plan for how to convert symbols for consistency. SYMBOL_MAP is a map {AbstractSymbol : (Trunk|TypedSymbol)} indicating how each AbstractSymbol is to be converted. If any problems are detected, describe the problem to logger.error() and raise a FatalException.""" # We want to do all of the consistency checks even if one of them # fails, so that the user gets as much feedback as possible. Set # this variable to True if any errors are found. error_found = False # Check that the planned preferred parents are OK for all # IncludedSymbols: for lod in symbol_map.itervalues(): if isinstance(lod, IncludedSymbol): stats = self.get_stats(lod) try: stats.check_preferred_parent_allowed(lod) except SymbolPlanException, e: logger.error('%s\n' % (e, )) error_found = True
def check_consistency(self, symbol_map): """Check the plan for how to convert symbols for consistency. SYMBOL_MAP is a map {AbstractSymbol : (Trunk|TypedSymbol)} indicating how each AbstractSymbol is to be converted. If any problems are detected, describe the problem to logger.error() and raise a FatalException.""" # We want to do all of the consistency checks even if one of them # fails, so that the user gets as much feedback as possible. Set # this variable to True if any errors are found. error_found = False # Check that the planned preferred parents are OK for all # IncludedSymbols: for lod in symbol_map.itervalues(): if isinstance(lod, IncludedSymbol): stats = self.get_stats(lod) try: stats.check_preferred_parent_allowed(lod) except SymbolPlanException, e: logger.error('%s\n' % (e,)) error_found = True
# so that the dbhash module used by anydbm will use bsddb3. try: import bsddb3 sys.modules['bsddb'] = sys.modules['bsddb3'] except ImportError: pass # 2. These DBM modules are not good for cvs2svn. import anydbm if anydbm._defaultmod.__name__ in ['dumbdbm', 'dbm']: logger.error( '%s: cvs2svn uses the anydbm package, which depends on lower level ' 'dbm\n' 'libraries. Your system has %s, with which cvs2svn is known to have\n' 'problems. To use cvs2svn, you must install a Python dbm library ' 'other than\n' 'dumbdbm or dbm. See ' 'http://python.org/doc/current/lib/module-anydbm.html\n' 'for more information.\n' % (error_prefix, anydbm._defaultmod.__name__,) ) sys.exit(1) # 3. If we are using the old bsddb185 module, then try prefer gdbm instead. # Unfortunately, gdbm appears not to be trouble free, either. if hasattr(anydbm._defaultmod, 'bsddb') \ and not hasattr(anydbm._defaultmod.bsddb, '__version__'): try: gdbm = __import__('gdbm') except ImportError: logger.warn(
def set_revision_info(self, revision, log, text): if revision in self.revisions_seen: # One common form of CVS repository corruption is that the # Deltatext block for revision 1.1 appears twice. CollectData # has already warned about this problem; here we can just ignore # it. return else: self.revisions_seen.add(revision) cvs_rev_id = self.cvs_file_items.original_ids[revision] if is_trunk_revision(revision): # On trunk, revisions are encountered in reverse order (1.<N> # ... 1.1) and deltas are inverted. The first text that we see # is the fulltext for the HEAD revision. After that, the text # corresponding to revision 1.N is the delta (1.<N+1> -> # 1.<N>)). We have to invert the deltas here so that we can # read the revisions out in dependency order; that is, for # revision 1.1 we want the fulltext, and for revision 1.<N> we # want the delta (1.<N-1> -> 1.<N>). This means that we can't # compute the delta for a revision until we see its logical # parent. When we finally see revision 1.1 (which is recognized # because it doesn't have a parent), we can record the diff (1.1 # -> 1.2) for revision 1.2, and also the fulltext for 1.1. if revision == self.head_revision: # This is HEAD, as fulltext. Initialize the RCSStream so # that we can compute deltas backwards in time. self._rcs_stream = RCSStream(text) self._rcs_stream_revision = revision else: # Any other trunk revision is a backward delta. Apply the # delta to the RCSStream to mutate it to the contents of this # revision, and also to get the reverse delta, which we store # as the forward delta of our child revision. try: text = self._rcs_stream.invert_diff(text) except MalformedDeltaException, e: logger.error( 'Malformed RCS delta in %s, revision %s: %s' % (self.cvs_file_items.cvs_file.rcs_path, revision, e) ) raise RuntimeError() text_record = DeltaTextRecord( self.cvs_file_items.original_ids[self._rcs_stream_revision], cvs_rev_id ) self.revision_collector._writeout(text_record, text) self._rcs_stream_revision = revision if revision == self.revision_1_1: # This is revision 1.1. Write its fulltext: text_record = FullTextRecord(cvs_rev_id) self.revision_collector._writeout( text_record, self._rcs_stream.get_text() ) # There will be no more trunk revisions delivered, so free the # RCSStream. del self._rcs_stream del self._rcs_stream_revision
def callback_create(self, option, opt_str, value, parser): logger.error( warning_prefix + ': The behaviour produced by the --create option is now the ' 'default;\n' 'passing the option is deprecated.\n')
# so that the dbhash module used by anydbm will use bsddb3. try: import bsddb3 sys.modules['bsddb'] = sys.modules['bsddb3'] except ImportError: pass # 2. These DBM modules are not good for cvs2svn. import anydbm if anydbm._defaultmod.__name__ in ['dumbdbm', 'dbm']: logger.error( '%s: cvs2svn uses the anydbm package, which depends on lower level ' 'dbm\n' 'libraries. Your system has %s, with which cvs2svn is known to have\n' 'problems. To use cvs2svn, you must install a Python dbm library ' 'other than\n' 'dumbdbm or dbm. See ' 'http://python.org/doc/current/lib/module-anydbm.html\n' 'for more information.\n' % ( error_prefix, anydbm._defaultmod.__name__, )) sys.exit(1) # 3. If we are using the old bsddb185 module, then try prefer gdbm instead. # Unfortunately, gdbm appears not to be trouble free, either. if hasattr(anydbm._defaultmod, 'bsddb') \ and not hasattr(anydbm._defaultmod.bsddb, '__version__'): try: gdbm = __import__('gdbm') except ImportError: logger.warn(
def callback_dump_only(self, option, opt_str, value, parser): parser.values.dump_only = True logger.error(warning_prefix + ': The --dump-only option is deprecated (it is implied ' 'by --dumpfile).\n')