Merge branch 'ab/receive-pack-use-after-free-fix'
[git/git.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:
118 t, e, traceback = sys.exc_info()
119 if e.retcode == 1:
120 # This means that no settings were found.
121 return True
122 else:
123 raise
124 else:
125 return False
126
127
128 def remove_section_if_empty(section):
129 """If the specified configuration section is empty, delete it."""
130
131 try:
132 empty = is_section_empty(section, local=True)
133 except CommandError:
134 # Older versions of git do not support the --local option, so
135 # if the first attempt fails, try without --local.
136 try:
137 empty = is_section_empty(section, local=False)
138 except CommandError:
139 sys.stderr.write(
140 '\nINFO: If configuration section "%s.*" is empty, you might want '
141 'to delete it.\n\n'
142 % (section,)
143 )
144 return
145
146 if empty:
147 sys.stderr.write('...removing section "%s.*"\n' % (section,))
148 read_output(['git', 'config', '--remove-section', section])
149 else:
150 sys.stderr.write(
151 '\nINFO: Configuration section "%s.*" still has contents. '
152 'It will not be deleted.\n\n'
153 % (section,)
154 )
155
156
157 def migrate_config(strict=False, retain=False, overwrite=False):
158 old = Config('hooks')
159 new = Config('multimailhook')
160 if not _check_old_config_exists(old):
161 sys.exit(
162 'Your repository has no post-receive-email configuration. '
163 'Nothing to do.'
164 )
165 if not _check_new_config_clear(new):
166 if overwrite:
167 sys.stderr.write('\nWARNING: Erasing the above values...\n\n')
168 erase_values(new, NEW_NAMES)
169 else:
170 sys.exit(
171 '\nERROR: Refusing to overwrite existing values. Use the --overwrite\n'
172 'option to continue anyway.'
173 )
174
175 name = 'showrev'
176 if name in old:
177 msg = 'git-multimail does not support "%s.%s"' % (old.section, name,)
178 if strict:
179 sys.exit(
180 'ERROR: %s.\n'
181 'Please unset that value then try again, or run without --strict.'
182 % (msg,)
183 )
184 else:
185 sys.stderr.write('\nWARNING: %s (ignoring).\n\n' % (msg,))
186
187 for name in ['mailinglist', 'announcelist']:
188 if name in old:
189 sys.stderr.write(
190 '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
191 )
192 old_recipients = old.get_all(name, default=None)
193 old_recipients = ', '.join(o.strip() for o in old_recipients)
194 new.set_recipients(name, old_recipients)
195
196 if strict:
197 sys.stderr.write(
198 '...setting "%s.commitlist" to the empty string\n' % (new.section,)
199 )
200 new.set_recipients('commitlist', '')
201 sys.stderr.write(
202 '...setting "%s.announceshortlog" to "true"\n' % (new.section,)
203 )
204 new.set('announceshortlog', 'true')
205
206 for name in ['envelopesender', 'emailmaxlines', 'diffopts', 'scancommitforcc']:
207 if name in old:
208 sys.stderr.write(
209 '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
210 )
211 new.set(name, old.get(name))
212
213 name = 'emailprefix'
214 if name in old:
215 sys.stderr.write(
216 '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
217 )
218 new.set(name, old.get(name))
219 elif strict:
220 sys.stderr.write(
221 '...setting "%s.%s" to "[SCM]" to preserve old subject lines\n'
222 % (new.section, name)
223 )
224 new.set(name, '[SCM]')
225
226 if not retain:
227 erase_values(old, OLD_NAMES)
228 remove_section_if_empty(old.section)
229
230 sys.stderr.write(INFO)
231 for name in NEW_NAMES:
232 if name not in OLD_NAMES:
233 sys.stderr.write(' "%s.%s"\n' % (new.section, name,))
234 sys.stderr.write('\n')
235
236
237 def main(args):
238 parser = optparse.OptionParser(
239 description=__doc__,
240 usage='%prog [OPTIONS]',
241 )
242
243 parser.add_option(
244 '--strict', action='store_true', default=False,
245 help=(
246 'Slavishly configure git-multimail as closely as possible to '
247 'the post-receive-email configuration. Default is to turn '
248 'on some new features that have no equivalent in post-receive-email.'
249 ),
250 )
251 parser.add_option(
252 '--retain', action='store_true', default=False,
253 help=(
254 'Retain the post-receive-email configuration values. '
255 'Default is to delete them after the new values are set.'
256 ),
257 )
258 parser.add_option(
259 '--overwrite', action='store_true', default=False,
260 help=(
261 'Overwrite any existing git-multimail configuration settings. '
262 'Default is to abort if such settings already exist.'
263 ),
264 )
265
266 (options, args) = parser.parse_args(args)
267
268 if args:
269 parser.error('Unexpected arguments: %s' % (' '.join(args),))
270
271 migrate_config(strict=options.strict, retain=options.retain, overwrite=options.overwrite)
272
273
274 main(sys.argv[1:])