Mercurial > public > mercurial-scm > hg
comparison hgext/patchbomb.py @ 12200:aebb39d45500
patchbomb: let diffstat prompt only once with complete summary
This changes the behaviour of --diffstat.
Before the user was asked for confirmation of each patch with its
description and diffstat, and a final summary.
Now there is only one prompt right before sending with a final
summary which does not include the patch descriptions, but the
message details and the diffstats:
Final summary:
From: sender
To: recipient(s)
Cc: (if present)
Bcc: (if present)
Reply-To: (if present)
Subject: [patch 0 of x [flags]] intro (if present)
a | 28 ++++++++++++++++++++++++++++
b | 15 +++++++++++++++
Subject: [patch 1 of x [flags]] subject
a | 28 ++++++++++++++++++++++++++++
[ ... ]
are you sure you want to send (yn)?
author | Christian Ebert <blacktrash@gmx.net> |
---|---|
date | Fri, 10 Sep 2010 15:32:14 +0200 |
parents | 17d604e522b4 |
children | 5bfa45651cf6 |
comparison
equal
deleted
inserted
replaced
12199:17d604e522b4 | 12200:aebb39d45500 |
---|---|
20 | 20 |
21 Each message refers to the first in the series using the In-Reply-To | 21 Each message refers to the first in the series using the In-Reply-To |
22 and References headers, so they will show up as a sequence in threaded | 22 and References headers, so they will show up as a sequence in threaded |
23 mail and news readers, and in mail archives. | 23 mail and news readers, and in mail archives. |
24 | 24 |
25 With the -d/--diffstat option, you will be prompted for each changeset | 25 With the -d/--diffstat option, you will be presented with a final |
26 with a diffstat summary and the changeset summary, so you can be sure | 26 summary of all messages and asked for confirmation before the messages |
27 you are sending the right changes. | 27 are sent. |
28 | 28 |
29 To configure other defaults, add a section like this to your hgrc | 29 To configure other defaults, add a section like this to your hgrc |
30 file:: | 30 file:: |
31 | 31 |
32 [email] | 32 [email] |
92 return r | 92 return r |
93 if default is not None: | 93 if default is not None: |
94 return default | 94 return default |
95 ui.warn(_('Please enter a valid value.\n')) | 95 ui.warn(_('Please enter a valid value.\n')) |
96 | 96 |
97 def cdiffstat(ui, summary, patchlines): | |
98 s = patch.diffstat(patchlines) | |
99 if summary: | |
100 ui.write(summary, '\n') | |
101 ui.write(s, '\n') | |
102 if ui.promptchoice(_('does the diffstat above look okay (yn)?'), | |
103 (_('&Yes'), _('&No'))): | |
104 raise util.Abort(_('diffstat rejected')) | |
105 return s | |
106 | |
107 def introneeded(opts, number): | 97 def introneeded(opts, number): |
108 '''is an introductory message required?''' | 98 '''is an introductory message required?''' |
109 return number > 1 or opts.get('intro') or opts.get('desc') | 99 return number > 1 or opts.get('intro') or opts.get('desc') |
110 | 100 |
111 def makepatch(ui, repo, patchlines, opts, _charsets, idx, total, | 101 def makepatch(ui, repo, patchlines, opts, _charsets, idx, total, |
138 if patchlines: | 128 if patchlines: |
139 patchlines.pop(0) | 129 patchlines.pop(0) |
140 while patchlines and not patchlines[0].strip(): | 130 while patchlines and not patchlines[0].strip(): |
141 patchlines.pop(0) | 131 patchlines.pop(0) |
142 | 132 |
133 ds = patch.diffstat(patchlines) | |
143 if opts.get('diffstat'): | 134 if opts.get('diffstat'): |
144 body += cdiffstat(ui, '\n'.join(desc), patchlines) + '\n\n' | 135 body += ds + '\n\n' |
145 | 136 |
146 if opts.get('attach') or opts.get('inline'): | 137 if opts.get('attach') or opts.get('inline'): |
147 msg = email.MIMEMultipart.MIMEMultipart() | 138 msg = email.MIMEMultipart.MIMEMultipart() |
148 if body: | 139 if body: |
149 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test'))) | 140 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test'))) |
179 else: | 170 else: |
180 tlen = len(str(total)) | 171 tlen = len(str(total)) |
181 subj = '[PATCH %0*d of %d%s] %s' % (tlen, idx, total, flag, subj) | 172 subj = '[PATCH %0*d of %d%s] %s' % (tlen, idx, total, flag, subj) |
182 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test')) | 173 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test')) |
183 msg['X-Mercurial-Node'] = node | 174 msg['X-Mercurial-Node'] = node |
184 return msg, subj | 175 return msg, subj, ds |
185 | 176 |
186 def patchbomb(ui, repo, *revs, **opts): | 177 def patchbomb(ui, repo, *revs, **opts): |
187 '''send changesets by email | 178 '''send changesets by email |
188 | 179 |
189 By default, diffs are sent in the format generated by | 180 By default, diffs are sent in the format generated by |
349 subj = '[PATCH %0*d of %d]' % (tlen, 0, len(patches)) | 340 subj = '[PATCH %0*d of %d]' % (tlen, 0, len(patches)) |
350 subj += ' ' + (opts.get('subject') or | 341 subj += ' ' + (opts.get('subject') or |
351 prompt(ui, 'Subject: ', rest=subj)) | 342 prompt(ui, 'Subject: ', rest=subj)) |
352 | 343 |
353 body = '' | 344 body = '' |
354 if opts.get('diffstat'): | 345 ds = patch.diffstat(jumbo) |
355 d = cdiffstat(ui, _('Final summary:\n'), jumbo) | 346 if ds and opts.get('diffstat'): |
356 if d: | 347 body = '\n' + ds |
357 body = '\n' + d | |
358 | 348 |
359 body = getdescription(body, sender) | 349 body = getdescription(body, sender) |
360 msg = mail.mimeencode(ui, body, _charsets, opts.get('test')) | 350 msg = mail.mimeencode(ui, body, _charsets, opts.get('test')) |
361 msg['Subject'] = mail.headencode(ui, subj, _charsets, | 351 msg['Subject'] = mail.headencode(ui, subj, _charsets, |
362 opts.get('test')) | 352 opts.get('test')) |
363 | 353 |
364 msgs.insert(0, (msg, subj)) | 354 msgs.insert(0, (msg, subj, ds)) |
365 return msgs | 355 return msgs |
366 | 356 |
367 def getbundlemsgs(bundle): | 357 def getbundlemsgs(bundle): |
368 subj = (opts.get('subject') | 358 subj = (opts.get('subject') |
369 or prompt(ui, 'Subject:', 'A bundle for your repository')) | 359 or prompt(ui, 'Subject:', 'A bundle for your repository')) |
378 datapart.add_header('Content-Disposition', 'attachment', | 368 datapart.add_header('Content-Disposition', 'attachment', |
379 filename=bundlename) | 369 filename=bundlename) |
380 email.Encoders.encode_base64(datapart) | 370 email.Encoders.encode_base64(datapart) |
381 msg.attach(datapart) | 371 msg.attach(datapart) |
382 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test')) | 372 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test')) |
383 return [(msg, subj)] | 373 return [(msg, subj, None)] |
384 | 374 |
385 sender = (opts.get('from') or ui.config('email', 'from') or | 375 sender = (opts.get('from') or ui.config('email', 'from') or |
386 ui.config('patchbomb', 'from') or | 376 ui.config('patchbomb', 'from') or |
387 prompt(ui, 'From', ui.username())) | 377 prompt(ui, 'From', ui.username())) |
388 | 378 |
391 elif bundle: | 381 elif bundle: |
392 msgs = getbundlemsgs(getbundle(dest)) | 382 msgs = getbundlemsgs(getbundle(dest)) |
393 else: | 383 else: |
394 msgs = getpatchmsgs(list(getpatches(revs))) | 384 msgs = getpatchmsgs(list(getpatches(revs))) |
395 | 385 |
386 showaddrs = [] | |
387 | |
396 def getaddrs(opt, prpt=None, default=None): | 388 def getaddrs(opt, prpt=None, default=None): |
397 addrs = opts.get(opt.replace('-', '_')) | 389 addrs = opts.get(opt.replace('-', '_')) |
390 if opt != 'reply-to': | |
391 showaddr = '%s:' % opt.capitalize() | |
392 else: | |
393 showaddr = 'Reply-To:' | |
394 | |
398 if addrs: | 395 if addrs: |
396 showaddrs.append('%s %s' % (showaddr, ', '.join(addrs))) | |
399 return mail.addrlistencode(ui, addrs, _charsets, | 397 return mail.addrlistencode(ui, addrs, _charsets, |
400 opts.get('test')) | 398 opts.get('test')) |
401 | 399 |
402 addrs = ui.config('email', opt) or ui.config('patchbomb', opt) or '' | 400 addrs = ui.config('email', opt) or ui.config('patchbomb', opt) or '' |
403 if not addrs and prpt: | 401 if not addrs and prpt: |
404 addrs = prompt(ui, prpt, default) | 402 addrs = prompt(ui, prpt, default) |
403 | |
404 if addrs: | |
405 showaddr = '%s %s' % (showaddr, addrs) | |
406 showaddrs.append(showaddr) | |
405 | 407 |
406 return mail.addrlistencode(ui, [addrs], _charsets, opts.get('test')) | 408 return mail.addrlistencode(ui, [addrs], _charsets, opts.get('test')) |
407 | 409 |
408 to = getaddrs('to', 'To') | 410 to = getaddrs('to', 'To') |
409 cc = getaddrs('cc', 'Cc', '') | 411 cc = getaddrs('cc', 'Cc', '') |
410 bcc = getaddrs('bcc') | 412 bcc = getaddrs('bcc') |
411 replyto = getaddrs('reply-to') | 413 replyto = getaddrs('reply-to') |
414 | |
415 if opts.get('diffstat'): | |
416 ui.write(_('\nFinal summary:\n\n')) | |
417 ui.write('From: %s\n' % sender) | |
418 for addr in showaddrs: | |
419 ui.write('%s\n' % addr) | |
420 for m, subj, ds in msgs: | |
421 ui.write('Subject: %s\n' % subj) | |
422 if ds: | |
423 ui.write(ds) | |
424 ui.write('\n') | |
425 if ui.promptchoice(_('are you sure you want to send (yn)?'), | |
426 (_('&Yes'), _('&No'))): | |
427 raise util.Abort(_('patchbomb canceled')) | |
412 | 428 |
413 ui.write('\n') | 429 ui.write('\n') |
414 | 430 |
415 parent = opts.get('in_reply_to') or None | 431 parent = opts.get('in_reply_to') or None |
416 # angle brackets may be omitted, they're not semantically part of the msg-id | 432 # angle brackets may be omitted, they're not semantically part of the msg-id |
423 first = True | 439 first = True |
424 | 440 |
425 sender_addr = email.Utils.parseaddr(sender)[1] | 441 sender_addr = email.Utils.parseaddr(sender)[1] |
426 sender = mail.addressencode(ui, sender, _charsets, opts.get('test')) | 442 sender = mail.addressencode(ui, sender, _charsets, opts.get('test')) |
427 sendmail = None | 443 sendmail = None |
428 for m, subj in msgs: | 444 for m, subj, ds in msgs: |
429 try: | 445 try: |
430 m['Message-Id'] = genmsgid(m['X-Mercurial-Node']) | 446 m['Message-Id'] = genmsgid(m['X-Mercurial-Node']) |
431 except TypeError: | 447 except TypeError: |
432 m['Message-Id'] = genmsgid('patchbomb') | 448 m['Message-Id'] = genmsgid('patchbomb') |
433 if parent: | 449 if parent: |