Mercurial > hg
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 } |