diff options
Diffstat (limited to 'eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/patchbomb.py')
-rw-r--r-- | eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/patchbomb.py | 553 |
1 files changed, 0 insertions, 553 deletions
diff --git a/eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/patchbomb.py b/eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/patchbomb.py deleted file mode 100644 index 93ea4cb..0000000 --- a/eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/hgext/patchbomb.py +++ /dev/null @@ -1,553 +0,0 @@ -# patchbomb.py - sending Mercurial changesets as patch emails -# -# Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. - -'''command to send changesets as (a series of) patch emails - -The series is started off with a "[PATCH 0 of N]" introduction, which -describes the series as a whole. - -Each patch email has a Subject line of "[PATCH M of N] ...", using the -first line of the changeset description as the subject text. The -message contains two or three body parts: - -- The changeset description. -- [Optional] The result of running diffstat on the patch. -- The patch itself, as generated by :hg:`export`. - -Each message refers to the first in the series using the In-Reply-To -and References headers, so they will show up as a sequence in threaded -mail and news readers, and in mail archives. - -To configure other defaults, add a section like this to your hgrc -file:: - - [email] - from = My Name <my@email> - to = recipient1, recipient2, ... - cc = cc1, cc2, ... - bcc = bcc1, bcc2, ... - reply-to = address1, address2, ... - -Use ``[patchbomb]`` as configuration section name if you need to -override global ``[email]`` address settings. - -Then you can use the :hg:`email` command to mail a series of -changesets as a patchbomb. - -You can also either configure the method option in the email section -to be a sendmail compatible mailer or fill out the [smtp] section so -that the patchbomb extension can automatically send patchbombs -directly from the commandline. See the [email] and [smtp] sections in -hgrc(5) for details. -''' - -import os, errno, socket, tempfile, cStringIO, time -import email.MIMEMultipart, email.MIMEBase -import email.Utils, email.Encoders, email.Generator -from mercurial import cmdutil, commands, hg, mail, patch, util, discovery, url -from mercurial.i18n import _ -from mercurial.node import bin - -def prompt(ui, prompt, default=None, rest=':'): - if not ui.interactive() and default is None: - raise util.Abort(_("%s Please enter a valid value" % (prompt + rest))) - if default: - prompt += ' [%s]' % default - prompt += rest - while True: - r = ui.prompt(prompt, default=default) - if r: - return r - if default is not None: - return default - ui.warn(_('Please enter a valid value.\n')) - -def introneeded(opts, number): - '''is an introductory message required?''' - return number > 1 or opts.get('intro') or opts.get('desc') - -def makepatch(ui, repo, patchlines, opts, _charsets, idx, total, - patchname=None): - - desc = [] - node = None - body = '' - - for line in patchlines: - if line.startswith('#'): - if line.startswith('# Node ID'): - node = line.split()[-1] - continue - if line.startswith('diff -r') or line.startswith('diff --git'): - break - desc.append(line) - - if not patchname and not node: - raise ValueError - - if opts.get('attach'): - body = ('\n'.join(desc[1:]).strip() or - 'Patch subject is complete summary.') - body += '\n\n\n' - - if opts.get('plain'): - while patchlines and patchlines[0].startswith('# '): - patchlines.pop(0) - if patchlines: - patchlines.pop(0) - while patchlines and not patchlines[0].strip(): - patchlines.pop(0) - - ds = patch.diffstat(patchlines) - if opts.get('diffstat'): - body += ds + '\n\n' - - if opts.get('attach') or opts.get('inline'): - msg = email.MIMEMultipart.MIMEMultipart() - if body: - msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test'))) - p = mail.mimetextpatch('\n'.join(patchlines), 'x-patch', opts.get('test')) - binnode = bin(node) - # if node is mq patch, it will have the patch file's name as a tag - if not patchname: - patchtags = [t for t in repo.nodetags(binnode) - if t.endswith('.patch') or t.endswith('.diff')] - if patchtags: - patchname = patchtags[0] - elif total > 1: - patchname = cmdutil.make_filename(repo, '%b-%n.patch', - binnode, seqno=idx, total=total) - else: - patchname = cmdutil.make_filename(repo, '%b.patch', binnode) - disposition = 'inline' - if opts.get('attach'): - disposition = 'attachment' - p['Content-Disposition'] = disposition + '; filename=' + patchname - msg.attach(p) - else: - body += '\n'.join(patchlines) - msg = mail.mimetextpatch(body, display=opts.get('test')) - - flag = ' '.join(opts.get('flag')) - if flag: - flag = ' ' + flag - - subj = desc[0].strip().rstrip('. ') - if not introneeded(opts, total): - subj = '[PATCH%s] %s' % (flag, opts.get('subject') or subj) - else: - tlen = len(str(total)) - subj = '[PATCH %0*d of %d%s] %s' % (tlen, idx, total, flag, subj) - msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test')) - msg['X-Mercurial-Node'] = node - return msg, subj, ds - -def patchbomb(ui, repo, *revs, **opts): - '''send changesets by email - - By default, diffs are sent in the format generated by - :hg:`export`, one per message. The series starts with a "[PATCH 0 - of N]" introduction, which describes the series as a whole. - - Each patch email has a Subject line of "[PATCH M of N] ...", using - the first line of the changeset description as the subject text. - The message contains two or three parts. First, the changeset - description. - - With the -d/--diffstat option, if the diffstat program is - installed, the result of running diffstat on the patch is inserted. - - Finally, the patch itself, as generated by :hg:`export`. - - With the -d/--diffstat or -c/--confirm options, you will be presented - with a final summary of all messages and asked for confirmation before - the messages are sent. - - By default the patch is included as text in the email body for - easy reviewing. Using the -a/--attach option will instead create - an attachment for the patch. With -i/--inline an inline attachment - will be created. - - With -o/--outgoing, emails will be generated for patches not found - in the destination repository (or only those which are ancestors - of the specified revisions if any are provided) - - With -b/--bundle, changesets are selected as for --outgoing, but a - single email containing a binary Mercurial bundle as an attachment - will be sent. - - With -m/--mbox, instead of previewing each patchbomb message in a - pager or sending the messages directly, it will create a UNIX - mailbox file with the patch emails. This mailbox file can be - previewed with any mail user agent which supports UNIX mbox - files. - - With -n/--test, all steps will run, but mail will not be sent. - You will be prompted for an email recipient address, a subject and - an introductory message describing the patches of your patchbomb. - Then when all is done, patchbomb messages are displayed. If the - PAGER environment variable is set, your pager will be fired up once - for each patchbomb message, so you can verify everything is alright. - - Examples:: - - hg email -r 3000 # send patch 3000 only - hg email -r 3000 -r 3001 # send patches 3000 and 3001 - hg email -r 3000:3005 # send patches 3000 through 3005 - hg email 3000 # send patch 3000 (deprecated) - - hg email -o # send all patches not in default - hg email -o DEST # send all patches not in DEST - hg email -o -r 3000 # send all ancestors of 3000 not in default - hg email -o -r 3000 DEST # send all ancestors of 3000 not in DEST - - hg email -b # send bundle of all patches not in default - hg email -b DEST # send bundle of all patches not in DEST - hg email -b -r 3000 # bundle of all ancestors of 3000 not in default - hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST - - hg email -o -m mbox && # generate an mbox file... - mutt -R -f mbox # ... and view it with mutt - hg email -o -m mbox && # generate an mbox file ... - formail -s sendmail \\ # ... and use formail to send from the mbox - -bm -t < mbox # ... using sendmail - - Before using this command, you will need to enable email in your - hgrc. See the [email] section in hgrc(5) for details. - ''' - - _charsets = mail._charsets(ui) - - bundle = opts.get('bundle') - date = opts.get('date') - mbox = opts.get('mbox') - outgoing = opts.get('outgoing') - rev = opts.get('rev') - # internal option used by pbranches - patches = opts.get('patches') - - def getoutgoing(dest, revs): - '''Return the revisions present locally but not in dest''' - dest = ui.expandpath(dest or 'default-push', dest or 'default') - dest, branches = hg.parseurl(dest) - revs, checkout = hg.addbranchrevs(repo, repo, branches, revs) - if revs: - revs = [repo.lookup(rev) for rev in revs] - other = hg.repository(hg.remoteui(repo, opts), dest) - ui.status(_('comparing with %s\n') % url.hidepassword(dest)) - o = discovery.findoutgoing(repo, other) - if not o: - ui.status(_("no changes found\n")) - return [] - o = repo.changelog.nodesbetween(o, revs)[0] - return [str(repo.changelog.rev(r)) for r in o] - - def getpatches(revs): - for r in cmdutil.revrange(repo, revs): - output = cStringIO.StringIO() - cmdutil.export(repo, [r], fp=output, - opts=patch.diffopts(ui, opts)) - yield output.getvalue().split('\n') - - def getbundle(dest): - tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-') - tmpfn = os.path.join(tmpdir, 'bundle') - try: - commands.bundle(ui, repo, tmpfn, dest, **opts) - return open(tmpfn, 'rb').read() - finally: - try: - os.unlink(tmpfn) - except: - pass - os.rmdir(tmpdir) - - if not (opts.get('test') or mbox): - # really sending - mail.validateconfig(ui) - - if not (revs or rev or outgoing or bundle or patches): - raise util.Abort(_('specify at least one changeset with -r or -o')) - - if outgoing and bundle: - raise util.Abort(_("--outgoing mode always on with --bundle;" - " do not re-specify --outgoing")) - - if outgoing or bundle: - if len(revs) > 1: - raise util.Abort(_("too many destinations")) - dest = revs and revs[0] or None - revs = [] - - if rev: - if revs: - raise util.Abort(_('use only one form to specify the revision')) - revs = rev - - if outgoing: - revs = getoutgoing(dest, rev) - if bundle: - opts['revs'] = revs - - # start - if date: - start_time = util.parsedate(date) - else: - start_time = util.makedate() - - def genmsgid(id): - return '<%s.%s@%s>' % (id[:20], int(start_time[0]), socket.getfqdn()) - - def getdescription(body, sender): - if opts.get('desc'): - body = open(opts.get('desc')).read() - else: - ui.write(_('\nWrite the introductory message for the ' - 'patch series.\n\n')) - body = ui.edit(body, sender) - return body - - def getpatchmsgs(patches, patchnames=None): - jumbo = [] - msgs = [] - - ui.write(_('This patch series consists of %d patches.\n\n') - % len(patches)) - - name = None - for i, p in enumerate(patches): - jumbo.extend(p) - if patchnames: - name = patchnames[i] - msg = makepatch(ui, repo, p, opts, _charsets, i + 1, - len(patches), name) - msgs.append(msg) - - if introneeded(opts, len(patches)): - tlen = len(str(len(patches))) - - flag = ' '.join(opts.get('flag')) - if flag: - subj = '[PATCH %0*d of %d %s]' % (tlen, 0, len(patches), flag) - else: - subj = '[PATCH %0*d of %d]' % (tlen, 0, len(patches)) - subj += ' ' + (opts.get('subject') or - prompt(ui, 'Subject: ', rest=subj)) - - body = '' - ds = patch.diffstat(jumbo) - if ds and opts.get('diffstat'): - body = '\n' + ds - - body = getdescription(body, sender) - msg = mail.mimeencode(ui, body, _charsets, opts.get('test')) - msg['Subject'] = mail.headencode(ui, subj, _charsets, - opts.get('test')) - - msgs.insert(0, (msg, subj, ds)) - return msgs - - def getbundlemsgs(bundle): - subj = (opts.get('subject') - or prompt(ui, 'Subject:', 'A bundle for your repository')) - - body = getdescription('', sender) - msg = email.MIMEMultipart.MIMEMultipart() - if body: - msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test'))) - datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle') - datapart.set_payload(bundle) - bundlename = '%s.hg' % opts.get('bundlename', 'bundle') - datapart.add_header('Content-Disposition', 'attachment', - filename=bundlename) - email.Encoders.encode_base64(datapart) - msg.attach(datapart) - msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test')) - return [(msg, subj, None)] - - sender = (opts.get('from') or ui.config('email', 'from') or - ui.config('patchbomb', 'from') or - prompt(ui, 'From', ui.username())) - - if patches: - msgs = getpatchmsgs(patches, opts.get('patchnames')) - elif bundle: - msgs = getbundlemsgs(getbundle(dest)) - else: - msgs = getpatchmsgs(list(getpatches(revs))) - - showaddrs = [] - - def getaddrs(opt, prpt=None, default=None): - addrs = opts.get(opt.replace('-', '_')) - if opt != 'reply-to': - showaddr = '%s:' % opt.capitalize() - else: - showaddr = 'Reply-To:' - - if addrs: - showaddrs.append('%s %s' % (showaddr, ', '.join(addrs))) - return mail.addrlistencode(ui, addrs, _charsets, opts.get('test')) - - addrs = ui.config('email', opt) or ui.config('patchbomb', opt) or '' - if not addrs and prpt: - addrs = prompt(ui, prpt, default) - - if addrs: - showaddrs.append('%s %s' % (showaddr, addrs)) - return mail.addrlistencode(ui, [addrs], _charsets, opts.get('test')) - - to = getaddrs('to', 'To') - cc = getaddrs('cc', 'Cc', '') - bcc = getaddrs('bcc') - replyto = getaddrs('reply-to') - - if opts.get('diffstat') or opts.get('confirm'): - ui.write(_('\nFinal summary:\n\n')) - ui.write('From: %s\n' % sender) - for addr in showaddrs: - ui.write('%s\n' % addr) - for m, subj, ds in msgs: - ui.write('Subject: %s\n' % subj) - if ds: - ui.write(ds) - ui.write('\n') - if ui.promptchoice(_('are you sure you want to send (yn)?'), - (_('&Yes'), _('&No'))): - raise util.Abort(_('patchbomb canceled')) - - ui.write('\n') - - parent = opts.get('in_reply_to') or None - # angle brackets may be omitted, they're not semantically part of the msg-id - if parent is not None: - if not parent.startswith('<'): - parent = '<' + parent - if not parent.endswith('>'): - parent += '>' - - first = True - - sender_addr = email.Utils.parseaddr(sender)[1] - sender = mail.addressencode(ui, sender, _charsets, opts.get('test')) - sendmail = None - for i, (m, subj, ds) in enumerate(msgs): - try: - m['Message-Id'] = genmsgid(m['X-Mercurial-Node']) - except TypeError: - m['Message-Id'] = genmsgid('patchbomb') - if parent: - m['In-Reply-To'] = parent - m['References'] = parent - if first: - parent = m['Message-Id'] - first = False - - m['User-Agent'] = 'Mercurial-patchbomb/%s' % util.version() - m['Date'] = email.Utils.formatdate(start_time[0], localtime=True) - - start_time = (start_time[0] + 1, start_time[1]) - m['From'] = sender - m['To'] = ', '.join(to) - if cc: - m['Cc'] = ', '.join(cc) - if bcc: - m['Bcc'] = ', '.join(bcc) - if replyto: - m['Reply-To'] = ', '.join(replyto) - if opts.get('test'): - ui.status(_('Displaying '), subj, ' ...\n') - ui.flush() - if 'PAGER' in os.environ and not ui.plain(): - fp = util.popen(os.environ['PAGER'], 'w') - else: - fp = ui - generator = email.Generator.Generator(fp, mangle_from_=False) - try: - generator.flatten(m, 0) - fp.write('\n') - except IOError, inst: - if inst.errno != errno.EPIPE: - raise - if fp is not ui: - fp.close() - elif mbox: - ui.status(_('Writing '), subj, ' ...\n') - ui.progress(_('writing'), i, item=subj, total=len(msgs)) - fp = open(mbox, 'In-Reply-To' in m and 'ab+' or 'wb+') - generator = email.Generator.Generator(fp, mangle_from_=True) - # Should be time.asctime(), but Windows prints 2-characters day - # of month instead of one. Make them print the same thing. - date = time.strftime('%a %b %d %H:%M:%S %Y', - time.localtime(start_time[0])) - fp.write('From %s %s\n' % (sender_addr, date)) - generator.flatten(m, 0) - fp.write('\n\n') - fp.close() - else: - if not sendmail: - sendmail = mail.connect(ui) - ui.status(_('Sending '), subj, ' ...\n') - ui.progress(_('sending'), i, item=subj, total=len(msgs)) - # Exim does not remove the Bcc field - del m['Bcc'] - fp = cStringIO.StringIO() - generator = email.Generator.Generator(fp, mangle_from_=False) - generator.flatten(m, 0) - sendmail(sender, to + bcc + cc, fp.getvalue()) - - ui.progress(_('writing'), None) - ui.progress(_('sending'), None) - -emailopts = [ - ('a', 'attach', None, _('send patches as attachments')), - ('i', 'inline', None, _('send patches as inline attachments')), - ('', 'bcc', [], _('email addresses of blind carbon copy recipients')), - ('c', 'cc', [], _('email addresses of copy recipients')), - ('', 'confirm', None, _('ask for confirmation before sending')), - ('d', 'diffstat', None, _('add diffstat output to messages')), - ('', 'date', '', _('use the given date as the sending date')), - ('', 'desc', '', _('use the given file as the series description')), - ('f', 'from', '', _('email address of sender')), - ('n', 'test', None, _('print messages that would be sent')), - ('m', 'mbox', '', - _('write messages to mbox file instead of sending them')), - ('', 'reply-to', [], _('email addresses replies should be sent to')), - ('s', 'subject', '', - _('subject of first message (intro or single patch)')), - ('', 'in-reply-to', '', - _('message identifier to reply to')), - ('', 'flag', [], _('flags to add in subject prefixes')), - ('t', 'to', [], _('email addresses of recipients')), - ] - - -cmdtable = { - "email": - (patchbomb, - [('g', 'git', None, _('use git extended diff format')), - ('', 'plain', None, _('omit hg patch header')), - ('o', 'outgoing', None, - _('send changes not found in the target repository')), - ('b', 'bundle', None, - _('send changes not in target as a binary bundle')), - ('', 'bundlename', 'bundle', - _('name of the bundle attachment file'), _('NAME')), - ('r', 'rev', [], - _('a revision to send'), _('REV')), - ('', 'force', None, - _('run even when remote repository is unrelated ' - '(with -b/--bundle)')), - ('', 'base', [], - _('a base changeset to specify instead of a destination ' - '(with -b/--bundle)'), - _('REV')), - ('', 'intro', None, - _('send an introduction email for a single patch')), - ] + emailopts + commands.remoteopts, - _('hg email [OPTION]... [DEST]...')) -} |