hgext/patchbomb.py
changeset 43626 cbcd72844df1
parent 43624 14b96072797d
child 43671 af3e341dbf03
equal deleted inserted replaced
43625:599e25add437 43626:cbcd72844df1
   319     subj = desc[0].strip().rstrip(b'. ')
   319     subj = desc[0].strip().rstrip(b'. ')
   320     if not numbered:
   320     if not numbered:
   321         subj = b' '.join([prefix, opts.get(b'subject') or subj])
   321         subj = b' '.join([prefix, opts.get(b'subject') or subj])
   322     else:
   322     else:
   323         subj = b' '.join([prefix, subj])
   323         subj = b' '.join([prefix, subj])
   324     msg[b'Subject'] = mail.headencode(ui, subj, _charsets, opts.get(b'test'))
   324     msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get(b'test'))
   325     msg[b'X-Mercurial-Node'] = node
   325     msg['X-Mercurial-Node'] = pycompat.sysstr(node)
   326     msg[b'X-Mercurial-Series-Index'] = b'%i' % idx
   326     msg['X-Mercurial-Series-Index'] = '%i' % idx
   327     msg[b'X-Mercurial-Series-Total'] = b'%i' % total
   327     msg['X-Mercurial-Series-Total'] = '%i' % total
   328     return msg, subj, ds
   328     return msg, subj, ds
   329 
   329 
   330 
   330 
   331 def _getpatches(repo, revs, **opts):
   331 def _getpatches(repo, revs, **opts):
   332     """return a list of patches for a list of revisions
   332     """return a list of patches for a list of revisions
   419         'attachment',
   419         'attachment',
   420         filename=encoding.strfromlocal(bundlename),
   420         filename=encoding.strfromlocal(bundlename),
   421     )
   421     )
   422     emailencoders.encode_base64(datapart)
   422     emailencoders.encode_base64(datapart)
   423     msg.attach(datapart)
   423     msg.attach(datapart)
   424     msg[b'Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
   424     msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
   425     return [(msg, subj, None)]
   425     return [(msg, subj, None)]
   426 
   426 
   427 
   427 
   428 def _makeintro(repo, sender, revs, patches, **opts):
   428 def _makeintro(repo, sender, revs, patches, **opts):
   429     """make an introduction email, asking the user for content if needed
   429     """make an introduction email, asking the user for content if needed
   452     else:
   452     else:
   453         diffstat = None
   453         diffstat = None
   454 
   454 
   455     body = _getdescription(repo, body, sender, **opts)
   455     body = _getdescription(repo, body, sender, **opts)
   456     msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
   456     msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
   457     msg[b'Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
   457     msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
   458     return (msg, subj, diffstat)
   458     return (msg, subj, diffstat)
   459 
   459 
   460 
   460 
   461 def _getpatchmsgs(repo, sender, revs, patchnames=None, **opts):
   461 def _getpatchmsgs(repo, sender, revs, patchnames=None, **opts):
   462     """return a list of emails from a list of patches
   462     """return a list of emails from a list of patches
   520         ui.status(_(b"no changes found\n"))
   520         ui.status(_(b"no changes found\n"))
   521     return revs
   521     return revs
   522 
   522 
   523 
   523 
   524 def _msgid(node, timestamp):
   524 def _msgid(node, timestamp):
   525     hostname = encoding.strtolocal(socket.getfqdn())
   525     try:
   526     hostname = encoding.environ.get(b'HGHOSTNAME', hostname)
   526         hostname = encoding.strfromlocal(encoding.environ[b'HGHOSTNAME'])
   527     return b'<%s.%d@%s>' % (node, timestamp, hostname)
   527     except KeyError:
       
   528         hostname = socket.getfqdn()
       
   529     return '<%s.%d@%s>' % (node, timestamp, hostname)
   528 
   530 
   529 
   531 
   530 emailopts = [
   532 emailopts = [
   531     (b'', b'body', None, _(b'send patches as inline message text (default)')),
   533     (b'', b'body', None, _(b'send patches as inline message text (default)')),
   532     (b'a', b'attach', None, _(b'send patches as attachments')),
   534     (b'a', b'attach', None, _(b'send patches as attachments')),
   910     ui.write(b'\n')
   912     ui.write(b'\n')
   911 
   913 
   912     parent = opts.get(b'in_reply_to') or None
   914     parent = opts.get(b'in_reply_to') or None
   913     # angle brackets may be omitted, they're not semantically part of the msg-id
   915     # angle brackets may be omitted, they're not semantically part of the msg-id
   914     if parent is not None:
   916     if parent is not None:
   915         if not parent.startswith(b'<'):
   917         parent = encoding.strfromlocal(parent)
   916             parent = b'<' + parent
   918         if not parent.startswith('<'):
   917         if not parent.endswith(b'>'):
   919             parent = '<' + parent
   918             parent += b'>'
   920         if not parent.endswith('>'):
       
   921             parent += '>'
   919 
   922 
   920     sender_addr = eutil.parseaddr(encoding.strfromlocal(sender))[1]
   923     sender_addr = eutil.parseaddr(encoding.strfromlocal(sender))[1]
   921     sender = mail.addressencode(ui, sender, _charsets, opts.get(b'test'))
   924     sender = mail.addressencode(ui, sender, _charsets, opts.get(b'test'))
   922     sendmail = None
   925     sendmail = None
   923     firstpatch = None
   926     firstpatch = None
   924     progress = ui.makeprogress(
   927     progress = ui.makeprogress(
   925         _(b'sending'), unit=_(b'emails'), total=len(msgs)
   928         _(b'sending'), unit=_(b'emails'), total=len(msgs)
   926     )
   929     )
   927     for i, (m, subj, ds) in enumerate(msgs):
   930     for i, (m, subj, ds) in enumerate(msgs):
   928         try:
   931         try:
   929             m[b'Message-Id'] = genmsgid(m[b'X-Mercurial-Node'])
   932             m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
   930             if not firstpatch:
   933             if not firstpatch:
   931                 firstpatch = m[b'Message-Id']
   934                 firstpatch = m['Message-Id']
   932             m[b'X-Mercurial-Series-Id'] = firstpatch
   935             m['X-Mercurial-Series-Id'] = firstpatch
   933         except TypeError:
   936         except TypeError:
   934             m[b'Message-Id'] = genmsgid(b'patchbomb')
   937             m['Message-Id'] = genmsgid('patchbomb')
   935         if parent:
   938         if parent:
   936             m[b'In-Reply-To'] = parent
   939             m['In-Reply-To'] = parent
   937             m[b'References'] = parent
   940             m['References'] = parent
   938         if not parent or b'X-Mercurial-Node' not in m:
   941         if not parent or 'X-Mercurial-Node' not in m:
   939             parent = m[b'Message-Id']
   942             parent = m['Message-Id']
   940 
   943 
   941         m[b'User-Agent'] = b'Mercurial-patchbomb/%s' % util.version()
   944         m['User-Agent'] = 'Mercurial-patchbomb/%s' % util.version().decode()
   942         m[b'Date'] = eutil.formatdate(start_time[0], localtime=True)
   945         m['Date'] = eutil.formatdate(start_time[0], localtime=True)
   943 
   946 
   944         start_time = (start_time[0] + 1, start_time[1])
   947         start_time = (start_time[0] + 1, start_time[1])
   945         m[b'From'] = sender
   948         m['From'] = sender
   946         m[b'To'] = ', '.join(to)
   949         m['To'] = ', '.join(to)
   947         if cc:
   950         if cc:
   948             m[b'Cc'] = ', '.join(cc)
   951             m['Cc'] = ', '.join(cc)
   949         if bcc:
   952         if bcc:
   950             m[b'Bcc'] = ', '.join(bcc)
   953             m['Bcc'] = ', '.join(bcc)
   951         if replyto:
   954         if replyto:
   952             m[b'Reply-To'] = ', '.join(replyto)
   955             m['Reply-To'] = ', '.join(replyto)
   953         # Fix up all headers to be native strings.
       
   954         # TODO(durin42): this should probably be cleaned up above in the future.
       
   955         if pycompat.ispy3:
       
   956             for hdr, val in list(m.items()):
       
   957                 change = False
       
   958                 if isinstance(hdr, bytes):
       
   959                     del m[hdr]
       
   960                     hdr = pycompat.strurl(hdr)
       
   961                     change = True
       
   962                 if isinstance(val, bytes):
       
   963                     # header value should be ASCII since it's encoded by
       
   964                     # mail.headencode(), but -n/--test disables it and raw
       
   965                     # value of platform encoding is stored.
       
   966                     val = encoding.strfromlocal(val)
       
   967                     if not change:
       
   968                         # prevent duplicate headers
       
   969                         del m[hdr]
       
   970                     change = True
       
   971                 if change:
       
   972                     m[hdr] = val
       
   973         if opts.get(b'test'):
   956         if opts.get(b'test'):
   974             ui.status(_(b'displaying '), subj, b' ...\n')
   957             ui.status(_(b'displaying '), subj, b' ...\n')
   975             ui.pager(b'email')
   958             ui.pager(b'email')
   976             generator = mail.Generator(ui, mangle_from_=False)
   959             generator = mail.Generator(ui, mangle_from_=False)
   977             try:
   960             try:
   985                 sendmail = mail.connect(ui, mbox=mbox)
   968                 sendmail = mail.connect(ui, mbox=mbox)
   986             ui.status(_(b'sending '), subj, b' ...\n')
   969             ui.status(_(b'sending '), subj, b' ...\n')
   987             progress.update(i, item=subj)
   970             progress.update(i, item=subj)
   988             if not mbox:
   971             if not mbox:
   989                 # Exim does not remove the Bcc field
   972                 # Exim does not remove the Bcc field
   990                 del m[b'Bcc']
   973                 del m['Bcc']
   991             fp = stringio()
   974             fp = stringio()
   992             generator = mail.Generator(fp, mangle_from_=False)
   975             generator = mail.Generator(fp, mangle_from_=False)
   993             generator.flatten(m, 0)
   976             generator.flatten(m, 0)
   994             alldests = to + bcc + cc
   977             alldests = to + bcc + cc
   995             sendmail(sender_addr, alldests, fp.getvalue())
   978             sendmail(sender_addr, alldests, fp.getvalue())