Merge branch 'jk/reduce-gc-aggressive-depth'
[git] / contrib / hooks / multimail / migrate-mailhook-config
1 #! /usr/bin/env python
2
3 """Migrate a post-receive-email configuration to be usable with git_multimail.py.
4
5 See README.migrate-from-post-receive-email for more information.
6
7 """
8
9 import sys
10 import optparse
11
12 from git_multimail import CommandError
13 from git_multimail import Config
14 from git_multimail import read_output
15
16
17 OLD_NAMES = [
18     'mailinglist',
19     'announcelist',
20     'envelopesender',
21     'emailprefix',
22     'showrev',
23     'emailmaxlines',
24     'diffopts',
25     'scancommitforcc',
26     ]
27
28 NEW_NAMES = [
29     'environment',
30     'reponame',
31     'mailinglist',
32     'refchangelist',
33     'commitlist',
34     'announcelist',
35     'announceshortlog',
36     'envelopesender',
37     'administrator',
38     'emailprefix',
39     'emailmaxlines',
40     'diffopts',
41     'emaildomain',
42     'scancommitforcc',
43     ]
44
45
46 INFO = """\
47
48 SUCCESS!
49
50 Your post-receive-email configuration has been converted to
51 git-multimail format.  Please see README and
52 README.migrate-from-post-receive-email to learn about other
53 git-multimail configuration possibilities.
54
55 For example, git-multimail has the following new options with no
56 equivalent in post-receive-email.  You might want to read about them
57 to see if they would be useful in your situation:
58
59 """
60
61
62 def _check_old_config_exists(old):
63     """Check that at least one old configuration value is set."""
64
65     for name in OLD_NAMES:
66         if name in old:
67             return True
68
69     return False
70
71
72 def _check_new_config_clear(new):
73     """Check that none of the new configuration names are set."""
74
75     retval = True
76     for name in NEW_NAMES:
77         if name in new:
78             if retval:
79                 sys.stderr.write('INFO: The following configuration values already exist:\n\n')
80             sys.stderr.write('    "%s.%s"\n' % (new.section, name))
81             retval = False
82
83     return retval
84
85
86 def erase_values(config, names):
87     for name in names:
88         if name in config:
89             try:
90                 sys.stderr.write('...unsetting "%s.%s"\n' % (config.section, name))
91                 config.unset_all(name)
92             except CommandError:
93                 sys.stderr.write(
94                     '\nWARNING: could not unset "%s.%s".  '
95                     'Perhaps it is not set at the --local level?\n\n'
96                     % (config.section, name)
97                     )
98
99
100 def is_section_empty(section, local):
101     """Return True iff the specified configuration section is empty.
102
103     Iff local is True, use the --local option when invoking 'git
104     config'."""
105
106     if local:
107         local_option = ['--local']
108     else:
109         local_option = []
110
111     try:
112         read_output(
113             ['git', 'config']
114             + local_option
115             + ['--get-regexp', '^%s\.' % (section,)]
116             )
117     except CommandError, e:
118         if e.retcode == 1:
119             # This means that no settings were found.
120             return True
121         else:
122             raise
123     else:
124         return False
125
126
127 def remove_section_if_empty(section):
128     """If the specified configuration section is empty, delete it."""
129
130     try:
131         empty = is_section_empty(section, local=True)
132     except CommandError:
133         # Older versions of git do not support the --local option, so
134         # if the first attempt fails, try without --local.
135         try:
136             empty = is_section_empty(section, local=False)
137         except CommandError:
138             sys.stderr.write(
139                 '\nINFO: If configuration section "%s.*" is empty, you might want '
140                 'to delete it.\n\n'
141                 % (section,)
142                 )
143             return
144
145     if empty:
146         sys.stderr.write('...removing section "%s.*"\n' % (section,))
147         read_output(['git', 'config', '--remove-section', section])
148     else:
149         sys.stderr.write(
150             '\nINFO: Configuration section "%s.*" still has contents.  '
151             'It will not be deleted.\n\n'
152             % (section,)
153             )
154
155
156 def migrate_config(strict=False, retain=False, overwrite=False):
157     old = Config('hooks')
158     new = Config('multimailhook')
159     if not _check_old_config_exists(old):
160         sys.exit(
161             'Your repository has no post-receive-email configuration.  '
162             'Nothing to do.'
163             )
164     if not _check_new_config_clear(new):
165         if overwrite:
166             sys.stderr.write('\nWARNING: Erasing the above values...\n\n')
167             erase_values(new, NEW_NAMES)
168         else:
169             sys.exit(
170                 '\nERROR: Refusing to overwrite existing values.  Use the --overwrite\n'
171                 'option to continue anyway.'
172                 )
173
174     name = 'showrev'
175     if name in old:
176         msg = 'git-multimail does not support "%s.%s"' % (old.section, name,)
177         if strict:
178             sys.exit(
179                 'ERROR: %s.\n'
180                 'Please unset that value then try again, or run without --strict.'
181                 % (msg,)
182                 )
183         else:
184             sys.stderr.write('\nWARNING: %s (ignoring).\n\n' % (msg,))
185
186     for name in ['mailinglist', 'announcelist']:
187         if name in old:
188             sys.stderr.write(
189                 '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
190                 )
191             new.set_recipients(name, old.get_recipients(name))
192
193     if strict:
194         sys.stderr.write(
195             '...setting "%s.commitlist" to the empty string\n' % (new.section,)
196             )
197         new.set_recipients('commitlist', '')
198         sys.stderr.write(
199             '...setting "%s.announceshortlog" to "true"\n' % (new.section,)
200             )
201         new.set('announceshortlog', 'true')
202
203     for name in ['envelopesender', 'emailmaxlines', 'diffopts', 'scancommitforcc']:
204         if name in old:
205             sys.stderr.write(
206                 '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
207                 )
208             new.set(name, old.get(name))
209
210     name = 'emailprefix'
211     if name in old:
212         sys.stderr.write(
213             '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
214             )
215         new.set(name, old.get(name))
216     elif strict:
217         sys.stderr.write(
218             '...setting "%s.%s" to "[SCM]" to preserve old subject lines\n'
219             % (new.section, name)
220             )
221         new.set(name, '[SCM]')
222
223     if not retain:
224         erase_values(old, OLD_NAMES)
225         remove_section_if_empty(old.section)
226
227     sys.stderr.write(INFO)
228     for name in NEW_NAMES:
229         if name not in OLD_NAMES:
230             sys.stderr.write('    "%s.%s"\n' % (new.section, name,))
231     sys.stderr.write('\n')
232
233
234 def main(args):
235     parser = optparse.OptionParser(
236         description=__doc__,
237         usage='%prog [OPTIONS]',
238         )
239
240     parser.add_option(
241         '--strict', action='store_true', default=False,
242         help=(
243             'Slavishly configure git-multimail as closely as possible to '
244             'the post-receive-email configuration.  Default is to turn '
245             'on some new features that have no equivalent in post-receive-email.'
246             ),
247         )
248     parser.add_option(
249         '--retain', action='store_true', default=False,
250         help=(
251             'Retain the post-receive-email configuration values.  '
252             'Default is to delete them after the new values are set.'
253             ),
254         )
255     parser.add_option(
256         '--overwrite', action='store_true', default=False,
257         help=(
258             'Overwrite any existing git-multimail configuration settings.  '
259             'Default is to abort if such settings already exist.'
260             ),
261         )
262
263     (options, args) = parser.parse_args(args)
264
265     if args:
266         parser.error('Unexpected arguments: %s' % (' '.join(args),))
267
268     migrate_config(strict=options.strict, retain=options.retain, overwrite=options.overwrite)
269
270
271 main(sys.argv[1:])