hgext/patchbomb.py
changeset 35044 71e63fe6b1ab
parent 34911 645b6684cf5b
child 35466 7906354cbc68
equal deleted inserted replaced
35043:5d4369079c86 35044:71e63fe6b1ab
    87     formatter,
    87     formatter,
    88     hg,
    88     hg,
    89     mail,
    89     mail,
    90     node as nodemod,
    90     node as nodemod,
    91     patch,
    91     patch,
       
    92     pycompat,
    92     registrar,
    93     registrar,
    93     repair,
    94     repair,
    94     scmutil,
    95     scmutil,
    95     templater,
    96     templater,
    96     util,
    97     util,
   316     ui = repo.ui
   317     ui = repo.ui
   317     tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-')
   318     tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-')
   318     tmpfn = os.path.join(tmpdir, 'bundle')
   319     tmpfn = os.path.join(tmpdir, 'bundle')
   319     btype = ui.config('patchbomb', 'bundletype')
   320     btype = ui.config('patchbomb', 'bundletype')
   320     if btype:
   321     if btype:
   321         opts['type'] = btype
   322         opts[r'type'] = btype
   322     try:
   323     try:
   323         commands.bundle(ui, repo, tmpfn, dest, **opts)
   324         commands.bundle(ui, repo, tmpfn, dest, **opts)
   324         return util.readfile(tmpfn)
   325         return util.readfile(tmpfn)
   325     finally:
   326     finally:
   326         try:
   327         try:
   336 
   337 
   337     The body can be obtained either from the command line option or entered by
   338     The body can be obtained either from the command line option or entered by
   338     the user through the editor.
   339     the user through the editor.
   339     """
   340     """
   340     ui = repo.ui
   341     ui = repo.ui
   341     if opts.get('desc'):
   342     if opts.get(r'desc'):
   342         body = open(opts.get('desc')).read()
   343         body = open(opts.get(r'desc')).read()
   343     else:
   344     else:
   344         ui.write(_('\nWrite the introductory message for the '
   345         ui.write(_('\nWrite the introductory message for the '
   345                    'patch series.\n\n'))
   346                    'patch series.\n\n'))
   346         body = ui.edit(defaultbody, sender, repopath=repo.path,
   347         body = ui.edit(defaultbody, sender, repopath=repo.path,
   347                        action='patchbombbody')
   348                        action='patchbombbody')
   357     This function returns a list of "email" tuples (subject, content, None).
   358     This function returns a list of "email" tuples (subject, content, None).
   358     The list is always one message long in that case.
   359     The list is always one message long in that case.
   359     """
   360     """
   360     ui = repo.ui
   361     ui = repo.ui
   361     _charsets = mail._charsets(ui)
   362     _charsets = mail._charsets(ui)
   362     subj = (opts.get('subject')
   363     subj = (opts.get(r'subject')
   363             or prompt(ui, 'Subject:', 'A bundle for your repository'))
   364             or prompt(ui, 'Subject:', 'A bundle for your repository'))
   364 
   365 
   365     body = _getdescription(repo, '', sender, **opts)
   366     body = _getdescription(repo, '', sender, **opts)
   366     msg = emailmod.MIMEMultipart.MIMEMultipart()
   367     msg = emailmod.MIMEMultipart.MIMEMultipart()
   367     if body:
   368     if body:
   368         msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test')))
   369         msg.attach(mail.mimeencode(ui, body, _charsets, opts.get(r'test')))
   369     datapart = emailmod.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
   370     datapart = emailmod.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
   370     datapart.set_payload(bundle)
   371     datapart.set_payload(bundle)
   371     bundlename = '%s.hg' % opts.get('bundlename', 'bundle')
   372     bundlename = '%s.hg' % opts.get(r'bundlename', 'bundle')
   372     datapart.add_header('Content-Disposition', 'attachment',
   373     datapart.add_header('Content-Disposition', 'attachment',
   373                         filename=bundlename)
   374                         filename=bundlename)
   374     emailmod.Encoders.encode_base64(datapart)
   375     emailmod.Encoders.encode_base64(datapart)
   375     msg.attach(datapart)
   376     msg.attach(datapart)
   376     msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test'))
   377     msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get(r'test'))
   377     return [(msg, subj, None)]
   378     return [(msg, subj, None)]
   378 
   379 
   379 def _makeintro(repo, sender, revs, patches, **opts):
   380 def _makeintro(repo, sender, revs, patches, **opts):
   380     """make an introduction email, asking the user for content if needed
   381     """make an introduction email, asking the user for content if needed
   381 
   382 
   382     email is returned as (subject, body, cumulative-diffstat)"""
   383     email is returned as (subject, body, cumulative-diffstat)"""
   383     ui = repo.ui
   384     ui = repo.ui
   384     _charsets = mail._charsets(ui)
   385     _charsets = mail._charsets(ui)
   385 
   386 
   386     # use the last revision which is likely to be a bookmarked head
   387     # use the last revision which is likely to be a bookmarked head
   387     prefix = _formatprefix(ui, repo, revs.last(), opts.get('flag'),
   388     prefix = _formatprefix(ui, repo, revs.last(), opts.get(r'flag'),
   388                            0, len(patches), numbered=True)
   389                            0, len(patches), numbered=True)
   389     subj = (opts.get('subject') or
   390     subj = (opts.get(r'subject') or
   390             prompt(ui, '(optional) Subject: ', rest=prefix, default=''))
   391             prompt(ui, '(optional) Subject: ', rest=prefix, default=''))
   391     if not subj:
   392     if not subj:
   392         return None         # skip intro if the user doesn't bother
   393         return None         # skip intro if the user doesn't bother
   393 
   394 
   394     subj = prefix + ' ' + subj
   395     subj = prefix + ' ' + subj
   395 
   396 
   396     body = ''
   397     body = ''
   397     if opts.get('diffstat'):
   398     if opts.get(r'diffstat'):
   398         # generate a cumulative diffstat of the whole patch series
   399         # generate a cumulative diffstat of the whole patch series
   399         diffstat = patch.diffstat(sum(patches, []))
   400         diffstat = patch.diffstat(sum(patches, []))
   400         body = '\n' + diffstat
   401         body = '\n' + diffstat
   401     else:
   402     else:
   402         diffstat = None
   403         diffstat = None
   403 
   404 
   404     body = _getdescription(repo, body, sender, **opts)
   405     body = _getdescription(repo, body, sender, **opts)
   405     msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
   406     msg = mail.mimeencode(ui, body, _charsets, opts.get(r'test'))
   406     msg['Subject'] = mail.headencode(ui, subj, _charsets,
   407     msg['Subject'] = mail.headencode(ui, subj, _charsets,
   407                                      opts.get('test'))
   408                                      opts.get(r'test'))
   408     return (msg, subj, diffstat)
   409     return (msg, subj, diffstat)
   409 
   410 
   410 def _getpatchmsgs(repo, sender, revs, patchnames=None, **opts):
   411 def _getpatchmsgs(repo, sender, revs, patchnames=None, **opts):
   411     """return a list of emails from a list of patches
   412     """return a list of emails from a list of patches
   412 
   413 
   413     This involves introduction message creation if necessary.
   414     This involves introduction message creation if necessary.
   414 
   415 
   415     This function returns a list of "email" tuples (subject, content, None).
   416     This function returns a list of "email" tuples (subject, content, None).
   416     """
   417     """
       
   418     bytesopts = pycompat.byteskwargs(opts)
   417     ui = repo.ui
   419     ui = repo.ui
   418     _charsets = mail._charsets(ui)
   420     _charsets = mail._charsets(ui)
   419     patches = list(_getpatches(repo, revs, **opts))
   421     patches = list(_getpatches(repo, revs, **opts))
   420     msgs = []
   422     msgs = []
   421 
   423 
   422     ui.write(_('this patch series consists of %d patches.\n\n')
   424     ui.write(_('this patch series consists of %d patches.\n\n')
   423              % len(patches))
   425              % len(patches))
   424 
   426 
   425     # build the intro message, or skip it if the user declines
   427     # build the intro message, or skip it if the user declines
   426     if introwanted(ui, opts, len(patches)):
   428     if introwanted(ui, bytesopts, len(patches)):
   427         msg = _makeintro(repo, sender, revs, patches, **opts)
   429         msg = _makeintro(repo, sender, revs, patches, **opts)
   428         if msg:
   430         if msg:
   429             msgs.append(msg)
   431             msgs.append(msg)
   430 
   432 
   431     # are we going to send more than one message?
   433     # are we going to send more than one message?
   435     name = None
   437     name = None
   436     assert len(revs) == len(patches)
   438     assert len(revs) == len(patches)
   437     for i, (r, p) in enumerate(zip(revs, patches)):
   439     for i, (r, p) in enumerate(zip(revs, patches)):
   438         if patchnames:
   440         if patchnames:
   439             name = patchnames[i]
   441             name = patchnames[i]
   440         msg = makepatch(ui, repo, r, p, opts, _charsets, i + 1,
   442         msg = makepatch(ui, repo, r, p, bytesopts, _charsets,
   441                         len(patches), numbered, name)
   443                         i + 1, len(patches), numbered, name)
   442         msgs.append(msg)
   444         msgs.append(msg)
   443 
   445 
   444     return msgs
   446     return msgs
   445 
   447 
   446 def _getoutgoing(repo, dest, revs):
   448 def _getoutgoing(repo, dest, revs):
   577           -bm -t < mbox         # ... using sendmail
   579           -bm -t < mbox         # ... using sendmail
   578 
   580 
   579     Before using this command, you will need to enable email in your
   581     Before using this command, you will need to enable email in your
   580     hgrc. See the [email] section in hgrc(5) for details.
   582     hgrc. See the [email] section in hgrc(5) for details.
   581     '''
   583     '''
       
   584     opts = pycompat.byteskwargs(opts)
   582 
   585 
   583     _charsets = mail._charsets(ui)
   586     _charsets = mail._charsets(ui)
   584 
   587 
   585     bundle = opts.get('bundle')
   588     bundle = opts.get('bundle')
   586     date = opts.get('date')
   589     date = opts.get('date')
   670     sender = (opts.get('from') or ui.config('email', 'from') or
   673     sender = (opts.get('from') or ui.config('email', 'from') or
   671               ui.config('patchbomb', 'from') or
   674               ui.config('patchbomb', 'from') or
   672               prompt(ui, 'From', ui.username()))
   675               prompt(ui, 'From', ui.username()))
   673 
   676 
   674     if bundle:
   677     if bundle:
   675         bundledata = _getbundle(repo, dest, **opts)
   678         stropts = pycompat.strkwargs(opts)
   676         bundleopts = opts.copy()
   679         bundledata = _getbundle(repo, dest, **stropts)
   677         bundleopts.pop('bundle', None)  # already processed
   680         bundleopts = stropts.copy()
       
   681         bundleopts.pop(r'bundle', None)  # already processed
   678         msgs = _getbundlemsgs(repo, sender, bundledata, **bundleopts)
   682         msgs = _getbundlemsgs(repo, sender, bundledata, **bundleopts)
   679     else:
   683     else:
   680         msgs = _getpatchmsgs(repo, sender, revs, **opts)
   684         msgs = _getpatchmsgs(repo, sender, revs, **pycompat.strkwargs(opts))
   681 
   685 
   682     showaddrs = []
   686     showaddrs = []
   683 
   687 
   684     def getaddrs(header, ask=False, default=None):
   688     def getaddrs(header, ask=False, default=None):
   685         configkey = header.lower()
   689         configkey = header.lower()