##// END OF EJS Templates
worker: support parallelization of functions with return values...
worker: support parallelization of functions with return values Currently worker supports running functions that return a progress iterator. Generalize it to handle function that return a progress iterator then a return value. It's unused in this commit, but will be used in the next one. Differential Revision: https://phab.mercurial-scm.org/D6515

File last commit:

r40382:943248e4 default
r42648:55b724df default
Show More
patchbomb.py
842 lines | 30.3 KiB | text/x-python | PythonLexer
Martin Geisler
patchbomb: add copyright and license header
r8252 # patchbomb.py - sending Mercurial changesets as patch emails
#
# Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
#
# This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Martin Geisler
patchbomb: add copyright and license header
r8252
Dirkjan Ochtman
extensions: change descriptions for hook-providing extensions...
r8935 '''command to send changesets as (a series of) patch emails
Dirkjan Ochtman
convert comments to docstrings in a bunch of extensions
r6666
Martin Geisler
patchbomb: word-wrap help texts at 70 characters
r7997 The series is started off with a "[PATCH 0 of N]" introduction, which
describes the series as a whole.
Dirkjan Ochtman
convert comments to docstrings in a bunch of extensions
r6666
Martin Geisler
patchbomb: wrap docstrings at 70 characters
r9269 Each patch email has a Subject line of "[PATCH M of N] ...", using the
first line of the changeset description as the subject text. The
message contains two or three body parts:
Dirkjan Ochtman
convert comments to docstrings in a bunch of extensions
r6666
Martin Geisler
patchbomb: use a list instead of indented paragraphs
r9300 - The changeset description.
- [Optional] The result of running diffstat on the patch.
Martin Geisler
Use hg role in help strings
r10973 - The patch itself, as generated by :hg:`export`.
Dirkjan Ochtman
convert comments to docstrings in a bunch of extensions
r6666
Martin Geisler
patchbomb: wrap docstrings at 70 characters
r9269 Each message refers to the first in the series using the In-Reply-To
and References headers, so they will show up as a sequence in threaded
mail and news readers, and in mail archives.
Dirkjan Ochtman
convert comments to docstrings in a bunch of extensions
r6666
Martin Geisler
patchbomb, help/hgweb: do not refer to config files as hgrc files
r13838 To configure other defaults, add a section like this to your
configuration file::
Dirkjan Ochtman
convert comments to docstrings in a bunch of extensions
r6666
[email]
from = My Name <my@email>
to = recipient1, recipient2, ...
cc = cc1, cc2, ...
bcc = bcc1, bcc2, ...
Cédric Duval
patchbomb: Reply-To support...
r11150 reply-to = address1, address2, ...
Dirkjan Ochtman
convert comments to docstrings in a bunch of extensions
r6666
Christian Ebert
patchbomb: document [patchbomb] config section for addresses
r10284 Use ``[patchbomb]`` as configuration section name if you need to
override global ``[email]`` address settings.
Martin Geisler
Use hg role in help strings
r10973 Then you can use the :hg:`email` command to mail a series of
changesets as a patchbomb.
Dirkjan Ochtman
convert comments to docstrings in a bunch of extensions
r6666
Martin Geisler
patchbomb: wrap docstrings at 70 characters
r9269 You can also either configure the method option in the email section
to be a sendmail compatible mailer or fill out the [smtp] section so
that the patchbomb extension can automatically send patchbombs
directly from the commandline. See the [email] and [smtp] sections in
hgrc(5) for details.
Pierre-Yves David
patchbomb: add a 'patchbomb.intro' option...
r23487
Bryan O'Sullivan
patchbomb: treat empty address list as no addresses...
r27697 By default, :hg:`email` will prompt for a ``To`` or ``CC`` header if
you do not supply one via configuration or the command line. You can
override this to never prompt by configuring an empty value::
[email]
cc =
Pierre-Yves David
patchbomb: add a 'patchbomb.intro' option...
r23487 You can control the default inclusion of an introduction message with the
``patchbomb.intro`` configuration option. The configuration is always
overwritten by command line flags like --intro and --desc::
[patchbomb]
intro=auto # include introduction message if more than 1 patch (default)
intro=never # never include an introduction message
intro=always # always include an introduction message
Pierre-Yves David
patchbomb: introduce a 'patchbomb.confirm' option...
r23488
Yuya Nishihara
patchbomb: add config knob to generate flags by template (issue5354)...
r31187 You can specify a template for flags to be added in subject prefixes. Flags
specified by --flag option are exported as ``{flags}`` keyword::
[patchbomb]
flagtemplate = "{separate(' ',
ifeq(branch, 'default', '', branch|upper),
flags)}"
Pierre-Yves David
patchbomb: introduce a 'patchbomb.confirm' option...
r23488 You can set patchbomb to always ask for confirmation by setting
``patchbomb.confirm`` to true.
Martin Geisler
patchbomb: wrapped docstrings at 78 characters
r9071 '''
timeless
patchbomb: use absolute_import
r28415 from __future__ import absolute_import
Vadim Gelfer
turn patchbomb script into an extension module....
r1669
Augie Fackler
patchbomb: use email.encoders instead of email.Encoders...
r39067 import email.encoders as emailencoders
Pulkit Goyal
py3: use email.generator module instead of email.Generator...
r36453 import email.generator as emailgen
Pulkit Goyal
patchbomb: use email.mime.base instead of email.MIMEBase...
r38491 import email.mime.base as emimebase
Pulkit Goyal
patchbomb: use email.mime.multipart instead of email.MIMEMultipart...
r38490 import email.mime.multipart as emimemultipart
Pulkit Goyal
py3: use email.utils module instead of email.Utils...
r36466 import email.utils as eutil
timeless
patchbomb: use absolute_import
r28415 import errno
import os
import socket
Augie Fackler
python2.4: fix imports of sub-packages of the email package...
r19810
Yuya Nishihara
py3: move up symbol imports to enforce import-checker rules...
r29205 from mercurial.i18n import _
timeless
patchbomb: use absolute_import
r28415 from mercurial import (
cmdutil,
commands,
Pulkit Goyal
py3: use encoding.strtolocal() to convert str to bytes...
r36467 encoding,
timeless
patchbomb: use absolute_import
r28415 error,
Yuya Nishihara
patchbomb: add config knob to generate flags by template (issue5354)...
r31187 formatter,
timeless
patchbomb: use absolute_import
r28415 hg,
mail,
node as nodemod,
patch,
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 pycompat,
Yuya Nishihara
registrar: move cmdutil.command to registrar module (API)...
r32337 registrar,
timeless
patchbomb: use absolute_import
r28415 scmutil,
Yuya Nishihara
patchbomb: add config knob to generate flags by template (issue5354)...
r31187 templater,
timeless
patchbomb: use absolute_import
r28415 util,
)
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 from mercurial.utils import dateutil
timeless
pycompat: switch to util.stringio for py3 compat
r28861 stringio = util.stringio
Vadim Gelfer
turn patchbomb script into an extension module....
r1669
Adrian Buehlmann
patchbomb: use cmdutil.command decorator
r14309 cmdtable = {}
Yuya Nishihara
registrar: move cmdutil.command to registrar module (API)...
r32337 command = registrar.command(cmdtable)
Boris Feld
configitems: register the 'patchbomb.bundletype' config
r34113
configtable = {}
configitem = registrar.configitem(configtable)
configitem('patchbomb', 'bundletype',
default=None,
)
Boris Feld
configitems: register the 'patchbomb.bcc' config
r34761 configitem('patchbomb', 'bcc',
default=None,
)
Boris Feld
configitems: register the 'patchbomb.cc' config
r34762 configitem('patchbomb', 'cc',
default=None,
)
Boris Feld
configitems: register the 'patchbomb.confirm' config
r34114 configitem('patchbomb', 'confirm',
default=False,
)
Boris Feld
configitems: register the 'patchbomb.flagtemplate' config
r34115 configitem('patchbomb', 'flagtemplate',
default=None,
)
Boris Feld
configitems: register the 'patchbomb.from' config
r34116 configitem('patchbomb', 'from',
default=None,
)
Boris Feld
configitems: register the 'patchbomb.intro' config
r34117 configitem('patchbomb', 'intro',
default='auto',
)
Boris Feld
configitems: register the 'patchbomb.publicurl' config
r34118 configitem('patchbomb', 'publicurl',
default=None,
)
Boris Feld
configitems: register the 'patchbomb.reply-to' config
r34763 configitem('patchbomb', 'reply-to',
default=None,
)
Yuya Nishihara
configitems: register 'email.to' and 'patchbomb.to'
r34912 configitem('patchbomb', 'to',
default=None,
)
Boris Feld
configitems: register the 'patchbomb.bundletype' config
r34113
Augie Fackler
patchbomb: work around email module really wanting to write unicode data...
r39057 if pycompat.ispy3:
_bytesgenerator = emailgen.BytesGenerator
else:
Yuya Nishihara
patchbomb: really use BytesGenerator API...
r39140 _bytesgenerator = emailgen.Generator
Augie Fackler
patchbomb: work around email module really wanting to write unicode data...
r39057
Augie Fackler
extensions: change magic "shipped with hg" string...
r29841 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
Augie Fackler
extensions: document that `testedwith = 'internal'` is special...
r25186 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
# be specifying the version(s) of Mercurial they are tested with, or
# leave the attribute unspecified.
Augie Fackler
extensions: change magic "shipped with hg" string...
r29841 testedwith = 'ships-with-hg-core'
Adrian Buehlmann
patchbomb: use cmdutil.command decorator
r14309
Pierre-Yves David
patchbomb: add experimental config of a "pullurl" and export it...
r26546 def _addpullheader(seq, ctx):
"""Add a header pointing to a public URL where the changeset is available
"""
repo = ctx.repo()
# experimental config: patchbomb.publicurl
# waiting for some logic that check that the changeset are available on the
# destination before patchbombing anything.
Augie Fackler
patchbomb: make variable name for publicurl always be publicurl
r32824 publicurl = repo.ui.config('patchbomb', 'publicurl')
Augie Fackler
patchbomb: look for non-empty publicurl, not a non-None one...
r32825 if publicurl:
Pierre-Yves David
patchbomb: add experimental config of a "pullurl" and export it...
r26546 return ('Available At %s\n'
Augie Fackler
patchbomb: make variable name for publicurl always be publicurl
r32824 '# hg pull %s -r %s' % (publicurl, publicurl, ctx))
Pierre-Yves David
patchbomb: add experimental config of a "pullurl" and export it...
r26546 return None
def uisetup(ui):
cmdutil.extraexport.append('pullurl')
cmdutil.extraexportmap['pullurl'] = _addpullheader
Boris Feld
repovfs: add a ward to check if locks are properly taken...
r33436 def reposetup(ui, repo):
if not repo.local():
return
repo._wlockfreeprefix.add('last-email.txt')
Pierre-Yves David
patchbomb: add experimental config of a "pullurl" and export it...
r26546
Alexander Solovyov
patchbomb: accept default if it is empty string...
r9648 def prompt(ui, prompt, default=None, rest=':'):
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354 if default:
prompt += ' [%s]' % default
Matt Mackall
patchbomb: drop loop in prompt...
r15166 return ui.prompt(prompt + rest, default)
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354
Pierre-Yves David
patchbomb: add a 'patchbomb.intro' option...
r23487 def introwanted(ui, opts, number):
Greg Ward
patchbomb: make it easy for the user to decline sending an intro message....
r15164 '''is an introductory message apparently wanted?'''
Boris Feld
configitems: register the 'patchbomb.intro' config
r34117 introconfig = ui.config('patchbomb', 'intro')
Pierre-Yves David
patchbomb: add a 'patchbomb.intro' option...
r23487 if opts.get('intro') or opts.get('desc'):
intro = True
elif introconfig == 'always':
intro = True
elif introconfig == 'never':
intro = False
elif introconfig == 'auto':
Martin von Zweigbergk
cleanup: some Yoda conditions, this patch removes...
r40065 intro = number > 1
Pierre-Yves David
patchbomb: add a 'patchbomb.intro' option...
r23487 else:
ui.write_err(_('warning: invalid patchbomb.intro value "%s"\n')
% introconfig)
ui.write_err(_('(should be one of always, never, auto)\n'))
Martin von Zweigbergk
cleanup: some Yoda conditions, this patch removes...
r40065 intro = number > 1
Pierre-Yves David
patchbomb: add a 'patchbomb.intro' option...
r23487 return intro
Cédric Duval
patchbomb: --desc implies --intro...
r10734
Yuya Nishihara
patchbomb: add config knob to generate flags by template (issue5354)...
r31187 def _formatflags(ui, repo, rev, flags):
"""build flag string optionally by template"""
tmpl = ui.config('patchbomb', 'flagtemplate')
if not tmpl:
return ' '.join(flags)
out = util.stringio()
opts = {'template': templater.unquotestring(tmpl)}
with formatter.templateformatter(ui, out, 'patchbombflag', opts) as fm:
fm.startitem()
fm.context(ctx=repo[rev])
fm.write('flags', '%s', fm.formatlist(flags, name='flag'))
return out.getvalue()
Yuya Nishihara
patchbomb: pass around ui and revs that are needed for flag template...
r31186 def _formatprefix(ui, repo, rev, flags, idx, total, numbered):
Yuya Nishihara
patchbomb: factor out function that builds a prefix string to patch subject...
r31183 """build prefix to patch subject"""
Yuya Nishihara
patchbomb: add config knob to generate flags by template (issue5354)...
r31187 flag = _formatflags(ui, repo, rev, flags)
Yuya Nishihara
patchbomb: factor out function that builds a prefix string to patch subject...
r31183 if flag:
flag = ' ' + flag
if not numbered:
return '[PATCH%s]' % flag
else:
Pulkit Goyal
py3: use b"%d" to covert integer to bytes instead of str...
r36686 tlen = len("%d" % total)
Yuya Nishihara
patchbomb: factor out function that builds a prefix string to patch subject...
r31183 return '[PATCH %0*d of %d%s]' % (tlen, idx, total, flag)
Yuya Nishihara
patchbomb: pass around ui and revs that are needed for flag template...
r31186 def makepatch(ui, repo, rev, patchlines, opts, _charsets, idx, total, numbered,
Martin Geisler
patchbomb: rename argument to avoid shadowing patch module
r12199 patchname=None):
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354
desc = []
node = None
body = ''
Martin Geisler
patchbomb: rename argument to avoid shadowing patch module
r12199 for line in patchlines:
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354 if line.startswith('#'):
if line.startswith('# Node ID'):
node = line.split()[-1]
continue
if line.startswith('diff -r') or line.startswith('diff --git'):
break
desc.append(line)
if not patchname and not node:
raise ValueError
Angel Ezquerra
patchbomb: add --body flag to send patches as inline message body text...
r16307 if opts.get('attach') and not opts.get('body'):
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354 body = ('\n'.join(desc[1:]).strip() or
'Patch subject is complete summary.')
body += '\n\n\n'
if opts.get('plain'):
Martin Geisler
patchbomb: rename argument to avoid shadowing patch module
r12199 while patchlines and patchlines[0].startswith('# '):
patchlines.pop(0)
if patchlines:
patchlines.pop(0)
while patchlines and not patchlines[0].strip():
patchlines.pop(0)
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354
Henning Schild
patch: remove unused git parameter from patch.diffstat()...
r30407 ds = patch.diffstat(patchlines)
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354 if opts.get('diffstat'):
Christian Ebert
patchbomb: let diffstat prompt only once with complete summary...
r12200 body += ds + '\n\n'
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354
Angel Ezquerra
patchbomb: add --body flag to send patches as inline message body text...
r16307 addattachment = opts.get('attach') or opts.get('inline')
if not addattachment or opts.get('body'):
body += '\n'.join(patchlines)
if addattachment:
Pulkit Goyal
patchbomb: use email.mime.multipart instead of email.MIMEMultipart...
r38490 msg = emimemultipart.MIMEMultipart()
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354 if body:
msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
Brodie Rao
cleanup: eradicate long lines
r16683 p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch',
opts.get('test'))
timeless
patchbomb: use absolute_import
r28415 binnode = nodemod.bin(node)
timeless
Generally replace "file name" with "filename" in help and comments.
r8761 # if node is mq patch, it will have the patch file's name as a tag
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354 if not patchname:
patchtags = [t for t in repo.nodetags(binnode)
if t.endswith('.patch') or t.endswith('.diff')]
if patchtags:
patchname = patchtags[0]
elif total > 1:
Yuya Nishihara
cmdutil: pass ctx to makefilename() in place of repo/node pair (API)
r36222 patchname = cmdutil.makefilename(repo[node], '%b-%n.patch',
seqno=idx, total=total)
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354 else:
Yuya Nishihara
cmdutil: pass ctx to makefilename() in place of repo/node pair (API)
r36222 patchname = cmdutil.makefilename(repo[node], '%b.patch')
Augie Fackler
patchbomb: use native strings when determining attachment disposition...
r39073 disposition = r'inline'
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354 if opts.get('attach'):
Augie Fackler
patchbomb: use native strings when determining attachment disposition...
r39073 disposition = r'attachment'
p[r'Content-Disposition'] = (
disposition + r'; filename=' + encoding.strfromlocal(patchname))
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354 msg.attach(p)
else:
msg = mail.mimetextpatch(body, display=opts.get('test'))
Yuya Nishihara
patchbomb: pass around ui and revs that are needed for flag template...
r31186 prefix = _formatprefix(ui, repo, rev, opts.get('flag'), idx, total,
numbered)
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354 subj = desc[0].strip().rstrip('. ')
Greg Ward
patchbomb: make it easy for the user to decline sending an intro message....
r15164 if not numbered:
Yuya Nishihara
patchbomb: factor out function that builds a prefix string to patch subject...
r31183 subj = ' '.join([prefix, opts.get('subject') or subj])
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354 else:
Yuya Nishihara
patchbomb: factor out function that builds a prefix string to patch subject...
r31183 subj = ' '.join([prefix, subj])
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
msg['X-Mercurial-Node'] = node
Pierre-Yves David
patchbomb: includes series information in the header...
r21282 msg['X-Mercurial-Series-Index'] = '%i' % idx
msg['X-Mercurial-Series-Total'] = '%i' % total
Christian Ebert
patchbomb: let diffstat prompt only once with complete summary...
r12200 return msg, subj, ds
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354
Pierre-Yves David
patchbomb: extract 'getpatches' closure in its own function...
r23210 def _getpatches(repo, revs, **opts):
"""return a list of patches for a list of revisions
Each patch in the list is itself a list of lines.
"""
ui = repo.ui
prev = repo['.'].rev()
Yuya Nishihara
patchbomb: factor out scmutil.revrange() calls...
r24568 for r in revs:
Pierre-Yves David
patchbomb: extract 'getpatches' closure in its own function...
r23210 if r == prev and (repo[None].files() or repo[None].deleted()):
ui.warn(_('warning: working directory has '
'uncommitted changes\n'))
timeless
pycompat: switch to util.stringio for py3 compat
r28861 output = stringio()
Yuya Nishihara
export: extract function to write patch to file object (API)...
r37621 cmdutil.exportfile(repo, [r], output,
opts=patch.difffeatureopts(ui, opts, git=True))
Pierre-Yves David
patchbomb: extract 'getpatches' closure in its own function...
r23210 yield output.getvalue().split('\n')
Pierre-Yves David
patchbomb: extract 'getbundle' closure in its own function...
r23211 def _getbundle(repo, dest, **opts):
"""return a bundle containing changesets missing in "dest"
The `opts` keyword-arguments are the same as the one accepted by the
`bundle` command.
The bundle is a returned as a single in-memory binary blob.
"""
ui = repo.ui
Yuya Nishihara
py3: wrap tempfile.mkdtemp() to use bytes path...
r38183 tmpdir = pycompat.mkdtemp(prefix='hg-email-bundle-')
Pierre-Yves David
patchbomb: extract 'getbundle' closure in its own function...
r23211 tmpfn = os.path.join(tmpdir, 'bundle')
Pierre-Yves David
patchbomb: add a 'bundletype' config under 'patchbomb'...
r26563 btype = ui.config('patchbomb', 'bundletype')
if btype:
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 opts[r'type'] = btype
Pierre-Yves David
patchbomb: extract 'getbundle' closure in its own function...
r23211 try:
commands.bundle(ui, repo, tmpfn, dest, **opts)
Bryan O'Sullivan
patchbomb: replace file I/O with util.readfile
r27767 return util.readfile(tmpfn)
Pierre-Yves David
patchbomb: extract 'getbundle' closure in its own function...
r23211 finally:
try:
os.unlink(tmpfn)
except OSError:
pass
os.rmdir(tmpdir)
Pierre-Yves David
patchbomb: extract 'getdescription' closure in its own function...
r23212 def _getdescription(repo, defaultbody, sender, **opts):
"""obtain the body of the introduction message and return it
This is also used for the body of email with an attached bundle.
The body can be obtained either from the command line option or entered by
the user through the editor.
"""
ui = repo.ui
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 if opts.get(r'desc'):
body = open(opts.get(r'desc')).read()
Pierre-Yves David
patchbomb: extract 'getdescription' closure in its own function...
r23212 else:
ui.write(_('\nWrite the introductory message for the '
'patch series.\n\n'))
Michael Bolin
editor: use an unambiguous path suffix for editor files...
r34030 body = ui.edit(defaultbody, sender, repopath=repo.path,
action='patchbombbody')
Pierre-Yves David
patchbomb: extract 'getdescription' closure in its own function...
r23212 # Save series description in case sendmail fails
Angel Ezquerra
localrepo: remove all external users of localrepo.opener...
r23877 msgfile = repo.vfs('last-email.txt', 'wb')
Pierre-Yves David
patchbomb: extract 'getdescription' closure in its own function...
r23212 msgfile.write(body)
msgfile.close()
return body
Pierre-Yves David
patchbomb: extract 'getbundle' closure in its own function...
r23211
Pierre-Yves David
patchbomb: extract 'getbundlemsgs' closure in its own function...
r23213 def _getbundlemsgs(repo, sender, bundle, **opts):
"""Get the full email for sending a given bundle
This function returns a list of "email" tuples (subject, content, None).
The list is always one message long in that case.
"""
ui = repo.ui
_charsets = mail._charsets(ui)
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 subj = (opts.get(r'subject')
Pierre-Yves David
patchbomb: extract 'getbundlemsgs' closure in its own function...
r23213 or prompt(ui, 'Subject:', 'A bundle for your repository'))
body = _getdescription(repo, '', sender, **opts)
Pulkit Goyal
patchbomb: use email.mime.multipart instead of email.MIMEMultipart...
r38490 msg = emimemultipart.MIMEMultipart()
Pierre-Yves David
patchbomb: extract 'getbundlemsgs' closure in its own function...
r23213 if body:
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get(r'test')))
Augie Fackler
patchbomb: use sysstrs when describing content-type...
r39069 datapart = emimebase.MIMEBase(r'application', r'x-mercurial-bundle')
Pierre-Yves David
patchbomb: extract 'getbundlemsgs' closure in its own function...
r23213 datapart.set_payload(bundle)
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 bundlename = '%s.hg' % opts.get(r'bundlename', 'bundle')
Augie Fackler
patchbomb: use native strs for email header keys and values...
r39068 datapart.add_header(r'Content-Disposition', r'attachment',
filename=encoding.strfromlocal(bundlename))
Augie Fackler
patchbomb: use email.encoders instead of email.Encoders...
r39067 emailencoders.encode_base64(datapart)
Pierre-Yves David
patchbomb: extract 'getbundlemsgs' closure in its own function...
r23213 msg.attach(datapart)
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get(r'test'))
Pierre-Yves David
patchbomb: extract 'getbundlemsgs' closure in its own function...
r23213 return [(msg, subj, None)]
Yuya Nishihara
patchbomb: pass around ui and revs that are needed for flag template...
r31186 def _makeintro(repo, sender, revs, patches, **opts):
Pierre-Yves David
patchbomb: extract 'makeintro' closure into its own function...
r23214 """make an introduction email, asking the user for content if needed
email is returned as (subject, body, cumulative-diffstat)"""
ui = repo.ui
_charsets = mail._charsets(ui)
Yuya Nishihara
patchbomb: pass around ui and revs that are needed for flag template...
r31186 # use the last revision which is likely to be a bookmarked head
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 prefix = _formatprefix(ui, repo, revs.last(), opts.get(r'flag'),
Yuya Nishihara
patchbomb: pass around ui and revs that are needed for flag template...
r31186 0, len(patches), numbered=True)
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 subj = (opts.get(r'subject') or
Pierre-Yves David
patchbomb: extract 'makeintro' closure into its own function...
r23214 prompt(ui, '(optional) Subject: ', rest=prefix, default=''))
if not subj:
return None # skip intro if the user doesn't bother
subj = prefix + ' ' + subj
body = ''
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 if opts.get(r'diffstat'):
Pierre-Yves David
patchbomb: extract 'makeintro' closure into its own function...
r23214 # generate a cumulative diffstat of the whole patch series
diffstat = patch.diffstat(sum(patches, []))
body = '\n' + diffstat
else:
diffstat = None
body = _getdescription(repo, body, sender, **opts)
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 msg = mail.mimeencode(ui, body, _charsets, opts.get(r'test'))
Pierre-Yves David
patchbomb: extract 'makeintro' closure into its own function...
r23214 msg['Subject'] = mail.headencode(ui, subj, _charsets,
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 opts.get(r'test'))
Pierre-Yves David
patchbomb: extract 'makeintro' closure into its own function...
r23214 return (msg, subj, diffstat)
Yuya Nishihara
patchbomb: build patch texts by _getpatchmsgs()...
r31185 def _getpatchmsgs(repo, sender, revs, patchnames=None, **opts):
Pierre-Yves David
patchbomb: extract 'getpatchmsgs' closure into its own function...
r23215 """return a list of emails from a list of patches
This involves introduction message creation if necessary.
This function returns a list of "email" tuples (subject, content, None).
"""
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 bytesopts = pycompat.byteskwargs(opts)
Pierre-Yves David
patchbomb: extract 'getpatchmsgs' closure into its own function...
r23215 ui = repo.ui
_charsets = mail._charsets(ui)
Yuya Nishihara
patchbomb: build patch texts by _getpatchmsgs()...
r31185 patches = list(_getpatches(repo, revs, **opts))
Pierre-Yves David
patchbomb: extract 'getpatchmsgs' closure into its own function...
r23215 msgs = []
ui.write(_('this patch series consists of %d patches.\n\n')
% len(patches))
# build the intro message, or skip it if the user declines
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 if introwanted(ui, bytesopts, len(patches)):
Yuya Nishihara
patchbomb: pass around ui and revs that are needed for flag template...
r31186 msg = _makeintro(repo, sender, revs, patches, **opts)
Pierre-Yves David
patchbomb: extract 'getpatchmsgs' closure into its own function...
r23215 if msg:
msgs.append(msg)
# are we going to send more than one message?
numbered = len(msgs) + len(patches) > 1
# now generate the actual patch messages
name = None
Yuya Nishihara
patchbomb: pass around ui and revs that are needed for flag template...
r31186 assert len(revs) == len(patches)
for i, (r, p) in enumerate(zip(revs, patches)):
Pierre-Yves David
patchbomb: extract 'getpatchmsgs' closure into its own function...
r23215 if patchnames:
name = patchnames[i]
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 msg = makepatch(ui, repo, r, p, bytesopts, _charsets,
i + 1, len(patches), numbered, name)
Pierre-Yves David
patchbomb: extract 'getpatchmsgs' closure into its own function...
r23215 msgs.append(msg)
return msgs
Pierre-Yves David
patchbomb: extract 'getoutgoing' closure into its own function...
r23486 def _getoutgoing(repo, dest, revs):
'''Return the revisions present locally but not in dest'''
ui = repo.ui
url = ui.expandpath(dest or 'default-push', dest or 'default')
url = hg.parseurl(url)[0]
ui.status(_('comparing with %s\n') % util.hidepassword(url))
Yuya Nishihara
patchbomb: factor out scmutil.revrange() calls...
r24568 revs = [r for r in revs if r >= 0]
Pierre-Yves David
patchbomb: extract 'getoutgoing' closure into its own function...
r23486 if not revs:
Boris Feld
patchbomb: use 'tiprev' when appropriate...
r35692 revs = [repo.changelog.tiprev()]
Pierre-Yves David
patchbomb: extract 'getoutgoing' closure into its own function...
r23486 revs = repo.revs('outgoing(%s) and ::%ld', dest or '', revs)
if not revs:
ui.status(_("no changes found\n"))
Yuya Nishihara
patchbomb: return outgoing revs as a smartset...
r24567 return revs
Pierre-Yves David
patchbomb: extract 'getoutgoing' closure into its own function...
r23486
Augie Fackler
patchbomb: extract function for generating message-id...
r39153 def _msgid(node, timestamp):
Augie Fackler
patchbomb: allow using HGHOSTNAME to force a hostname...
r39154 hostname = encoding.strtolocal(socket.getfqdn())
hostname = encoding.environ.get('HGHOSTNAME', hostname)
return '<%s.%d@%s>' % (node, timestamp, hostname)
Augie Fackler
patchbomb: extract function for generating message-id...
r39153
Adrian Buehlmann
patchbomb: use cmdutil.command decorator
r14309 emailopts = [
Angel Ezquerra
patchbomb: add --body flag to send patches as inline message body text...
r16307 ('', 'body', None, _('send patches as inline message text (default)')),
Adrian Buehlmann
patchbomb: use cmdutil.command decorator
r14309 ('a', 'attach', None, _('send patches as attachments')),
('i', 'inline', None, _('send patches as inline attachments')),
av6
commands: adjust metavariables as appropriate...
r40382 ('', 'bcc', [],
_('email addresses of blind carbon copy recipients'), _('EMAIL')),
('c', 'cc', [], _('email addresses of copy recipients'), _('EMAIL')),
Adrian Buehlmann
patchbomb: use cmdutil.command decorator
r14309 ('', 'confirm', None, _('ask for confirmation before sending')),
('d', 'diffstat', None, _('add diffstat output to messages')),
av6
commands: adjust metavariables as appropriate...
r40382 ('', 'date', '', _('use the given date as the sending date'), _('DATE')),
('', 'desc', '',
_('use the given file as the series description'), _('FILE')),
('f', 'from', '', _('email address of sender'), _('EMAIL')),
Adrian Buehlmann
patchbomb: use cmdutil.command decorator
r14309 ('n', 'test', None, _('print messages that would be sent')),
av6
commands: adjust metavariables as appropriate...
r40382 ('m', 'mbox', '',
_('write messages to mbox file instead of sending them'), _('FILE')),
('', 'reply-to', [],
_('email addresses replies should be sent to'), _('EMAIL')),
('s', 'subject', '',
_('subject of first message (intro or single patch)'), _('TEXT')),
('', 'in-reply-to', '', _('message identifier to reply to'), _('MSGID')),
('', 'flag', [], _('flags to add in subject prefixes'), _('FLAG')),
('t', 'to', [], _('email addresses of recipients'), _('EMAIL'))]
Adrian Buehlmann
patchbomb: use cmdutil.command decorator
r14309
@command('email',
[('g', 'git', None, _('use git extended diff format')),
('', 'plain', None, _('omit hg patch header')),
('o', 'outgoing', None,
_('send changes not found in the target repository')),
('b', 'bundle', None, _('send changes not in target as a binary bundle')),
av6
commands: adjust metavariables as appropriate...
r40382 ('B', 'bookmark', '',
_('send changes only reachable by given bookmark'), _('BOOKMARK')),
Adrian Buehlmann
patchbomb: use cmdutil.command decorator
r14309 ('', 'bundlename', 'bundle',
_('name of the bundle attachment file'), _('NAME')),
('r', 'rev', [], _('a revision to send'), _('REV')),
('', 'force', None, _('run even when remote repository is unrelated '
'(with -b/--bundle)')),
('', 'base', [], _('a base changeset to specify instead of a destination '
'(with -b/--bundle)'), _('REV')),
('', 'intro', None, _('send an introduction email for a single patch')),
Yuya Nishihara
commands: move templates of common command options to cmdutil (API)...
r32375 ] + emailopts + cmdutil.remoteopts,
rdamazio@google.com
help: assigning categories to existing commands...
r40329 _('hg email [OPTION]... [DEST]...'),
helpcategory=command.CATEGORY_IMPORT_EXPORT)
timeless
patchbomb: rename email function...
r27150 def email(ui, repo, *revs, **opts):
John Goerzen
Slight refining to help text in patchbomb.py
r4283 '''send changesets by email
Vadim Gelfer
turn patchbomb script into an extension module....
r1669
Martin Geisler
Use our custom hg reStructuredText role some more...
r11193 By default, diffs are sent in the format generated by
:hg:`export`, one per message. The series starts with a "[PATCH 0
of N]" introduction, which describes the series as a whole.
Vadim Gelfer
add documentation for email command.
r1672
Martin Geisler
patchbomb: wrap docstrings at 70 characters
r9269 Each patch email has a Subject line of "[PATCH M of N] ...", using
the first line of the changeset description as the subject text.
The message contains two or three parts. First, the changeset
timeless
patchbomb: move command option help from the extension (patchbomb) to the command (email)
r12749 description.
With the -d/--diffstat option, if the diffstat program is
installed, the result of running diffstat on the patch is inserted.
Finally, the patch itself, as generated by :hg:`export`.
Julian Cowley
patchbomb: -c is not an alias for the --confirm option...
r17880 With the -d/--diffstat or --confirm options, you will be presented
timeless
patchbomb: move command option help from the extension (patchbomb) to the command (email)
r12749 with a final summary of all messages and asked for confirmation before
the messages are sent.
Brendan Cully
Add --outgoing option to patchbomb
r4262
Martin Geisler
patchbomb: wrap docstrings at 70 characters
r9269 By default the patch is included as text in the email body for
easy reviewing. Using the -a/--attach option will instead create
an attachment for the patch. With -i/--inline an inline attachment
Angel Ezquerra
patchbomb: add --body flag to send patches as inline message body text...
r16307 will be created. You can include a patch both as text in the email
body and as a regular or an inline attachment by combining the
-a/--attach or -i/--inline with the --body option.
Martin Geisler
patchbomb: describe --attach and --inline options in help
r8472
David Demelier
patchbomb: add -B option to select a bookmark...
r32639 With -B/--bookmark changesets reachable by the given bookmark are
selected.
Martin Geisler
patchbomb: wrap docstrings at 70 characters
r9269 With -o/--outgoing, emails will be generated for patches not found
in the destination repository (or only those which are ancestors
of the specified revisions if any are provided)
John Goerzen
Improve documentation for patchbomb and email
r4280
Martin Geisler
patchbomb: wrap docstrings at 70 characters
r9269 With -b/--bundle, changesets are selected as for --outgoing, but a
single email containing a binary Mercurial bundle as an attachment
Pierre-Yves David
patchbomb: add a 'bundletype' config under 'patchbomb'...
r26563 will be sent. Use the ``patchbomb.bundletype`` config option to
control the bundle type as with :hg:`bundle --type`.
John Goerzen
Improve documentation for patchbomb and email
r4280
timeless
patchbomb: move command option help from the extension (patchbomb) to the command (email)
r12749 With -m/--mbox, instead of previewing each patchbomb message in a
pager or sending the messages directly, it will create a UNIX
mailbox file with the patch emails. This mailbox file can be
previewed with any mail user agent which supports UNIX mbox
files.
With -n/--test, all steps will run, but mail will not be sent.
You will be prompted for an email recipient address, a subject and
an introductory message describing the patches of your patchbomb.
Yuya Nishihara
patchbomb: use modern pager to display -n/--test result (BC)...
r31489 Then when all is done, patchbomb messages are displayed.
timeless
patchbomb: move command option help from the extension (patchbomb) to the command (email)
r12749
Nicolas Dumazet
patchbomb: save introductory message in .hg/last-email.txt
r13198 In case email sending fails, you will find a backup of your series
Pierre-Yves David
patchbomb: introduce a 'patchbomb.confirm' option...
r23488 introductory message in ``.hg/last-email.txt``.
The default behavior of this command can be customized through
configuration. (See :hg:`help patchbomb` for details)
Nicolas Dumazet
patchbomb: save introductory message in .hg/last-email.txt
r13198
Christian Ebert
patchbomb: reST syntax for literal blocks in help text
r9289 Examples::
John Goerzen
Improve documentation for patchbomb and email
r4280
Christian Ebert
patchbomb: reST syntax for literal blocks in help text
r9289 hg email -r 3000 # send patch 3000 only
hg email -r 3000 -r 3001 # send patches 3000 and 3001
hg email -r 3000:3005 # send patches 3000 through 3005
hg email 3000 # send patch 3000 (deprecated)
John Goerzen
Improve documentation for patchbomb and email
r4280
Christian Ebert
patchbomb: reST syntax for literal blocks in help text
r9289 hg email -o # send all patches not in default
hg email -o DEST # send all patches not in DEST
hg email -o -r 3000 # send all ancestors of 3000 not in default
hg email -o -r 3000 DEST # send all ancestors of 3000 not in DEST
John Goerzen
Improve documentation for patchbomb and email
r4280
David Demelier
patchbomb: add -B option to select a bookmark...
r32639 hg email -B feature # send all ancestors of feature bookmark
Christian Ebert
patchbomb: reST syntax for literal blocks in help text
r9289 hg email -b # send bundle of all patches not in default
hg email -b DEST # send bundle of all patches not in DEST
hg email -b -r 3000 # bundle of all ancestors of 3000 not in default
hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST
John Goerzen
Improve documentation for patchbomb and email
r4280
timeless
patchbomb: move command option help from the extension (patchbomb) to the command (email)
r12749 hg email -o -m mbox && # generate an mbox file...
mutt -R -f mbox # ... and view it with mutt
hg email -o -m mbox && # generate an mbox file ...
Martin Geisler
patchbomb: fix stray backslash in docstring...
r12839 formail -s sendmail \\ # ... and use formail to send from the mbox
timeless
patchbomb: move command option help from the extension (patchbomb) to the command (email)
r12749 -bm -t < mbox # ... using sendmail
Martin Geisler
patchbomb: wrap docstrings at 70 characters
r9269 Before using this command, you will need to enable email in your
hgrc. See the [email] section in hgrc(5) for details.
Brendan Cully
Add --outgoing option to patchbomb
r4262 '''
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 opts = pycompat.byteskwargs(opts)
Brendan Cully
Add --outgoing option to patchbomb
r4262
Christian Ebert
patchbomb: mime-encode headers and parts not containing patches...
r7115 _charsets = mail._charsets(ui)
Christian Ebert
patchbomb: reduce number of opts.get calls...
r11413 bundle = opts.get('bundle')
date = opts.get('date')
mbox = opts.get('mbox')
outgoing = opts.get('outgoing')
rev = opts.get('rev')
David Demelier
patchbomb: add -B option to select a bookmark...
r32639 bookmark = opts.get('bookmark')
Christian Ebert
patchbomb: reduce number of opts.get calls...
r11413
if not (opts.get('test') or mbox):
Christian Ebert
Catch smtp exceptions
r5472 # really sending
Bryan O'Sullivan
patchbomb: Validate email config before we start prompting for info.
r4489 mail.validateconfig(ui)
David Demelier
patchbomb: add -B option to select a bookmark...
r32639 if not (revs or rev or outgoing or bundle or bookmark):
raise error.Abort(_('specify at least one changeset with -B, -r or -o'))
Bryan O'Sullivan
patchbomb: Fail early if no revs given to email
r4493
Christian Ebert
patchbomb: reduce number of opts.get calls...
r11413 if outgoing and bundle:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("--outgoing mode always on with --bundle;"
Christian Ebert
patchbomb: break lines > 80 chars (coding style)
r5746 " do not re-specify --outgoing"))
David Demelier
patchbomb: avoid -r and -B options at the same time
r32695 if rev and bookmark:
raise error.Abort(_("-r and -B are mutually exclusive"))
John Goerzen
Add ability to send bundles to patchbomb extension
r4278
Christian Ebert
patchbomb: reduce number of opts.get calls...
r11413 if outgoing or bundle:
Brendan Cully
Add --outgoing option to patchbomb
r4262 if len(revs) > 1:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("too many destinations"))
Jordi Gutiérrez Hermoso
style: kill ersatz if-else ternary operators...
r24306 if revs:
dest = revs[0]
else:
dest = None
Brendan Cully
Add --outgoing option to patchbomb
r4262 revs = []
Christian Ebert
patchbomb: reduce number of opts.get calls...
r11413 if rev:
Brendan Cully
Add --outgoing option to patchbomb
r4262 if revs:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('use only one form to specify the revision'))
Christian Ebert
patchbomb: reduce number of opts.get calls...
r11413 revs = rev
David Demelier
patchbomb: add -B option to select a bookmark...
r32639 elif bookmark:
if bookmark not in repo._bookmarks:
raise error.Abort(_("bookmark '%s' not found") % bookmark)
David Demelier
scmutil: move repair.stripbmrevset as scmutil.bookmarkrevs (API)
r38146 revs = scmutil.bookmarkrevs(repo, bookmark)
Brendan Cully
Add --outgoing option to patchbomb
r4262
Yuya Nishihara
patchbomb: factor out scmutil.revrange() calls...
r24568 revs = scmutil.revrange(repo, revs)
Christian Ebert
patchbomb: reduce number of opts.get calls...
r11413 if outgoing:
Yuya Nishihara
patchbomb: factor out scmutil.revrange() calls...
r24568 revs = _getoutgoing(repo, dest, revs)
Christian Ebert
patchbomb: reduce number of opts.get calls...
r11413 if bundle:
Pulkit Goyal
py3: use b"%d" to covert integer to bytes instead of str...
r36686 opts['revs'] = ["%d" % r for r in revs]
Brendan Cully
Add --outgoing option to patchbomb
r4262
Pierre-Yves David
patchbomb: check that targets exist at the publicurl...
r26626 # check if revision exist on the public destination
publicurl = repo.ui.config('patchbomb', 'publicurl')
Augie Fackler
patchbomb: look for non-empty publicurl, not a non-None one...
r32825 if publicurl:
Kyle Lippincott
debug: add newlines at the end of three locations that appear to need it...
r35482 repo.ui.debug('checking that revision exist in the public repo\n')
Pierre-Yves David
patchbomb: check that targets exist at the publicurl...
r26626 try:
publicpeer = hg.peer(repo, {}, publicurl)
except error.RepoError:
repo.ui.write_err(_('unable to access public repo: %s\n')
% publicurl)
raise
if not publicpeer.capable('known'):
Kyle Lippincott
debug: add newlines at the end of three locations that appear to need it...
r35482 repo.ui.debug('skipping existence checks: public repo too old\n')
Pierre-Yves David
patchbomb: check that targets exist at the publicurl...
r26626 else:
out = [repo[r] for r in revs]
known = publicpeer.known(h.node() for h in out)
missing = []
for idx, h in enumerate(out):
if not known[idx]:
missing.append(h)
if missing:
Martin von Zweigbergk
cleanup: some Yoda conditions, this patch removes...
r40065 if len(missing) > 1:
Pierre-Yves David
patchbomb: check that targets exist at the publicurl...
r26626 msg = _('public "%s" is missing %s and %i others')
msg %= (publicurl, missing[0], len(missing) - 1)
else:
msg = _('public url %s is missing %s')
msg %= (publicurl, missing[0])
Gregory Szorc
patchbomb: resolve revs before evaluating %ld revset...
r36428 missingrevs = [ctx.rev() for ctx in missing]
timeless
patchbomb: fix public-is-missing hint...
r28907 revhint = ' '.join('-r %s' % h
Gregory Szorc
patchbomb: resolve revs before evaluating %ld revset...
r36428 for h in repo.set('heads(%ld)', missingrevs))
timeless
patchbomb: use single quotes around command hint...
r28981 hint = _("use 'hg push %s %s'") % (publicurl, revhint)
Pierre-Yves David
patchbomb: check that targets exist at the publicurl...
r26626 raise error.Abort(msg, hint=hint)
Brendan Cully
Add --outgoing option to patchbomb
r4262 # start
Christian Ebert
patchbomb: reduce number of opts.get calls...
r11413 if date:
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 start_time = dateutil.parsedate(date)
Bryan O'Sullivan
patchbomb: add --date option
r4566 else:
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 start_time = dateutil.makedate()
Vadim Gelfer
turn patchbomb script into an extension module....
r1669
def genmsgid(id):
Augie Fackler
patchbomb: extract function for generating message-id...
r39153 return _msgid(id[:20], int(start_time[0]))
Vadim Gelfer
turn patchbomb script into an extension module....
r1669
Matt Mackall
patchbomb: mark ancient option deprecated...
r25825 # deprecated config: patchbomb.from
Christian Ebert
patchbomb: consistently use opts.get
r5818 sender = (opts.get('from') or ui.config('email', 'from') or
Vadim Gelfer
rename [patchbomb] section to [email] section in hgrc. old name still ok.
r2198 ui.config('patchbomb', 'from') or
Dirkjan Ochtman
patchbomb: extract a bunch of nested functions...
r7354 prompt(ui, 'From', ui.username()))
Vadim Gelfer
turn patchbomb script into an extension module....
r1669
Yuya Nishihara
patchbomb: drop internal option for pbranch extension (API)...
r31184 if bundle:
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 stropts = pycompat.strkwargs(opts)
bundledata = _getbundle(repo, dest, **stropts)
bundleopts = stropts.copy()
bundleopts.pop(r'bundle', None) # already processed
Pierre-Yves David
patchbomb: extract 'getbundlemsgs' closure in its own function...
r23213 msgs = _getbundlemsgs(repo, sender, bundledata, **bundleopts)
John Goerzen
Add ability to send bundles to patchbomb extension
r4278 else:
Pulkit Goyal
py3: handle keyword arguments correctly in hgext/patchbomb.py...
r35035 msgs = _getpatchmsgs(repo, sender, revs, **pycompat.strkwargs(opts))
Vadim Gelfer
turn patchbomb script into an extension module....
r1669
Christian Ebert
patchbomb: let diffstat prompt only once with complete summary...
r12200 showaddrs = []
Greg Ward
patchbomb: simplify some contorted logic and odd variable names.
r15162 def getaddrs(header, ask=False, default=None):
configkey = header.lower()
opt = header.replace('-', '_').lower()
addrs = opts.get(opt)
Cédric Duval
patchbomb: Reply-To support...
r11150 if addrs:
Greg Ward
patchbomb: simplify some contorted logic and odd variable names.
r15162 showaddrs.append('%s: %s' % (header, ', '.join(addrs)))
Christian Ebert
patchbomb: consistent code style in getaddrs()
r12264 return mail.addrlistencode(ui, addrs, _charsets, opts.get('test'))
Marti Raudsepp
patchbomb: fix parsing of multiple addresses, allow multiple addrs in --to/cc/bcc...
r9947
Greg Ward
patchbomb: simplify some contorted logic and odd variable names.
r15162 # not on the command line: fallback to config and then maybe ask
addr = (ui.config('email', configkey) or
Bryan O'Sullivan
patchbomb: treat empty address list as no addresses...
r27697 ui.config('patchbomb', configkey))
if not addr:
specified = (ui.hasconfig('email', configkey) or
ui.hasconfig('patchbomb', configkey))
if not specified and ask:
addr = prompt(ui, header, default=default)
Greg Ward
patchbomb: simplify some contorted logic and odd variable names.
r15162 if addr:
showaddrs.append('%s: %s' % (header, addr))
Greg Ward
patchbomb: make it easy for the user to decline sending an intro message....
r15164 return mail.addrlistencode(ui, [addr], _charsets, opts.get('test'))
Augie Fackler
patchbomb: make getaddrs function easier to work with...
r32826 elif default:
return mail.addrlistencode(
ui, [default], _charsets, opts.get('test'))
return []
Marti Raudsepp
patchbomb: fix parsing of multiple addresses, allow multiple addrs in --to/cc/bcc...
r9947
Greg Ward
patchbomb: simplify some contorted logic and odd variable names.
r15162 to = getaddrs('To', ask=True)
Greg Ward
patchbomb: make it easy for the user to decline sending an intro message....
r15164 if not to:
# we can get here in non-interactive mode
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('no recipient addresses provided'))
Augie Fackler
patchbomb: make getaddrs function easier to work with...
r32826 cc = getaddrs('Cc', ask=True, default='')
bcc = getaddrs('Bcc')
Greg Ward
patchbomb: simplify some contorted logic and odd variable names.
r15162 replyto = getaddrs('Reply-To')
Christian Ebert
optionally send blind carbon copies...
r2679
Pierre-Yves David
patchbomb: introduce a 'patchbomb.confirm' option...
r23488 confirm = ui.configbool('patchbomb', 'confirm')
confirm |= bool(opts.get('diffstat') or opts.get('confirm'))
if confirm:
Pierre-Yves David
patchbomb: add label and color to the confirm output...
r23173 ui.write(_('\nFinal summary:\n\n'), label='patchbomb.finalsummary')
ui.write(('From: %s\n' % sender), label='patchbomb.from')
Christian Ebert
patchbomb: let diffstat prompt only once with complete summary...
r12200 for addr in showaddrs:
Pierre-Yves David
patchbomb: add label and color to the confirm output...
r23173 ui.write('%s\n' % addr, label='patchbomb.to')
Christian Ebert
patchbomb: let diffstat prompt only once with complete summary...
r12200 for m, subj, ds in msgs:
Pierre-Yves David
patchbomb: add label and color to the confirm output...
r23173 ui.write(('Subject: %s\n' % subj), label='patchbomb.subject')
Christian Ebert
patchbomb: let diffstat prompt only once with complete summary...
r12200 if ds:
Pierre-Yves David
patchbomb: add label and color to the confirm output...
r23173 ui.write(ds, label='patchbomb.diffstats')
Christian Ebert
patchbomb: let diffstat prompt only once with complete summary...
r12200 ui.write('\n')
Matt Mackall
ui: merge prompt text components into a singe string...
r19226 if ui.promptchoice(_('are you sure you want to send (yn)?'
'$$ &Yes $$ &No')):
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('patchbomb canceled'))
Christian Ebert
patchbomb: let diffstat prompt only once with complete summary...
r12200
Vadim Gelfer
turn patchbomb script into an extension module....
r1669 ui.write('\n')
Henrik Stuart <henrik.stuart at edlund.dk>
patchbomb: Support initial in-reply-to header...
r8025 parent = opts.get('in_reply_to') or None
Cédric Duval
patchbomb: do not assume the presence of angle brackets around msg-id...
r8826 # angle brackets may be omitted, they're not semantically part of the msg-id
if parent is not None:
if not parent.startswith('<'):
parent = '<' + parent
if not parent.endswith('>'):
parent += '>'
Pulkit Goyal
py3: convert bytes to str using encoding.strfromlocal...
r36468 sender_addr = eutil.parseaddr(encoding.strfromlocal(sender))[1]
Christian Ebert
patchbomb: mime-encode headers and parts not containing patches...
r7115 sender = mail.addressencode(ui, sender, _charsets, opts.get('test'))
Matt Mackall
patchbomb: undo backout and fix bugs in the earlier patch
r5973 sendmail = None
Augie Fackler
patchbomb: always use message-id of first patch for series-id...
r21726 firstpatch = None
Martin von Zweigbergk
patchbomb: use progress helper...
r38422 progress = ui.makeprogress(_('sending'), unit=_('emails'), total=len(msgs))
Yuya Nishihara
patchbomb: show progress when sending emails or writing mbox
r12265 for i, (m, subj, ds) in enumerate(msgs):
Vadim Gelfer
turn patchbomb script into an extension module....
r1669 try:
m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
Augie Fackler
patchbomb: always use message-id of first patch for series-id...
r21726 if not firstpatch:
firstpatch = m['Message-Id']
Augie Fackler
patchbomb: reorder header insertions to clarify code...
r21727 m['X-Mercurial-Series-Id'] = firstpatch
Vadim Gelfer
turn patchbomb script into an extension module....
r1669 except TypeError:
m['Message-Id'] = genmsgid('patchbomb')
if parent:
m['In-Reply-To'] = parent
Benoit Allard
email: add References field in the header...
r7413 m['References'] = parent
Thomas Arendsen Hein
patchbomb: respect --in-reply-to for all mails if no intro message is sent...
r17859 if not parent or 'X-Mercurial-Node' not in m:
Vadim Gelfer
turn patchbomb script into an extension module....
r1669 parent = m['Message-Id']
Cédric Duval
patchbomb: with --in-reply-to, still thread message under first in series...
r8514
Henrik Stuart
patchbomb: add user agent header to mails
r8160 m['User-Agent'] = 'Mercurial-patchbomb/%s' % util.version()
Pulkit Goyal
py3: use email.utils module instead of email.Utils...
r36466 m['Date'] = eutil.formatdate(start_time[0], localtime=True)
Volker Kleinfeld
patchbomb does not handle email time stamp plattform independent
r2443
Christian Ebert
patchbomb: fix timezone offset in message date header...
r4027 start_time = (start_time[0] + 1, start_time[1])
Vadim Gelfer
turn patchbomb script into an extension module....
r1669 m['From'] = sender
m['To'] = ', '.join(to)
Christian Ebert
patchbomb: add linebreaks after colons (coding style)
r5785 if cc:
m['Cc'] = ', '.join(cc)
if bcc:
m['Bcc'] = ', '.join(bcc)
Cédric Duval
patchbomb: Reply-To support...
r11150 if replyto:
m['Reply-To'] = ', '.join(replyto)
Augie Fackler
patchbomb: ensure all headers and values given to email mod are native strings...
r38799 # Fix up all headers to be native strings.
# TODO(durin42): this should probably be cleaned up above in the future.
if pycompat.ispy3:
for hdr, val in list(m.items()):
Augie Fackler
patchbomb: don't unintentionally duplicate headers...
r39070 change = False
Augie Fackler
patchbomb: ensure all headers and values given to email mod are native strings...
r38799 if isinstance(hdr, bytes):
del m[hdr]
hdr = pycompat.strurl(hdr)
Augie Fackler
patchbomb: don't unintentionally duplicate headers...
r39070 change = True
Augie Fackler
patchbomb: ensure all headers and values given to email mod are native strings...
r38799 if isinstance(val, bytes):
val = pycompat.strurl(val)
Augie Fackler
patchbomb: don't unintentionally duplicate headers...
r39070 if not change:
# prevent duplicate headers
del m[hdr]
change = True
if change:
m[hdr] = val
Christian Ebert
patchbomb: consistently use opts.get
r5818 if opts.get('test'):
Martin Geisler
patchbomb: lowercase status messages
r16931 ui.status(_('displaying '), subj, ' ...\n')
Yuya Nishihara
patchbomb: use modern pager to display -n/--test result (BC)...
r31489 ui.pager('email')
Yuya Nishihara
patchbomb: really use BytesGenerator API...
r39140 generator = _bytesgenerator(ui, mangle_from_=False)
Vadim Gelfer
patchbomb: ignore exception if pager quits.
r1871 try:
Benoit Boissinot
patchbomb: Fix mangling of lines beginning with From...
r6447 generator.flatten(m, 0)
Yuya Nishihara
patchbomb: use modern pager to display -n/--test result (BC)...
r31489 ui.write('\n')
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except IOError as inst:
Vadim Gelfer
patchbomb: ignore exception if pager quits.
r1871 if inst.errno != errno.EPIPE:
raise
Mads Kiilerich
mail: mbox handling as a part of mail handling, refactored from patchbomb
r15560 else:
if not sendmail:
Gregory Szorc
mail: unsupport smtp.verifycert (BC)...
r29285 sendmail = mail.connect(ui, mbox=mbox)
Martin Geisler
patchbomb: lowercase status messages
r16931 ui.status(_('sending '), subj, ' ...\n')
Martin von Zweigbergk
patchbomb: use progress helper...
r38422 progress.update(i, item=subj)
Mads Kiilerich
mail: mbox handling as a part of mail handling, refactored from patchbomb
r15560 if not mbox:
# Exim does not remove the Bcc field
del m['Bcc']
timeless
pycompat: switch to util.stringio for py3 compat
r28861 fp = stringio()
Yuya Nishihara
patchbomb: really use BytesGenerator API...
r39140 generator = _bytesgenerator(fp, mangle_from_=False)
Benoit Boissinot
patchbomb: Fix mangling of lines beginning with From...
r6447 generator.flatten(m, 0)
Augie Fackler
patchbomb: python 3 really wants those email addresses in unicode...
r39062 alldests = to + bcc + cc
alldests = [encoding.strfromlocal(d) for d in alldests]
sendmail(sender_addr, alldests, fp.getvalue())
Vadim Gelfer
turn patchbomb script into an extension module....
r1669
Martin von Zweigbergk
patchbomb: use progress helper...
r38422 progress.complete()