hgext/patchbomb.py
changeset 7115 c5c2d43b01da
parent 7095 0ed11838bd1a
child 7192 f31ba106fc19
equal deleted inserted replaced
7114:30e49d54c537 7115:c5c2d43b01da
   108     hg email -b -r 3000 DEST  # bundle of all ancestors of 3000 not in DEST
   108     hg email -b -r 3000 DEST  # bundle of all ancestors of 3000 not in DEST
   109 
   109 
   110     Before using this command, you will need to enable email in your hgrc.
   110     Before using this command, you will need to enable email in your hgrc.
   111     See the [email] section in hgrc(5) for details.
   111     See the [email] section in hgrc(5) for details.
   112     '''
   112     '''
       
   113 
       
   114     _charsets = mail._charsets(ui)
   113 
   115 
   114     def prompt(prompt, default = None, rest = ': ', empty_ok = False):
   116     def prompt(prompt, default = None, rest = ': ', empty_ok = False):
   115         if not ui.interactive:
   117         if not ui.interactive:
   116             return default
   118             return default
   117         if default:
   119         if default:
   174         if opts.get('diffstat'):
   176         if opts.get('diffstat'):
   175             body += cdiffstat('\n'.join(desc), patch) + '\n\n'
   177             body += cdiffstat('\n'.join(desc), patch) + '\n\n'
   176         if opts.get('attach') or opts.get('inline'):
   178         if opts.get('attach') or opts.get('inline'):
   177             msg = email.MIMEMultipart.MIMEMultipart()
   179             msg = email.MIMEMultipart.MIMEMultipart()
   178             if body:
   180             if body:
   179                 msg.attach(email.MIMEText.MIMEText(body, 'plain'))
   181                 msg.attach(mail.mimeencode(ui, body, _charsets,
       
   182                                            opts.get('test')))
   180             p = email.MIMEText.MIMEText('\n'.join(patch), 'x-patch')
   183             p = email.MIMEText.MIMEText('\n'.join(patch), 'x-patch')
   181             binnode = bin(node)
   184             binnode = bin(node)
   182             # if node is mq patch, it will have patch file name as tag
   185             # if node is mq patch, it will have patch file name as tag
   183             patchname = [t for t in repo.nodetags(binnode)
   186             patchname = [t for t in repo.nodetags(binnode)
   184                          if t.endswith('.patch') or t.endswith('.diff')]
   187                          if t.endswith('.patch') or t.endswith('.diff')]
   202         if total == 1:
   205         if total == 1:
   203             subj = '[PATCH] ' + (opts.get('subject') or subj)
   206             subj = '[PATCH] ' + (opts.get('subject') or subj)
   204         else:
   207         else:
   205             tlen = len(str(total))
   208             tlen = len(str(total))
   206             subj = '[PATCH %0*d of %d] %s' % (tlen, idx, total, subj)
   209             subj = '[PATCH %0*d of %d] %s' % (tlen, idx, total, subj)
   207         msg['Subject'] = subj
   210         msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
   208         msg['X-Mercurial-Node'] = node
   211         msg['X-Mercurial-Node'] = node
   209         return msg
   212         return msg, subj
   210 
   213 
   211     def outgoing(dest, revs):
   214     def outgoing(dest, revs):
   212         '''Return the revisions present locally but not in dest'''
   215         '''Return the revisions present locally but not in dest'''
   213         dest = ui.expandpath(dest or 'default-push', dest or 'default')
   216         dest = ui.expandpath(dest or 'default-push', dest or 'default')
   214         revs = [repo.lookup(rev) for rev in revs]
   217         revs = [repo.lookup(rev) for rev in revs]
   326                 d = cdiffstat(_('Final summary:\n'), jumbo)
   329                 d = cdiffstat(_('Final summary:\n'), jumbo)
   327                 if d:
   330                 if d:
   328                     body = '\n' + d
   331                     body = '\n' + d
   329 
   332 
   330             body = getdescription(body, sender)
   333             body = getdescription(body, sender)
   331             msg = email.MIMEText.MIMEText(body)
   334             msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
   332             msg['Subject'] = subj
   335             msg['Subject'] = mail.headencode(ui, subj, _charsets,
   333 
   336                                              opts.get('test'))
   334             msgs.insert(0, msg)
   337 
       
   338             msgs.insert(0, (msg, subj))
   335         return msgs
   339         return msgs
   336 
   340 
   337     def getbundlemsgs(bundle):
   341     def getbundlemsgs(bundle):
   338         subj = (opts.get('subject')
   342         subj = (opts.get('subject')
   339                 or prompt('Subject:', default='A bundle for your repository'))
   343                 or prompt('Subject:', default='A bundle for your repository'))
   340 
   344 
   341         body = getdescription('', sender)
   345         body = getdescription('', sender)
   342         msg = email.MIMEMultipart.MIMEMultipart()
   346         msg = email.MIMEMultipart.MIMEMultipart()
   343         if body:
   347         if body:
   344             msg.attach(email.MIMEText.MIMEText(body, 'plain'))
   348             msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
   345         datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
   349         datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
   346         datapart.set_payload(bundle)
   350         datapart.set_payload(bundle)
   347         datapart.add_header('Content-Disposition', 'attachment',
   351         datapart.add_header('Content-Disposition', 'attachment',
   348                             filename='bundle.hg')
   352                             filename='bundle.hg')
   349         email.Encoders.encode_base64(datapart)
   353         email.Encoders.encode_base64(datapart)
   350         msg.attach(datapart)
   354         msg.attach(datapart)
   351         msg['Subject'] = subj
   355         msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
   352         return [msg]
   356         return [(msg, subj)]
   353 
   357 
   354     sender = (opts.get('from') or ui.config('email', 'from') or
   358     sender = (opts.get('from') or ui.config('email', 'from') or
   355               ui.config('patchbomb', 'from') or
   359               ui.config('patchbomb', 'from') or
   356               prompt('From', ui.username()))
   360               prompt('From', ui.username()))
   357 
   361 
   362 
   366 
   363     def getaddrs(opt, prpt, default = None):
   367     def getaddrs(opt, prpt, default = None):
   364         addrs = opts.get(opt) or (ui.config('email', opt) or
   368         addrs = opts.get(opt) or (ui.config('email', opt) or
   365                                   ui.config('patchbomb', opt) or
   369                                   ui.config('patchbomb', opt) or
   366                                   prompt(prpt, default = default)).split(',')
   370                                   prompt(prpt, default = default)).split(',')
   367         return [a.strip() for a in addrs if a.strip()]
   371         return [mail.addressencode(ui, a.strip(), _charsets, opts.get('test'))
       
   372                 for a in addrs if a.strip()]
   368 
   373 
   369     to = getaddrs('to', 'To')
   374     to = getaddrs('to', 'To')
   370     cc = getaddrs('cc', 'Cc', '')
   375     cc = getaddrs('cc', 'Cc', '')
   371 
   376 
   372     bcc = opts.get('bcc') or (ui.config('email', 'bcc') or
   377     bcc = opts.get('bcc') or (ui.config('email', 'bcc') or
   373                           ui.config('patchbomb', 'bcc') or '').split(',')
   378                           ui.config('patchbomb', 'bcc') or '').split(',')
   374     bcc = [a.strip() for a in bcc if a.strip()]
   379     bcc = [mail.addressencode(ui, a.strip(), _charsets, opts.get('test'))
       
   380            for a in bcc if a.strip()]
   375 
   381 
   376     ui.write('\n')
   382     ui.write('\n')
   377 
   383 
   378     parent = None
   384     parent = None
   379 
   385 
   380     sender_addr = email.Utils.parseaddr(sender)[1]
   386     sender_addr = email.Utils.parseaddr(sender)[1]
       
   387     sender = mail.addressencode(ui, sender, _charsets, opts.get('test'))
   381     sendmail = None
   388     sendmail = None
   382     for m in msgs:
   389     for m, subj in msgs:
   383         try:
   390         try:
   384             m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
   391             m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
   385         except TypeError:
   392         except TypeError:
   386             m['Message-Id'] = genmsgid('patchbomb')
   393             m['Message-Id'] = genmsgid('patchbomb')
   387         if parent:
   394         if parent:
   396         if cc:
   403         if cc:
   397             m['Cc']  = ', '.join(cc)
   404             m['Cc']  = ', '.join(cc)
   398         if bcc:
   405         if bcc:
   399             m['Bcc'] = ', '.join(bcc)
   406             m['Bcc'] = ', '.join(bcc)
   400         if opts.get('test'):
   407         if opts.get('test'):
   401             ui.status(_('Displaying '), m['Subject'], ' ...\n')
   408             ui.status(_('Displaying '), subj, ' ...\n')
   402             ui.flush()
   409             ui.flush()
   403             if 'PAGER' in os.environ:
   410             if 'PAGER' in os.environ:
   404                 fp = util.popen(os.environ['PAGER'], 'w')
   411                 fp = util.popen(os.environ['PAGER'], 'w')
   405             else:
   412             else:
   406                 fp = ui
   413                 fp = ui
   412                 if inst.errno != errno.EPIPE:
   419                 if inst.errno != errno.EPIPE:
   413                     raise
   420                     raise
   414             if fp is not ui:
   421             if fp is not ui:
   415                 fp.close()
   422                 fp.close()
   416         elif opts.get('mbox'):
   423         elif opts.get('mbox'):
   417             ui.status(_('Writing '), m['Subject'], ' ...\n')
   424             ui.status(_('Writing '), subj, ' ...\n')
   418             fp = open(opts.get('mbox'), 'In-Reply-To' in m and 'ab+' or 'wb+')
   425             fp = open(opts.get('mbox'), 'In-Reply-To' in m and 'ab+' or 'wb+')
   419             generator = email.Generator.Generator(fp, mangle_from_=True)
   426             generator = email.Generator.Generator(fp, mangle_from_=True)
   420             date = util.datestr(start_time, '%a %b %d %H:%M:%S %Y')
   427             date = util.datestr(start_time, '%a %b %d %H:%M:%S %Y')
   421             fp.write('From %s %s\n' % (sender_addr, date))
   428             fp.write('From %s %s\n' % (sender_addr, date))
   422             generator.flatten(m, 0)
   429             generator.flatten(m, 0)
   423             fp.write('\n\n')
   430             fp.write('\n\n')
   424             fp.close()
   431             fp.close()
   425         else:
   432         else:
   426             if not sendmail:
   433             if not sendmail:
   427                 sendmail = mail.connect(ui)
   434                 sendmail = mail.connect(ui)
   428             ui.status(_('Sending '), m['Subject'], ' ...\n')
   435             ui.status(_('Sending '), subj, ' ...\n')
   429             # Exim does not remove the Bcc field
   436             # Exim does not remove the Bcc field
   430             del m['Bcc']
   437             del m['Bcc']
   431             fp = cStringIO.StringIO()
   438             fp = cStringIO.StringIO()
   432             generator = email.Generator.Generator(fp, mangle_from_=False)
   439             generator = email.Generator.Generator(fp, mangle_from_=False)
   433             generator.flatten(m, 0)
   440             generator.flatten(m, 0)