from stgit import argparse, stack, git, version, templates
from stgit.config import config
from stgit.run import Run
+from stgit.lib import git as gitlib
help = 'Send a patch or series of patches by e-mail'
kind = 'patch'
%(prefix)s - 'prefix ' string passed on the command line
%(shortdescr)s - the first line of the patch description"""
+args = [argparse.patch_range(argparse.applied_patches,
+ argparse.unapplied_patches,
+ argparse.hidden_patches)]
options = [
opt('-a', '--all', action = 'store_true',
short = 'E-mail all the applied patches'),
short = 'Password for SMTP authentication'),
opt('-T', '--smtp-tls', action = 'store_true',
short = 'Use SMTP with TLS encryption'),
- opt('-b', '--branch',
+ opt('-b', '--branch', args = [argparse.stg_branches],
short = 'Use BRANCH instead of the default branch'),
opt('-m', '--mbox', action = 'store_true',
short = 'Generate an mbox file instead of sending')
try:
sender = str(git.user())
except git.GitException:
- sender = str(git.author())
-
+ try:
+ sender = str(git.author())
+ except git.GitException:
+ pass
if not sender:
- raise CmdException, 'unknown sender details'
+ raise CmdException, ('Unknown sender name and e-mail; you should'
+ ' for example set git config user.name and'
+ ' user.email')
+ sender = email.Utils.parseaddr(sender)
- return address_or_alias(sender)
+ return email.Utils.formataddr(address_or_alias(sender))
+
+def __addr_list(msg, header):
+ return [addr for name, addr in
+ email.Utils.getaddresses(msg.get_all(header, []))]
def __parse_addresses(msg):
"""Return a two elements tuple: (from, [to])
"""
- def __addr_list(msg, header):
- return [name_addr[1] for name_addr in
- email.Utils.getaddresses(msg.get_all(header, []))]
-
from_addr_list = __addr_list(msg, 'From')
if len(from_addr_list) == 0:
raise CmdException, 'No "From" address'
if len(to_addr_list) == 0:
raise CmdException, 'No "To/Cc/Bcc" addresses'
- return (from_addr_list[0], to_addr_list)
+ return (from_addr_list[0], set(to_addr_list))
def __send_message_sendmail(sendmail, msg):
"""Send the message using the sendmail command.
"""Build the address headers and check existing headers in the
template.
"""
- def __replace_header(header, addr):
- if addr:
- crt_addr = msg[header]
- del msg[header]
-
- if crt_addr:
- msg[header] = address_or_alias(', '.join([crt_addr, addr]))
- else:
- msg[header] = address_or_alias(addr)
+ def __addr_pairs(msg, header, extra):
+ pairs = email.Utils.getaddresses(msg.get_all(header, []) + extra)
+ # remove pairs without an address and resolve the aliases
+ return [address_or_alias(p) for p in pairs if p[1]]
+
+ def __update_header(header, addr = '', ignore = ()):
+ addr_pairs = __addr_pairs(msg, header, [addr])
+ del msg[header]
+ # remove the duplicates and filter the addresses
+ addr_dict = dict((addr, email.Utils.formataddr((name, addr)))
+ for name, addr in addr_pairs if addr not in ignore)
+ if addr_dict:
+ msg[header] = ', '.join(addr_dict.itervalues())
+ return set(addr_dict.iterkeys())
to_addr = ''
cc_addr = ''
+ extra_cc_addr = ''
bcc_addr = ''
autobcc = config.get('stgit.autobcc') or ''
if options.to:
to_addr = ', '.join(options.to)
if options.cc:
- cc_addr = ', '.join(options.cc + extra_cc)
- cc_addr = ', '.join(options.cc + extra_cc)
- elif extra_cc:
- cc_addr = ', '.join(extra_cc)
+ cc_addr = ', '.join(options.cc)
+ if extra_cc:
+ extra_cc_addr = ', '.join(extra_cc)
if options.bcc:
bcc_addr = ', '.join(options.bcc + [autobcc])
elif autobcc:
bcc_addr = autobcc
- __replace_header('To', to_addr)
- __replace_header('Cc', cc_addr)
- __replace_header('Bcc', bcc_addr)
+ # if an address is on a header, ignore it from the rest
+ to_set = __update_header('To', to_addr)
+ cc_set = __update_header('Cc', cc_addr, to_set)
+ bcc_set = __update_header('Bcc', bcc_addr, to_set.union(cc_set))
+
+ # --auto generated addresses, don't include the sender
+ from_set = __update_header('From')
+ __update_header('Cc', extra_cc_addr, to_set.union(bcc_set).union(from_set))
+
+ # update other address headers
+ __update_header('Reply-To')
+ __update_header('Mail-Reply-To')
+ __update_header('Mail-Followup-To')
def __get_signers_list(msg):
"""Return the address list generated from signed-off-by and
acked-by lines in the message.
"""
addr_list = []
-
- r = re.compile('^(signed-off-by|acked-by|cc):\s+(.+)$', re.I)
+ tags = '%s|%s|%s|%s|%s|%s|%s' % (
+ 'signed-off-by',
+ 'acked-by',
+ 'cc',
+ 'reviewed-by',
+ 'reported-by',
+ 'tested-by',
+ 'reported-and-tested-by')
+ regex = '^(%s):\s+(.+)$' % tags
+
+ r = re.compile(regex, re.I)
for line in msg.split('\n'):
m = r.match(line)
if m:
ref_id = '<%s>' % ref_id.strip(' \t\n<>')
msg['In-Reply-To'] = ref_id
msg['References'] = ref_id
- msg['User-Agent'] = 'StGIT/%s' % version.version
+ msg['User-Agent'] = 'StGit/%s' % version.version
def __encode_message(msg):
# 7 or 8 bit encoding
'number': number_str,
'shortlog': stack.shortlog(crt_series.get_patch(p)
for p in patches),
- 'diffstat': git.diffstat(git.diff(
+ 'diffstat': gitlib.diffstat(git.diff(
rev1 = git_id(crt_series, '%s^' % patches[0]),
- rev2 = git_id(crt_series, '%s' % patches[-1])))}
+ rev2 = git_id(crt_series, '%s' % patches[-1]),
+ diff_flags = options.diff_flags))}
try:
msg_string = tmpl % tmpl_dict
# for backward template compatibility
'endofheaders': '',
'diff': diff,
- 'diffstat': git.diffstat(diff),
+ 'diffstat': gitlib.diffstat(diff),
# for backward template compatibility
'date': '',
'version': version_str,
else:
raise CmdException, 'Incorrect options. Unknown patches to send'
+ # early test for sender identity
+ __get_sender()
+
out.start('Checking the validity of the patches')
for p in patches:
if crt_series.empty_patch(p):