##// END OF EJS Templates
patchbomb: make --bundle respect --desc
Patrick Mezard -
r5753:ea1016b3 default
parent child Browse files
Show More
@@ -1,447 +1,449
1 1 # Command for sending a collection of Mercurial changesets as a series
2 2 # of patch emails.
3 3 #
4 4 # The series is started off with a "[PATCH 0 of N]" introduction,
5 5 # which describes the series as a whole.
6 6 #
7 7 # Each patch email has a Subject line of "[PATCH M of N] ...", using
8 8 # the first line of the changeset description as the subject text.
9 9 # The message contains two or three body parts:
10 10 #
11 11 # The remainder of the changeset description.
12 12 #
13 13 # [Optional] If the diffstat program is installed, the result of
14 14 # running diffstat on the patch.
15 15 #
16 16 # The patch itself, as generated by "hg export".
17 17 #
18 18 # Each message refers to all of its predecessors using the In-Reply-To
19 19 # and References headers, so they will show up as a sequence in
20 20 # threaded mail and news readers, and in mail archives.
21 21 #
22 22 # For each changeset, you will be prompted with a diffstat summary and
23 23 # the changeset summary, so you can be sure you are sending the right
24 24 # changes.
25 25 #
26 26 # To enable this extension:
27 27 #
28 28 # [extensions]
29 29 # hgext.patchbomb =
30 30 #
31 31 # To configure other defaults, add a section like this to your hgrc
32 32 # file:
33 33 #
34 34 # [email]
35 35 # from = My Name <my@email>
36 36 # to = recipient1, recipient2, ...
37 37 # cc = cc1, cc2, ...
38 38 # bcc = bcc1, bcc2, ...
39 39 #
40 40 # Then you can use the "hg email" command to mail a series of changesets
41 41 # as a patchbomb.
42 42 #
43 43 # To avoid sending patches prematurely, it is a good idea to first run
44 44 # the "email" command with the "-n" option (test only). You will be
45 45 # prompted for an email recipient address, a subject an an introductory
46 46 # message describing the patches of your patchbomb. Then when all is
47 47 # done, patchbomb messages are displayed. If PAGER environment variable
48 48 # is set, your pager will be fired up once for each patchbomb message, so
49 49 # you can verify everything is alright.
50 50 #
51 51 # The "-m" (mbox) option is also very useful. Instead of previewing
52 52 # each patchbomb message in a pager or sending the messages directly,
53 53 # it will create a UNIX mailbox file with the patch emails. This
54 54 # mailbox file can be previewed with any mail user agent which supports
55 55 # UNIX mbox files, i.e. with mutt:
56 56 #
57 57 # % mutt -R -f mbox
58 58 #
59 59 # When you are previewing the patchbomb messages, you can use `formail'
60 60 # (a utility that is commonly installed as part of the procmail package),
61 61 # to send each message out:
62 62 #
63 63 # % formail -s sendmail -bm -t < mbox
64 64 #
65 65 # That should be all. Now your patchbomb is on its way out.
66 66
67 67 import os, errno, socket, tempfile
68 68 import email.MIMEMultipart, email.MIMEText, email.MIMEBase
69 69 import email.Utils, email.Encoders
70 70 from mercurial import cmdutil, commands, hg, mail, ui, patch, util
71 71 from mercurial.i18n import _
72 72 from mercurial.node import *
73 73
74 74 def patchbomb(ui, repo, *revs, **opts):
75 75 '''send changesets by email
76 76
77 77 By default, diffs are sent in the format generated by hg export,
78 78 one per message. The series starts with a "[PATCH 0 of N]"
79 79 introduction, which describes the series as a whole.
80 80
81 81 Each patch email has a Subject line of "[PATCH M of N] ...", using
82 82 the first line of the changeset description as the subject text.
83 83 The message contains two or three body parts. First, the rest of
84 84 the changeset description. Next, (optionally) if the diffstat
85 85 program is installed, the result of running diffstat on the patch.
86 86 Finally, the patch itself, as generated by "hg export".
87 87
88 88 With --outgoing, emails will be generated for patches not
89 89 found in the destination repository (or only those which are
90 90 ancestors of the specified revisions if any are provided)
91 91
92 92 With --bundle, changesets are selected as for --outgoing,
93 93 but a single email containing a binary Mercurial bundle as an
94 94 attachment will be sent.
95 95
96 96 Examples:
97 97
98 98 hg email -r 3000 # send patch 3000 only
99 99 hg email -r 3000 -r 3001 # send patches 3000 and 3001
100 100 hg email -r 3000:3005 # send patches 3000 through 3005
101 101 hg email 3000 # send patch 3000 (deprecated)
102 102
103 103 hg email -o # send all patches not in default
104 104 hg email -o DEST # send all patches not in DEST
105 105 hg email -o -r 3000 # send all ancestors of 3000 not in default
106 106 hg email -o -r 3000 DEST # send all ancestors of 3000 not in DEST
107 107
108 108 hg email -b # send bundle of all patches not in default
109 109 hg email -b DEST # send bundle of all patches not in DEST
110 110 hg email -b -r 3000 # bundle of all ancestors of 3000 not in default
111 111 hg email -b -r 3000 DEST # bundle of all ancestors of 3000 not in DEST
112 112
113 113 Before using this command, you will need to enable email in your hgrc.
114 114 See the [email] section in hgrc(5) for details.
115 115 '''
116 116
117 117 def prompt(prompt, default = None, rest = ': ', empty_ok = False):
118 118 try:
119 119 # readline gives raw_input editing capabilities, but is not
120 120 # present on windows
121 121 import readline
122 122 except ImportError: pass
123 123
124 124 if default: prompt += ' [%s]' % default
125 125 prompt += rest
126 126 while True:
127 127 r = raw_input(prompt)
128 128 if r: return r
129 129 if default is not None: return default
130 130 if empty_ok: return r
131 131 ui.warn(_('Please enter a valid value.\n'))
132 132
133 133 def confirm(s, denial):
134 134 if not prompt(s, default = 'y', rest = '? ').lower().startswith('y'):
135 135 raise util.Abort(denial)
136 136
137 137 def cdiffstat(summary, patchlines):
138 138 s = patch.diffstat(patchlines)
139 139 if s:
140 140 if summary:
141 141 ui.write(summary, '\n')
142 142 ui.write(s, '\n')
143 143 confirm(_('Does the diffstat above look okay'),
144 144 _('diffstat rejected'))
145 145 elif s is None:
146 146 ui.warn(_('No diffstat information available.\n'))
147 147 s = ''
148 148 return s
149 149
150 150 def makepatch(patch, idx, total):
151 151 desc = []
152 152 node = None
153 153 body = ''
154 154 for line in patch:
155 155 if line.startswith('#'):
156 156 if line.startswith('# Node ID'): node = line.split()[-1]
157 157 continue
158 158 if (line.startswith('diff -r')
159 159 or line.startswith('diff --git')):
160 160 break
161 161 desc.append(line)
162 162 if not node: raise ValueError
163 163
164 164 #body = ('\n'.join(desc[1:]).strip() or
165 165 # 'Patch subject is complete summary.')
166 166 #body += '\n\n\n'
167 167
168 168 if opts['plain']:
169 169 while patch and patch[0].startswith('# '): patch.pop(0)
170 170 if patch: patch.pop(0)
171 171 while patch and not patch[0].strip(): patch.pop(0)
172 172 if opts['diffstat']:
173 173 body += cdiffstat('\n'.join(desc), patch) + '\n\n'
174 174 if opts['attach']:
175 175 msg = email.MIMEMultipart.MIMEMultipart()
176 176 if body: msg.attach(email.MIMEText.MIMEText(body, 'plain'))
177 177 p = email.MIMEText.MIMEText('\n'.join(patch), 'x-patch')
178 178 binnode = bin(node)
179 179 # if node is mq patch, it will have patch file name as tag
180 180 patchname = [t for t in repo.nodetags(binnode)
181 181 if t.endswith('.patch') or t.endswith('.diff')]
182 182 if patchname:
183 183 patchname = patchname[0]
184 184 elif total > 1:
185 185 patchname = cmdutil.make_filename(repo, '%b-%n.patch',
186 186 binnode, idx, total)
187 187 else:
188 188 patchname = cmdutil.make_filename(repo, '%b.patch', binnode)
189 189 p['Content-Disposition'] = 'inline; filename=' + patchname
190 190 msg.attach(p)
191 191 else:
192 192 body += '\n'.join(patch)
193 193 msg = email.MIMEText.MIMEText(body)
194 194
195 195 subj = desc[0].strip().rstrip('. ')
196 196 if total == 1:
197 197 subj = '[PATCH] ' + (opts['subject'] or subj)
198 198 else:
199 199 tlen = len(str(total))
200 200 subj = '[PATCH %0*d of %d] %s' % (tlen, idx, total, subj)
201 201 msg['Subject'] = subj
202 202 msg['X-Mercurial-Node'] = node
203 203 return msg
204 204
205 205 def outgoing(dest, revs):
206 206 '''Return the revisions present locally but not in dest'''
207 207 dest = ui.expandpath(dest or 'default-push', dest or 'default')
208 208 revs = [repo.lookup(rev) for rev in revs]
209 209 other = hg.repository(ui, dest)
210 210 ui.status(_('comparing with %s\n') % dest)
211 211 o = repo.findoutgoing(other)
212 212 if not o:
213 213 ui.status(_("no changes found\n"))
214 214 return []
215 215 o = repo.changelog.nodesbetween(o, revs or None)[0]
216 216 return [str(repo.changelog.rev(r)) for r in o]
217 217
218 218 def getbundle(dest):
219 219 tmpdir = tempfile.mkdtemp(prefix='hg-email-bundle-')
220 220 tmpfn = os.path.join(tmpdir, 'bundle')
221 221 try:
222 222 commands.bundle(ui, repo, tmpfn, dest, **opts)
223 223 return open(tmpfn, 'rb').read()
224 224 finally:
225 225 try:
226 226 os.unlink(tmpfn)
227 227 except:
228 228 pass
229 229 os.rmdir(tmpdir)
230 230
231 231 if not (opts['test'] or opts['mbox']):
232 232 # really sending
233 233 mail.validateconfig(ui)
234 234
235 235 if not (revs or opts.get('rev')
236 236 or opts.get('outgoing') or opts.get('bundle')):
237 237 raise util.Abort(_('specify at least one changeset with -r or -o'))
238 238
239 239 cmdutil.setremoteconfig(ui, opts)
240 240 if opts.get('outgoing') and opts.get('bundle'):
241 241 raise util.Abort(_("--outgoing mode always on with --bundle; do not re-specify --outgoing"))
242 242
243 243 if opts.get('outgoing') or opts.get('bundle'):
244 244 if len(revs) > 1:
245 245 raise util.Abort(_("too many destinations"))
246 246 dest = revs and revs[0] or None
247 247 revs = []
248 248
249 249 if opts.get('rev'):
250 250 if revs:
251 251 raise util.Abort(_('use only one form to specify the revision'))
252 252 revs = opts.get('rev')
253 253
254 254 if opts.get('outgoing'):
255 255 revs = outgoing(dest, opts.get('rev'))
256 256 if opts.get('bundle'):
257 257 opts['revs'] = revs
258 258
259 259 # start
260 260 if opts.get('date'):
261 261 start_time = util.parsedate(opts['date'])
262 262 else:
263 263 start_time = util.makedate()
264 264
265 265 def genmsgid(id):
266 266 return '<%s.%s@%s>' % (id[:20], int(start_time[0]), socket.getfqdn())
267 267
268 def getdescription(body, sender):
269 if opts['desc']:
270 body = open(opts['desc']).read()
271 else:
272 ui.write(_('\nWrite the introductory message for the '
273 'patch series.\n\n'))
274 body = ui.edit(body, sender)
275 return body
276
268 277 def getexportmsgs():
269 278 patches = []
270 279
271 280 class exportee:
272 281 def __init__(self, container):
273 282 self.lines = []
274 283 self.container = container
275 284 self.name = 'email'
276 285
277 286 def write(self, data):
278 287 self.lines.append(data)
279 288
280 289 def close(self):
281 290 self.container.append(''.join(self.lines).split('\n'))
282 291 self.lines = []
283 292
284 293 commands.export(ui, repo, *revs, **{'output': exportee(patches),
285 294 'switch_parent': False,
286 295 'text': None,
287 296 'git': opts.get('git')})
288 297
289 298 jumbo = []
290 299 msgs = []
291 300
292 301 ui.write(_('This patch series consists of %d patches.\n\n') % len(patches))
293 302
294 303 for p, i in zip(patches, xrange(len(patches))):
295 304 jumbo.extend(p)
296 305 msgs.append(makepatch(p, i + 1, len(patches)))
297 306
298 307 if len(patches) > 1:
299 308 tlen = len(str(len(patches)))
300 309
301 310 subj = '[PATCH %0*d of %d] %s' % (
302 311 tlen, 0,
303 312 len(patches),
304 313 opts['subject'] or
305 314 prompt('Subject:', rest = ' [PATCH %0*d of %d] ' % (tlen, 0,
306 315 len(patches))))
307 316
308 317 body = ''
309 318 if opts['diffstat']:
310 319 d = cdiffstat(_('Final summary:\n'), jumbo)
311 320 if d: body = '\n' + d
312 321
313 if opts['desc']:
314 body = open(opts['desc']).read()
315 else:
316 ui.write(_('\nWrite the introductory message for the '
317 'patch series.\n\n'))
318 body = ui.edit(body, sender)
319
322 body = getdescription(body, sender)
320 323 msg = email.MIMEText.MIMEText(body)
321 324 msg['Subject'] = subj
322 325
323 326 msgs.insert(0, msg)
324 327 return msgs
325 328
326 329 def getbundlemsgs(bundle):
327 330 subj = (opts['subject']
328 331 or prompt('Subject:', default='A bundle for your repository'))
329 ui.write(_('\nWrite the introductory message for the bundle.\n\n'))
330 body = ui.edit('', sender)
331 332
333 body = getdescription('', sender)
332 334 msg = email.MIMEMultipart.MIMEMultipart()
333 335 if body:
334 336 msg.attach(email.MIMEText.MIMEText(body, 'plain'))
335 337 datapart = email.MIMEBase.MIMEBase('application', 'x-mercurial-bundle')
336 338 datapart.set_payload(bundle)
337 339 datapart.add_header('Content-Disposition', 'attachment',
338 340 filename='bundle.hg')
339 341 email.Encoders.encode_base64(datapart)
340 342 msg.attach(datapart)
341 343 msg['Subject'] = subj
342 344 return [msg]
343 345
344 346 sender = (opts['from'] or ui.config('email', 'from') or
345 347 ui.config('patchbomb', 'from') or
346 348 prompt('From', ui.username()))
347 349
348 350 if opts.get('bundle'):
349 351 msgs = getbundlemsgs(getbundle(dest))
350 352 else:
351 353 msgs = getexportmsgs()
352 354
353 355 def getaddrs(opt, prpt, default = None):
354 356 addrs = opts[opt] or (ui.config('email', opt) or
355 357 ui.config('patchbomb', opt) or
356 358 prompt(prpt, default = default)).split(',')
357 359 return [a.strip() for a in addrs if a.strip()]
358 360
359 361 to = getaddrs('to', 'To')
360 362 cc = getaddrs('cc', 'Cc', '')
361 363
362 364 bcc = opts['bcc'] or (ui.config('email', 'bcc') or
363 365 ui.config('patchbomb', 'bcc') or '').split(',')
364 366 bcc = [a.strip() for a in bcc if a.strip()]
365 367
366 368 ui.write('\n')
367 369
368 370 parent = None
369 371
370 372 sender_addr = email.Utils.parseaddr(sender)[1]
371 373 for m in msgs:
372 374 try:
373 375 m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
374 376 except TypeError:
375 377 m['Message-Id'] = genmsgid('patchbomb')
376 378 if parent:
377 379 m['In-Reply-To'] = parent
378 380 else:
379 381 parent = m['Message-Id']
380 382 m['Date'] = util.datestr(date=start_time,
381 383 format="%a, %d %b %Y %H:%M:%S", timezone=True)
382 384
383 385 start_time = (start_time[0] + 1, start_time[1])
384 386 m['From'] = sender
385 387 m['To'] = ', '.join(to)
386 388 if cc: m['Cc'] = ', '.join(cc)
387 389 if bcc: m['Bcc'] = ', '.join(bcc)
388 390 if opts['test']:
389 391 ui.status('Displaying ', m['Subject'], ' ...\n')
390 392 ui.flush()
391 393 if 'PAGER' in os.environ:
392 394 fp = os.popen(os.environ['PAGER'], 'w')
393 395 else:
394 396 fp = ui
395 397 try:
396 398 fp.write(m.as_string(0))
397 399 fp.write('\n')
398 400 except IOError, inst:
399 401 if inst.errno != errno.EPIPE:
400 402 raise
401 403 if fp is not ui:
402 404 fp.close()
403 405 elif opts['mbox']:
404 406 ui.status('Writing ', m['Subject'], ' ...\n')
405 407 fp = open(opts['mbox'], m.has_key('In-Reply-To') and 'ab+' or 'wb+')
406 408 date = util.datestr(date=start_time,
407 409 format='%a %b %d %H:%M:%S %Y', timezone=False)
408 410 fp.write('From %s %s\n' % (sender_addr, date))
409 411 fp.write(m.as_string(0))
410 412 fp.write('\n\n')
411 413 fp.close()
412 414 else:
413 415 ui.status('Sending ', m['Subject'], ' ...\n')
414 416 # Exim does not remove the Bcc field
415 417 del m['Bcc']
416 418 mail.sendmail(ui, sender, to + bcc + cc, m.as_string(0))
417 419
418 420 cmdtable = {
419 421 "email":
420 422 (patchbomb,
421 423 [('a', 'attach', None, _('send patches as inline attachments')),
422 424 ('', 'bcc', [], _('email addresses of blind copy recipients')),
423 425 ('c', 'cc', [], _('email addresses of copy recipients')),
424 426 ('d', 'diffstat', None, _('add diffstat output to messages')),
425 427 ('', 'date', '', _('use the given date as the sending date')),
426 428 ('', 'desc', '', _('use the given file as the series description')),
427 429 ('g', 'git', None, _('use git extended diff format')),
428 430 ('f', 'from', '', _('email address of sender')),
429 431 ('', 'plain', None, _('omit hg patch header')),
430 432 ('n', 'test', None, _('print messages that would be sent')),
431 433 ('m', 'mbox', '',
432 434 _('write messages to mbox file instead of sending them')),
433 435 ('o', 'outgoing', None,
434 436 _('send changes not found in the target repository')),
435 437 ('b', 'bundle', None,
436 438 _('send changes not in target as a binary bundle')),
437 439 ('r', 'rev', [], _('a revision to send')),
438 440 ('s', 'subject', '',
439 441 _('subject of first message (intro or single patch)')),
440 442 ('t', 'to', [], _('email addresses of recipients')),
441 443 ('', 'force', None,
442 444 _('run even when remote repository is unrelated (with -b)')),
443 445 ('', 'base', [],
444 446 _('a base changeset to specify instead of a destination (with -b)')),
445 447 ] + commands.remoteopts,
446 448 _('hg email [OPTION]... [DEST]...'))
447 449 }
@@ -1,20 +1,46
1 1 #!/bin/sh
2 2
3 fixheaders()
4 {
5 sed -e 's/\(Message-Id:.*@\).*/\1/' \
6 -e 's/\(In-Reply-To:.*@\).*/\1/' \
7 -e 's/===.*/===/'
8 }
9
3 10 echo "[extensions]" >> $HGRCPATH
4 11 echo "patchbomb=" >> $HGRCPATH
5 12
6 hg init
13 hg init t
14 cd t
7 15 echo a > a
8 16 hg commit -Ama -d '1 0'
9 17
10 18 hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar tip | \
11 sed -e 's/\(Message-Id:.*@\).*/\1/'
19 fixheaders
12 20
13 21 echo b > b
14 22 hg commit -Amb -d '2 0'
15 23
16 24 hg email --date '1970-1-1 0:2' -n -f quux -t foo -c bar -s test 0:tip | \
17 sed -e 's/\(Message-Id:.*@\).*/\1/' | \
18 sed -e 's/\(In-Reply-To:.*@\).*/\1/'
25 fixheaders
19 26
20 27 hg email -m test.mbox -f quux -t foo -c bar -s test 0:tip
28
29 cd ..
30
31 hg clone -q t t2
32 cd t2
33 echo c > c
34 hg commit -Amc -d '3 0'
35
36 cat > description <<EOF
37 a multiline
38
39 description
40 EOF
41
42 echo % test bundle and description
43 hg email --date '1970-1-1 0:3' -n -f quux -t foo \
44 -c bar -s test -r tip -b --desc description | \
45 fixheaders
46
@@ -1,109 +1,146
1 1 adding a
2 2 This patch series consists of 1 patches.
3 3
4 4
5 5 Displaying [PATCH] a ...
6 6 Content-Type: text/plain; charset="us-ascii"
7 7 MIME-Version: 1.0
8 8 Content-Transfer-Encoding: 7bit
9 9 Subject: [PATCH] a
10 10 X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
11 11 Message-Id: <8580ff50825a50c8f716.60@
12 12 Date: Thu, 01 Jan 1970 00:01:00 +0000
13 13 From: quux
14 14 To: foo
15 15 Cc: bar
16 16
17 17 # HG changeset patch
18 18 # User test
19 19 # Date 1 0
20 20 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
21 21 # Parent 0000000000000000000000000000000000000000
22 22 a
23 23
24 24 diff -r 000000000000 -r 8580ff50825a a
25 25 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
26 26 +++ b/a Thu Jan 01 00:00:01 1970 +0000
27 27 @@ -0,0 +1,1 @@
28 28 +a
29 29
30 30 adding b
31 31 This patch series consists of 2 patches.
32 32
33 33
34 34 Write the introductory message for the patch series.
35 35
36 36
37 37 Displaying [PATCH 0 of 2] test ...
38 38 Content-Type: text/plain; charset="us-ascii"
39 39 MIME-Version: 1.0
40 40 Content-Transfer-Encoding: 7bit
41 41 Subject: [PATCH 0 of 2] test
42 42 Message-Id: <patchbomb.120@
43 43 Date: Thu, 01 Jan 1970 00:02:00 +0000
44 44 From: quux
45 45 To: foo
46 46 Cc: bar
47 47
48 48
49 49 Displaying [PATCH 1 of 2] a ...
50 50 Content-Type: text/plain; charset="us-ascii"
51 51 MIME-Version: 1.0
52 52 Content-Transfer-Encoding: 7bit
53 53 Subject: [PATCH 1 of 2] a
54 54 X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
55 55 Message-Id: <8580ff50825a50c8f716.121@
56 56 In-Reply-To: <patchbomb.120@
57 57 Date: Thu, 01 Jan 1970 00:02:01 +0000
58 58 From: quux
59 59 To: foo
60 60 Cc: bar
61 61
62 62 # HG changeset patch
63 63 # User test
64 64 # Date 1 0
65 65 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
66 66 # Parent 0000000000000000000000000000000000000000
67 67 a
68 68
69 69 diff -r 000000000000 -r 8580ff50825a a
70 70 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
71 71 +++ b/a Thu Jan 01 00:00:01 1970 +0000
72 72 @@ -0,0 +1,1 @@
73 73 +a
74 74
75 75 Displaying [PATCH 2 of 2] b ...
76 76 Content-Type: text/plain; charset="us-ascii"
77 77 MIME-Version: 1.0
78 78 Content-Transfer-Encoding: 7bit
79 79 Subject: [PATCH 2 of 2] b
80 80 X-Mercurial-Node: 97d72e5f12c7e84f85064aa72e5a297142c36ed9
81 81 Message-Id: <97d72e5f12c7e84f8506.122@
82 82 In-Reply-To: <patchbomb.120@
83 83 Date: Thu, 01 Jan 1970 00:02:02 +0000
84 84 From: quux
85 85 To: foo
86 86 Cc: bar
87 87
88 88 # HG changeset patch
89 89 # User test
90 90 # Date 2 0
91 91 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9
92 92 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab
93 93 b
94 94
95 95 diff -r 8580ff50825a -r 97d72e5f12c7 b
96 96 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
97 97 +++ b/b Thu Jan 01 00:00:02 1970 +0000
98 98 @@ -0,0 +1,1 @@
99 99 +b
100 100
101 101 This patch series consists of 2 patches.
102 102
103 103
104 104 Write the introductory message for the patch series.
105 105
106 106
107 107 Writing [PATCH 0 of 2] test ...
108 108 Writing [PATCH 1 of 2] a ...
109 109 Writing [PATCH 2 of 2] b ...
110 adding c
111 % test bundle and description
112 searching for changes
113
114 Displaying test ...
115 Content-Type: multipart/mixed; boundary="===
116 MIME-Version: 1.0
117 Subject: test
118 Message-Id: <patchbomb.180@
119 Date: Thu, 01 Jan 1970 00:03:00 +0000
120 From: quux
121 To: foo
122 Cc: bar
123
124 --===
125 Content-Type: text/plain; charset="us-ascii"
126 MIME-Version: 1.0
127 Content-Transfer-Encoding: 7bit
128
129 a multiline
130
131 description
132
133 --===
134 Content-Type: application/x-mercurial-bundle
135 MIME-Version: 1.0
136 Content-Disposition: attachment; filename="bundle.hg"
137 Content-Transfer-Encoding: base64
138
139 SEcxMEJaaDkxQVkmU1nvR7I3AAAN////lFYQWj1/4HwRkdC/AywIAk0E4pfoSIIIgQCgGEQOcLAA
140 2tA1VPyp4mkeoG0EaaPU0GTT1GjRiNPIg9CZGBqZ6UbU9J+KFU09DNUaGgAAAAAANAGgAAAAA1U8
141 oGgAADQGgAANNANAAAAAAZipFLz3XoakCEQB3PVPyHJVi1iYkAAKQAZQGpQGZESInRnCFMqLDla2
142 Bx3qfRQeA2N4lnzKkAmP8kR2asievLLXXebVU8Vg4iEBqcJNJAxIapSU6SM4888ZAciRG6MYAIEE
143 SlIBpFisgGkyRjX//TMtfcUAEsGu56+YnE1OlTZmzKm8BSu2rvo4rHAYYaadIFFuTy0LYgIkgLVD
144 sgVa2F19D1tx9+hgbAygLgQwaIqcDdgA4BjQgIiz/AEP72++llgDKhKducqodGE4B0ETqF3JFOFC
145 Q70eyNw=
146 --===
General Comments 0
You need to be logged in to leave comments. Login now