#! /usr/bin/env python2 """Migrate a post-receive-email configuration to be usable with git_multimail.py. See README.migrate-from-post-receive-email for more information. """ import sys import optparse from git_multimail import CommandError from git_multimail import Config from git_multimail import read_output OLD_NAMES = [ 'mailinglist', 'announcelist', 'envelopesender', 'emailprefix', 'showrev', 'emailmaxlines', 'diffopts', ] NEW_NAMES = [ 'environment', 'reponame', 'mailinglist', 'refchangelist', 'commitlist', 'announcelist', 'announceshortlog', 'envelopesender', 'administrator', 'emailprefix', 'emailmaxlines', 'diffopts', 'emaildomain', ] INFO = """\ SUCCESS! Your post-receive-email configuration has been converted to git-multimail format. Please see README and README.migrate-from-post-receive-email to learn about other git-multimail configuration possibilities. For example, git-multimail has the following new options with no equivalent in post-receive-email. You might want to read about them to see if they would be useful in your situation: """ def _check_old_config_exists(old): """Check that at least one old configuration value is set.""" for name in OLD_NAMES: if old.has_key(name): return True return False def _check_new_config_clear(new): """Check that none of the new configuration names are set.""" retval = True for name in NEW_NAMES: if new.has_key(name): if retval: sys.stderr.write('INFO: The following configuration values already exist:\n\n') sys.stderr.write(' "%s.%s"\n' % (new.section, name)) retval = False return retval def erase_values(config, names): for name in names: if config.has_key(name): try: sys.stderr.write('...unsetting "%s.%s"\n' % (config.section, name)) config.unset_all(name) except CommandError: sys.stderr.write( '\nWARNING: could not unset "%s.%s". ' 'Perhaps it is not set at the --local level?\n\n' % (config.section, name) ) def is_section_empty(section, local): """Return True iff the specified configuration section is empty. Iff local is True, use the --local option when invoking 'git config'.""" if local: local_option = ['--local'] else: local_option = [] try: read_output( ['git', 'config'] + local_option + ['--get-regexp', '^%s\.' % (section,)] ) except CommandError, e: if e.retcode == 1: # This means that no settings were found. return True else: raise else: return False def remove_section_if_empty(section): """If the specified configuration section is empty, delete it.""" try: empty = is_section_empty(section, local=True) except CommandError: # Older versions of git do not support the --local option, so # if the first attempt fails, try without --local. try: empty = is_section_empty(section, local=False) except CommandError: sys.stderr.write( '\nINFO: If configuration section "%s.*" is empty, you might want ' 'to delete it.\n\n' % (section,) ) return if empty: sys.stderr.write('...removing section "%s.*"\n' % (section,)) read_output(['git', 'config', '--remove-section', section]) else: sys.stderr.write( '\nINFO: Configuration section "%s.*" still has contents. ' 'It will not be deleted.\n\n' % (section,) ) def migrate_config(strict=False, retain=False, overwrite=False): old = Config('hooks') new = Config('multimailhook') if not _check_old_config_exists(old): sys.exit( 'Your repository has no post-receive-email configuration. ' 'Nothing to do.' ) if not _check_new_config_clear(new): if overwrite: sys.stderr.write('\nWARNING: Erasing the above values...\n\n') erase_values(new, NEW_NAMES) else: sys.exit( '\nERROR: Refusing to overwrite existing values. Use the --overwrite\n' 'option to continue anyway.' ) name = 'showrev' if old.has_key(name): msg = 'git-multimail does not support "%s.%s"' % (old.section, name,) if strict: sys.exit( 'ERROR: %s.\n' 'Please unset that value then try again, or run without --strict.' % (msg,) ) else: sys.stderr.write('\nWARNING: %s (ignoring).\n\n' % (msg,)) for name in ['mailinglist', 'announcelist']: if old.has_key(name): sys.stderr.write( '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name) ) new.set_recipients(name, old.get_recipients(name)) if strict: sys.stderr.write( '...setting "%s.commitlist" to the empty string\n' % (new.section,) ) new.set_recipients('commitlist', '') sys.stderr.write( '...setting "%s.announceshortlog" to "true"\n' % (new.section,) ) new.set('announceshortlog', 'true') for name in ['envelopesender', 'emailmaxlines', 'diffopts']: if old.has_key(name): sys.stderr.write( '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name) ) new.set(name, old.get(name)) name = 'emailprefix' if old.has_key(name): sys.stderr.write( '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name) ) new.set(name, old.get(name)) elif strict: sys.stderr.write( '...setting "%s.%s" to "[SCM]" to preserve old subject lines\n' % (new.section, name) ) new.set(name, '[SCM]') if not retain: erase_values(old, OLD_NAMES) remove_section_if_empty(old.section) sys.stderr.write(INFO) for name in NEW_NAMES: if name not in OLD_NAMES: sys.stderr.write(' "%s.%s"\n' % (new.section, name,)) sys.stderr.write('\n') def main(args): parser = optparse.OptionParser( description=__doc__, usage='%prog [OPTIONS]', ) parser.add_option( '--strict', action='store_true', default=False, help=( 'Slavishly configure git-multimail as closely as possible to ' 'the post-receive-email configuration. Default is to turn ' 'on some new features that have no equivalent in post-receive-email.' ), ) parser.add_option( '--retain', action='store_true', default=False, help=( 'Retain the post-receive-email configuration values. ' 'Default is to delete them after the new values are set.' ), ) parser.add_option( '--overwrite', action='store_true', default=False, help=( 'Overwrite any existing git-multimail configuration settings. ' 'Default is to abort if such settings already exist.' ), ) (options, args) = parser.parse_args(args) if args: parser.error('Unexpected arguments: %s' % (' '.join(args),)) migrate_config(strict=options.strict, retain=options.retain, overwrite=options.overwrite) main(sys.argv[1:])