hgext/patchbomb.py
changeset 27150 88aaddb1af88
parent 26626 dca161728dc9
child 27697 0ce0cfee497f
equal deleted inserted replaced
27149:2f804a38351e 27150:88aaddb1af88
    56 You can set patchbomb to always ask for confirmation by setting
    56 You can set patchbomb to always ask for confirmation by setting
    57 ``patchbomb.confirm`` to true.
    57 ``patchbomb.confirm`` to true.
    58 '''
    58 '''
    59 
    59 
    60 import os, errno, socket, tempfile, cStringIO
    60 import os, errno, socket, tempfile, cStringIO
    61 import email
    61 import email as emailmod
    62 
    62 
    63 from mercurial import cmdutil, commands, hg, mail, patch, util, error
    63 from mercurial import cmdutil, commands, hg, mail, patch, util, error
    64 from mercurial import scmutil
    64 from mercurial import scmutil
    65 from mercurial.i18n import _
    65 from mercurial.i18n import _
    66 from mercurial.node import bin
    66 from mercurial.node import bin
   153     addattachment = opts.get('attach') or opts.get('inline')
   153     addattachment = opts.get('attach') or opts.get('inline')
   154     if not addattachment or opts.get('body'):
   154     if not addattachment or opts.get('body'):
   155         body += '\n'.join(patchlines)
   155         body += '\n'.join(patchlines)
   156 
   156 
   157     if addattachment:
   157     if addattachment:
   158         msg = email.MIMEMultipart.MIMEMultipart()
   158         msg = emailmod.MIMEMultipart.MIMEMultipart()
   159         if body:
   159         if body:
   160             msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
   160             msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
   161         p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch',
   161         p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch',
   162                                opts.get('test'))
   162                                opts.get('test'))
   163         binnode = bin(node)
   163         binnode = bin(node)
   270     _charsets = mail._charsets(ui)
   270     _charsets = mail._charsets(ui)
   271     subj = (opts.get('subject')
   271     subj = (opts.get('subject')
   272             or prompt(ui, 'Subject:', 'A bundle for your repository'))
   272             or prompt(ui, 'Subject:', 'A bundle for your repository'))
   273 
   273 
   274     body = _getdescription(repo, '', sender, **opts)
   274     body = _getdescription(repo, '', sender, **opts)
   275     msg = email.MIMEMultipart.MIMEMultipart()
   275     msg = emailmod.MIMEMultipart.MIMEMultipart()
   276     if body:
   276     if body:
   277         msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
   277         msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
   278     datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
   278     datapart = emailmod.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
   279     datapart.set_payload(bundle)
   279     datapart.set_payload(bundle)
   280     bundlename = '%s.hg' % opts.get('bundlename', 'bundle')
   280     bundlename = '%s.hg' % opts.get('bundlename', 'bundle')
   281     datapart.add_header('Content-Disposition', 'attachment',
   281     datapart.add_header('Content-Disposition', 'attachment',
   282                         filename=bundlename)
   282                         filename=bundlename)
   283     email.Encoders.encode_base64(datapart)
   283     emailmod.Encoders.encode_base64(datapart)
   284     msg.attach(datapart)
   284     msg.attach(datapart)
   285     msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
   285     msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
   286     return [(msg, subj, None)]
   286     return [(msg, subj, None)]
   287 
   287 
   288 def _makeintro(repo, sender, patches, **opts):
   288 def _makeintro(repo, sender, patches, **opts):
   401     ('', 'base', [], _('a base changeset to specify instead of a destination '
   401     ('', 'base', [], _('a base changeset to specify instead of a destination '
   402        '(with -b/--bundle)'), _('REV')),
   402        '(with -b/--bundle)'), _('REV')),
   403     ('', 'intro', None, _('send an introduction email for a single patch')),
   403     ('', 'intro', None, _('send an introduction email for a single patch')),
   404     ] + emailopts + commands.remoteopts,
   404     ] + emailopts + commands.remoteopts,
   405     _('hg email [OPTION]... [DEST]...'))
   405     _('hg email [OPTION]... [DEST]...'))
   406 def patchbomb(ui, repo, *revs, **opts):
   406 def email(ui, repo, *revs, **opts):
   407     '''send changesets by email
   407     '''send changesets by email
   408 
   408 
   409     By default, diffs are sent in the format generated by
   409     By default, diffs are sent in the format generated by
   410     :hg:`export`, one per message. The series starts with a "[PATCH 0
   410     :hg:`export`, one per message. The series starts with a "[PATCH 0
   411     of N]" introduction, which describes the series as a whole.
   411     of N]" introduction, which describes the series as a whole.
   639         if not parent.startswith('<'):
   639         if not parent.startswith('<'):
   640             parent = '<' + parent
   640             parent = '<' + parent
   641         if not parent.endswith('>'):
   641         if not parent.endswith('>'):
   642             parent += '>'
   642             parent += '>'
   643 
   643 
   644     sender_addr = email.Utils.parseaddr(sender)[1]
   644     sender_addr = emailmod.Utils.parseaddr(sender)[1]
   645     sender = mail.addressencode(ui, sender, _charsets, opts.get('test'))
   645     sender = mail.addressencode(ui, sender, _charsets, opts.get('test'))
   646     sendmail = None
   646     sendmail = None
   647     firstpatch = None
   647     firstpatch = None
   648     for i, (m, subj, ds) in enumerate(msgs):
   648     for i, (m, subj, ds) in enumerate(msgs):
   649         try:
   649         try:
   658             m['References'] = parent
   658             m['References'] = parent
   659         if not parent or 'X-Mercurial-Node' not in m:
   659         if not parent or 'X-Mercurial-Node' not in m:
   660             parent = m['Message-Id']
   660             parent = m['Message-Id']
   661 
   661 
   662         m['User-Agent'] = 'Mercurial-patchbomb/%s' % util.version()
   662         m['User-Agent'] = 'Mercurial-patchbomb/%s' % util.version()
   663         m['Date'] = email.Utils.formatdate(start_time[0], localtime=True)
   663         m['Date'] = emailmod.Utils.formatdate(start_time[0], localtime=True)
   664 
   664 
   665         start_time = (start_time[0] + 1, start_time[1])
   665         start_time = (start_time[0] + 1, start_time[1])
   666         m['From'] = sender
   666         m['From'] = sender
   667         m['To'] = ', '.join(to)
   667         m['To'] = ', '.join(to)
   668         if cc:
   668         if cc:
   676             ui.flush()
   676             ui.flush()
   677             if 'PAGER' in os.environ and not ui.plain():
   677             if 'PAGER' in os.environ and not ui.plain():
   678                 fp = util.popen(os.environ['PAGER'], 'w')
   678                 fp = util.popen(os.environ['PAGER'], 'w')
   679             else:
   679             else:
   680                 fp = ui
   680                 fp = ui
   681             generator = email.Generator.Generator(fp, mangle_from_=False)
   681             generator = emailmod.Generator.Generator(fp, mangle_from_=False)
   682             try:
   682             try:
   683                 generator.flatten(m, 0)
   683                 generator.flatten(m, 0)
   684                 fp.write('\n')
   684                 fp.write('\n')
   685             except IOError as inst:
   685             except IOError as inst:
   686                 if inst.errno != errno.EPIPE:
   686                 if inst.errno != errno.EPIPE:
   700             ui.progress(_('sending'), i, item=subj, total=len(msgs))
   700             ui.progress(_('sending'), i, item=subj, total=len(msgs))
   701             if not mbox:
   701             if not mbox:
   702                 # Exim does not remove the Bcc field
   702                 # Exim does not remove the Bcc field
   703                 del m['Bcc']
   703                 del m['Bcc']
   704             fp = cStringIO.StringIO()
   704             fp = cStringIO.StringIO()
   705             generator = email.Generator.Generator(fp, mangle_from_=False)
   705             generator = emailmod.Generator.Generator(fp, mangle_from_=False)
   706             generator.flatten(m, 0)
   706             generator.flatten(m, 0)
   707             sendmail(sender_addr, to + bcc + cc, fp.getvalue())
   707             sendmail(sender_addr, to + bcc + cc, fp.getvalue())
   708 
   708 
   709     ui.progress(_('writing'), None)
   709     ui.progress(_('writing'), None)
   710     ui.progress(_('sending'), None)
   710     ui.progress(_('sending'), None)