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