comparison hgext/patchbomb.py @ 4278:cfe886c14ddf

Add ability to send bundles to patchbomb extension
author John Goerzen <jgoerzen@complete.org>
date Mon, 26 Mar 2007 13:37:48 -0500
parents f51317e24114
children 126d1967a3f8
comparison
equal deleted inserted replaced
4277:0ce23256e454 4278:cfe886c14ddf
61 # 61 #
62 # % formail -s sendmail -bm -t < mbox 62 # % formail -s sendmail -bm -t < mbox
63 # 63 #
64 # That should be all. Now your patchbomb is on its way out. 64 # That should be all. Now your patchbomb is on its way out.
65 65
66 import os, errno, socket 66 import os, errno, socket, tempfile
67 import email.MIMEMultipart, email.MIMEText, email.Utils 67 import email.MIMEMultipart, email.MIMEText, email.MIMEBase
68 import email.Utils, email.Encoders
68 from mercurial import cmdutil, commands, hg, mail, ui, patch, util 69 from mercurial import cmdutil, commands, hg, mail, ui, patch, util
69 from mercurial.i18n import _ 70 from mercurial.i18n import _
70 from mercurial.node import * 71 from mercurial.node import *
71 72
72 try: 73 try:
182 ui.status(_("no changes found\n")) 183 ui.status(_("no changes found\n"))
183 return [] 184 return []
184 o = repo.changelog.nodesbetween(o, revs or None)[0] 185 o = repo.changelog.nodesbetween(o, revs or None)[0]
185 return [str(repo.changelog.rev(r)) for r in o] 186 return [str(repo.changelog.rev(r)) for r in o]
186 187
188 def getbundle(dest, revs):
189 tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-')
190 tmpfn = os.path.join(tmpdir, 'bundle')
191 try:
192 commands.bundle(ui, repo, tmpfn, dest, *revs, **{'force': 0})
193 return open(tmpfn).read()
194 finally:
195 try:
196 os.unlink(tmpfn)
197 except:
198 pass
199 os.rmdir(tmpdir)
200
187 # option handling 201 # option handling
188 commands.setremoteconfig(ui, opts) 202 commands.setremoteconfig(ui, opts)
189 if opts.get('outgoing'): 203 if opts.get('outgoint') and opts.get('bundle'):
204 raise util.Abort(_("--outgoing mode always on with --bundle; do not re-specify --outgoing"))
205
206 if opts.get('outgoing') or opts.get('bundle'):
190 if len(revs) > 1: 207 if len(revs) > 1:
191 raise util.Abort(_("too many destinations")) 208 raise util.Abort(_("too many destinations"))
192 dest = revs and revs[0] or None 209 dest = revs and revs[0] or None
193 revs = [] 210 revs = []
194 211
203 # start 220 # start
204 start_time = util.makedate() 221 start_time = util.makedate()
205 222
206 def genmsgid(id): 223 def genmsgid(id):
207 return '<%s.%s@%s>' % (id[:20], int(start_time[0]), socket.getfqdn()) 224 return '<%s.%s@%s>' % (id[:20], int(start_time[0]), socket.getfqdn())
208
209 patches = []
210
211 class exportee:
212 def __init__(self, container):
213 self.lines = []
214 self.container = container
215 self.name = 'email'
216
217 def write(self, data):
218 self.lines.append(data)
219
220 def close(self):
221 self.container.append(''.join(self.lines).split('\n'))
222 self.lines = []
223
224 commands.export(ui, repo, *revs, **{'output': exportee(patches),
225 'switch_parent': False,
226 'text': None,
227 'git': opts.get('git')})
228
229 jumbo = []
230 msgs = []
231
232 ui.write(_('This patch series consists of %d patches.\n\n') % len(patches))
233
234 for p, i in zip(patches, xrange(len(patches))):
235 jumbo.extend(p)
236 msgs.append(makepatch(p, i + 1, len(patches)))
237 225
238 sender = (opts['from'] or ui.config('email', 'from') or 226 sender = (opts['from'] or ui.config('email', 'from') or
239 ui.config('patchbomb', 'from') or 227 ui.config('patchbomb', 'from') or
240 prompt('From', ui.username())) 228 prompt('From', ui.username()))
241 229
242 def getaddrs(opt, prpt, default = None): 230 def getaddrs(opt, prpt, default = None):
243 addrs = opts[opt] or (ui.config('email', opt) or 231 addrs = opts[opt] or (ui.config('email', opt) or
244 ui.config('patchbomb', opt) or 232 ui.config('patchbomb', opt) or
245 prompt(prpt, default = default)).split(',') 233 prompt(prpt, default = default)).split(',')
246 return [a.strip() for a in addrs if a.strip()] 234 return [a.strip() for a in addrs if a.strip()]
235
247 to = getaddrs('to', 'To') 236 to = getaddrs('to', 'To')
248 cc = getaddrs('cc', 'Cc', '') 237 cc = getaddrs('cc', 'Cc', '')
249 238
250 bcc = opts['bcc'] or (ui.config('email', 'bcc') or 239 bcc = opts['bcc'] or (ui.config('email', 'bcc') or
251 ui.config('patchbomb', 'bcc') or '').split(',') 240 ui.config('patchbomb', 'bcc') or '').split(',')
252 bcc = [a.strip() for a in bcc if a.strip()] 241 bcc = [a.strip() for a in bcc if a.strip()]
253 242
254 if len(patches) > 1: 243 def getexportmsgs():
255 tlen = len(str(len(patches))) 244 patches = []
256 245
257 subj = '[PATCH %0*d of %d] %s' % ( 246 class exportee:
258 tlen, 0, 247 def __init__(self, container):
259 len(patches), 248 self.lines = []
260 opts['subject'] or 249 self.container = container
261 prompt('Subject:', rest = ' [PATCH %0*d of %d] ' % (tlen, 0, 250 self.name = 'email'
262 len(patches)))) 251
263 252 def write(self, data):
264 body = '' 253 self.lines.append(data)
265 if opts['diffstat']: 254
266 d = cdiffstat(_('Final summary:\n'), jumbo) 255 def close(self):
267 if d: body = '\n' + d 256 self.container.append(''.join(self.lines).split('\n'))
268 257 self.lines = []
269 ui.write(_('\nWrite the introductory message for the patch series.\n\n')) 258
270 body = ui.edit(body, sender) 259 commands.export(ui, repo, *revs, **{'output': exportee(patches),
271 260 'switch_parent': False,
272 msg = email.MIMEText.MIMEText(body) 261 'text': None,
262 'git': opts.get('git')})
263
264 jumbo = []
265 msgs = []
266
267 ui.write(_('This patch series consists of %d patches.\n\n') % len(patches))
268
269 for p, i in zip(patches, xrange(len(patches))):
270 jumbo.extend(p)
271 msgs.append(makepatch(p, i + 1, len(patches)))
272
273 if len(patches) > 1:
274 tlen = len(str(len(patches)))
275
276 subj = '[PATCH %0*d of %d] %s' % (
277 tlen, 0,
278 len(patches),
279 opts['subject'] or
280 prompt('Subject:', rest = ' [PATCH %0*d of %d] ' % (tlen, 0,
281 len(patches))))
282
283 body = ''
284 if opts['diffstat']:
285 d = cdiffstat(_('Final summary:\n'), jumbo)
286 if d: body = '\n' + d
287
288 ui.write(_('\nWrite the introductory message for the patch series.\n\n'))
289 body = ui.edit(body, sender)
290
291 msg = email.MIMEText.MIMEText(body)
292 msg['Subject'] = subj
293
294 msgs.insert(0, msg)
295 return msgs
296
297 def getbundlemsgs(bundle):
298 subj = opts['subject'] or \
299 prompt('Subject:', default='A bundle for your repository')
300 ui.write(_('\nWrite the introductory message for the bundle.\n\n'))
301 body = ui.edit('', sender)
302
303 msg = email.MIMEMultipart.MIMEMultipart()
304 if body:
305 msg.attach(email.MIMEText.MIMEText(body, 'plain'))
306 datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
307 datapart.set_payload(bundle)
308 email.Encoders.encode_base64(datapart)
309 msg.attach(datapart)
273 msg['Subject'] = subj 310 msg['Subject'] = subj
274 311 return [msg]
275 msgs.insert(0, msg) 312
313 if opts.get('bundle'):
314 msgs = getbundlemsgs(getbundle(dest, revs))
315 else:
316 msgs = getexportmsgs()
276 317
277 ui.write('\n') 318 ui.write('\n')
278 319
279 if not opts['test'] and not opts['mbox']: 320 if not opts['test'] and not opts['mbox']:
280 mailer = mail.connect(ui) 321 mailer = mail.connect(ui)
334 ('f', 'from', '', 'email address of sender'), 375 ('f', 'from', '', 'email address of sender'),
335 ('', 'plain', None, 'omit hg patch header'), 376 ('', 'plain', None, 'omit hg patch header'),
336 ('n', 'test', None, 'print messages that would be sent'), 377 ('n', 'test', None, 'print messages that would be sent'),
337 ('m', 'mbox', '', 'write messages to mbox file instead of sending them'), 378 ('m', 'mbox', '', 'write messages to mbox file instead of sending them'),
338 ('o', 'outgoing', None, _('send changes not found in the target repository')), 379 ('o', 'outgoing', None, _('send changes not found in the target repository')),
380 ('b', 'bundle', None, _('send changes not in target as a binary bundle')),
339 ('r', 'rev', [], _('a revision to send')), 381 ('r', 'rev', [], _('a revision to send')),
340 ('s', 'subject', '', 'subject of first message (intro or single patch)'), 382 ('s', 'subject', '', 'subject of first message (intro or single patch)'),
341 ('t', 'to', [], 'email addresses of recipients')] + commands.remoteopts, 383 ('t', 'to', [], 'email addresses of recipients')] + commands.remoteopts,
342 "hg email [OPTION]... [DEST]...") 384 "hg email [OPTION]... [DEST]...")
343 } 385 }