##// END OF EJS Templates
util: flush stdout before calling external processes...
Mads Kiilerich -
r13439:d724a693 stable
parent child Browse files
Show More
@@ -1,1562 +1,1566
1 1 # util.py - Mercurial utility functions and platform specfic implementations
2 2 #
3 3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 """Mercurial utility functions and platform specfic implementations.
11 11
12 12 This contains helper routines that are independent of the SCM core and
13 13 hide platform-specific details from the core.
14 14 """
15 15
16 16 from i18n import _
17 17 import error, osutil, encoding
18 18 import errno, re, shutil, sys, tempfile, traceback
19 19 import os, stat, time, calendar, textwrap, unicodedata, signal
20 20 import imp, socket
21 21
22 22 # Python compatibility
23 23
24 24 def sha1(s):
25 25 return _fastsha1(s)
26 26
27 27 def _fastsha1(s):
28 28 # This function will import sha1 from hashlib or sha (whichever is
29 29 # available) and overwrite itself with it on the first call.
30 30 # Subsequent calls will go directly to the imported function.
31 31 if sys.version_info >= (2, 5):
32 32 from hashlib import sha1 as _sha1
33 33 else:
34 34 from sha import sha as _sha1
35 35 global _fastsha1, sha1
36 36 _fastsha1 = sha1 = _sha1
37 37 return _sha1(s)
38 38
39 39 import __builtin__
40 40
41 41 if sys.version_info[0] < 3:
42 42 def fakebuffer(sliceable, offset=0):
43 43 return sliceable[offset:]
44 44 else:
45 45 def fakebuffer(sliceable, offset=0):
46 46 return memoryview(sliceable)[offset:]
47 47 try:
48 48 buffer
49 49 except NameError:
50 50 __builtin__.buffer = fakebuffer
51 51
52 52 import subprocess
53 53 closefds = os.name == 'posix'
54 54
55 55 def popen2(cmd, env=None, newlines=False):
56 56 # Setting bufsize to -1 lets the system decide the buffer size.
57 57 # The default for bufsize is 0, meaning unbuffered. This leads to
58 58 # poor performance on Mac OS X: http://bugs.python.org/issue4194
59 59 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
60 60 close_fds=closefds,
61 61 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
62 62 universal_newlines=newlines,
63 63 env=env)
64 64 return p.stdin, p.stdout
65 65
66 66 def popen3(cmd, env=None, newlines=False):
67 67 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
68 68 close_fds=closefds,
69 69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
70 70 stderr=subprocess.PIPE,
71 71 universal_newlines=newlines,
72 72 env=env)
73 73 return p.stdin, p.stdout, p.stderr
74 74
75 75 def version():
76 76 """Return version information if available."""
77 77 try:
78 78 import __version__
79 79 return __version__.version
80 80 except ImportError:
81 81 return 'unknown'
82 82
83 83 # used by parsedate
84 84 defaultdateformats = (
85 85 '%Y-%m-%d %H:%M:%S',
86 86 '%Y-%m-%d %I:%M:%S%p',
87 87 '%Y-%m-%d %H:%M',
88 88 '%Y-%m-%d %I:%M%p',
89 89 '%Y-%m-%d',
90 90 '%m-%d',
91 91 '%m/%d',
92 92 '%m/%d/%y',
93 93 '%m/%d/%Y',
94 94 '%a %b %d %H:%M:%S %Y',
95 95 '%a %b %d %I:%M:%S%p %Y',
96 96 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
97 97 '%b %d %H:%M:%S %Y',
98 98 '%b %d %I:%M:%S%p %Y',
99 99 '%b %d %H:%M:%S',
100 100 '%b %d %I:%M:%S%p',
101 101 '%b %d %H:%M',
102 102 '%b %d %I:%M%p',
103 103 '%b %d %Y',
104 104 '%b %d',
105 105 '%H:%M:%S',
106 106 '%I:%M:%S%p',
107 107 '%H:%M',
108 108 '%I:%M%p',
109 109 )
110 110
111 111 extendeddateformats = defaultdateformats + (
112 112 "%Y",
113 113 "%Y-%m",
114 114 "%b",
115 115 "%b %Y",
116 116 )
117 117
118 118 def cachefunc(func):
119 119 '''cache the result of function calls'''
120 120 # XXX doesn't handle keywords args
121 121 cache = {}
122 122 if func.func_code.co_argcount == 1:
123 123 # we gain a small amount of time because
124 124 # we don't need to pack/unpack the list
125 125 def f(arg):
126 126 if arg not in cache:
127 127 cache[arg] = func(arg)
128 128 return cache[arg]
129 129 else:
130 130 def f(*args):
131 131 if args not in cache:
132 132 cache[args] = func(*args)
133 133 return cache[args]
134 134
135 135 return f
136 136
137 137 def lrucachefunc(func):
138 138 '''cache most recent results of function calls'''
139 139 cache = {}
140 140 order = []
141 141 if func.func_code.co_argcount == 1:
142 142 def f(arg):
143 143 if arg not in cache:
144 144 if len(cache) > 20:
145 145 del cache[order.pop(0)]
146 146 cache[arg] = func(arg)
147 147 else:
148 148 order.remove(arg)
149 149 order.append(arg)
150 150 return cache[arg]
151 151 else:
152 152 def f(*args):
153 153 if args not in cache:
154 154 if len(cache) > 20:
155 155 del cache[order.pop(0)]
156 156 cache[args] = func(*args)
157 157 else:
158 158 order.remove(args)
159 159 order.append(args)
160 160 return cache[args]
161 161
162 162 return f
163 163
164 164 class propertycache(object):
165 165 def __init__(self, func):
166 166 self.func = func
167 167 self.name = func.__name__
168 168 def __get__(self, obj, type=None):
169 169 result = self.func(obj)
170 170 setattr(obj, self.name, result)
171 171 return result
172 172
173 173 def pipefilter(s, cmd):
174 174 '''filter string S through command CMD, returning its output'''
175 175 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
176 176 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
177 177 pout, perr = p.communicate(s)
178 178 return pout
179 179
180 180 def tempfilter(s, cmd):
181 181 '''filter string S through a pair of temporary files with CMD.
182 182 CMD is used as a template to create the real command to be run,
183 183 with the strings INFILE and OUTFILE replaced by the real names of
184 184 the temporary files generated.'''
185 185 inname, outname = None, None
186 186 try:
187 187 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
188 188 fp = os.fdopen(infd, 'wb')
189 189 fp.write(s)
190 190 fp.close()
191 191 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
192 192 os.close(outfd)
193 193 cmd = cmd.replace('INFILE', inname)
194 194 cmd = cmd.replace('OUTFILE', outname)
195 195 code = os.system(cmd)
196 196 if sys.platform == 'OpenVMS' and code & 1:
197 197 code = 0
198 198 if code:
199 199 raise Abort(_("command '%s' failed: %s") %
200 200 (cmd, explain_exit(code)))
201 201 fp = open(outname, 'rb')
202 202 r = fp.read()
203 203 fp.close()
204 204 return r
205 205 finally:
206 206 try:
207 207 if inname:
208 208 os.unlink(inname)
209 209 except:
210 210 pass
211 211 try:
212 212 if outname:
213 213 os.unlink(outname)
214 214 except:
215 215 pass
216 216
217 217 filtertable = {
218 218 'tempfile:': tempfilter,
219 219 'pipe:': pipefilter,
220 220 }
221 221
222 222 def filter(s, cmd):
223 223 "filter a string through a command that transforms its input to its output"
224 224 for name, fn in filtertable.iteritems():
225 225 if cmd.startswith(name):
226 226 return fn(s, cmd[len(name):].lstrip())
227 227 return pipefilter(s, cmd)
228 228
229 229 def binary(s):
230 230 """return true if a string is binary data"""
231 231 return bool(s and '\0' in s)
232 232
233 233 def increasingchunks(source, min=1024, max=65536):
234 234 '''return no less than min bytes per chunk while data remains,
235 235 doubling min after each chunk until it reaches max'''
236 236 def log2(x):
237 237 if not x:
238 238 return 0
239 239 i = 0
240 240 while x:
241 241 x >>= 1
242 242 i += 1
243 243 return i - 1
244 244
245 245 buf = []
246 246 blen = 0
247 247 for chunk in source:
248 248 buf.append(chunk)
249 249 blen += len(chunk)
250 250 if blen >= min:
251 251 if min < max:
252 252 min = min << 1
253 253 nmin = 1 << log2(blen)
254 254 if nmin > min:
255 255 min = nmin
256 256 if min > max:
257 257 min = max
258 258 yield ''.join(buf)
259 259 blen = 0
260 260 buf = []
261 261 if buf:
262 262 yield ''.join(buf)
263 263
264 264 Abort = error.Abort
265 265
266 266 def always(fn):
267 267 return True
268 268
269 269 def never(fn):
270 270 return False
271 271
272 272 def pathto(root, n1, n2):
273 273 '''return the relative path from one place to another.
274 274 root should use os.sep to separate directories
275 275 n1 should use os.sep to separate directories
276 276 n2 should use "/" to separate directories
277 277 returns an os.sep-separated path.
278 278
279 279 If n1 is a relative path, it's assumed it's
280 280 relative to root.
281 281 n2 should always be relative to root.
282 282 '''
283 283 if not n1:
284 284 return localpath(n2)
285 285 if os.path.isabs(n1):
286 286 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
287 287 return os.path.join(root, localpath(n2))
288 288 n2 = '/'.join((pconvert(root), n2))
289 289 a, b = splitpath(n1), n2.split('/')
290 290 a.reverse()
291 291 b.reverse()
292 292 while a and b and a[-1] == b[-1]:
293 293 a.pop()
294 294 b.pop()
295 295 b.reverse()
296 296 return os.sep.join((['..'] * len(a)) + b) or '.'
297 297
298 298 def canonpath(root, cwd, myname, auditor=None):
299 299 """return the canonical path of myname, given cwd and root"""
300 300 if endswithsep(root):
301 301 rootsep = root
302 302 else:
303 303 rootsep = root + os.sep
304 304 name = myname
305 305 if not os.path.isabs(name):
306 306 name = os.path.join(root, cwd, name)
307 307 name = os.path.normpath(name)
308 308 if auditor is None:
309 309 auditor = path_auditor(root)
310 310 if name != rootsep and name.startswith(rootsep):
311 311 name = name[len(rootsep):]
312 312 auditor(name)
313 313 return pconvert(name)
314 314 elif name == root:
315 315 return ''
316 316 else:
317 317 # Determine whether `name' is in the hierarchy at or beneath `root',
318 318 # by iterating name=dirname(name) until that causes no change (can't
319 319 # check name == '/', because that doesn't work on windows). For each
320 320 # `name', compare dev/inode numbers. If they match, the list `rel'
321 321 # holds the reversed list of components making up the relative file
322 322 # name we want.
323 323 root_st = os.stat(root)
324 324 rel = []
325 325 while True:
326 326 try:
327 327 name_st = os.stat(name)
328 328 except OSError:
329 329 break
330 330 if samestat(name_st, root_st):
331 331 if not rel:
332 332 # name was actually the same as root (maybe a symlink)
333 333 return ''
334 334 rel.reverse()
335 335 name = os.path.join(*rel)
336 336 auditor(name)
337 337 return pconvert(name)
338 338 dirname, basename = os.path.split(name)
339 339 rel.append(basename)
340 340 if dirname == name:
341 341 break
342 342 name = dirname
343 343
344 344 raise Abort('%s not under root' % myname)
345 345
346 346 _hgexecutable = None
347 347
348 348 def main_is_frozen():
349 349 """return True if we are a frozen executable.
350 350
351 351 The code supports py2exe (most common, Windows only) and tools/freeze
352 352 (portable, not much used).
353 353 """
354 354 return (hasattr(sys, "frozen") or # new py2exe
355 355 hasattr(sys, "importers") or # old py2exe
356 356 imp.is_frozen("__main__")) # tools/freeze
357 357
358 358 def hgexecutable():
359 359 """return location of the 'hg' executable.
360 360
361 361 Defaults to $HG or 'hg' in the search path.
362 362 """
363 363 if _hgexecutable is None:
364 364 hg = os.environ.get('HG')
365 365 if hg:
366 366 set_hgexecutable(hg)
367 367 elif main_is_frozen():
368 368 set_hgexecutable(sys.executable)
369 369 else:
370 370 exe = find_exe('hg') or os.path.basename(sys.argv[0])
371 371 set_hgexecutable(exe)
372 372 return _hgexecutable
373 373
374 374 def set_hgexecutable(path):
375 375 """set location of the 'hg' executable"""
376 376 global _hgexecutable
377 377 _hgexecutable = path
378 378
379 379 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
380 380 '''enhanced shell command execution.
381 381 run with environment maybe modified, maybe in different dir.
382 382
383 383 if command fails and onerr is None, return status. if ui object,
384 384 print error message and return status, else raise onerr object as
385 385 exception.
386 386
387 387 if out is specified, it is assumed to be a file-like object that has a
388 388 write() method. stdout and stderr will be redirected to out.'''
389 try:
390 sys.stdout.flush()
391 except Exception:
392 pass
389 393 def py2shell(val):
390 394 'convert python object into string that is useful to shell'
391 395 if val is None or val is False:
392 396 return '0'
393 397 if val is True:
394 398 return '1'
395 399 return str(val)
396 400 origcmd = cmd
397 401 cmd = quotecommand(cmd)
398 402 env = dict(os.environ)
399 403 env.update((k, py2shell(v)) for k, v in environ.iteritems())
400 404 env['HG'] = hgexecutable()
401 405 if out is None:
402 406 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
403 407 env=env, cwd=cwd)
404 408 else:
405 409 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
406 410 env=env, cwd=cwd, stdout=subprocess.PIPE,
407 411 stderr=subprocess.STDOUT)
408 412 for line in proc.stdout:
409 413 out.write(line)
410 414 proc.wait()
411 415 rc = proc.returncode
412 416 if sys.platform == 'OpenVMS' and rc & 1:
413 417 rc = 0
414 418 if rc and onerr:
415 419 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
416 420 explain_exit(rc)[0])
417 421 if errprefix:
418 422 errmsg = '%s: %s' % (errprefix, errmsg)
419 423 try:
420 424 onerr.warn(errmsg + '\n')
421 425 except AttributeError:
422 426 raise onerr(errmsg)
423 427 return rc
424 428
425 429 def checksignature(func):
426 430 '''wrap a function with code to check for calling errors'''
427 431 def check(*args, **kwargs):
428 432 try:
429 433 return func(*args, **kwargs)
430 434 except TypeError:
431 435 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
432 436 raise error.SignatureError
433 437 raise
434 438
435 439 return check
436 440
437 441 def unlinkpath(f):
438 442 """unlink and remove the directory if it is empty"""
439 443 os.unlink(f)
440 444 # try removing directories that might now be empty
441 445 try:
442 446 os.removedirs(os.path.dirname(f))
443 447 except OSError:
444 448 pass
445 449
446 450 def copyfile(src, dest):
447 451 "copy a file, preserving mode and atime/mtime"
448 452 if os.path.islink(src):
449 453 try:
450 454 os.unlink(dest)
451 455 except:
452 456 pass
453 457 os.symlink(os.readlink(src), dest)
454 458 else:
455 459 try:
456 460 shutil.copyfile(src, dest)
457 461 shutil.copymode(src, dest)
458 462 except shutil.Error, inst:
459 463 raise Abort(str(inst))
460 464
461 465 def copyfiles(src, dst, hardlink=None):
462 466 """Copy a directory tree using hardlinks if possible"""
463 467
464 468 if hardlink is None:
465 469 hardlink = (os.stat(src).st_dev ==
466 470 os.stat(os.path.dirname(dst)).st_dev)
467 471
468 472 num = 0
469 473 if os.path.isdir(src):
470 474 os.mkdir(dst)
471 475 for name, kind in osutil.listdir(src):
472 476 srcname = os.path.join(src, name)
473 477 dstname = os.path.join(dst, name)
474 478 hardlink, n = copyfiles(srcname, dstname, hardlink)
475 479 num += n
476 480 else:
477 481 if hardlink:
478 482 try:
479 483 os_link(src, dst)
480 484 except (IOError, OSError):
481 485 hardlink = False
482 486 shutil.copy(src, dst)
483 487 else:
484 488 shutil.copy(src, dst)
485 489 num += 1
486 490
487 491 return hardlink, num
488 492
489 493 class path_auditor(object):
490 494 '''ensure that a filesystem path contains no banned components.
491 495 the following properties of a path are checked:
492 496
493 497 - ends with a directory separator
494 498 - under top-level .hg
495 499 - starts at the root of a windows drive
496 500 - contains ".."
497 501 - traverses a symlink (e.g. a/symlink_here/b)
498 502 - inside a nested repository (a callback can be used to approve
499 503 some nested repositories, e.g., subrepositories)
500 504 '''
501 505
502 506 def __init__(self, root, callback=None):
503 507 self.audited = set()
504 508 self.auditeddir = set()
505 509 self.root = root
506 510 self.callback = callback
507 511
508 512 def __call__(self, path):
509 513 if path in self.audited:
510 514 return
511 515 # AIX ignores "/" at end of path, others raise EISDIR.
512 516 if endswithsep(path):
513 517 raise Abort(_("path ends in directory separator: %s") % path)
514 518 normpath = os.path.normcase(path)
515 519 parts = splitpath(normpath)
516 520 if (os.path.splitdrive(path)[0]
517 521 or parts[0].lower() in ('.hg', '.hg.', '')
518 522 or os.pardir in parts):
519 523 raise Abort(_("path contains illegal component: %s") % path)
520 524 if '.hg' in path.lower():
521 525 lparts = [p.lower() for p in parts]
522 526 for p in '.hg', '.hg.':
523 527 if p in lparts[1:]:
524 528 pos = lparts.index(p)
525 529 base = os.path.join(*parts[:pos])
526 530 raise Abort(_('path %r is inside repo %r') % (path, base))
527 531 def check(prefix):
528 532 curpath = os.path.join(self.root, prefix)
529 533 try:
530 534 st = os.lstat(curpath)
531 535 except OSError, err:
532 536 # EINVAL can be raised as invalid path syntax under win32.
533 537 # They must be ignored for patterns can be checked too.
534 538 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
535 539 raise
536 540 else:
537 541 if stat.S_ISLNK(st.st_mode):
538 542 raise Abort(_('path %r traverses symbolic link %r') %
539 543 (path, prefix))
540 544 elif (stat.S_ISDIR(st.st_mode) and
541 545 os.path.isdir(os.path.join(curpath, '.hg'))):
542 546 if not self.callback or not self.callback(curpath):
543 547 raise Abort(_('path %r is inside repo %r') %
544 548 (path, prefix))
545 549 parts.pop()
546 550 prefixes = []
547 551 while parts:
548 552 prefix = os.sep.join(parts)
549 553 if prefix in self.auditeddir:
550 554 break
551 555 check(prefix)
552 556 prefixes.append(prefix)
553 557 parts.pop()
554 558
555 559 self.audited.add(path)
556 560 # only add prefixes to the cache after checking everything: we don't
557 561 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
558 562 self.auditeddir.update(prefixes)
559 563
560 564 def lookup_reg(key, name=None, scope=None):
561 565 return None
562 566
563 567 def hidewindow():
564 568 """Hide current shell window.
565 569
566 570 Used to hide the window opened when starting asynchronous
567 571 child process under Windows, unneeded on other systems.
568 572 """
569 573 pass
570 574
571 575 if os.name == 'nt':
572 576 from windows import *
573 577 else:
574 578 from posix import *
575 579
576 580 def makelock(info, pathname):
577 581 try:
578 582 return os.symlink(info, pathname)
579 583 except OSError, why:
580 584 if why.errno == errno.EEXIST:
581 585 raise
582 586 except AttributeError: # no symlink in os
583 587 pass
584 588
585 589 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
586 590 os.write(ld, info)
587 591 os.close(ld)
588 592
589 593 def readlock(pathname):
590 594 try:
591 595 return os.readlink(pathname)
592 596 except OSError, why:
593 597 if why.errno not in (errno.EINVAL, errno.ENOSYS):
594 598 raise
595 599 except AttributeError: # no symlink in os
596 600 pass
597 601 fp = posixfile(pathname)
598 602 r = fp.read()
599 603 fp.close()
600 604 return r
601 605
602 606 def fstat(fp):
603 607 '''stat file object that may not have fileno method.'''
604 608 try:
605 609 return os.fstat(fp.fileno())
606 610 except AttributeError:
607 611 return os.stat(fp.name)
608 612
609 613 # File system features
610 614
611 615 def checkcase(path):
612 616 """
613 617 Check whether the given path is on a case-sensitive filesystem
614 618
615 619 Requires a path (like /foo/.hg) ending with a foldable final
616 620 directory component.
617 621 """
618 622 s1 = os.stat(path)
619 623 d, b = os.path.split(path)
620 624 p2 = os.path.join(d, b.upper())
621 625 if path == p2:
622 626 p2 = os.path.join(d, b.lower())
623 627 try:
624 628 s2 = os.stat(p2)
625 629 if s2 == s1:
626 630 return False
627 631 return True
628 632 except:
629 633 return True
630 634
631 635 _fspathcache = {}
632 636 def fspath(name, root):
633 637 '''Get name in the case stored in the filesystem
634 638
635 639 The name is either relative to root, or it is an absolute path starting
636 640 with root. Note that this function is unnecessary, and should not be
637 641 called, for case-sensitive filesystems (simply because it's expensive).
638 642 '''
639 643 # If name is absolute, make it relative
640 644 if name.lower().startswith(root.lower()):
641 645 l = len(root)
642 646 if name[l] == os.sep or name[l] == os.altsep:
643 647 l = l + 1
644 648 name = name[l:]
645 649
646 650 if not os.path.lexists(os.path.join(root, name)):
647 651 return None
648 652
649 653 seps = os.sep
650 654 if os.altsep:
651 655 seps = seps + os.altsep
652 656 # Protect backslashes. This gets silly very quickly.
653 657 seps.replace('\\','\\\\')
654 658 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
655 659 dir = os.path.normcase(os.path.normpath(root))
656 660 result = []
657 661 for part, sep in pattern.findall(name):
658 662 if sep:
659 663 result.append(sep)
660 664 continue
661 665
662 666 if dir not in _fspathcache:
663 667 _fspathcache[dir] = os.listdir(dir)
664 668 contents = _fspathcache[dir]
665 669
666 670 lpart = part.lower()
667 671 lenp = len(part)
668 672 for n in contents:
669 673 if lenp == len(n) and n.lower() == lpart:
670 674 result.append(n)
671 675 break
672 676 else:
673 677 # Cannot happen, as the file exists!
674 678 result.append(part)
675 679 dir = os.path.join(dir, lpart)
676 680
677 681 return ''.join(result)
678 682
679 683 def checkexec(path):
680 684 """
681 685 Check whether the given path is on a filesystem with UNIX-like exec flags
682 686
683 687 Requires a directory (like /foo/.hg)
684 688 """
685 689
686 690 # VFAT on some Linux versions can flip mode but it doesn't persist
687 691 # a FS remount. Frequently we can detect it if files are created
688 692 # with exec bit on.
689 693
690 694 try:
691 695 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
692 696 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
693 697 try:
694 698 os.close(fh)
695 699 m = os.stat(fn).st_mode & 0777
696 700 new_file_has_exec = m & EXECFLAGS
697 701 os.chmod(fn, m ^ EXECFLAGS)
698 702 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
699 703 finally:
700 704 os.unlink(fn)
701 705 except (IOError, OSError):
702 706 # we don't care, the user probably won't be able to commit anyway
703 707 return False
704 708 return not (new_file_has_exec or exec_flags_cannot_flip)
705 709
706 710 def checklink(path):
707 711 """check whether the given path is on a symlink-capable filesystem"""
708 712 # mktemp is not racy because symlink creation will fail if the
709 713 # file already exists
710 714 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
711 715 try:
712 716 os.symlink(".", name)
713 717 os.unlink(name)
714 718 return True
715 719 except (OSError, AttributeError):
716 720 return False
717 721
718 722 def checknlink(testfile):
719 723 '''check whether hardlink count reporting works properly'''
720 724
721 725 # testfile may be open, so we need a separate file for checking to
722 726 # work around issue2543 (or testfile may get lost on Samba shares)
723 727 f1 = testfile + ".hgtmp1"
724 728 if os.path.lexists(f1):
725 729 return False
726 730 try:
727 731 posixfile(f1, 'w').close()
728 732 except IOError:
729 733 return False
730 734
731 735 f2 = testfile + ".hgtmp2"
732 736 fd = None
733 737 try:
734 738 try:
735 739 os_link(f1, f2)
736 740 except OSError:
737 741 return False
738 742
739 743 # nlinks() may behave differently for files on Windows shares if
740 744 # the file is open.
741 745 fd = posixfile(f2)
742 746 return nlinks(f2) > 1
743 747 finally:
744 748 if fd is not None:
745 749 fd.close()
746 750 for f in (f1, f2):
747 751 try:
748 752 os.unlink(f)
749 753 except OSError:
750 754 pass
751 755
752 756 return False
753 757
754 758 def endswithsep(path):
755 759 '''Check path ends with os.sep or os.altsep.'''
756 760 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
757 761
758 762 def splitpath(path):
759 763 '''Split path by os.sep.
760 764 Note that this function does not use os.altsep because this is
761 765 an alternative of simple "xxx.split(os.sep)".
762 766 It is recommended to use os.path.normpath() before using this
763 767 function if need.'''
764 768 return path.split(os.sep)
765 769
766 770 def gui():
767 771 '''Are we running in a GUI?'''
768 772 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
769 773
770 774 def mktempcopy(name, emptyok=False, createmode=None):
771 775 """Create a temporary file with the same contents from name
772 776
773 777 The permission bits are copied from the original file.
774 778
775 779 If the temporary file is going to be truncated immediately, you
776 780 can use emptyok=True as an optimization.
777 781
778 782 Returns the name of the temporary file.
779 783 """
780 784 d, fn = os.path.split(name)
781 785 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
782 786 os.close(fd)
783 787 # Temporary files are created with mode 0600, which is usually not
784 788 # what we want. If the original file already exists, just copy
785 789 # its mode. Otherwise, manually obey umask.
786 790 try:
787 791 st_mode = os.lstat(name).st_mode & 0777
788 792 except OSError, inst:
789 793 if inst.errno != errno.ENOENT:
790 794 raise
791 795 st_mode = createmode
792 796 if st_mode is None:
793 797 st_mode = ~umask
794 798 st_mode &= 0666
795 799 os.chmod(temp, st_mode)
796 800 if emptyok:
797 801 return temp
798 802 try:
799 803 try:
800 804 ifp = posixfile(name, "rb")
801 805 except IOError, inst:
802 806 if inst.errno == errno.ENOENT:
803 807 return temp
804 808 if not getattr(inst, 'filename', None):
805 809 inst.filename = name
806 810 raise
807 811 ofp = posixfile(temp, "wb")
808 812 for chunk in filechunkiter(ifp):
809 813 ofp.write(chunk)
810 814 ifp.close()
811 815 ofp.close()
812 816 except:
813 817 try: os.unlink(temp)
814 818 except: pass
815 819 raise
816 820 return temp
817 821
818 822 class atomictempfile(object):
819 823 """file-like object that atomically updates a file
820 824
821 825 All writes will be redirected to a temporary copy of the original
822 826 file. When rename is called, the copy is renamed to the original
823 827 name, making the changes visible.
824 828 """
825 829 def __init__(self, name, mode='w+b', createmode=None):
826 830 self.__name = name
827 831 self._fp = None
828 832 self.temp = mktempcopy(name, emptyok=('w' in mode),
829 833 createmode=createmode)
830 834 self._fp = posixfile(self.temp, mode)
831 835
832 836 def __getattr__(self, name):
833 837 return getattr(self._fp, name)
834 838
835 839 def rename(self):
836 840 if not self._fp.closed:
837 841 self._fp.close()
838 842 rename(self.temp, localpath(self.__name))
839 843
840 844 def close(self):
841 845 if not self._fp:
842 846 return
843 847 if not self._fp.closed:
844 848 try:
845 849 os.unlink(self.temp)
846 850 except: pass
847 851 self._fp.close()
848 852
849 853 def __del__(self):
850 854 self.close()
851 855
852 856 def makedirs(name, mode=None):
853 857 """recursive directory creation with parent mode inheritance"""
854 858 parent = os.path.abspath(os.path.dirname(name))
855 859 try:
856 860 os.mkdir(name)
857 861 if mode is not None:
858 862 os.chmod(name, mode)
859 863 return
860 864 except OSError, err:
861 865 if err.errno == errno.EEXIST:
862 866 return
863 867 if not name or parent == name or err.errno != errno.ENOENT:
864 868 raise
865 869 makedirs(parent, mode)
866 870 makedirs(name, mode)
867 871
868 872 class opener(object):
869 873 """Open files relative to a base directory
870 874
871 875 This class is used to hide the details of COW semantics and
872 876 remote file access from higher level code.
873 877 """
874 878 def __init__(self, base, audit=True):
875 879 self.base = base
876 880 if audit:
877 881 self.auditor = path_auditor(base)
878 882 else:
879 883 self.auditor = always
880 884 self.createmode = None
881 885 self._trustnlink = None
882 886
883 887 @propertycache
884 888 def _can_symlink(self):
885 889 return checklink(self.base)
886 890
887 891 def _fixfilemode(self, name):
888 892 if self.createmode is None:
889 893 return
890 894 os.chmod(name, self.createmode & 0666)
891 895
892 896 def __call__(self, path, mode="r", text=False, atomictemp=False):
893 897 self.auditor(path)
894 898 f = os.path.join(self.base, path)
895 899
896 900 if not text and "b" not in mode:
897 901 mode += "b" # for that other OS
898 902
899 903 nlink = -1
900 904 dirname, basename = os.path.split(f)
901 905 # If basename is empty, then the path is malformed because it points
902 906 # to a directory. Let the posixfile() call below raise IOError.
903 907 if basename and mode not in ('r', 'rb'):
904 908 if atomictemp:
905 909 if not os.path.isdir(dirname):
906 910 makedirs(dirname, self.createmode)
907 911 return atomictempfile(f, mode, self.createmode)
908 912 try:
909 913 if 'w' in mode:
910 914 unlink(f)
911 915 nlink = 0
912 916 else:
913 917 # nlinks() may behave differently for files on Windows
914 918 # shares if the file is open.
915 919 fd = posixfile(f)
916 920 nlink = nlinks(f)
917 921 if nlink < 1:
918 922 nlink = 2 # force mktempcopy (issue1922)
919 923 fd.close()
920 924 except (OSError, IOError), e:
921 925 if e.errno != errno.ENOENT:
922 926 raise
923 927 nlink = 0
924 928 if not os.path.isdir(dirname):
925 929 makedirs(dirname, self.createmode)
926 930 if nlink > 0:
927 931 if self._trustnlink is None:
928 932 self._trustnlink = nlink > 1 or checknlink(f)
929 933 if nlink > 1 or not self._trustnlink:
930 934 rename(mktempcopy(f), f)
931 935 fp = posixfile(f, mode)
932 936 if nlink == 0:
933 937 self._fixfilemode(f)
934 938 return fp
935 939
936 940 def symlink(self, src, dst):
937 941 self.auditor(dst)
938 942 linkname = os.path.join(self.base, dst)
939 943 try:
940 944 os.unlink(linkname)
941 945 except OSError:
942 946 pass
943 947
944 948 dirname = os.path.dirname(linkname)
945 949 if not os.path.exists(dirname):
946 950 makedirs(dirname, self.createmode)
947 951
948 952 if self._can_symlink:
949 953 try:
950 954 os.symlink(src, linkname)
951 955 except OSError, err:
952 956 raise OSError(err.errno, _('could not symlink to %r: %s') %
953 957 (src, err.strerror), linkname)
954 958 else:
955 959 f = self(dst, "w")
956 960 f.write(src)
957 961 f.close()
958 962 self._fixfilemode(dst)
959 963
960 964 class chunkbuffer(object):
961 965 """Allow arbitrary sized chunks of data to be efficiently read from an
962 966 iterator over chunks of arbitrary size."""
963 967
964 968 def __init__(self, in_iter):
965 969 """in_iter is the iterator that's iterating over the input chunks.
966 970 targetsize is how big a buffer to try to maintain."""
967 971 def splitbig(chunks):
968 972 for chunk in chunks:
969 973 if len(chunk) > 2**20:
970 974 pos = 0
971 975 while pos < len(chunk):
972 976 end = pos + 2 ** 18
973 977 yield chunk[pos:end]
974 978 pos = end
975 979 else:
976 980 yield chunk
977 981 self.iter = splitbig(in_iter)
978 982 self._queue = []
979 983
980 984 def read(self, l):
981 985 """Read L bytes of data from the iterator of chunks of data.
982 986 Returns less than L bytes if the iterator runs dry."""
983 987 left = l
984 988 buf = ''
985 989 queue = self._queue
986 990 while left > 0:
987 991 # refill the queue
988 992 if not queue:
989 993 target = 2**18
990 994 for chunk in self.iter:
991 995 queue.append(chunk)
992 996 target -= len(chunk)
993 997 if target <= 0:
994 998 break
995 999 if not queue:
996 1000 break
997 1001
998 1002 chunk = queue.pop(0)
999 1003 left -= len(chunk)
1000 1004 if left < 0:
1001 1005 queue.insert(0, chunk[left:])
1002 1006 buf += chunk[:left]
1003 1007 else:
1004 1008 buf += chunk
1005 1009
1006 1010 return buf
1007 1011
1008 1012 def filechunkiter(f, size=65536, limit=None):
1009 1013 """Create a generator that produces the data in the file size
1010 1014 (default 65536) bytes at a time, up to optional limit (default is
1011 1015 to read all data). Chunks may be less than size bytes if the
1012 1016 chunk is the last chunk in the file, or the file is a socket or
1013 1017 some other type of file that sometimes reads less data than is
1014 1018 requested."""
1015 1019 assert size >= 0
1016 1020 assert limit is None or limit >= 0
1017 1021 while True:
1018 1022 if limit is None:
1019 1023 nbytes = size
1020 1024 else:
1021 1025 nbytes = min(limit, size)
1022 1026 s = nbytes and f.read(nbytes)
1023 1027 if not s:
1024 1028 break
1025 1029 if limit:
1026 1030 limit -= len(s)
1027 1031 yield s
1028 1032
1029 1033 def makedate():
1030 1034 lt = time.localtime()
1031 1035 if lt[8] == 1 and time.daylight:
1032 1036 tz = time.altzone
1033 1037 else:
1034 1038 tz = time.timezone
1035 1039 t = time.mktime(lt)
1036 1040 if t < 0:
1037 1041 hint = _("check your clock")
1038 1042 raise Abort(_("negative timestamp: %d") % t, hint=hint)
1039 1043 return t, tz
1040 1044
1041 1045 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1042 1046 """represent a (unixtime, offset) tuple as a localized time.
1043 1047 unixtime is seconds since the epoch, and offset is the time zone's
1044 1048 number of seconds away from UTC. if timezone is false, do not
1045 1049 append time zone to string."""
1046 1050 t, tz = date or makedate()
1047 1051 if t < 0:
1048 1052 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1049 1053 tz = 0
1050 1054 if "%1" in format or "%2" in format:
1051 1055 sign = (tz > 0) and "-" or "+"
1052 1056 minutes = abs(tz) // 60
1053 1057 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1054 1058 format = format.replace("%2", "%02d" % (minutes % 60))
1055 1059 s = time.strftime(format, time.gmtime(float(t) - tz))
1056 1060 return s
1057 1061
1058 1062 def shortdate(date=None):
1059 1063 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1060 1064 return datestr(date, format='%Y-%m-%d')
1061 1065
1062 1066 def strdate(string, format, defaults=[]):
1063 1067 """parse a localized time string and return a (unixtime, offset) tuple.
1064 1068 if the string cannot be parsed, ValueError is raised."""
1065 1069 def timezone(string):
1066 1070 tz = string.split()[-1]
1067 1071 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1068 1072 sign = (tz[0] == "+") and 1 or -1
1069 1073 hours = int(tz[1:3])
1070 1074 minutes = int(tz[3:5])
1071 1075 return -sign * (hours * 60 + minutes) * 60
1072 1076 if tz == "GMT" or tz == "UTC":
1073 1077 return 0
1074 1078 return None
1075 1079
1076 1080 # NOTE: unixtime = localunixtime + offset
1077 1081 offset, date = timezone(string), string
1078 1082 if offset is not None:
1079 1083 date = " ".join(string.split()[:-1])
1080 1084
1081 1085 # add missing elements from defaults
1082 1086 usenow = False # default to using biased defaults
1083 1087 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1084 1088 found = [True for p in part if ("%"+p) in format]
1085 1089 if not found:
1086 1090 date += "@" + defaults[part][usenow]
1087 1091 format += "@%" + part[0]
1088 1092 else:
1089 1093 # We've found a specific time element, less specific time
1090 1094 # elements are relative to today
1091 1095 usenow = True
1092 1096
1093 1097 timetuple = time.strptime(date, format)
1094 1098 localunixtime = int(calendar.timegm(timetuple))
1095 1099 if offset is None:
1096 1100 # local timezone
1097 1101 unixtime = int(time.mktime(timetuple))
1098 1102 offset = unixtime - localunixtime
1099 1103 else:
1100 1104 unixtime = localunixtime + offset
1101 1105 return unixtime, offset
1102 1106
1103 1107 def parsedate(date, formats=None, bias={}):
1104 1108 """parse a localized date/time and return a (unixtime, offset) tuple.
1105 1109
1106 1110 The date may be a "unixtime offset" string or in one of the specified
1107 1111 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1108 1112 """
1109 1113 if not date:
1110 1114 return 0, 0
1111 1115 if isinstance(date, tuple) and len(date) == 2:
1112 1116 return date
1113 1117 if not formats:
1114 1118 formats = defaultdateformats
1115 1119 date = date.strip()
1116 1120 try:
1117 1121 when, offset = map(int, date.split(' '))
1118 1122 except ValueError:
1119 1123 # fill out defaults
1120 1124 now = makedate()
1121 1125 defaults = {}
1122 1126 nowmap = {}
1123 1127 for part in ("d", "mb", "yY", "HI", "M", "S"):
1124 1128 # this piece is for rounding the specific end of unknowns
1125 1129 b = bias.get(part)
1126 1130 if b is None:
1127 1131 if part[0] in "HMS":
1128 1132 b = "00"
1129 1133 else:
1130 1134 b = "0"
1131 1135
1132 1136 # this piece is for matching the generic end to today's date
1133 1137 n = datestr(now, "%" + part[0])
1134 1138
1135 1139 defaults[part] = (b, n)
1136 1140
1137 1141 for format in formats:
1138 1142 try:
1139 1143 when, offset = strdate(date, format, defaults)
1140 1144 except (ValueError, OverflowError):
1141 1145 pass
1142 1146 else:
1143 1147 break
1144 1148 else:
1145 1149 raise Abort(_('invalid date: %r') % date)
1146 1150 # validate explicit (probably user-specified) date and
1147 1151 # time zone offset. values must fit in signed 32 bits for
1148 1152 # current 32-bit linux runtimes. timezones go from UTC-12
1149 1153 # to UTC+14
1150 1154 if abs(when) > 0x7fffffff:
1151 1155 raise Abort(_('date exceeds 32 bits: %d') % when)
1152 1156 if when < 0:
1153 1157 raise Abort(_('negative date value: %d') % when)
1154 1158 if offset < -50400 or offset > 43200:
1155 1159 raise Abort(_('impossible time zone offset: %d') % offset)
1156 1160 return when, offset
1157 1161
1158 1162 def matchdate(date):
1159 1163 """Return a function that matches a given date match specifier
1160 1164
1161 1165 Formats include:
1162 1166
1163 1167 '{date}' match a given date to the accuracy provided
1164 1168
1165 1169 '<{date}' on or before a given date
1166 1170
1167 1171 '>{date}' on or after a given date
1168 1172
1169 1173 >>> p1 = parsedate("10:29:59")
1170 1174 >>> p2 = parsedate("10:30:00")
1171 1175 >>> p3 = parsedate("10:30:59")
1172 1176 >>> p4 = parsedate("10:31:00")
1173 1177 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1174 1178 >>> f = matchdate("10:30")
1175 1179 >>> f(p1[0])
1176 1180 False
1177 1181 >>> f(p2[0])
1178 1182 True
1179 1183 >>> f(p3[0])
1180 1184 True
1181 1185 >>> f(p4[0])
1182 1186 False
1183 1187 >>> f(p5[0])
1184 1188 False
1185 1189 """
1186 1190
1187 1191 def lower(date):
1188 1192 d = dict(mb="1", d="1")
1189 1193 return parsedate(date, extendeddateformats, d)[0]
1190 1194
1191 1195 def upper(date):
1192 1196 d = dict(mb="12", HI="23", M="59", S="59")
1193 1197 for days in ("31", "30", "29"):
1194 1198 try:
1195 1199 d["d"] = days
1196 1200 return parsedate(date, extendeddateformats, d)[0]
1197 1201 except:
1198 1202 pass
1199 1203 d["d"] = "28"
1200 1204 return parsedate(date, extendeddateformats, d)[0]
1201 1205
1202 1206 date = date.strip()
1203 1207 if date[0] == "<":
1204 1208 when = upper(date[1:])
1205 1209 return lambda x: x <= when
1206 1210 elif date[0] == ">":
1207 1211 when = lower(date[1:])
1208 1212 return lambda x: x >= when
1209 1213 elif date[0] == "-":
1210 1214 try:
1211 1215 days = int(date[1:])
1212 1216 except ValueError:
1213 1217 raise Abort(_("invalid day spec: %s") % date[1:])
1214 1218 when = makedate()[0] - days * 3600 * 24
1215 1219 return lambda x: x >= when
1216 1220 elif " to " in date:
1217 1221 a, b = date.split(" to ")
1218 1222 start, stop = lower(a), upper(b)
1219 1223 return lambda x: x >= start and x <= stop
1220 1224 else:
1221 1225 start, stop = lower(date), upper(date)
1222 1226 return lambda x: x >= start and x <= stop
1223 1227
1224 1228 def shortuser(user):
1225 1229 """Return a short representation of a user name or email address."""
1226 1230 f = user.find('@')
1227 1231 if f >= 0:
1228 1232 user = user[:f]
1229 1233 f = user.find('<')
1230 1234 if f >= 0:
1231 1235 user = user[f + 1:]
1232 1236 f = user.find(' ')
1233 1237 if f >= 0:
1234 1238 user = user[:f]
1235 1239 f = user.find('.')
1236 1240 if f >= 0:
1237 1241 user = user[:f]
1238 1242 return user
1239 1243
1240 1244 def email(author):
1241 1245 '''get email of author.'''
1242 1246 r = author.find('>')
1243 1247 if r == -1:
1244 1248 r = None
1245 1249 return author[author.find('<') + 1:r]
1246 1250
1247 1251 def _ellipsis(text, maxlength):
1248 1252 if len(text) <= maxlength:
1249 1253 return text, False
1250 1254 else:
1251 1255 return "%s..." % (text[:maxlength - 3]), True
1252 1256
1253 1257 def ellipsis(text, maxlength=400):
1254 1258 """Trim string to at most maxlength (default: 400) characters."""
1255 1259 try:
1256 1260 # use unicode not to split at intermediate multi-byte sequence
1257 1261 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1258 1262 maxlength)
1259 1263 if not truncated:
1260 1264 return text
1261 1265 return utext.encode(encoding.encoding)
1262 1266 except (UnicodeDecodeError, UnicodeEncodeError):
1263 1267 return _ellipsis(text, maxlength)[0]
1264 1268
1265 1269 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1266 1270 '''yield every hg repository under path, recursively.'''
1267 1271 def errhandler(err):
1268 1272 if err.filename == path:
1269 1273 raise err
1270 1274 if followsym and hasattr(os.path, 'samestat'):
1271 1275 def _add_dir_if_not_there(dirlst, dirname):
1272 1276 match = False
1273 1277 samestat = os.path.samestat
1274 1278 dirstat = os.stat(dirname)
1275 1279 for lstdirstat in dirlst:
1276 1280 if samestat(dirstat, lstdirstat):
1277 1281 match = True
1278 1282 break
1279 1283 if not match:
1280 1284 dirlst.append(dirstat)
1281 1285 return not match
1282 1286 else:
1283 1287 followsym = False
1284 1288
1285 1289 if (seen_dirs is None) and followsym:
1286 1290 seen_dirs = []
1287 1291 _add_dir_if_not_there(seen_dirs, path)
1288 1292 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1289 1293 dirs.sort()
1290 1294 if '.hg' in dirs:
1291 1295 yield root # found a repository
1292 1296 qroot = os.path.join(root, '.hg', 'patches')
1293 1297 if os.path.isdir(os.path.join(qroot, '.hg')):
1294 1298 yield qroot # we have a patch queue repo here
1295 1299 if recurse:
1296 1300 # avoid recursing inside the .hg directory
1297 1301 dirs.remove('.hg')
1298 1302 else:
1299 1303 dirs[:] = [] # don't descend further
1300 1304 elif followsym:
1301 1305 newdirs = []
1302 1306 for d in dirs:
1303 1307 fname = os.path.join(root, d)
1304 1308 if _add_dir_if_not_there(seen_dirs, fname):
1305 1309 if os.path.islink(fname):
1306 1310 for hgname in walkrepos(fname, True, seen_dirs):
1307 1311 yield hgname
1308 1312 else:
1309 1313 newdirs.append(d)
1310 1314 dirs[:] = newdirs
1311 1315
1312 1316 _rcpath = None
1313 1317
1314 1318 def os_rcpath():
1315 1319 '''return default os-specific hgrc search path'''
1316 1320 path = system_rcpath()
1317 1321 path.extend(user_rcpath())
1318 1322 path = [os.path.normpath(f) for f in path]
1319 1323 return path
1320 1324
1321 1325 def rcpath():
1322 1326 '''return hgrc search path. if env var HGRCPATH is set, use it.
1323 1327 for each item in path, if directory, use files ending in .rc,
1324 1328 else use item.
1325 1329 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1326 1330 if no HGRCPATH, use default os-specific path.'''
1327 1331 global _rcpath
1328 1332 if _rcpath is None:
1329 1333 if 'HGRCPATH' in os.environ:
1330 1334 _rcpath = []
1331 1335 for p in os.environ['HGRCPATH'].split(os.pathsep):
1332 1336 if not p:
1333 1337 continue
1334 1338 p = expandpath(p)
1335 1339 if os.path.isdir(p):
1336 1340 for f, kind in osutil.listdir(p):
1337 1341 if f.endswith('.rc'):
1338 1342 _rcpath.append(os.path.join(p, f))
1339 1343 else:
1340 1344 _rcpath.append(p)
1341 1345 else:
1342 1346 _rcpath = os_rcpath()
1343 1347 return _rcpath
1344 1348
1345 1349 def bytecount(nbytes):
1346 1350 '''return byte count formatted as readable string, with units'''
1347 1351
1348 1352 units = (
1349 1353 (100, 1 << 30, _('%.0f GB')),
1350 1354 (10, 1 << 30, _('%.1f GB')),
1351 1355 (1, 1 << 30, _('%.2f GB')),
1352 1356 (100, 1 << 20, _('%.0f MB')),
1353 1357 (10, 1 << 20, _('%.1f MB')),
1354 1358 (1, 1 << 20, _('%.2f MB')),
1355 1359 (100, 1 << 10, _('%.0f KB')),
1356 1360 (10, 1 << 10, _('%.1f KB')),
1357 1361 (1, 1 << 10, _('%.2f KB')),
1358 1362 (1, 1, _('%.0f bytes')),
1359 1363 )
1360 1364
1361 1365 for multiplier, divisor, format in units:
1362 1366 if nbytes >= divisor * multiplier:
1363 1367 return format % (nbytes / float(divisor))
1364 1368 return units[-1][2] % nbytes
1365 1369
1366 1370 def drop_scheme(scheme, path):
1367 1371 sc = scheme + ':'
1368 1372 if path.startswith(sc):
1369 1373 path = path[len(sc):]
1370 1374 if path.startswith('//'):
1371 1375 if scheme == 'file':
1372 1376 i = path.find('/', 2)
1373 1377 if i == -1:
1374 1378 return ''
1375 1379 # On Windows, absolute paths are rooted at the current drive
1376 1380 # root. On POSIX they are rooted at the file system root.
1377 1381 if os.name == 'nt':
1378 1382 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1379 1383 path = os.path.join(droot, path[i + 1:])
1380 1384 else:
1381 1385 path = path[i:]
1382 1386 else:
1383 1387 path = path[2:]
1384 1388 return path
1385 1389
1386 1390 def uirepr(s):
1387 1391 # Avoid double backslash in Windows path repr()
1388 1392 return repr(s).replace('\\\\', '\\')
1389 1393
1390 1394 # delay import of textwrap
1391 1395 def MBTextWrapper(**kwargs):
1392 1396 class tw(textwrap.TextWrapper):
1393 1397 """
1394 1398 Extend TextWrapper for double-width characters.
1395 1399
1396 1400 Some Asian characters use two terminal columns instead of one.
1397 1401 A good example of this behavior can be seen with u'\u65e5\u672c',
1398 1402 the two Japanese characters for "Japan":
1399 1403 len() returns 2, but when printed to a terminal, they eat 4 columns.
1400 1404
1401 1405 (Note that this has nothing to do whatsoever with unicode
1402 1406 representation, or encoding of the underlying string)
1403 1407 """
1404 1408 def __init__(self, **kwargs):
1405 1409 textwrap.TextWrapper.__init__(self, **kwargs)
1406 1410
1407 1411 def _cutdown(self, str, space_left):
1408 1412 l = 0
1409 1413 ucstr = unicode(str, encoding.encoding)
1410 1414 colwidth = unicodedata.east_asian_width
1411 1415 for i in xrange(len(ucstr)):
1412 1416 l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
1413 1417 if space_left < l:
1414 1418 return (ucstr[:i].encode(encoding.encoding),
1415 1419 ucstr[i:].encode(encoding.encoding))
1416 1420 return str, ''
1417 1421
1418 1422 # overriding of base class
1419 1423 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1420 1424 space_left = max(width - cur_len, 1)
1421 1425
1422 1426 if self.break_long_words:
1423 1427 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1424 1428 cur_line.append(cut)
1425 1429 reversed_chunks[-1] = res
1426 1430 elif not cur_line:
1427 1431 cur_line.append(reversed_chunks.pop())
1428 1432
1429 1433 global MBTextWrapper
1430 1434 MBTextWrapper = tw
1431 1435 return tw(**kwargs)
1432 1436
1433 1437 def wrap(line, width, initindent='', hangindent=''):
1434 1438 maxindent = max(len(hangindent), len(initindent))
1435 1439 if width <= maxindent:
1436 1440 # adjust for weird terminal size
1437 1441 width = max(78, maxindent + 1)
1438 1442 wrapper = MBTextWrapper(width=width,
1439 1443 initial_indent=initindent,
1440 1444 subsequent_indent=hangindent)
1441 1445 return wrapper.fill(line)
1442 1446
1443 1447 def iterlines(iterator):
1444 1448 for chunk in iterator:
1445 1449 for line in chunk.splitlines():
1446 1450 yield line
1447 1451
1448 1452 def expandpath(path):
1449 1453 return os.path.expanduser(os.path.expandvars(path))
1450 1454
1451 1455 def hgcmd():
1452 1456 """Return the command used to execute current hg
1453 1457
1454 1458 This is different from hgexecutable() because on Windows we want
1455 1459 to avoid things opening new shell windows like batch files, so we
1456 1460 get either the python call or current executable.
1457 1461 """
1458 1462 if main_is_frozen():
1459 1463 return [sys.executable]
1460 1464 return gethgcmd()
1461 1465
1462 1466 def rundetached(args, condfn):
1463 1467 """Execute the argument list in a detached process.
1464 1468
1465 1469 condfn is a callable which is called repeatedly and should return
1466 1470 True once the child process is known to have started successfully.
1467 1471 At this point, the child process PID is returned. If the child
1468 1472 process fails to start or finishes before condfn() evaluates to
1469 1473 True, return -1.
1470 1474 """
1471 1475 # Windows case is easier because the child process is either
1472 1476 # successfully starting and validating the condition or exiting
1473 1477 # on failure. We just poll on its PID. On Unix, if the child
1474 1478 # process fails to start, it will be left in a zombie state until
1475 1479 # the parent wait on it, which we cannot do since we expect a long
1476 1480 # running process on success. Instead we listen for SIGCHLD telling
1477 1481 # us our child process terminated.
1478 1482 terminated = set()
1479 1483 def handler(signum, frame):
1480 1484 terminated.add(os.wait())
1481 1485 prevhandler = None
1482 1486 if hasattr(signal, 'SIGCHLD'):
1483 1487 prevhandler = signal.signal(signal.SIGCHLD, handler)
1484 1488 try:
1485 1489 pid = spawndetached(args)
1486 1490 while not condfn():
1487 1491 if ((pid in terminated or not testpid(pid))
1488 1492 and not condfn()):
1489 1493 return -1
1490 1494 time.sleep(0.1)
1491 1495 return pid
1492 1496 finally:
1493 1497 if prevhandler is not None:
1494 1498 signal.signal(signal.SIGCHLD, prevhandler)
1495 1499
1496 1500 try:
1497 1501 any, all = any, all
1498 1502 except NameError:
1499 1503 def any(iterable):
1500 1504 for i in iterable:
1501 1505 if i:
1502 1506 return True
1503 1507 return False
1504 1508
1505 1509 def all(iterable):
1506 1510 for i in iterable:
1507 1511 if not i:
1508 1512 return False
1509 1513 return True
1510 1514
1511 1515 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1512 1516 """Return the result of interpolating items in the mapping into string s.
1513 1517
1514 1518 prefix is a single character string, or a two character string with
1515 1519 a backslash as the first character if the prefix needs to be escaped in
1516 1520 a regular expression.
1517 1521
1518 1522 fn is an optional function that will be applied to the replacement text
1519 1523 just before replacement.
1520 1524
1521 1525 escape_prefix is an optional flag that allows using doubled prefix for
1522 1526 its escaping.
1523 1527 """
1524 1528 fn = fn or (lambda s: s)
1525 1529 patterns = '|'.join(mapping.keys())
1526 1530 if escape_prefix:
1527 1531 patterns += '|' + prefix
1528 1532 if len(prefix) > 1:
1529 1533 prefix_char = prefix[1:]
1530 1534 else:
1531 1535 prefix_char = prefix
1532 1536 mapping[prefix_char] = prefix_char
1533 1537 r = re.compile(r'%s(%s)' % (prefix, patterns))
1534 1538 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1535 1539
1536 1540 def getport(port):
1537 1541 """Return the port for a given network service.
1538 1542
1539 1543 If port is an integer, it's returned as is. If it's a string, it's
1540 1544 looked up using socket.getservbyname(). If there's no matching
1541 1545 service, util.Abort is raised.
1542 1546 """
1543 1547 try:
1544 1548 return int(port)
1545 1549 except ValueError:
1546 1550 pass
1547 1551
1548 1552 try:
1549 1553 return socket.getservbyname(port)
1550 1554 except socket.error:
1551 1555 raise Abort(_("no port number associated with service '%s'") % port)
1552 1556
1553 1557 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1554 1558 '0': False, 'no': False, 'false': False, 'off': False,
1555 1559 'never': False}
1556 1560
1557 1561 def parsebool(s):
1558 1562 """Parse s into a boolean.
1559 1563
1560 1564 If s is not a valid boolean, returns None.
1561 1565 """
1562 1566 return _booleans.get(s.lower(), None)
@@ -1,573 +1,573
1 1 Setting up test
2 2
3 3 $ hg init test
4 4 $ cd test
5 5 $ echo 0 > afile
6 6 $ hg add afile
7 7 $ hg commit -m "0.0"
8 8 $ echo 1 >> afile
9 9 $ hg commit -m "0.1"
10 10 $ echo 2 >> afile
11 11 $ hg commit -m "0.2"
12 12 $ echo 3 >> afile
13 13 $ hg commit -m "0.3"
14 14 $ hg update -C 0
15 15 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
16 16 $ echo 1 >> afile
17 17 $ hg commit -m "1.1"
18 18 created new head
19 19 $ echo 2 >> afile
20 20 $ hg commit -m "1.2"
21 21 $ echo "a line" > fred
22 22 $ echo 3 >> afile
23 23 $ hg add fred
24 24 $ hg commit -m "1.3"
25 25 $ hg mv afile adifferentfile
26 26 $ hg commit -m "1.3m"
27 27 $ hg update -C 3
28 28 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
29 29 $ hg mv afile anotherfile
30 30 $ hg commit -m "0.3m"
31 31 $ hg verify
32 32 checking changesets
33 33 checking manifests
34 34 crosschecking files in changesets and manifests
35 35 checking files
36 36 4 files, 9 changesets, 7 total revisions
37 37 $ cd ..
38 38 $ hg init empty
39 39
40 40 Bundle --all
41 41
42 42 $ hg -R test bundle --all all.hg
43 43 9 changesets found
44 44
45 45 Bundle test to full.hg
46 46
47 47 $ hg -R test bundle full.hg empty
48 48 searching for changes
49 49 9 changesets found
50 50
51 51 Unbundle full.hg in test
52 52
53 53 $ hg -R test unbundle full.hg
54 54 adding changesets
55 55 adding manifests
56 56 adding file changes
57 57 added 0 changesets with 0 changes to 4 files
58 58 (run 'hg update' to get a working copy)
59 59
60 60 Verify empty
61 61
62 62 $ hg -R empty heads
63 63 [1]
64 64 $ hg -R empty verify
65 65 checking changesets
66 66 checking manifests
67 67 crosschecking files in changesets and manifests
68 68 checking files
69 69 0 files, 0 changesets, 0 total revisions
70 70
71 71 Pull full.hg into test (using --cwd)
72 72
73 73 $ hg --cwd test pull ../full.hg
74 74 pulling from ../full.hg
75 75 searching for changes
76 76 no changes found
77 77
78 78 Pull full.hg into empty (using --cwd)
79 79
80 80 $ hg --cwd empty pull ../full.hg
81 81 pulling from ../full.hg
82 82 requesting all changes
83 83 adding changesets
84 84 adding manifests
85 85 adding file changes
86 86 added 9 changesets with 7 changes to 4 files (+1 heads)
87 87 (run 'hg heads' to see heads, 'hg merge' to merge)
88 88
89 89 Rollback empty
90 90
91 91 $ hg -R empty rollback
92 92 rolling back to revision -1 (undo pull)
93 93
94 94 Pull full.hg into empty again (using --cwd)
95 95
96 96 $ hg --cwd empty pull ../full.hg
97 97 pulling from ../full.hg
98 98 requesting all changes
99 99 adding changesets
100 100 adding manifests
101 101 adding file changes
102 102 added 9 changesets with 7 changes to 4 files (+1 heads)
103 103 (run 'hg heads' to see heads, 'hg merge' to merge)
104 104
105 105 Pull full.hg into test (using -R)
106 106
107 107 $ hg -R test pull full.hg
108 108 pulling from full.hg
109 109 searching for changes
110 110 no changes found
111 111
112 112 Pull full.hg into empty (using -R)
113 113
114 114 $ hg -R empty pull full.hg
115 115 pulling from full.hg
116 116 searching for changes
117 117 no changes found
118 118
119 119 Rollback empty
120 120
121 121 $ hg -R empty rollback
122 122 rolling back to revision -1 (undo pull)
123 123
124 124 Pull full.hg into empty again (using -R)
125 125
126 126 $ hg -R empty pull full.hg
127 127 pulling from full.hg
128 128 requesting all changes
129 129 adding changesets
130 130 adding manifests
131 131 adding file changes
132 132 added 9 changesets with 7 changes to 4 files (+1 heads)
133 133 (run 'hg heads' to see heads, 'hg merge' to merge)
134 134
135 135 Log -R full.hg in fresh empty
136 136
137 137 $ rm -r empty
138 138 $ hg init empty
139 139 $ cd empty
140 140 $ hg -R bundle://../full.hg log
141 141 changeset: 8:aa35859c02ea
142 142 tag: tip
143 143 parent: 3:eebf5a27f8ca
144 144 user: test
145 145 date: Thu Jan 01 00:00:00 1970 +0000
146 146 summary: 0.3m
147 147
148 148 changeset: 7:a6a34bfa0076
149 149 user: test
150 150 date: Thu Jan 01 00:00:00 1970 +0000
151 151 summary: 1.3m
152 152
153 153 changeset: 6:7373c1169842
154 154 user: test
155 155 date: Thu Jan 01 00:00:00 1970 +0000
156 156 summary: 1.3
157 157
158 158 changeset: 5:1bb50a9436a7
159 159 user: test
160 160 date: Thu Jan 01 00:00:00 1970 +0000
161 161 summary: 1.2
162 162
163 163 changeset: 4:095197eb4973
164 164 parent: 0:f9ee2f85a263
165 165 user: test
166 166 date: Thu Jan 01 00:00:00 1970 +0000
167 167 summary: 1.1
168 168
169 169 changeset: 3:eebf5a27f8ca
170 170 user: test
171 171 date: Thu Jan 01 00:00:00 1970 +0000
172 172 summary: 0.3
173 173
174 174 changeset: 2:e38ba6f5b7e0
175 175 user: test
176 176 date: Thu Jan 01 00:00:00 1970 +0000
177 177 summary: 0.2
178 178
179 179 changeset: 1:34c2bf6b0626
180 180 user: test
181 181 date: Thu Jan 01 00:00:00 1970 +0000
182 182 summary: 0.1
183 183
184 184 changeset: 0:f9ee2f85a263
185 185 user: test
186 186 date: Thu Jan 01 00:00:00 1970 +0000
187 187 summary: 0.0
188 188
189 189 Make sure bundlerepo doesn't leak tempfiles (issue2491)
190 190
191 191 $ ls .hg
192 192 00changelog.i
193 193 cache
194 194 requires
195 195 store
196 196
197 197 Pull ../full.hg into empty (with hook)
198 198
199 199 $ echo '[hooks]' >> .hg/hgrc
200 200 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup' >> .hg/hgrc
201 201
202 202 doesn't work (yet ?)
203 203
204 204 hg -R bundle://../full.hg verify
205 205
206 206 $ hg pull bundle://../full.hg
207 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:../full.hg
208 207 pulling from bundle://../full.hg
209 208 requesting all changes
210 209 adding changesets
211 210 adding manifests
212 211 adding file changes
213 212 added 9 changesets with 7 changes to 4 files (+1 heads)
213 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:../full.hg
214 214 (run 'hg heads' to see heads, 'hg merge' to merge)
215 215
216 216 Rollback empty
217 217
218 218 $ hg rollback
219 219 rolling back to revision -1 (undo pull)
220 220 $ cd ..
221 221
222 222 Log -R bundle:empty+full.hg
223 223
224 224 $ hg -R bundle:empty+full.hg log --template="{rev} "; echo ""
225 225 8 7 6 5 4 3 2 1 0
226 226
227 227 Pull full.hg into empty again (using -R; with hook)
228 228
229 229 $ hg -R empty pull full.hg
230 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:empty+full.hg
231 230 pulling from full.hg
232 231 requesting all changes
233 232 adding changesets
234 233 adding manifests
235 234 adding file changes
236 235 added 9 changesets with 7 changes to 4 files (+1 heads)
236 changegroup hook: HG_NODE=f9ee2f85a263049e9ae6d37a0e67e96194ffb735 HG_SOURCE=pull HG_URL=bundle:empty+full.hg
237 237 (run 'hg heads' to see heads, 'hg merge' to merge)
238 238
239 239 Create partial clones
240 240
241 241 $ rm -r empty
242 242 $ hg init empty
243 243 $ hg clone -r 3 test partial
244 244 adding changesets
245 245 adding manifests
246 246 adding file changes
247 247 added 4 changesets with 4 changes to 1 files
248 248 updating to branch default
249 249 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
250 250 $ hg clone partial partial2
251 251 updating to branch default
252 252 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
253 253 $ cd partial
254 254
255 255 Log -R full.hg in partial
256 256
257 257 $ hg -R bundle://../full.hg log
258 258 changeset: 8:aa35859c02ea
259 259 tag: tip
260 260 parent: 3:eebf5a27f8ca
261 261 user: test
262 262 date: Thu Jan 01 00:00:00 1970 +0000
263 263 summary: 0.3m
264 264
265 265 changeset: 7:a6a34bfa0076
266 266 user: test
267 267 date: Thu Jan 01 00:00:00 1970 +0000
268 268 summary: 1.3m
269 269
270 270 changeset: 6:7373c1169842
271 271 user: test
272 272 date: Thu Jan 01 00:00:00 1970 +0000
273 273 summary: 1.3
274 274
275 275 changeset: 5:1bb50a9436a7
276 276 user: test
277 277 date: Thu Jan 01 00:00:00 1970 +0000
278 278 summary: 1.2
279 279
280 280 changeset: 4:095197eb4973
281 281 parent: 0:f9ee2f85a263
282 282 user: test
283 283 date: Thu Jan 01 00:00:00 1970 +0000
284 284 summary: 1.1
285 285
286 286 changeset: 3:eebf5a27f8ca
287 287 user: test
288 288 date: Thu Jan 01 00:00:00 1970 +0000
289 289 summary: 0.3
290 290
291 291 changeset: 2:e38ba6f5b7e0
292 292 user: test
293 293 date: Thu Jan 01 00:00:00 1970 +0000
294 294 summary: 0.2
295 295
296 296 changeset: 1:34c2bf6b0626
297 297 user: test
298 298 date: Thu Jan 01 00:00:00 1970 +0000
299 299 summary: 0.1
300 300
301 301 changeset: 0:f9ee2f85a263
302 302 user: test
303 303 date: Thu Jan 01 00:00:00 1970 +0000
304 304 summary: 0.0
305 305
306 306
307 307 Incoming full.hg in partial
308 308
309 309 $ hg incoming bundle://../full.hg
310 310 comparing with bundle://../full.hg
311 311 searching for changes
312 312 changeset: 4:095197eb4973
313 313 parent: 0:f9ee2f85a263
314 314 user: test
315 315 date: Thu Jan 01 00:00:00 1970 +0000
316 316 summary: 1.1
317 317
318 318 changeset: 5:1bb50a9436a7
319 319 user: test
320 320 date: Thu Jan 01 00:00:00 1970 +0000
321 321 summary: 1.2
322 322
323 323 changeset: 6:7373c1169842
324 324 user: test
325 325 date: Thu Jan 01 00:00:00 1970 +0000
326 326 summary: 1.3
327 327
328 328 changeset: 7:a6a34bfa0076
329 329 user: test
330 330 date: Thu Jan 01 00:00:00 1970 +0000
331 331 summary: 1.3m
332 332
333 333 changeset: 8:aa35859c02ea
334 334 tag: tip
335 335 parent: 3:eebf5a27f8ca
336 336 user: test
337 337 date: Thu Jan 01 00:00:00 1970 +0000
338 338 summary: 0.3m
339 339
340 340
341 341 Outgoing -R full.hg vs partial2 in partial
342 342
343 343 $ hg -R bundle://../full.hg outgoing ../partial2
344 344 comparing with ../partial2
345 345 searching for changes
346 346 changeset: 4:095197eb4973
347 347 parent: 0:f9ee2f85a263
348 348 user: test
349 349 date: Thu Jan 01 00:00:00 1970 +0000
350 350 summary: 1.1
351 351
352 352 changeset: 5:1bb50a9436a7
353 353 user: test
354 354 date: Thu Jan 01 00:00:00 1970 +0000
355 355 summary: 1.2
356 356
357 357 changeset: 6:7373c1169842
358 358 user: test
359 359 date: Thu Jan 01 00:00:00 1970 +0000
360 360 summary: 1.3
361 361
362 362 changeset: 7:a6a34bfa0076
363 363 user: test
364 364 date: Thu Jan 01 00:00:00 1970 +0000
365 365 summary: 1.3m
366 366
367 367 changeset: 8:aa35859c02ea
368 368 tag: tip
369 369 parent: 3:eebf5a27f8ca
370 370 user: test
371 371 date: Thu Jan 01 00:00:00 1970 +0000
372 372 summary: 0.3m
373 373
374 374
375 375 Outgoing -R does-not-exist.hg vs partial2 in partial
376 376
377 377 $ hg -R bundle://../does-not-exist.hg outgoing ../partial2
378 378 abort: No such file or directory: ../does-not-exist.hg
379 379 [255]
380 380 $ cd ..
381 381
382 382 Direct clone from bundle (all-history)
383 383
384 384 $ hg clone full.hg full-clone
385 385 requesting all changes
386 386 adding changesets
387 387 adding manifests
388 388 adding file changes
389 389 added 9 changesets with 7 changes to 4 files (+1 heads)
390 390 updating to branch default
391 391 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
392 392 $ hg -R full-clone heads
393 393 changeset: 8:aa35859c02ea
394 394 tag: tip
395 395 parent: 3:eebf5a27f8ca
396 396 user: test
397 397 date: Thu Jan 01 00:00:00 1970 +0000
398 398 summary: 0.3m
399 399
400 400 changeset: 7:a6a34bfa0076
401 401 user: test
402 402 date: Thu Jan 01 00:00:00 1970 +0000
403 403 summary: 1.3m
404 404
405 405 $ rm -r full-clone
406 406
407 407 When cloning from a non-copiable repository into '', do not
408 408 recurse infinitely (issue 2528)
409 409
410 410 $ hg clone full.hg ''
411 411 abort: No such file or directory
412 412 [255]
413 413
414 414 test for http://mercurial.selenic.com/bts/issue216
415 415
416 416 Unbundle incremental bundles into fresh empty in one go
417 417
418 418 $ rm -r empty
419 419 $ hg init empty
420 420 $ hg -R test bundle --base null -r 0 ../0.hg
421 421 1 changesets found
422 422 $ hg -R test bundle --base 0 -r 1 ../1.hg
423 423 1 changesets found
424 424 $ hg -R empty unbundle -u ../0.hg ../1.hg
425 425 adding changesets
426 426 adding manifests
427 427 adding file changes
428 428 added 1 changesets with 1 changes to 1 files
429 429 adding changesets
430 430 adding manifests
431 431 adding file changes
432 432 added 1 changesets with 1 changes to 1 files
433 433 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
434 434
435 435 test for 540d1059c802
436 436
437 437 test for 540d1059c802
438 438
439 439 $ hg init orig
440 440 $ cd orig
441 441 $ echo foo > foo
442 442 $ hg add foo
443 443 $ hg ci -m 'add foo'
444 444
445 445 $ hg clone . ../copy
446 446 updating to branch default
447 447 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
448 448 $ hg tag foo
449 449
450 450 $ cd ../copy
451 451 $ echo >> foo
452 452 $ hg ci -m 'change foo'
453 453 $ hg bundle ../bundle.hg ../orig
454 454 searching for changes
455 455 1 changesets found
456 456
457 457 $ cd ../orig
458 458 $ hg incoming ../bundle.hg
459 459 comparing with ../bundle.hg
460 460 searching for changes
461 461 changeset: 2:ed1b79f46b9a
462 462 tag: tip
463 463 parent: 0:bbd179dfa0a7
464 464 user: test
465 465 date: Thu Jan 01 00:00:00 1970 +0000
466 466 summary: change foo
467 467
468 468 $ cd ..
469 469
470 470 test for http://mercurial.selenic.com/bts/issue1144
471 471
472 472 test that verify bundle does not traceback
473 473
474 474 partial history bundle, fails w/ unkown parent
475 475
476 476 $ hg -R bundle.hg verify
477 477 abort: 00changelog.i@bbd179dfa0a7: unknown parent!
478 478 [255]
479 479
480 480 full history bundle, refuses to verify non-local repo
481 481
482 482 $ hg -R all.hg verify
483 483 abort: cannot verify bundle or remote repos
484 484 [255]
485 485
486 486 but, regular verify must continue to work
487 487
488 488 $ hg -R orig verify
489 489 checking changesets
490 490 checking manifests
491 491 crosschecking files in changesets and manifests
492 492 checking files
493 493 2 files, 2 changesets, 2 total revisions
494 494
495 495 diff against bundle
496 496
497 497 $ hg init b
498 498 $ cd b
499 499 $ hg -R ../all.hg diff -r tip
500 500 diff -r aa35859c02ea anotherfile
501 501 --- a/anotherfile Thu Jan 01 00:00:00 1970 +0000
502 502 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
503 503 @@ -1,4 +0,0 @@
504 504 -0
505 505 -1
506 506 -2
507 507 -3
508 508 $ cd ..
509 509
510 510 bundle single branch
511 511
512 512 $ hg init branchy
513 513 $ cd branchy
514 514 $ echo a >a
515 515 $ hg ci -Ama
516 516 adding a
517 517 $ echo b >b
518 518 $ hg ci -Amb
519 519 adding b
520 520 $ echo b1 >b1
521 521 $ hg ci -Amb1
522 522 adding b1
523 523 $ hg up 0
524 524 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
525 525 $ echo c >c
526 526 $ hg ci -Amc
527 527 adding c
528 528 created new head
529 529 $ echo c1 >c1
530 530 $ hg ci -Amc1
531 531 adding c1
532 532 $ hg clone -q .#tip part
533 533
534 534 == bundling via incoming
535 535
536 536 $ hg in -R part --bundle incoming.hg --template "{node}\n" .
537 537 comparing with .
538 538 searching for changes
539 539 d2ae7f538514cd87c17547b0de4cea71fe1af9fb
540 540 5ece8e77363e2b5269e27c66828b72da29e4341a
541 541
542 542 == bundling
543 543
544 544 $ hg bundle bundle.hg part --debug
545 545 searching for changes
546 546 common changesets up to c0025332f9ed
547 547 2 changesets found
548 548 list of changesets:
549 549 d2ae7f538514cd87c17547b0de4cea71fe1af9fb
550 550 5ece8e77363e2b5269e27c66828b72da29e4341a
551 551 bundling: 0 changesets
552 552 bundling: 0 changesets
553 553 bundling: 0 changesets
554 554 bundling: 1 changesets
555 555 bundling: 1 changesets
556 556 bundling: 1 changesets
557 557 bundling: 2 changesets
558 558 bundling: 0/2 manifests (0.00%)
559 559 bundling: 0/2 manifests (0.00%)
560 560 bundling: 0/2 manifests (0.00%)
561 561 bundling: 1/2 manifests (50.00%)
562 562 bundling: 1/2 manifests (50.00%)
563 563 bundling: 1/2 manifests (50.00%)
564 564 bundling: 2/2 manifests (100.00%)
565 565 bundling: b 0/2 files (0.00%)
566 566 bundling: b 0/2 files (0.00%)
567 567 bundling: b 0/2 files (0.00%)
568 568 bundling: b 0/2 files (0.00%)
569 569 bundling: b1 1/2 files (50.00%)
570 570 bundling: b1 1/2 files (50.00%)
571 571 bundling: b1 1/2 files (50.00%)
572 572 bundling: b1 1/2 files (50.00%)
573 573
@@ -1,148 +1,148
1 1 This test makes sure that we don't mark a file as merged with its ancestor
2 2 when we do a merge.
3 3
4 4 $ cat <<EOF > merge
5 5 > import sys, os
6 6 > print "merging for", os.path.basename(sys.argv[1])
7 7 > EOF
8 8 $ HGMERGE="python ../merge"; export HGMERGE
9 9
10 10 Creating base:
11 11
12 12 $ hg init a
13 13 $ cd a
14 14 $ echo 1 > foo
15 15 $ echo 1 > bar
16 16 $ echo 1 > baz
17 17 $ echo 1 > quux
18 18 $ hg add foo bar baz quux
19 19 $ hg commit -m "base"
20 20
21 21 $ cd ..
22 22 $ hg clone a b
23 23 updating to branch default
24 24 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
25 25
26 26 Creating branch a:
27 27
28 28 $ cd a
29 29 $ echo 2a > foo
30 30 $ echo 2a > bar
31 31 $ hg commit -m "branch a"
32 32
33 33 Creating branch b:
34 34
35 35 $ cd ..
36 36 $ cd b
37 37 $ echo 2b > foo
38 38 $ echo 2b > baz
39 39 $ hg commit -m "branch b"
40 40
41 41 We shouldn't have anything but n state here:
42 42
43 43 $ hg debugstate --nodates | grep -v "^n"
44 44 [1]
45 45
46 46 Merging:
47 47
48 48 $ hg pull ../a
49 49 pulling from ../a
50 50 searching for changes
51 51 adding changesets
52 52 adding manifests
53 53 adding file changes
54 54 added 1 changesets with 2 changes to 2 files (+1 heads)
55 55 (run 'hg heads' to see heads, 'hg merge' to merge)
56 56
57 57 $ hg merge -v
58 merging for foo
59 58 resolving manifests
60 59 getting bar
61 60 merging foo
61 merging for foo
62 62 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
63 63 (branch merge, don't forget to commit)
64 64
65 65 $ echo 2m > foo
66 66 $ echo 2b > baz
67 67 $ echo new > quux
68 68
69 69 We shouldn't have anything but foo in merge state here:
70 70
71 71 $ hg debugstate --nodates | grep "^m"
72 72 m 644 3 foo
73 73
74 74 $ hg ci -m "merge"
75 75
76 76 main: we should have a merge here:
77 77
78 78 $ hg debugindex .hg/store/00changelog.i
79 79 rev offset length base linkrev nodeid p1 p2
80 80 0 0 73 0 0 cdca01651b96 000000000000 000000000000
81 81 1 73 68 1 1 f6718a9cb7f3 cdca01651b96 000000000000
82 82 2 141 68 2 2 bdd988058d16 cdca01651b96 000000000000
83 83 3 209 66 3 3 d8a521142a3c f6718a9cb7f3 bdd988058d16
84 84
85 85 log should show foo and quux changed:
86 86
87 87 $ hg log -v -r tip
88 88 changeset: 3:d8a521142a3c
89 89 tag: tip
90 90 parent: 1:f6718a9cb7f3
91 91 parent: 2:bdd988058d16
92 92 user: test
93 93 date: Thu Jan 01 00:00:00 1970 +0000
94 94 files: foo quux
95 95 description:
96 96 merge
97 97
98 98
99 99
100 100 foo: we should have a merge here:
101 101
102 102 $ hg debugindex .hg/store/data/foo.i
103 103 rev offset length base linkrev nodeid p1 p2
104 104 0 0 3 0 0 b8e02f643373 000000000000 000000000000
105 105 1 3 4 1 1 2ffeddde1b65 b8e02f643373 000000000000
106 106 2 7 4 2 2 33d1fb69067a b8e02f643373 000000000000
107 107 3 11 4 3 3 aa27919ee430 2ffeddde1b65 33d1fb69067a
108 108
109 109 bar: we should not have a merge here:
110 110
111 111 $ hg debugindex .hg/store/data/bar.i
112 112 rev offset length base linkrev nodeid p1 p2
113 113 0 0 3 0 0 b8e02f643373 000000000000 000000000000
114 114 1 3 4 1 2 33d1fb69067a b8e02f643373 000000000000
115 115
116 116 baz: we should not have a merge here:
117 117
118 118 $ hg debugindex .hg/store/data/baz.i
119 119 rev offset length base linkrev nodeid p1 p2
120 120 0 0 3 0 0 b8e02f643373 000000000000 000000000000
121 121 1 3 4 1 1 2ffeddde1b65 b8e02f643373 000000000000
122 122
123 123 quux: we should not have a merge here:
124 124
125 125 $ hg debugindex .hg/store/data/quux.i
126 126 rev offset length base linkrev nodeid p1 p2
127 127 0 0 3 0 0 b8e02f643373 000000000000 000000000000
128 128 1 3 5 1 3 6128c0f33108 b8e02f643373 000000000000
129 129
130 130 Manifest entries should match tips of all files:
131 131
132 132 $ hg manifest --debug
133 133 33d1fb69067a0139622a3fa3b7ba1cdb1367972e 644 bar
134 134 2ffeddde1b65b4827f6746174a145474129fa2ce 644 baz
135 135 aa27919ee4303cfd575e1fb932dd64d75aa08be4 644 foo
136 136 6128c0f33108e8cfbb4e0824d13ae48b466d7280 644 quux
137 137
138 138 Everything should be clean now:
139 139
140 140 $ hg status
141 141
142 142 $ hg verify
143 143 checking changesets
144 144 checking manifests
145 145 crosschecking files in changesets and manifests
146 146 checking files
147 147 4 files, 4 changesets, 10 total revisions
148 148
@@ -1,481 +1,481
1 1 commit hooks can see env vars
2 2
3 3 $ hg init a
4 4 $ cd a
5 5 $ echo "[hooks]" > .hg/hgrc
6 6 $ echo 'commit = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py commit' >> .hg/hgrc
7 7 $ echo 'commit.b = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py commit.b' >> .hg/hgrc
8 8 $ echo 'precommit = unset HG_LOCAL HG_NODE HG_TAG; python "$TESTDIR"/printenv.py precommit' >> .hg/hgrc
9 9 $ echo 'pretxncommit = unset HG_LOCAL HG_TAG; python "$TESTDIR"/printenv.py pretxncommit' >> .hg/hgrc
10 10 $ echo 'pretxncommit.tip = hg -q tip' >> .hg/hgrc
11 11 $ echo 'pre-identify = python "$TESTDIR"/printenv.py pre-identify 1' >> .hg/hgrc
12 12 $ echo 'pre-cat = python "$TESTDIR"/printenv.py pre-cat' >> .hg/hgrc
13 13 $ echo 'post-cat = python "$TESTDIR"/printenv.py post-cat' >> .hg/hgrc
14 14 $ echo a > a
15 15 $ hg add a
16 16 $ hg commit -m a
17 17 precommit hook: HG_PARENT1=0000000000000000000000000000000000000000
18 18 pretxncommit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a
19 19 0:cb9a9f314b8b
20 20 commit hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
21 21 commit.b hook: HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
22 22
23 23 $ hg clone . ../b
24 24 updating to branch default
25 25 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
26 26 $ cd ../b
27 27
28 28 changegroup hooks can see env vars
29 29
30 30 $ echo '[hooks]' > .hg/hgrc
31 31 $ echo 'prechangegroup = python "$TESTDIR"/printenv.py prechangegroup' >> .hg/hgrc
32 32 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup' >> .hg/hgrc
33 33 $ echo 'incoming = python "$TESTDIR"/printenv.py incoming' >> .hg/hgrc
34 34
35 35 pretxncommit and commit hooks can see both parents of merge
36 36
37 37 $ cd ../a
38 38 $ echo b >> a
39 39 $ hg commit -m a1 -d "1 0"
40 40 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
41 41 pretxncommit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
42 42 1:ab228980c14d
43 43 commit hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
44 44 commit.b hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
45 45 $ hg update -C 0
46 46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 47 $ echo b > b
48 48 $ hg add b
49 49 $ hg commit -m b -d '1 0'
50 50 precommit hook: HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
51 51 pretxncommit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
52 52 2:ee9deb46ab31
53 53 commit hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
54 54 commit.b hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
55 55 created new head
56 56 $ hg merge 1
57 57 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
58 58 (branch merge, don't forget to commit)
59 59 $ hg commit -m merge -d '2 0'
60 60 precommit hook: HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
61 61 pretxncommit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a
62 62 3:07f3376c1e65
63 63 commit hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
64 64 commit.b hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
65 65
66 66 test generic hooks
67 67
68 68 $ hg id
69 69 pre-identify hook: HG_ARGS=id HG_OPTS={'branch': None, 'id': None, 'num': None, 'rev': '', 'tags': None} HG_PATS=[]
70 70 warning: pre-identify hook exited with status 1
71 71 [1]
72 72 $ hg cat b
73 73 pre-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b']
74 74 b
75 75 post-cat hook: HG_ARGS=cat b HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': ''} HG_PATS=['b'] HG_RESULT=0
76 76
77 77 $ cd ../b
78 78 $ hg pull ../a
79 pulling from ../a
80 searching for changes
79 81 prechangegroup hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a
82 adding changesets
83 adding manifests
84 adding file changes
85 added 3 changesets with 2 changes to 2 files
80 86 changegroup hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a
81 87 incoming hook: HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_URL=file:$TESTTMP/a
82 88 incoming hook: HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a
83 89 incoming hook: HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_URL=file:$TESTTMP/a
84 pulling from ../a
85 searching for changes
86 adding changesets
87 adding manifests
88 adding file changes
89 added 3 changesets with 2 changes to 2 files
90 90 (run 'hg update' to get a working copy)
91 91
92 92 tag hooks can see env vars
93 93
94 94 $ cd ../a
95 95 $ echo 'pretag = python "$TESTDIR"/printenv.py pretag' >> .hg/hgrc
96 96 $ echo 'tag = unset HG_PARENT1 HG_PARENT2; python "$TESTDIR"/printenv.py tag' >> .hg/hgrc
97 97 $ hg tag -d '3 0' a
98 98 pretag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
99 99 precommit hook: HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
100 100 pretxncommit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a
101 101 4:539e4b31b6dc
102 102 commit hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
103 103 commit.b hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
104 104 tag hook: HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
105 105 $ hg tag -l la
106 106 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
107 107 tag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
108 108
109 109 pretag hook can forbid tagging
110 110
111 111 $ echo 'pretag.forbid = python "$TESTDIR"/printenv.py pretag.forbid 1' >> .hg/hgrc
112 112 $ hg tag -d '4 0' fa
113 113 pretag hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
114 114 pretag.forbid hook: HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
115 115 abort: pretag.forbid hook exited with status 1
116 116 [255]
117 117 $ hg tag -l fla
118 118 pretag hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
119 119 pretag.forbid hook: HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
120 120 abort: pretag.forbid hook exited with status 1
121 121 [255]
122 122
123 123 pretxncommit hook can see changeset, can roll back txn, changeset no
124 124 more there after
125 125
126 126 $ echo 'pretxncommit.forbid0 = hg tip -q' >> .hg/hgrc
127 127 $ echo 'pretxncommit.forbid1 = python "$TESTDIR"/printenv.py pretxncommit.forbid 1' >> .hg/hgrc
128 128 $ echo z > z
129 129 $ hg add z
130 130 $ hg -q tip
131 131 4:539e4b31b6dc
132 132 $ hg commit -m 'fail' -d '4 0'
133 133 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
134 134 pretxncommit hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
135 135 5:6f611f8018c1
136 136 5:6f611f8018c1
137 137 pretxncommit.forbid hook: HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
138 138 transaction abort!
139 139 rollback completed
140 140 abort: pretxncommit.forbid1 hook exited with status 1
141 141 [255]
142 142 $ hg -q tip
143 143 4:539e4b31b6dc
144 144
145 145 precommit hook can prevent commit
146 146
147 147 $ echo 'precommit.forbid = python "$TESTDIR"/printenv.py precommit.forbid 1' >> .hg/hgrc
148 148 $ hg commit -m 'fail' -d '4 0'
149 149 precommit hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
150 150 precommit.forbid hook: HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
151 151 abort: precommit.forbid hook exited with status 1
152 152 [255]
153 153 $ hg -q tip
154 154 4:539e4b31b6dc
155 155
156 156 preupdate hook can prevent update
157 157
158 158 $ echo 'preupdate = python "$TESTDIR"/printenv.py preupdate' >> .hg/hgrc
159 159 $ hg update 1
160 160 preupdate hook: HG_PARENT1=ab228980c14d
161 161 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
162 162
163 163 update hook
164 164
165 165 $ echo 'update = python "$TESTDIR"/printenv.py update' >> .hg/hgrc
166 166 $ hg update
167 167 preupdate hook: HG_PARENT1=539e4b31b6dc
168 168 update hook: HG_ERROR=0 HG_PARENT1=539e4b31b6dc
169 169 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
170 170
171 171 prechangegroup hook can prevent incoming changes
172 172
173 173 $ cd ../b
174 174 $ hg -q tip
175 175 3:07f3376c1e65
176 176 $ echo '[hooks]' > .hg/hgrc
177 177 $ echo 'prechangegroup.forbid = python "$TESTDIR"/printenv.py prechangegroup.forbid 1' >> .hg/hgrc
178 178 $ hg pull ../a
179 prechangegroup.forbid hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a
180 179 pulling from ../a
181 180 searching for changes
181 prechangegroup.forbid hook: HG_SOURCE=pull HG_URL=file:$TESTTMP/a
182 182 abort: prechangegroup.forbid hook exited with status 1
183 183 [255]
184 184
185 185 pretxnchangegroup hook can see incoming changes, can roll back txn,
186 186 incoming changes no longer there after
187 187
188 188 $ echo '[hooks]' > .hg/hgrc
189 189 $ echo 'pretxnchangegroup.forbid0 = hg tip -q' >> .hg/hgrc
190 190 $ echo 'pretxnchangegroup.forbid1 = python "$TESTDIR"/printenv.py pretxnchangegroup.forbid 1' >> .hg/hgrc
191 191 $ hg pull ../a
192 4:539e4b31b6dc
193 pretxnchangegroup.forbid hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_URL=file:$TESTTMP/a
194 192 pulling from ../a
195 193 searching for changes
196 194 adding changesets
197 195 adding manifests
198 196 adding file changes
199 197 added 1 changesets with 1 changes to 1 files
198 4:539e4b31b6dc
199 pretxnchangegroup.forbid hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_URL=file:$TESTTMP/a
200 200 transaction abort!
201 201 rollback completed
202 202 abort: pretxnchangegroup.forbid1 hook exited with status 1
203 203 [255]
204 204 $ hg -q tip
205 205 3:07f3376c1e65
206 206
207 207 outgoing hooks can see env vars
208 208
209 209 $ rm .hg/hgrc
210 210 $ echo '[hooks]' > ../a/.hg/hgrc
211 211 $ echo 'preoutgoing = python "$TESTDIR"/printenv.py preoutgoing' >> ../a/.hg/hgrc
212 212 $ echo 'outgoing = python "$TESTDIR"/printenv.py outgoing' >> ../a/.hg/hgrc
213 213 $ hg pull ../a
214 preoutgoing hook: HG_SOURCE=pull
215 outgoing hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull
216 214 pulling from ../a
217 215 searching for changes
216 preoutgoing hook: HG_SOURCE=pull
218 217 adding changesets
218 outgoing hook: HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull
219 219 adding manifests
220 220 adding file changes
221 221 added 1 changesets with 1 changes to 1 files
222 222 (run 'hg update' to get a working copy)
223 223 $ hg rollback
224 224 rolling back to revision 3 (undo pull)
225 225
226 226 preoutgoing hook can prevent outgoing changes
227 227
228 228 $ echo 'preoutgoing.forbid = python "$TESTDIR"/printenv.py preoutgoing.forbid 1' >> ../a/.hg/hgrc
229 229 $ hg pull ../a
230 pulling from ../a
231 searching for changes
230 232 preoutgoing hook: HG_SOURCE=pull
231 233 preoutgoing.forbid hook: HG_SOURCE=pull
232 pulling from ../a
233 searching for changes
234 234 abort: preoutgoing.forbid hook exited with status 1
235 235 [255]
236 236
237 237 outgoing hooks work for local clones
238 238
239 239 $ cd ..
240 240 $ echo '[hooks]' > a/.hg/hgrc
241 241 $ echo 'preoutgoing = python "$TESTDIR"/printenv.py preoutgoing' >> a/.hg/hgrc
242 242 $ echo 'outgoing = python "$TESTDIR"/printenv.py outgoing' >> a/.hg/hgrc
243 243 $ hg clone a c
244 244 preoutgoing hook: HG_SOURCE=clone
245 245 outgoing hook: HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone
246 246 updating to branch default
247 247 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 248 $ rm -rf c
249 249
250 250 preoutgoing hook can prevent outgoing changes for local clones
251 251
252 252 $ echo 'preoutgoing.forbid = python "$TESTDIR"/printenv.py preoutgoing.forbid 1' >> a/.hg/hgrc
253 253 $ hg clone a zzz
254 254 preoutgoing hook: HG_SOURCE=clone
255 255 preoutgoing.forbid hook: HG_SOURCE=clone
256 256 abort: preoutgoing.forbid hook exited with status 1
257 257 [255]
258 258 $ cd b
259 259
260 260 $ cat > hooktests.py <<EOF
261 261 > from mercurial import util
262 262 >
263 263 > uncallable = 0
264 264 >
265 265 > def printargs(args):
266 266 > args.pop('ui', None)
267 267 > args.pop('repo', None)
268 268 > a = list(args.items())
269 269 > a.sort()
270 270 > print 'hook args:'
271 271 > for k, v in a:
272 272 > print ' ', k, v
273 273 >
274 274 > def passhook(**args):
275 275 > printargs(args)
276 276 >
277 277 > def failhook(**args):
278 278 > printargs(args)
279 279 > return True
280 280 >
281 281 > class LocalException(Exception):
282 282 > pass
283 283 >
284 284 > def raisehook(**args):
285 285 > raise LocalException('exception from hook')
286 286 >
287 287 > def aborthook(**args):
288 288 > raise util.Abort('raise abort from hook')
289 289 >
290 290 > def brokenhook(**args):
291 291 > return 1 + {}
292 292 >
293 293 > class container:
294 294 > unreachable = 1
295 295 > EOF
296 296
297 297 test python hooks
298 298
299 299 $ PYTHONPATH="`pwd`:$PYTHONPATH"
300 300 $ export PYTHONPATH
301 301
302 302 $ echo '[hooks]' > ../a/.hg/hgrc
303 303 $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
304 304 $ hg pull ../a 2>&1 | grep 'raised an exception'
305 305 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
306 306
307 307 $ echo '[hooks]' > ../a/.hg/hgrc
308 308 $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
309 309 $ hg pull ../a 2>&1 | grep 'raised an exception'
310 310 error: preoutgoing.raise hook raised an exception: exception from hook
311 311
312 312 $ echo '[hooks]' > ../a/.hg/hgrc
313 313 $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
314 314 $ hg pull ../a
315 315 pulling from ../a
316 316 searching for changes
317 317 error: preoutgoing.abort hook failed: raise abort from hook
318 318 abort: raise abort from hook
319 319 [255]
320 320
321 321 $ echo '[hooks]' > ../a/.hg/hgrc
322 322 $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
323 323 $ hg pull ../a
324 324 pulling from ../a
325 325 searching for changes
326 326 hook args:
327 327 hooktype preoutgoing
328 328 source pull
329 329 abort: preoutgoing.fail hook failed
330 330 [255]
331 331
332 332 $ echo '[hooks]' > ../a/.hg/hgrc
333 333 $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
334 334 $ hg pull ../a
335 335 pulling from ../a
336 336 searching for changes
337 337 abort: preoutgoing.uncallable hook is invalid ("hooktests.uncallable" is not callable)
338 338 [255]
339 339
340 340 $ echo '[hooks]' > ../a/.hg/hgrc
341 341 $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
342 342 $ hg pull ../a
343 343 pulling from ../a
344 344 searching for changes
345 345 abort: preoutgoing.nohook hook is invalid ("hooktests.nohook" is not defined)
346 346 [255]
347 347
348 348 $ echo '[hooks]' > ../a/.hg/hgrc
349 349 $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
350 350 $ hg pull ../a
351 351 pulling from ../a
352 352 searching for changes
353 353 abort: preoutgoing.nomodule hook is invalid ("nomodule" not in a module)
354 354 [255]
355 355
356 356 $ echo '[hooks]' > ../a/.hg/hgrc
357 357 $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
358 358 $ hg pull ../a
359 359 pulling from ../a
360 360 searching for changes
361 361 abort: preoutgoing.badmodule hook is invalid (import of "nomodule" failed)
362 362 [255]
363 363
364 364 $ echo '[hooks]' > ../a/.hg/hgrc
365 365 $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
366 366 $ hg pull ../a
367 367 pulling from ../a
368 368 searching for changes
369 369 abort: preoutgoing.unreachable hook is invalid (import of "hooktests.container" failed)
370 370 [255]
371 371
372 372 $ echo '[hooks]' > ../a/.hg/hgrc
373 373 $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
374 374 $ hg pull ../a
375 375 pulling from ../a
376 376 searching for changes
377 377 hook args:
378 378 hooktype preoutgoing
379 379 source pull
380 380 adding changesets
381 381 adding manifests
382 382 adding file changes
383 383 added 1 changesets with 1 changes to 1 files
384 384 (run 'hg update' to get a working copy)
385 385
386 386 make sure --traceback works
387 387
388 388 $ echo '[hooks]' > .hg/hgrc
389 389 $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
390 390
391 391 $ echo aa > a
392 392 $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
393 393 Traceback (most recent call last):
394 394
395 395 $ cd ..
396 396 $ hg init c
397 397 $ cd c
398 398
399 399 $ cat > hookext.py <<EOF
400 400 > def autohook(**args):
401 401 > print "Automatically installed hook"
402 402 >
403 403 > def reposetup(ui, repo):
404 404 > repo.ui.setconfig("hooks", "commit.auto", autohook)
405 405 > EOF
406 406 $ echo '[extensions]' >> .hg/hgrc
407 407 $ echo 'hookext = hookext.py' >> .hg/hgrc
408 408
409 409 $ touch foo
410 410 $ hg add foo
411 411 $ hg ci -d '0 0' -m 'add foo'
412 412 Automatically installed hook
413 413 $ echo >> foo
414 414 $ hg ci --debug -d '0 0' -m 'change foo'
415 415 foo
416 416 calling hook commit.auto: <function autohook at *> (glob)
417 417 Automatically installed hook
418 418 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
419 419
420 420 $ hg showconfig hooks
421 421 hooks.commit.auto=<function autohook at *> (glob)
422 422
423 423 test python hook configured with python:[file]:[hook] syntax
424 424
425 425 $ cd ..
426 426 $ mkdir d
427 427 $ cd d
428 428 $ hg init repo
429 429 $ mkdir hooks
430 430
431 431 $ cd hooks
432 432 $ cat > testhooks.py <<EOF
433 433 > def testhook(**args):
434 434 > print 'hook works'
435 435 > EOF
436 436 $ echo '[hooks]' > ../repo/.hg/hgrc
437 437 $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
438 438
439 439 $ cd ../repo
440 440 $ hg commit -d '0 0'
441 441 hook works
442 442 nothing changed
443 443 [1]
444 444
445 445 $ cd ../../b
446 446
447 447 make sure --traceback works on hook import failure
448 448
449 449 $ cat > importfail.py <<EOF
450 450 > import somebogusmodule
451 451 > # dereference something in the module to force demandimport to load it
452 452 > somebogusmodule.whatever
453 453 > EOF
454 454
455 455 $ echo '[hooks]' > .hg/hgrc
456 456 $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
457 457
458 458 $ echo a >> a
459 459 $ hg --traceback commit -ma 2>&1 | egrep '^(exception|Traceback|ImportError)'
460 460 exception from first failed import attempt:
461 461 Traceback (most recent call last):
462 462 ImportError: No module named somebogusmodule
463 463 exception from second failed import attempt:
464 464 Traceback (most recent call last):
465 465 ImportError: No module named hgext_importfail
466 466 Traceback (most recent call last):
467 467
468 468 Issue1827: Hooks Update & Commit not completely post operation
469 469
470 470 commit and update hooks should run after command completion
471 471
472 472 $ echo '[hooks]' > .hg/hgrc
473 473 $ echo 'commit = hg id' >> .hg/hgrc
474 474 $ echo 'update = hg id' >> .hg/hgrc
475 475 $ echo bb > a
476 476 $ hg ci -ma
477 477 223eafe2750c tip
478 478 $ hg up 0
479 479 cb9a9f314b8b
480 480 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
481 481
@@ -1,87 +1,87
1 1
2 2 $ hg init test
3 3 $ cd test
4 4 $ echo foo>foo
5 5 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
6 6 $ echo foo>foo.d/foo
7 7 $ echo bar>foo.d/bAr.hg.d/BaR
8 8 $ echo bar>foo.d/baR.d.hg/bAR
9 9 $ hg commit -A -m 1
10 10 adding foo
11 11 adding foo.d/bAr.hg.d/BaR
12 12 adding foo.d/baR.d.hg/bAR
13 13 adding foo.d/foo
14 14 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid
15 15 $ hg --config server.uncompressed=False serve -p $HGPORT1 -d --pid-file=../hg2.pid
16 16
17 17 Test server address cannot be reused
18 18
19 19 $ hg serve -p $HGPORT1 2>&1
20 20 abort: cannot start server at ':$HGPORT1': Address already in use
21 21 [255]
22 22 $ cd ..
23 23 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
24 24
25 25 clone via stream
26 26
27 27 $ hg clone --uncompressed http://localhost:$HGPORT/ copy 2>&1
28 28 streaming all changes
29 29 6 files to transfer, 606 bytes of data
30 30 transferred * bytes in * seconds (*B/sec) (glob)
31 31 updating to branch default
32 32 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
33 33 $ hg verify -R copy
34 34 checking changesets
35 35 checking manifests
36 36 crosschecking files in changesets and manifests
37 37 checking files
38 38 4 files, 1 changesets, 4 total revisions
39 39
40 40 try to clone via stream, should use pull instead
41 41
42 42 $ hg clone --uncompressed http://localhost:$HGPORT1/ copy2
43 43 requesting all changes
44 44 adding changesets
45 45 adding manifests
46 46 adding file changes
47 47 added 1 changesets with 4 changes to 4 files
48 48 updating to branch default
49 49 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
50 50
51 51 clone via pull
52 52
53 53 $ hg clone http://localhost:$HGPORT1/ copy-pull
54 54 requesting all changes
55 55 adding changesets
56 56 adding manifests
57 57 adding file changes
58 58 added 1 changesets with 4 changes to 4 files
59 59 updating to branch default
60 60 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
61 61 $ hg verify -R copy-pull
62 62 checking changesets
63 63 checking manifests
64 64 crosschecking files in changesets and manifests
65 65 checking files
66 66 4 files, 1 changesets, 4 total revisions
67 67 $ cd test
68 68 $ echo bar > bar
69 69 $ hg commit -A -d '1 0' -m 2
70 70 adding bar
71 71 $ cd ..
72 72
73 73 pull
74 74
75 75 $ cd copy-pull
76 76 $ echo '[hooks]' >> .hg/hgrc
77 77 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup' >> .hg/hgrc
78 78 $ hg pull
79 changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=http://localhost:$HGPORT1/
80 79 pulling from http://localhost:$HGPORT1/
81 80 searching for changes
82 81 adding changesets
83 82 adding manifests
84 83 adding file changes
85 84 added 1 changesets with 1 changes to 1 files
85 changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=http://localhost:$HGPORT1/
86 86 (run 'hg update' to get a working copy)
87 87 $ cd ..
@@ -1,269 +1,269
1 1 Proper https client requires the built-in ssl from Python 2.6.
2 2
3 3 $ "$TESTDIR/hghave" ssl || exit 80
4 4
5 5 Certificates created with:
6 6 printf '.\n.\n.\n.\n.\nlocalhost\nhg@localhost\n' | \
7 7 openssl req -newkey rsa:512 -keyout priv.pem -nodes -x509 -days 9000 -out pub.pem
8 8 Can be dumped with:
9 9 openssl x509 -in pub.pem -text
10 10
11 11 $ cat << EOT > priv.pem
12 12 > -----BEGIN PRIVATE KEY-----
13 13 > MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEApjCWeYGrIa/Vo7LH
14 14 > aRF8ou0tbgHKE33Use/whCnKEUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8
15 15 > j/xgSwIDAQABAkBxHC6+Qlf0VJXGlb6NL16yEVVTQxqDS6hA9zqu6TZjrr0YMfzc
16 16 > EGNIiZGt7HCBL0zO+cPDg/LeCZc6HQhf0KrhAiEAzlJq4hWWzvguWFIJWSoBeBUG
17 17 > MF1ACazQO7PYE8M0qfECIQDONHHP0SKZzz/ZwBZcAveC5K61f/v9hONFwbeYulzR
18 18 > +wIgc9SvbtgB/5Yzpp//4ZAEnR7oh5SClCvyB+KSx52K3nECICbhQphhoXmI10wy
19 19 > aMTellaq0bpNMHFDziqH9RsqAHhjAiEAgYGxfzkftt5IUUn/iFK89aaIpyrpuaAh
20 20 > HY8gUVkVRVs=
21 21 > -----END PRIVATE KEY-----
22 22 > EOT
23 23
24 24 $ cat << EOT > pub.pem
25 25 > -----BEGIN CERTIFICATE-----
26 26 > MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNV
27 27 > BAMMCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEw
28 28 > MTAxNDIwMzAxNFoXDTM1MDYwNTIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0
29 29 > MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANL
30 30 > ADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnKEUm34rDaXQd4lxxX
31 31 > 6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA+amm
32 32 > r24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQw
33 33 > DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAFArvQFiAZJgQczRsbYlG1xl
34 34 > t+truk37w5B3m3Ick1ntRcQrqs+hf0CO1q6Squ144geYaQ8CDirSR92fICELI1c=
35 35 > -----END CERTIFICATE-----
36 36 > EOT
37 37 $ cat priv.pem pub.pem >> server.pem
38 38 $ PRIV=`pwd`/server.pem
39 39
40 40 $ cat << EOT > pub-other.pem
41 41 > -----BEGIN CERTIFICATE-----
42 42 > MIIBqzCCAVWgAwIBAgIJALwZS731c/ORMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNV
43 43 > BAMMCWxvY2FsaG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEw
44 44 > MTAxNDIwNDUxNloXDTM1MDYwNTIwNDUxNlowMTESMBAGA1UEAwwJbG9jYWxob3N0
45 45 > MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhvc3QwXDANBgkqhkiG9w0BAQEFAANL
46 46 > ADBIAkEAsxsapLbHrqqUKuQBxdpK4G3m2LjtyrTSdpzzzFlecxd5yhNP6AyWrufo
47 47 > K4VMGo2xlu9xOo88nDSUNSKPuD09MwIDAQABo1AwTjAdBgNVHQ4EFgQUoIB1iMhN
48 48 > y868rpQ2qk9dHnU6ebswHwYDVR0jBBgwFoAUoIB1iMhNy868rpQ2qk9dHnU6ebsw
49 49 > DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJ544f125CsE7J2t55PdFaF6
50 50 > bBlNBb91FCywBgSjhBjf+GG3TNPwrPdc3yqeq+hzJiuInqbOBv9abmMyq8Wsoig=
51 51 > -----END CERTIFICATE-----
52 52 > EOT
53 53
54 54 pub.pem patched with other notBefore / notAfter:
55 55
56 56 $ cat << EOT > pub-not-yet.pem
57 57 > -----BEGIN CERTIFICATE-----
58 58 > MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNVBAMMCWxvY2Fs
59 59 > aG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTM1MDYwNTIwMzAxNFoXDTM1MDYw
60 60 > NTIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhv
61 61 > c3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnK
62 62 > EUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA
63 63 > +ammr24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQwDAYDVR0T
64 64 > BAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJXV41gWnkgC7jcpPpFRSUSZaxyzrXmD1CIqQf0WgVDb
65 65 > /12E0vR2DuZitgzUYtBaofM81aTtc0a2/YsrmqePGm0=
66 66 > -----END CERTIFICATE-----
67 67 > EOT
68 68 $ cat priv.pem pub-not-yet.pem > server-not-yet.pem
69 69
70 70 $ cat << EOT > pub-expired.pem
71 71 > -----BEGIN CERTIFICATE-----
72 72 > MIIBqzCCAVWgAwIBAgIJANAXFFyWjGnRMA0GCSqGSIb3DQEBBQUAMDExEjAQBgNVBAMMCWxvY2Fs
73 73 > aG9zdDEbMBkGCSqGSIb3DQEJARYMaGdAbG9jYWxob3N0MB4XDTEwMTAxNDIwMzAxNFoXDTEwMTAx
74 74 > NDIwMzAxNFowMTESMBAGA1UEAwwJbG9jYWxob3N0MRswGQYJKoZIhvcNAQkBFgxoZ0Bsb2NhbGhv
75 75 > c3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEApjCWeYGrIa/Vo7LHaRF8ou0tbgHKE33Use/whCnK
76 76 > EUm34rDaXQd4lxxX6aDWg06n9tiVStAKTgQAHJY8j/xgSwIDAQABo1AwTjAdBgNVHQ4EFgQUE6sA
77 77 > +ammr24dGX0kpjxOgO45hzQwHwYDVR0jBBgwFoAUE6sA+ammr24dGX0kpjxOgO45hzQwDAYDVR0T
78 78 > BAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAJfk57DTRf2nUbYaMSlVAARxMNbFGOjQhAUtY400GhKt
79 79 > 2uiKCNGKXVXD3AHWe13yHc5KttzbHQStE5Nm/DlWBWQ=
80 80 > -----END CERTIFICATE-----
81 81 > EOT
82 82 $ cat priv.pem pub-expired.pem > server-expired.pem
83 83
84 84 $ hg init test
85 85 $ cd test
86 86 $ echo foo>foo
87 87 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
88 88 $ echo foo>foo.d/foo
89 89 $ echo bar>foo.d/bAr.hg.d/BaR
90 90 $ echo bar>foo.d/baR.d.hg/bAR
91 91 $ hg commit -A -m 1
92 92 adding foo
93 93 adding foo.d/bAr.hg.d/BaR
94 94 adding foo.d/baR.d.hg/bAR
95 95 adding foo.d/foo
96 96 $ hg serve -p $HGPORT -d --pid-file=../hg0.pid --certificate=$PRIV
97 97 $ cat ../hg0.pid >> $DAEMON_PIDS
98 98
99 99 Test server address cannot be reused
100 100
101 101 $ hg serve -p $HGPORT --certificate=$PRIV 2>&1
102 102 abort: cannot start server at ':$HGPORT': Address already in use
103 103 [255]
104 104 $ cd ..
105 105
106 106 clone via pull
107 107
108 108 $ hg clone https://localhost:$HGPORT/ copy-pull
109 109 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
110 110 requesting all changes
111 111 adding changesets
112 112 adding manifests
113 113 adding file changes
114 114 added 1 changesets with 4 changes to 4 files
115 115 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
116 116 updating to branch default
117 117 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 118 $ hg verify -R copy-pull
119 119 checking changesets
120 120 checking manifests
121 121 crosschecking files in changesets and manifests
122 122 checking files
123 123 4 files, 1 changesets, 4 total revisions
124 124 $ cd test
125 125 $ echo bar > bar
126 126 $ hg commit -A -d '1 0' -m 2
127 127 adding bar
128 128 $ cd ..
129 129
130 130 pull without cacert
131 131
132 132 $ cd copy-pull
133 133 $ echo '[hooks]' >> .hg/hgrc
134 134 $ echo "changegroup = python '$TESTDIR'/printenv.py changegroup" >> .hg/hgrc
135 135 $ hg pull
136 136 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
137 changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=https://localhost:$HGPORT/
138 137 pulling from https://localhost:$HGPORT/
139 138 searching for changes
140 139 adding changesets
141 140 adding manifests
142 141 adding file changes
143 142 added 1 changesets with 1 changes to 1 files
143 changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_URL=https://localhost:$HGPORT/
144 144 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
145 145 (run 'hg update' to get a working copy)
146 146 $ cd ..
147 147
148 148 cacert configured in local repo
149 149
150 150 $ cp copy-pull/.hg/hgrc copy-pull/.hg/hgrc.bu
151 151 $ echo "[web]" >> copy-pull/.hg/hgrc
152 152 $ echo "cacerts=`pwd`/pub.pem" >> copy-pull/.hg/hgrc
153 153 $ hg -R copy-pull pull --traceback
154 154 pulling from https://localhost:$HGPORT/
155 155 searching for changes
156 156 no changes found
157 157 $ mv copy-pull/.hg/hgrc.bu copy-pull/.hg/hgrc
158 158
159 159 cacert configured globally, also testing expansion of environment
160 160 variables in the filename
161 161
162 162 $ echo "[web]" >> $HGRCPATH
163 163 $ echo 'cacerts=$P/pub.pem' >> $HGRCPATH
164 164 $ P=`pwd` hg -R copy-pull pull
165 165 pulling from https://localhost:$HGPORT/
166 166 searching for changes
167 167 no changes found
168 168 $ P=`pwd` hg -R copy-pull pull --insecure
169 169 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
170 170 pulling from https://localhost:$HGPORT/
171 171 searching for changes
172 172 no changes found
173 173
174 174 cacert mismatch
175 175
176 176 $ hg -R copy-pull pull --config web.cacerts=pub.pem https://127.0.0.1:$HGPORT/
177 177 abort: 127.0.0.1 certificate error: certificate is for localhost (use --insecure to connect insecurely)
178 178 [255]
179 179 $ hg -R copy-pull pull --config web.cacerts=pub.pem https://127.0.0.1:$HGPORT/ --insecure
180 180 warning: 127.0.0.1 certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
181 181 pulling from https://127.0.0.1:$HGPORT/
182 182 searching for changes
183 183 no changes found
184 184 $ hg -R copy-pull pull --config web.cacerts=pub-other.pem
185 185 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
186 186 [255]
187 187 $ hg -R copy-pull pull --config web.cacerts=pub-other.pem --insecure
188 188 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
189 189 pulling from https://localhost:$HGPORT/
190 190 searching for changes
191 191 no changes found
192 192
193 193 Test server cert which isn't valid yet
194 194
195 195 $ hg -R test serve -p $HGPORT1 -d --pid-file=hg1.pid --certificate=server-not-yet.pem
196 196 $ cat hg1.pid >> $DAEMON_PIDS
197 197 $ hg -R copy-pull pull --config web.cacerts=pub-not-yet.pem https://localhost:$HGPORT1/
198 198 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
199 199 [255]
200 200
201 201 Test server cert which no longer is valid
202 202
203 203 $ hg -R test serve -p $HGPORT2 -d --pid-file=hg2.pid --certificate=server-expired.pem
204 204 $ cat hg2.pid >> $DAEMON_PIDS
205 205 $ hg -R copy-pull pull --config web.cacerts=pub-expired.pem https://localhost:$HGPORT2/
206 206 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
207 207 [255]
208 208
209 209 Fingerprints
210 210
211 211 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
212 212 $ echo "localhost = 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca" >> copy-pull/.hg/hgrc
213 213 $ echo "127.0.0.1 = 914f1aff87249c09b6859b88b1906d30756491ca" >> copy-pull/.hg/hgrc
214 214
215 215 - works without cacerts
216 216 $ hg -R copy-pull id https://localhost:$HGPORT/ --config web.cacerts=
217 217 5fed3813f7f5
218 218
219 219 - fails when cert doesn't match hostname (port is ignored)
220 220 $ hg -R copy-pull id https://localhost:$HGPORT1/
221 221 abort: invalid certificate for localhost with fingerprint 28:ff:71:bf:65:31:14:23:ad:62:92:b4:0e:31:99:18:fc:83:e3:9b
222 222 [255]
223 223
224 224 - ignores that certificate doesn't match hostname
225 225 $ hg -R copy-pull id https://127.0.0.1:$HGPORT/
226 226 5fed3813f7f5
227 227
228 228 Prepare for connecting through proxy
229 229
230 230 $ kill `cat hg1.pid`
231 231 $ sleep 1
232 232
233 233 $ ("$TESTDIR/tinyproxy.py" $HGPORT1 localhost >proxy.log 2>&1 </dev/null &
234 234 $ echo $! > proxy.pid)
235 235 $ cat proxy.pid >> $DAEMON_PIDS
236 236 $ sleep 2
237 237
238 238 $ echo "[http_proxy]" >> copy-pull/.hg/hgrc
239 239 $ echo "always=True" >> copy-pull/.hg/hgrc
240 240 $ echo "[hostfingerprints]" >> copy-pull/.hg/hgrc
241 241 $ echo "localhost =" >> copy-pull/.hg/hgrc
242 242
243 243 Test unvalidated https through proxy
244 244
245 245 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure --traceback
246 246 warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
247 247 pulling from https://localhost:$HGPORT/
248 248 searching for changes
249 249 no changes found
250 250
251 251 Test https with cacert and fingerprint through proxy
252 252
253 253 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub.pem
254 254 pulling from https://localhost:$HGPORT/
255 255 searching for changes
256 256 no changes found
257 257 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull https://127.0.0.1:$HGPORT/
258 258 pulling from https://127.0.0.1:$HGPORT/
259 259 searching for changes
260 260 no changes found
261 261
262 262 Test https with cert problems through proxy
263 263
264 264 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub-other.pem
265 265 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
266 266 [255]
267 267 $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --config web.cacerts=pub-expired.pem https://localhost:$HGPORT2/
268 268 abort: error: *:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed (glob)
269 269 [255]
@@ -1,62 +1,62
1 1
2 2 $ cat > echo.py <<EOF
3 3 > #!/usr/bin/env python
4 4 > import os, sys
5 5 > try:
6 6 > import msvcrt
7 7 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
8 8 > msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
9 9 > except ImportError:
10 10 > pass
11 11 >
12 12 > for k in ('HG_FILE', 'HG_MY_ISLINK', 'HG_OTHER_ISLINK', 'HG_BASE_ISLINK'):
13 13 > print k, os.environ[k]
14 14 > EOF
15 15
16 16 Create 2 heads containing the same file, once as
17 17 a file, once as a link. Bundle was generated with:
18 18
19 19 # hg init t
20 20 # cd t
21 21 # echo a > a
22 22 # hg ci -qAm t0 -d '0 0'
23 23 # echo l > l
24 24 # hg ci -qAm t1 -d '1 0'
25 25 # hg up -C 0
26 26 # ln -s a l
27 27 # hg ci -qAm t2 -d '2 0'
28 28 # echo l2 > l2
29 29 # hg ci -qAm t3 -d '3 0'
30 30
31 31 $ hg init t
32 32 $ cd t
33 33 $ hg -q pull "$TESTDIR/test-merge-symlinks.hg"
34 34 $ hg up -C 3
35 35 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 36
37 37 Merge them and display *_ISLINK vars
38 38 merge heads
39 39
40 40 $ hg merge --tool="python ../echo.py"
41 merging l
41 42 HG_FILE l
42 43 HG_MY_ISLINK 1
43 44 HG_OTHER_ISLINK 0
44 45 HG_BASE_ISLINK 0
45 merging l
46 46 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
47 47 (branch merge, don't forget to commit)
48 48
49 49 Test working directory symlink bit calculation wrt copies,
50 50 especially on non-supporting systems.
51 51 merge working directory
52 52
53 53 $ hg up -C 2
54 54 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
55 55 $ hg copy l l2
56 56 $ HGMERGE="python ../echo.py" hg up 3
57 merging l2
57 58 HG_FILE l2
58 59 HG_MY_ISLINK 1
59 60 HG_OTHER_ISLINK 0
60 61 HG_BASE_ISLINK 0
61 merging l2
62 62 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
@@ -1,778 +1,778
1 1 test merge-tools configuration - mostly exercising filemerge.py
2 2
3 3 $ unset HGMERGE # make sure HGMERGE doesn't interfere with the test
4 4 $ hg init
5 5
6 6 revision 0
7 7
8 8 $ echo "revision 0" > f
9 9 $ echo "space" >> f
10 10 $ hg commit -Am "revision 0"
11 11 adding f
12 12
13 13 revision 1
14 14
15 15 $ echo "revision 1" > f
16 16 $ echo "space" >> f
17 17 $ hg commit -Am "revision 1"
18 18 $ hg update 0 > /dev/null
19 19
20 20 revision 2
21 21
22 22 $ echo "revision 2" > f
23 23 $ echo "space" >> f
24 24 $ hg commit -Am "revision 2"
25 25 created new head
26 26 $ hg update 0 > /dev/null
27 27
28 28 revision 3 - simple to merge
29 29
30 30 $ echo "revision 3" >> f
31 31 $ hg commit -Am "revision 3"
32 32 created new head
33 33 $ echo "[merge-tools]" > .hg/hgrc
34 34 $ echo
35 35
36 36 $ beforemerge() {
37 37 > cat .hg/hgrc
38 38 > echo "# hg update -C 1"
39 39 > hg update -C 1 > /dev/null
40 40 > }
41 41 $ aftermerge() {
42 42 > echo "# cat f"
43 43 > cat f
44 44 > echo "# hg stat"
45 45 > hg stat
46 46 > rm -f f.orig
47 47 > echo
48 48 > }
49 49 $ domerge() {
50 50 > beforemerge
51 51 > echo "# hg merge $*"
52 52 > hg merge $*
53 53 > aftermerge
54 54 > }
55 55 $ echo
56 56
57 57
58 58 Tool selection
59 59
60 60 $ echo
61 61
62 62
63 63 default is internal merge:
64 64
65 65 $ beforemerge
66 66 [merge-tools]
67 67 # hg update -C 1
68 68
69 69 hg merge -r 2
70 70 override $PATH to ensure hgmerge not visible; use $PYTHON in case we're
71 71 running from a devel copy, not a temp installation
72 72
73 73 $ PATH="$BINDIR" $PYTHON "$BINDIR"/hg merge -r 2
74 74 merging f
75 75 warning: conflicts during merge.
76 76 merging f failed!
77 77 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
78 78 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
79 79 [1]
80 80 $ aftermerge
81 81 # cat f
82 82 <<<<<<< local
83 83 revision 1
84 84 =======
85 85 revision 2
86 86 >>>>>>> other
87 87 space
88 88 # hg stat
89 89 M f
90 90 ? f.orig
91 91
92 92
93 93 simplest hgrc using false for merge:
94 94
95 95 $ echo "false.whatever=" >> .hg/hgrc
96 96 $ domerge -r 2
97 97 [merge-tools]
98 98 false.whatever=
99 99 # hg update -C 1
100 100 # hg merge -r 2
101 101 merging f
102 102 merging f failed!
103 103 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
104 104 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
105 105 # cat f
106 106 revision 1
107 107 space
108 108 # hg stat
109 109 M f
110 110 ? f.orig
111 111
112 112
113 113 true with higher .priority gets precedence:
114 114
115 115 $ echo "true.priority=1" >> .hg/hgrc
116 116 $ domerge -r 2
117 117 [merge-tools]
118 118 false.whatever=
119 119 true.priority=1
120 120 # hg update -C 1
121 121 # hg merge -r 2
122 122 merging f
123 123 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
124 124 (branch merge, don't forget to commit)
125 125 # cat f
126 126 revision 1
127 127 space
128 128 # hg stat
129 129 M f
130 130
131 131
132 132 unless lowered on command line:
133 133
134 134 $ domerge -r 2 --config merge-tools.true.priority=-7
135 135 [merge-tools]
136 136 false.whatever=
137 137 true.priority=1
138 138 # hg update -C 1
139 139 # hg merge -r 2 --config merge-tools.true.priority=-7
140 140 merging f
141 141 merging f failed!
142 142 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
143 143 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
144 144 # cat f
145 145 revision 1
146 146 space
147 147 # hg stat
148 148 M f
149 149 ? f.orig
150 150
151 151
152 152 or false set higher on command line:
153 153
154 154 $ domerge -r 2 --config merge-tools.false.priority=117
155 155 [merge-tools]
156 156 false.whatever=
157 157 true.priority=1
158 158 # hg update -C 1
159 159 # hg merge -r 2 --config merge-tools.false.priority=117
160 160 merging f
161 161 merging f failed!
162 162 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
163 163 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
164 164 # cat f
165 165 revision 1
166 166 space
167 167 # hg stat
168 168 M f
169 169 ? f.orig
170 170
171 171
172 172 or true.executable not found in PATH:
173 173
174 174 $ domerge -r 2 --config merge-tools.true.executable=nonexistingmergetool
175 175 [merge-tools]
176 176 false.whatever=
177 177 true.priority=1
178 178 # hg update -C 1
179 179 # hg merge -r 2 --config merge-tools.true.executable=nonexistingmergetool
180 180 merging f
181 181 merging f failed!
182 182 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
183 183 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
184 184 # cat f
185 185 revision 1
186 186 space
187 187 # hg stat
188 188 M f
189 189 ? f.orig
190 190
191 191
192 192 or true.executable with bogus path:
193 193
194 194 $ domerge -r 2 --config merge-tools.true.executable=/nonexisting/mergetool
195 195 [merge-tools]
196 196 false.whatever=
197 197 true.priority=1
198 198 # hg update -C 1
199 199 # hg merge -r 2 --config merge-tools.true.executable=/nonexisting/mergetool
200 200 merging f
201 201 merging f failed!
202 202 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
203 203 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
204 204 # cat f
205 205 revision 1
206 206 space
207 207 # hg stat
208 208 M f
209 209 ? f.orig
210 210
211 211
212 212 but true.executable set to cat found in PATH works:
213 213
214 214 $ echo "true.executable=cat" >> .hg/hgrc
215 215 $ domerge -r 2
216 216 [merge-tools]
217 217 false.whatever=
218 218 true.priority=1
219 219 true.executable=cat
220 220 # hg update -C 1
221 221 # hg merge -r 2
222 merging f
222 223 revision 1
223 224 space
224 225 revision 0
225 226 space
226 227 revision 2
227 228 space
228 merging f
229 229 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
230 230 (branch merge, don't forget to commit)
231 231 # cat f
232 232 revision 1
233 233 space
234 234 # hg stat
235 235 M f
236 236
237 237
238 238 and true.executable set to cat with path works:
239 239
240 240 $ domerge -r 2 --config merge-tools.true.executable=cat
241 241 [merge-tools]
242 242 false.whatever=
243 243 true.priority=1
244 244 true.executable=cat
245 245 # hg update -C 1
246 246 # hg merge -r 2 --config merge-tools.true.executable=cat
247 merging f
247 248 revision 1
248 249 space
249 250 revision 0
250 251 space
251 252 revision 2
252 253 space
253 merging f
254 254 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
255 255 (branch merge, don't forget to commit)
256 256 # cat f
257 257 revision 1
258 258 space
259 259 # hg stat
260 260 M f
261 261
262 262 $ echo
263 263
264 264
265 265 Tool selection and merge-patterns
266 266
267 267 $ echo
268 268
269 269
270 270 merge-patterns specifies new tool false:
271 271
272 272 $ domerge -r 2 --config merge-patterns.f=false
273 273 [merge-tools]
274 274 false.whatever=
275 275 true.priority=1
276 276 true.executable=cat
277 277 # hg update -C 1
278 278 # hg merge -r 2 --config merge-patterns.f=false
279 279 merging f
280 280 merging f failed!
281 281 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
282 282 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
283 283 # cat f
284 284 revision 1
285 285 space
286 286 # hg stat
287 287 M f
288 288 ? f.orig
289 289
290 290
291 291 merge-patterns specifies executable not found in PATH and gets warning:
292 292
293 293 $ domerge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=nonexistingmergetool
294 294 [merge-tools]
295 295 false.whatever=
296 296 true.priority=1
297 297 true.executable=cat
298 298 # hg update -C 1
299 299 # hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=nonexistingmergetool
300 300 couldn't find merge tool true specified for f
301 301 merging f
302 302 merging f failed!
303 303 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
304 304 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
305 305 # cat f
306 306 revision 1
307 307 space
308 308 # hg stat
309 309 M f
310 310 ? f.orig
311 311
312 312
313 313 merge-patterns specifies executable with bogus path and gets warning:
314 314
315 315 $ domerge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=/nonexisting/mergetool
316 316 [merge-tools]
317 317 false.whatever=
318 318 true.priority=1
319 319 true.executable=cat
320 320 # hg update -C 1
321 321 # hg merge -r 2 --config merge-patterns.f=true --config merge-tools.true.executable=/nonexisting/mergetool
322 322 couldn't find merge tool true specified for f
323 323 merging f
324 324 merging f failed!
325 325 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
326 326 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
327 327 # cat f
328 328 revision 1
329 329 space
330 330 # hg stat
331 331 M f
332 332 ? f.orig
333 333
334 334 $ echo
335 335
336 336
337 337 ui.merge overrules priority
338 338
339 339 $ echo
340 340
341 341
342 342 ui.merge specifies false:
343 343
344 344 $ domerge -r 2 --config ui.merge=false
345 345 [merge-tools]
346 346 false.whatever=
347 347 true.priority=1
348 348 true.executable=cat
349 349 # hg update -C 1
350 350 # hg merge -r 2 --config ui.merge=false
351 351 merging f
352 352 merging f failed!
353 353 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
354 354 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
355 355 # cat f
356 356 revision 1
357 357 space
358 358 # hg stat
359 359 M f
360 360 ? f.orig
361 361
362 362
363 363 ui.merge specifies internal:fail:
364 364
365 365 $ domerge -r 2 --config ui.merge=internal:fail
366 366 [merge-tools]
367 367 false.whatever=
368 368 true.priority=1
369 369 true.executable=cat
370 370 # hg update -C 1
371 371 # hg merge -r 2 --config ui.merge=internal:fail
372 372 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
373 373 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
374 374 # cat f
375 375 revision 1
376 376 space
377 377 # hg stat
378 378 M f
379 379
380 380
381 381 ui.merge specifies internal:local:
382 382
383 383 $ domerge -r 2 --config ui.merge=internal:local
384 384 [merge-tools]
385 385 false.whatever=
386 386 true.priority=1
387 387 true.executable=cat
388 388 # hg update -C 1
389 389 # hg merge -r 2 --config ui.merge=internal:local
390 390 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
391 391 (branch merge, don't forget to commit)
392 392 # cat f
393 393 revision 1
394 394 space
395 395 # hg stat
396 396 M f
397 397
398 398
399 399 ui.merge specifies internal:other:
400 400
401 401 $ domerge -r 2 --config ui.merge=internal:other
402 402 [merge-tools]
403 403 false.whatever=
404 404 true.priority=1
405 405 true.executable=cat
406 406 # hg update -C 1
407 407 # hg merge -r 2 --config ui.merge=internal:other
408 408 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
409 409 (branch merge, don't forget to commit)
410 410 # cat f
411 411 revision 2
412 412 space
413 413 # hg stat
414 414 M f
415 415
416 416
417 417 ui.merge specifies internal:prompt:
418 418
419 419 $ domerge -r 2 --config ui.merge=internal:prompt
420 420 [merge-tools]
421 421 false.whatever=
422 422 true.priority=1
423 423 true.executable=cat
424 424 # hg update -C 1
425 425 # hg merge -r 2 --config ui.merge=internal:prompt
426 426 no tool found to merge f
427 427 keep (l)ocal or take (o)ther? l
428 428 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
429 429 (branch merge, don't forget to commit)
430 430 # cat f
431 431 revision 1
432 432 space
433 433 # hg stat
434 434 M f
435 435
436 436
437 437 ui.merge specifies internal:dump:
438 438
439 439 $ domerge -r 2 --config ui.merge=internal:dump
440 440 [merge-tools]
441 441 false.whatever=
442 442 true.priority=1
443 443 true.executable=cat
444 444 # hg update -C 1
445 445 # hg merge -r 2 --config ui.merge=internal:dump
446 446 merging f
447 447 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
448 448 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
449 449 # cat f
450 450 revision 1
451 451 space
452 452 # hg stat
453 453 M f
454 454 ? f.base
455 455 ? f.local
456 456 ? f.orig
457 457 ? f.other
458 458
459 459
460 460 f.base:
461 461
462 462 $ cat f.base
463 463 revision 0
464 464 space
465 465
466 466 f.local:
467 467
468 468 $ cat f.local
469 469 revision 1
470 470 space
471 471
472 472 f.other:
473 473
474 474 $ cat f.other
475 475 revision 2
476 476 space
477 477 $ rm f.base f.local f.other
478 478 $ echo
479 479
480 480
481 481 ui.merge specifies internal:other but is overruled by pattern for false:
482 482
483 483 $ domerge -r 2 --config ui.merge=internal:other --config merge-patterns.f=false
484 484 [merge-tools]
485 485 false.whatever=
486 486 true.priority=1
487 487 true.executable=cat
488 488 # hg update -C 1
489 489 # hg merge -r 2 --config ui.merge=internal:other --config merge-patterns.f=false
490 490 merging f
491 491 merging f failed!
492 492 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
493 493 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
494 494 # cat f
495 495 revision 1
496 496 space
497 497 # hg stat
498 498 M f
499 499 ? f.orig
500 500
501 501 $ echo
502 502
503 503
504 504 Premerge
505 505
506 506 $ echo
507 507
508 508
509 509 ui.merge specifies internal:other but is overruled by --tool=false
510 510
511 511 $ domerge -r 2 --config ui.merge=internal:other --tool=false
512 512 [merge-tools]
513 513 false.whatever=
514 514 true.priority=1
515 515 true.executable=cat
516 516 # hg update -C 1
517 517 # hg merge -r 2 --config ui.merge=internal:other --tool=false
518 518 merging f
519 519 merging f failed!
520 520 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
521 521 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
522 522 # cat f
523 523 revision 1
524 524 space
525 525 # hg stat
526 526 M f
527 527 ? f.orig
528 528
529 529 HGMERGE specifies internal:other but is overruled by --tool=false
530 530
531 531 $ HGMERGE=internal:other ; export HGMERGE
532 532 $ domerge -r 2 --tool=false
533 533 [merge-tools]
534 534 false.whatever=
535 535 true.priority=1
536 536 true.executable=cat
537 537 # hg update -C 1
538 538 # hg merge -r 2 --tool=false
539 539 merging f
540 540 merging f failed!
541 541 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
542 542 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
543 543 # cat f
544 544 revision 1
545 545 space
546 546 # hg stat
547 547 M f
548 548 ? f.orig
549 549
550 550 $ unset HGMERGE # make sure HGMERGE doesn't interfere with remaining tests
551 551
552 552 Default is silent simplemerge:
553 553
554 554 $ domerge -r 3
555 555 [merge-tools]
556 556 false.whatever=
557 557 true.priority=1
558 558 true.executable=cat
559 559 # hg update -C 1
560 560 # hg merge -r 3
561 561 merging f
562 562 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
563 563 (branch merge, don't forget to commit)
564 564 # cat f
565 565 revision 1
566 566 space
567 567 revision 3
568 568 # hg stat
569 569 M f
570 570
571 571
572 572 .premerge=True is same:
573 573
574 574 $ domerge -r 3 --config merge-tools.true.premerge=True
575 575 [merge-tools]
576 576 false.whatever=
577 577 true.priority=1
578 578 true.executable=cat
579 579 # hg update -C 1
580 580 # hg merge -r 3 --config merge-tools.true.premerge=True
581 581 merging f
582 582 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
583 583 (branch merge, don't forget to commit)
584 584 # cat f
585 585 revision 1
586 586 space
587 587 revision 3
588 588 # hg stat
589 589 M f
590 590
591 591
592 592 .premerge=False executes merge-tool:
593 593
594 594 $ domerge -r 3 --config merge-tools.true.premerge=False
595 595 [merge-tools]
596 596 false.whatever=
597 597 true.priority=1
598 598 true.executable=cat
599 599 # hg update -C 1
600 600 # hg merge -r 3 --config merge-tools.true.premerge=False
601 merging f
601 602 revision 1
602 603 space
603 604 revision 0
604 605 space
605 606 revision 0
606 607 space
607 608 revision 3
608 merging f
609 609 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
610 610 (branch merge, don't forget to commit)
611 611 # cat f
612 612 revision 1
613 613 space
614 614 # hg stat
615 615 M f
616 616
617 617 $ echo
618 618
619 619
620 620 Tool execution
621 621
622 622 $ echo
623 623
624 624 $ echo '# set tools.args explicit to include $base $local $other $output:' # default '$local $base $other'
625 625 # set tools.args explicit to include $base $local $other $output:
626 626 $ beforemerge
627 627 [merge-tools]
628 628 false.whatever=
629 629 true.priority=1
630 630 true.executable=cat
631 631 # hg update -C 1
632 632 $ hg merge -r 2 --config merge-tools.true.executable=head --config merge-tools.true.args='$base $local $other $output' \
633 633 > | sed 's,==> .* <==,==> ... <==,g'
634 merging f
634 635 ==> ... <==
635 636 revision 0
636 637 space
637 638
638 639 ==> ... <==
639 640 revision 1
640 641 space
641 642
642 643 ==> ... <==
643 644 revision 2
644 645 space
645 646
646 647 ==> ... <==
647 648 revision 1
648 649 space
649 merging f
650 650 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
651 651 (branch merge, don't forget to commit)
652 652 $ aftermerge
653 653 # cat f
654 654 revision 1
655 655 space
656 656 # hg stat
657 657 M f
658 658
659 659 $ echo '# Merge with "echo mergeresult > $local":'
660 660 # Merge with "echo mergeresult > $local":
661 661 $ beforemerge
662 662 [merge-tools]
663 663 false.whatever=
664 664 true.priority=1
665 665 true.executable=cat
666 666 # hg update -C 1
667 667 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > $local'
668 668 merging f
669 669 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
670 670 (branch merge, don't forget to commit)
671 671 $ aftermerge
672 672 # cat f
673 673 mergeresult
674 674 # hg stat
675 675 M f
676 676
677 677 $ echo '# - and $local is the file f:'
678 678 # - and $local is the file f:
679 679 $ beforemerge
680 680 [merge-tools]
681 681 false.whatever=
682 682 true.priority=1
683 683 true.executable=cat
684 684 # hg update -C 1
685 685 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > f'
686 686 merging f
687 687 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
688 688 (branch merge, don't forget to commit)
689 689 $ aftermerge
690 690 # cat f
691 691 mergeresult
692 692 # hg stat
693 693 M f
694 694
695 695 $ echo '# Merge with "echo mergeresult > $output" - the variable is a bit magic:'
696 696 # Merge with "echo mergeresult > $output" - the variable is a bit magic:
697 697 $ beforemerge
698 698 [merge-tools]
699 699 false.whatever=
700 700 true.priority=1
701 701 true.executable=cat
702 702 # hg update -C 1
703 703 $ hg merge -r 2 --config merge-tools.true.executable=echo --config merge-tools.true.args='mergeresult > $output'
704 704 merging f
705 705 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
706 706 (branch merge, don't forget to commit)
707 707 $ aftermerge
708 708 # cat f
709 709 mergeresult
710 710 # hg stat
711 711 M f
712 712
713 713
714 714 Merge using tool with a path that must be quoted:
715 715
716 716 $ beforemerge
717 717 [merge-tools]
718 718 false.whatever=
719 719 true.priority=1
720 720 true.executable=cat
721 721 # hg update -C 1
722 722 $ cat <<EOF > 'my merge tool'
723 723 > #!/bin/sh
724 724 > cat "\$1" "\$2" "\$3" > "\$4"
725 725 > EOF
726 726 $ chmod +x 'my merge tool'
727 727 $ hg merge -r 2 --config merge-tools.true.executable='./my merge tool' --config merge-tools.true.args='$base $local $other $output'
728 728 merging f
729 729 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
730 730 (branch merge, don't forget to commit)
731 731 $ rm -f 'my merge tool'
732 732 $ aftermerge
733 733 # cat f
734 734 revision 0
735 735 space
736 736 revision 1
737 737 space
738 738 revision 2
739 739 space
740 740 # hg stat
741 741 M f
742 742
743 743 $ echo
744 744
745 745
746 746 Merge post-processing
747 747
748 748 $ echo
749 749
750 750
751 751 cat is a bad merge-tool and doesn't change:
752 752
753 753 $ domerge -y -r 2 --config merge-tools.true.checkchanged=1
754 754 [merge-tools]
755 755 false.whatever=
756 756 true.priority=1
757 757 true.executable=cat
758 758 # hg update -C 1
759 759 # hg merge -y -r 2 --config merge-tools.true.checkchanged=1
760 merging f
760 761 revision 1
761 762 space
762 763 revision 0
763 764 space
764 765 revision 2
765 766 space
766 merging f
767 767 output file f appears unchanged
768 768 was merge successful (yn)? n
769 769 merging f failed!
770 770 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
771 771 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
772 772 # cat f
773 773 revision 1
774 774 space
775 775 # hg stat
776 776 M f
777 777 ? f.orig
778 778
@@ -1,176 +1,176
1 1 $ cat <<EOF > merge
2 2 > import sys, os
3 3 >
4 4 > try:
5 5 > import msvcrt
6 6 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
7 7 > msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
8 8 > except ImportError:
9 9 > pass
10 10 >
11 11 > print "merging for", os.path.basename(sys.argv[1])
12 12 > EOF
13 13 $ HGMERGE="python ../merge"; export HGMERGE
14 14
15 15 $ mkdir t
16 16 $ cd t
17 17 $ hg init
18 18 $ echo This is file a1 > a
19 19 $ hg add a
20 20 $ hg commit -m "commit #0"
21 21 $ echo This is file b1 > b
22 22 $ hg add b
23 23 $ hg commit -m "commit #1"
24 24
25 25 $ hg update 0
26 26 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
27 27 $ echo This is file c1 > c
28 28 $ hg add c
29 29 $ hg commit -m "commit #2"
30 30 created new head
31 31 $ echo This is file b1 > b
32 32 no merges expected
33 33 $ hg merge -P 1
34 34 changeset: 1:b8bb4a988f25
35 35 user: test
36 36 date: Thu Jan 01 00:00:00 1970 +0000
37 37 summary: commit #1
38 38
39 39 $ hg merge 1
40 40 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
41 41 (branch merge, don't forget to commit)
42 42 $ hg diff --nodates
43 43 diff -r 49035e18a8e6 b
44 44 --- /dev/null
45 45 +++ b/b
46 46 @@ -0,0 +1,1 @@
47 47 +This is file b1
48 48 $ hg status
49 49 M b
50 50 $ cd ..; rm -r t
51 51
52 52 $ mkdir t
53 53 $ cd t
54 54 $ hg init
55 55 $ echo This is file a1 > a
56 56 $ hg add a
57 57 $ hg commit -m "commit #0"
58 58 $ echo This is file b1 > b
59 59 $ hg add b
60 60 $ hg commit -m "commit #1"
61 61
62 62 $ hg update 0
63 63 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
64 64 $ echo This is file c1 > c
65 65 $ hg add c
66 66 $ hg commit -m "commit #2"
67 67 created new head
68 68 $ echo This is file b2 > b
69 69 merge should fail
70 70 $ hg merge 1
71 71 abort: untracked file in working directory differs from file in requested revision: 'b'
72 72 [255]
73 73 merge of b expected
74 74 $ hg merge -f 1
75 merging b
75 76 merging for b
76 merging b
77 77 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
78 78 (branch merge, don't forget to commit)
79 79 $ hg diff --nodates
80 80 diff -r 49035e18a8e6 b
81 81 --- /dev/null
82 82 +++ b/b
83 83 @@ -0,0 +1,1 @@
84 84 +This is file b2
85 85 $ hg status
86 86 M b
87 87 $ cd ..; rm -r t
88 88
89 89 $ mkdir t
90 90 $ cd t
91 91 $ hg init
92 92 $ echo This is file a1 > a
93 93 $ hg add a
94 94 $ hg commit -m "commit #0"
95 95 $ echo This is file b1 > b
96 96 $ hg add b
97 97 $ hg commit -m "commit #1"
98 98 $ echo This is file b22 > b
99 99 $ hg commit -m "commit #2"
100 100 $ hg update 1
101 101 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 102 $ echo This is file c1 > c
103 103 $ hg add c
104 104 $ hg commit -m "commit #3"
105 105 created new head
106 106
107 107 Contents of b should be "this is file b1"
108 108 $ cat b
109 109 This is file b1
110 110
111 111 $ echo This is file b22 > b
112 112 merge fails
113 113 $ hg merge 2
114 114 abort: outstanding uncommitted changes (use 'hg status' to list changes)
115 115 [255]
116 116 $ echo %% merge expected!
117 117 %% merge expected!
118 118 $ hg merge -f 2
119 119 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
120 120 (branch merge, don't forget to commit)
121 121 $ hg diff --nodates
122 122 diff -r 85de557015a8 b
123 123 --- a/b
124 124 +++ b/b
125 125 @@ -1,1 +1,1 @@
126 126 -This is file b1
127 127 +This is file b22
128 128 $ hg status
129 129 M b
130 130 $ cd ..; rm -r t
131 131
132 132 $ mkdir t
133 133 $ cd t
134 134 $ hg init
135 135 $ echo This is file a1 > a
136 136 $ hg add a
137 137 $ hg commit -m "commit #0"
138 138 $ echo This is file b1 > b
139 139 $ hg add b
140 140 $ hg commit -m "commit #1"
141 141 $ echo This is file b22 > b
142 142 $ hg commit -m "commit #2"
143 143 $ hg update 1
144 144 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
145 145 $ echo This is file c1 > c
146 146 $ hg add c
147 147 $ hg commit -m "commit #3"
148 148 created new head
149 149 $ echo This is file b33 > b
150 150 merge of b should fail
151 151 $ hg merge 2
152 152 abort: outstanding uncommitted changes (use 'hg status' to list changes)
153 153 [255]
154 154 merge of b expected
155 155 $ hg merge -f 2
156 merging b
156 157 merging for b
157 merging b
158 158 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
159 159 (branch merge, don't forget to commit)
160 160 $ hg diff --nodates
161 161 diff -r 85de557015a8 b
162 162 --- a/b
163 163 +++ b/b
164 164 @@ -1,1 +1,1 @@
165 165 -This is file b1
166 166 +This is file b33
167 167 $ hg status
168 168 M b
169 169
170 170 Test for issue2364
171 171
172 172 $ hg up -qC .
173 173 $ hg rm b
174 174 $ hg ci -md
175 175 $ hg revert -r -2 b
176 176 $ hg up -q -- -2
@@ -1,2164 +1,2164
1 1 $ fixheaders()
2 2 > {
3 3 > sed -e 's/\(Message-Id:.*@\).*/\1/' \
4 4 > -e 's/\(In-Reply-To:.*@\).*/\1/' \
5 5 > -e 's/\(References:.*@\).*/\1/' \
6 6 > -e 's/\(User-Agent:.*\)\/.*/\1/' \
7 7 > -e 's/===.*/===/'
8 8 > }
9 9 $ echo "[extensions]" >> $HGRCPATH
10 10 $ echo "patchbomb=" >> $HGRCPATH
11 11
12 12 $ hg init t
13 13 $ cd t
14 14 $ echo a > a
15 15 $ hg commit -Ama -d '1 0'
16 16 adding a
17 17
18 18 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -r tip
19 19 This patch series consists of 1 patches.
20 20
21 21
22 22 Displaying [PATCH] a ...
23 23 Content-Type: text/plain; charset="us-ascii"
24 24 MIME-Version: 1.0
25 25 Content-Transfer-Encoding: 7bit
26 26 Subject: [PATCH] a
27 27 X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
28 28 Message-Id: <8580ff50825a50c8f716.60@* (glob)
29 29 User-Agent: Mercurial-patchbomb/* (glob)
30 30 Date: Thu, 01 Jan 1970 00:01:00 +0000
31 31 From: quux
32 32 To: foo
33 33 Cc: bar
34 34
35 35 # HG changeset patch
36 36 # User test
37 37 # Date 1 0
38 38 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
39 39 # Parent 0000000000000000000000000000000000000000
40 40 a
41 41
42 42 diff -r 000000000000 -r 8580ff50825a a
43 43 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
44 44 +++ b/a Thu Jan 01 00:00:01 1970 +0000
45 45 @@ -0,0 +1,1 @@
46 46 +a
47 47
48 48
49 49 $ hg --config ui.interactive=1 email --confirm -n -f quux -t foo -c bar -r tip<<EOF
50 50 > n
51 51 > EOF
52 52 This patch series consists of 1 patches.
53 53
54 54
55 55 Final summary:
56 56
57 57 From: quux
58 58 To: foo
59 59 Cc: bar
60 60 Subject: [PATCH] a
61 61 a | 1 +
62 62 1 files changed, 1 insertions(+), 0 deletions(-)
63 63
64 64 are you sure you want to send (yn)? abort: patchbomb canceled
65 65 [255]
66 66
67 67 $ echo b > b
68 68 $ hg commit -Amb -d '2 0'
69 69 adding b
70 70
71 71 $ hg email --date '1970-1-1 0:2' -n -f quux -t foo -c bar -s test -r 0:tip
72 72 This patch series consists of 2 patches.
73 73
74 74
75 75 Write the introductory message for the patch series.
76 76
77 77
78 78 Displaying [PATCH 0 of 2] test ...
79 79 Content-Type: text/plain; charset="us-ascii"
80 80 MIME-Version: 1.0
81 81 Content-Transfer-Encoding: 7bit
82 82 Subject: [PATCH 0 of 2] test
83 83 Message-Id: <patchbomb\.120@[^>]*> (re)
84 84 User-Agent: Mercurial-patchbomb/* (glob)
85 85 Date: Thu, 01 Jan 1970 00:02:00 +0000
86 86 From: quux
87 87 To: foo
88 88 Cc: bar
89 89
90 90
91 91 Displaying [PATCH 1 of 2] a ...
92 92 Content-Type: text/plain; charset="us-ascii"
93 93 MIME-Version: 1.0
94 94 Content-Transfer-Encoding: 7bit
95 95 Subject: [PATCH 1 of 2] a
96 96 X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
97 97 Message-Id: <8580ff50825a50c8f716\.121@[^>]*> (re)
98 98 In-Reply-To: <patchbomb\.120@[^>]*> (re)
99 99 References: <patchbomb\.120@[^>]*> (re)
100 100 User-Agent: Mercurial-patchbomb/* (glob)
101 101 Date: Thu, 01 Jan 1970 00:02:01 +0000
102 102 From: quux
103 103 To: foo
104 104 Cc: bar
105 105
106 106 # HG changeset patch
107 107 # User test
108 108 # Date 1 0
109 109 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
110 110 # Parent 0000000000000000000000000000000000000000
111 111 a
112 112
113 113 diff -r 000000000000 -r 8580ff50825a a
114 114 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
115 115 +++ b/a Thu Jan 01 00:00:01 1970 +0000
116 116 @@ -0,0 +1,1 @@
117 117 +a
118 118
119 119 Displaying [PATCH 2 of 2] b ...
120 120 Content-Type: text/plain; charset="us-ascii"
121 121 MIME-Version: 1.0
122 122 Content-Transfer-Encoding: 7bit
123 123 Subject: [PATCH 2 of 2] b
124 124 X-Mercurial-Node: 97d72e5f12c7e84f85064aa72e5a297142c36ed9
125 125 Message-Id: <97d72e5f12c7e84f8506\.122@[^>]*> (re)
126 126 In-Reply-To: <patchbomb\.120@[^>]*> (re)
127 127 References: <patchbomb\.120@[^>]*> (re)
128 128 User-Agent: Mercurial-patchbomb/* (glob)
129 129 Date: Thu, 01 Jan 1970 00:02:02 +0000
130 130 From: quux
131 131 To: foo
132 132 Cc: bar
133 133
134 134 # HG changeset patch
135 135 # User test
136 136 # Date 2 0
137 137 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9
138 138 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab
139 139 b
140 140
141 141 diff -r 8580ff50825a -r 97d72e5f12c7 b
142 142 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
143 143 +++ b/b Thu Jan 01 00:00:02 1970 +0000
144 144 @@ -0,0 +1,1 @@
145 145 +b
146 146
147 147
148 148 .hg/last-email.txt
149 149
150 150 $ cat > editor << '__EOF__'
151 151 > #!/bin/sh
152 152 > echo "a precious introductory message" > "$1"
153 153 > __EOF__
154 154 $ chmod +x editor
155 155 $ HGEDITOR="'`pwd`'"/editor hg email -n -t foo -s test -r 0:tip > /dev/null
156 156 $ cat .hg/last-email.txt
157 157 a precious introductory message
158 158
159 159 $ hg email -m test.mbox -f quux -t foo -c bar -s test 0:tip \
160 160 > --config extensions.progress= --config progress.assume-tty=1 \
161 161 > --config progress.delay=0 --config progress.refresh=0
162 \rwriting [ ] 0/3\rwriting [ ] 0/3\r \r\r \r\rwriting [====================> ] 1/3\rwriting [====================> ] 1/3\r \r\r \r\rwriting [==========================================> ] 2/3\rwriting [==========================================> ] 2/3\r \rThis patch series consists of 2 patches. (esc)
162 This patch series consists of 2 patches.
163 163
164 164
165 165 Write the introductory message for the patch series.
166 166
167
167 \rwriting [ ] 0/3\rwriting [ ] 0/3\r \r\r \r\rwriting [====================> ] 1/3\rwriting [====================> ] 1/3\r \r\r \r\rwriting [==========================================> ] 2/3\rwriting [==========================================> ] 2/3\r \r (esc)
168 168 Writing [PATCH 0 of 2] test ...
169 169 Writing [PATCH 1 of 2] a ...
170 170 Writing [PATCH 2 of 2] b ...
171 171
172 172 $ cd ..
173 173
174 174 $ hg clone -q t t2
175 175 $ cd t2
176 176 $ echo c > c
177 177 $ hg commit -Amc -d '3 0'
178 178 adding c
179 179
180 180 $ cat > description <<EOF
181 181 > a multiline
182 182 >
183 183 > description
184 184 > EOF
185 185
186 186
187 187 test bundle and description:
188 188 $ hg email --date '1970-1-1 0:3' -n -f quux -t foo \
189 189 > -c bar -s test -r tip -b --desc description | fixheaders
190 190 searching for changes
191 191 1 changesets found
192 192
193 193 Displaying test ...
194 194 Content-Type: multipart/mixed; boundary="===
195 195 MIME-Version: 1.0
196 196 Subject: test
197 197 Message-Id: <patchbomb.180@
198 198 User-Agent: Mercurial-patchbomb
199 199 Date: Thu, 01 Jan 1970 00:03:00 +0000
200 200 From: quux
201 201 To: foo
202 202 Cc: bar
203 203
204 204 --===
205 205 Content-Type: text/plain; charset="us-ascii"
206 206 MIME-Version: 1.0
207 207 Content-Transfer-Encoding: 7bit
208 208
209 209 a multiline
210 210
211 211 description
212 212
213 213 --===
214 214 Content-Type: application/x-mercurial-bundle
215 215 MIME-Version: 1.0
216 216 Content-Disposition: attachment; filename="bundle.hg"
217 217 Content-Transfer-Encoding: base64
218 218
219 219 SEcxMEJaaDkxQVkmU1nvR7I3AAAN////lFYQWj1/4HwRkdC/AywIAk0E4pfoSIIIgQCgGEQOcLAA
220 220 2tA1VPyp4mkeoG0EaaPU0GTT1GjRiNPIg9CZGBqZ6UbU9J+KFU09DNUaGgAAAAAANAGgAAAAA1U8
221 221 oGgAADQGgAANNANAAAAAAZipFLz3XoakCEQB3PVPyHJVi1iYkAAKQAZQGpQGZESInRnCFMqLDla2
222 222 Bx3qfRQeA2N4lnzKkAmP8kR2asievLLXXebVU8Vg4iEBqcJNJAxIapSU6SM4888ZAciRG6MYAIEE
223 223 SlIBpFisgGkyRjX//TMtfcUAEsGu56+YnE1OlTZmzKm8BSu2rvo4rHAYYaadIFFuTy0LYgIkgLVD
224 224 sgVa2F19D1tx9+hgbAygLgQwaIqcDdgA4BjQgIiz/AEP72++llgDKhKducqodGE4B0ETqF3JFOFC
225 225 Q70eyNw=
226 226 --===
227 227
228 228 utf-8 patch:
229 229 $ python -c 'fp = open("utf", "wb"); fp.write("h\xC3\xB6mma!\n"); fp.close();'
230 230 $ hg commit -A -d '4 0' -m 'charset=utf-8; content-transfer-encoding: base64'
231 231 adding description
232 232 adding utf
233 233
234 234 no mime encoding for email --test:
235 235 $ hg email --date '1970-1-1 0:4' -f quux -t foo -c bar -r tip -n | fixheaders > mailtest
236 236
237 237 md5sum of 8-bit output:
238 238 $ $TESTDIR/md5sum.py mailtest
239 239 e726c29b3008e77994c7572563e57c34 mailtest
240 240
241 241 $ rm mailtest
242 242
243 243 mime encoded mbox (base64):
244 244 $ hg email --date '1970-1-1 0:4' -f quux -t foo -c bar -r tip -m mbox
245 245 This patch series consists of 1 patches.
246 246
247 247
248 248 Writing [PATCH] charset=utf-8; content-transfer-encoding: base64 ...
249 249
250 250 $ cat mbox
251 251 From quux Thu Jan 01 00:04:01 1970
252 252 Content-Type: text/plain; charset="utf-8"
253 253 MIME-Version: 1.0
254 254 Content-Transfer-Encoding: base64
255 255 Subject: [PATCH] charset=utf-8; content-transfer-encoding: base64
256 256 X-Mercurial-Node: c3c9e37db9f4fe4882cda39baf42fed6bad8b15a
257 257 Message-Id: <c3c9e37db9f4fe4882cd.240@* (glob)
258 258 User-Agent: Mercurial-patchbomb/* (glob)
259 259 Date: Thu, 01 Jan 1970 00:04:00 +0000
260 260 From: quux
261 261 To: foo
262 262 Cc: bar
263 263
264 264 IyBIRyBjaGFuZ2VzZXQgcGF0Y2gKIyBVc2VyIHRlc3QKIyBEYXRlIDQgMAojIE5vZGUgSUQgYzNj
265 265 OWUzN2RiOWY0ZmU0ODgyY2RhMzliYWY0MmZlZDZiYWQ4YjE1YQojIFBhcmVudCAgZmYyYzlmYTIw
266 266 MThiMTVmYTc0YjMzMzYzYmRhOTUyNzMyM2UyYTk5ZgpjaGFyc2V0PXV0Zi04OyBjb250ZW50LXRy
267 267 YW5zZmVyLWVuY29kaW5nOiBiYXNlNjQKCmRpZmYgLXIgZmYyYzlmYTIwMThiIC1yIGMzYzllMzdk
268 268 YjlmNCBkZXNjcmlwdGlvbgotLS0gL2Rldi9udWxsCVRodSBKYW4gMDEgMDA6MDA6MDAgMTk3MCAr
269 269 MDAwMAorKysgYi9kZXNjcmlwdGlvbglUaHUgSmFuIDAxIDAwOjAwOjA0IDE5NzAgKzAwMDAKQEAg
270 270 LTAsMCArMSwzIEBACithIG11bHRpbGluZQorCitkZXNjcmlwdGlvbgpkaWZmIC1yIGZmMmM5ZmEy
271 271 MDE4YiAtciBjM2M5ZTM3ZGI5ZjQgdXRmCi0tLSAvZGV2L251bGwJVGh1IEphbiAwMSAwMDowMDow
272 272 MCAxOTcwICswMDAwCisrKyBiL3V0ZglUaHUgSmFuIDAxIDAwOjAwOjA0IDE5NzAgKzAwMDAKQEAg
273 273 LTAsMCArMSwxIEBACitow7ZtbWEhCg==
274 274
275 275
276 276 $ rm mbox
277 277
278 278 mime encoded mbox (quoted-printable):
279 279 $ python -c 'fp = open("qp", "wb"); fp.write("%s\nfoo\n\nbar\n" % ("x" * 1024)); fp.close();'
280 280 $ hg commit -A -d '4 0' -m 'charset=utf-8; content-transfer-encoding: quoted-printable'
281 281 adding qp
282 282
283 283 no mime encoding for email --test:
284 284 $ hg email --date '1970-1-1 0:4' -f quux -t foo -c bar -r tip -n | \
285 285 > fixheaders > mailtest
286 286 md5sum of qp output:
287 287 $ $TESTDIR/md5sum.py mailtest
288 288 0402c7d033e04044e423bb04816f9dae mailtest
289 289 $ rm mailtest
290 290
291 291 mime encoded mbox (quoted-printable):
292 292 $ hg email --date '1970-1-1 0:4' -f quux -t foo -c bar -r tip -m mbox
293 293 This patch series consists of 1 patches.
294 294
295 295
296 296 Writing [PATCH] charset=utf-8; content-transfer-encoding: quoted-printable ...
297 297 $ cat mbox | fixheaders
298 298 From quux Thu Jan 01 00:04:01 1970
299 299 Content-Type: text/plain; charset="us-ascii"
300 300 MIME-Version: 1.0
301 301 Content-Transfer-Encoding: quoted-printable
302 302 Subject: [PATCH] charset=utf-8; content-transfer-encoding: quoted-printable
303 303 X-Mercurial-Node: c655633f8c87700bb38cc6a59a2753bdc5a6c376
304 304 Message-Id: <c655633f8c87700bb38c.240@
305 305 User-Agent: Mercurial-patchbomb
306 306 Date: Thu, 01 Jan 1970 00:04:00 +0000
307 307 From: quux
308 308 To: foo
309 309 Cc: bar
310 310
311 311 # HG changeset patch
312 312 # User test
313 313 # Date 4 0
314 314 # Node ID c655633f8c87700bb38cc6a59a2753bdc5a6c376
315 315 # Parent c3c9e37db9f4fe4882cda39baf42fed6bad8b15a
316 316 charset=3Dutf-8; content-transfer-encoding: quoted-printable
317 317
318 318 diff -r c3c9e37db9f4 -r c655633f8c87 qp
319 319 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
320 320 +++ b/qp Thu Jan 01 00:00:04 1970 +0000
321 321 @@ -0,0 +1,4 @@
322 322 +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
323 323 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
324 324 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
325 325 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
326 326 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
327 327 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
328 328 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
329 329 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
330 330 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
331 331 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
332 332 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
333 333 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
334 334 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
335 335 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
336 336 +foo
337 337 +
338 338 +bar
339 339
340 340
341 341
342 342 $ rm mbox
343 343
344 344 iso-8859-1 patch:
345 345 $ python -c 'fp = open("isolatin", "wb"); fp.write("h\xF6mma!\n"); fp.close();'
346 346 $ hg commit -A -d '5 0' -m 'charset=us-ascii; content-transfer-encoding: 8bit'
347 347 adding isolatin
348 348
349 349 fake ascii mbox:
350 350 $ hg email --date '1970-1-1 0:5' -f quux -t foo -c bar -r tip -m mbox
351 351 This patch series consists of 1 patches.
352 352
353 353
354 354 Writing [PATCH] charset=us-ascii; content-transfer-encoding: 8bit ...
355 355 $ fixheaders < mbox > mboxfix
356 356
357 357 md5sum of 8-bit output:
358 358 $ $TESTDIR/md5sum.py mboxfix
359 359 9ea043d8fc43a71045114508baed144b mboxfix
360 360
361 361 test diffstat for single patch:
362 362 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -d -y -r 2 | \
363 363 > fixheaders
364 364 This patch series consists of 1 patches.
365 365
366 366
367 367 Final summary:
368 368
369 369 From: quux
370 370 To: foo
371 371 Cc: bar
372 372 Subject: [PATCH] test
373 373 c | 1 +
374 374 1 files changed, 1 insertions(+), 0 deletions(-)
375 375
376 376 are you sure you want to send (yn)? y
377 377
378 378 Displaying [PATCH] test ...
379 379 Content-Type: text/plain; charset="us-ascii"
380 380 MIME-Version: 1.0
381 381 Content-Transfer-Encoding: 7bit
382 382 Subject: [PATCH] test
383 383 X-Mercurial-Node: ff2c9fa2018b15fa74b33363bda9527323e2a99f
384 384 Message-Id: <ff2c9fa2018b15fa74b3.60@
385 385 User-Agent: Mercurial-patchbomb
386 386 Date: Thu, 01 Jan 1970 00:01:00 +0000
387 387 From: quux
388 388 To: foo
389 389 Cc: bar
390 390
391 391 c | 1 +
392 392 1 files changed, 1 insertions(+), 0 deletions(-)
393 393
394 394
395 395 # HG changeset patch
396 396 # User test
397 397 # Date 3 0
398 398 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f
399 399 # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
400 400 c
401 401
402 402 diff -r 97d72e5f12c7 -r ff2c9fa2018b c
403 403 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
404 404 +++ b/c Thu Jan 01 00:00:03 1970 +0000
405 405 @@ -0,0 +1,1 @@
406 406 +c
407 407
408 408
409 409 test diffstat for multiple patches:
410 410 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -d -y \
411 411 > -r 0:1 | fixheaders
412 412 This patch series consists of 2 patches.
413 413
414 414
415 415 Write the introductory message for the patch series.
416 416
417 417
418 418 Final summary:
419 419
420 420 From: quux
421 421 To: foo
422 422 Cc: bar
423 423 Subject: [PATCH 0 of 2] test
424 424 a | 1 +
425 425 b | 1 +
426 426 2 files changed, 2 insertions(+), 0 deletions(-)
427 427 Subject: [PATCH 1 of 2] a
428 428 a | 1 +
429 429 1 files changed, 1 insertions(+), 0 deletions(-)
430 430 Subject: [PATCH 2 of 2] b
431 431 b | 1 +
432 432 1 files changed, 1 insertions(+), 0 deletions(-)
433 433
434 434 are you sure you want to send (yn)? y
435 435
436 436 Displaying [PATCH 0 of 2] test ...
437 437 Content-Type: text/plain; charset="us-ascii"
438 438 MIME-Version: 1.0
439 439 Content-Transfer-Encoding: 7bit
440 440 Subject: [PATCH 0 of 2] test
441 441 Message-Id: <patchbomb.60@
442 442 User-Agent: Mercurial-patchbomb
443 443 Date: Thu, 01 Jan 1970 00:01:00 +0000
444 444 From: quux
445 445 To: foo
446 446 Cc: bar
447 447
448 448
449 449 a | 1 +
450 450 b | 1 +
451 451 2 files changed, 2 insertions(+), 0 deletions(-)
452 452
453 453 Displaying [PATCH 1 of 2] a ...
454 454 Content-Type: text/plain; charset="us-ascii"
455 455 MIME-Version: 1.0
456 456 Content-Transfer-Encoding: 7bit
457 457 Subject: [PATCH 1 of 2] a
458 458 X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
459 459 Message-Id: <8580ff50825a50c8f716.61@
460 460 In-Reply-To: <patchbomb.60@
461 461 References: <patchbomb.60@
462 462 User-Agent: Mercurial-patchbomb
463 463 Date: Thu, 01 Jan 1970 00:01:01 +0000
464 464 From: quux
465 465 To: foo
466 466 Cc: bar
467 467
468 468 a | 1 +
469 469 1 files changed, 1 insertions(+), 0 deletions(-)
470 470
471 471
472 472 # HG changeset patch
473 473 # User test
474 474 # Date 1 0
475 475 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
476 476 # Parent 0000000000000000000000000000000000000000
477 477 a
478 478
479 479 diff -r 000000000000 -r 8580ff50825a a
480 480 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
481 481 +++ b/a Thu Jan 01 00:00:01 1970 +0000
482 482 @@ -0,0 +1,1 @@
483 483 +a
484 484
485 485 Displaying [PATCH 2 of 2] b ...
486 486 Content-Type: text/plain; charset="us-ascii"
487 487 MIME-Version: 1.0
488 488 Content-Transfer-Encoding: 7bit
489 489 Subject: [PATCH 2 of 2] b
490 490 X-Mercurial-Node: 97d72e5f12c7e84f85064aa72e5a297142c36ed9
491 491 Message-Id: <97d72e5f12c7e84f8506.62@
492 492 In-Reply-To: <patchbomb.60@
493 493 References: <patchbomb.60@
494 494 User-Agent: Mercurial-patchbomb
495 495 Date: Thu, 01 Jan 1970 00:01:02 +0000
496 496 From: quux
497 497 To: foo
498 498 Cc: bar
499 499
500 500 b | 1 +
501 501 1 files changed, 1 insertions(+), 0 deletions(-)
502 502
503 503
504 504 # HG changeset patch
505 505 # User test
506 506 # Date 2 0
507 507 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9
508 508 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab
509 509 b
510 510
511 511 diff -r 8580ff50825a -r 97d72e5f12c7 b
512 512 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
513 513 +++ b/b Thu Jan 01 00:00:02 1970 +0000
514 514 @@ -0,0 +1,1 @@
515 515 +b
516 516
517 517
518 518 test inline for single patch:
519 519 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -i -r 2 | \
520 520 > fixheaders
521 521 This patch series consists of 1 patches.
522 522
523 523
524 524 Displaying [PATCH] test ...
525 525 Content-Type: multipart/mixed; boundary="===
526 526 MIME-Version: 1.0
527 527 Subject: [PATCH] test
528 528 X-Mercurial-Node: ff2c9fa2018b15fa74b33363bda9527323e2a99f
529 529 Message-Id: <ff2c9fa2018b15fa74b3.60@
530 530 User-Agent: Mercurial-patchbomb
531 531 Date: Thu, 01 Jan 1970 00:01:00 +0000
532 532 From: quux
533 533 To: foo
534 534 Cc: bar
535 535
536 536 --===
537 537 Content-Type: text/x-patch; charset="us-ascii"
538 538 MIME-Version: 1.0
539 539 Content-Transfer-Encoding: 7bit
540 540 Content-Disposition: inline; filename=t2.patch
541 541
542 542 # HG changeset patch
543 543 # User test
544 544 # Date 3 0
545 545 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f
546 546 # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
547 547 c
548 548
549 549 diff -r 97d72e5f12c7 -r ff2c9fa2018b c
550 550 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
551 551 +++ b/c Thu Jan 01 00:00:03 1970 +0000
552 552 @@ -0,0 +1,1 @@
553 553 +c
554 554
555 555 --===
556 556
557 557
558 558 test inline for single patch (quoted-printable):
559 559 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -i -r 4 | \
560 560 > fixheaders
561 561 This patch series consists of 1 patches.
562 562
563 563
564 564 Displaying [PATCH] test ...
565 565 Content-Type: multipart/mixed; boundary="===
566 566 MIME-Version: 1.0
567 567 Subject: [PATCH] test
568 568 X-Mercurial-Node: c655633f8c87700bb38cc6a59a2753bdc5a6c376
569 569 Message-Id: <c655633f8c87700bb38c.60@
570 570 User-Agent: Mercurial-patchbomb
571 571 Date: Thu, 01 Jan 1970 00:01:00 +0000
572 572 From: quux
573 573 To: foo
574 574 Cc: bar
575 575
576 576 --===
577 577 Content-Type: text/x-patch; charset="us-ascii"
578 578 MIME-Version: 1.0
579 579 Content-Transfer-Encoding: quoted-printable
580 580 Content-Disposition: inline; filename=t2.patch
581 581
582 582 # HG changeset patch
583 583 # User test
584 584 # Date 4 0
585 585 # Node ID c655633f8c87700bb38cc6a59a2753bdc5a6c376
586 586 # Parent c3c9e37db9f4fe4882cda39baf42fed6bad8b15a
587 587 charset=3Dutf-8; content-transfer-encoding: quoted-printable
588 588
589 589 diff -r c3c9e37db9f4 -r c655633f8c87 qp
590 590 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
591 591 +++ b/qp Thu Jan 01 00:00:04 1970 +0000
592 592 @@ -0,0 +1,4 @@
593 593 +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
594 594 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
595 595 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
596 596 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
597 597 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
598 598 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
599 599 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
600 600 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
601 601 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
602 602 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
603 603 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
604 604 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
605 605 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
606 606 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
607 607 +foo
608 608 +
609 609 +bar
610 610
611 611 --===
612 612
613 613 test inline for multiple patches:
614 614 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -i \
615 615 > -r 0:1 -r 4 | fixheaders
616 616 This patch series consists of 3 patches.
617 617
618 618
619 619 Write the introductory message for the patch series.
620 620
621 621
622 622 Displaying [PATCH 0 of 3] test ...
623 623 Content-Type: text/plain; charset="us-ascii"
624 624 MIME-Version: 1.0
625 625 Content-Transfer-Encoding: 7bit
626 626 Subject: [PATCH 0 of 3] test
627 627 Message-Id: <patchbomb.60@
628 628 User-Agent: Mercurial-patchbomb
629 629 Date: Thu, 01 Jan 1970 00:01:00 +0000
630 630 From: quux
631 631 To: foo
632 632 Cc: bar
633 633
634 634
635 635 Displaying [PATCH 1 of 3] a ...
636 636 Content-Type: multipart/mixed; boundary="===
637 637 MIME-Version: 1.0
638 638 Subject: [PATCH 1 of 3] a
639 639 X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
640 640 Message-Id: <8580ff50825a50c8f716.61@
641 641 In-Reply-To: <patchbomb.60@
642 642 References: <patchbomb.60@
643 643 User-Agent: Mercurial-patchbomb
644 644 Date: Thu, 01 Jan 1970 00:01:01 +0000
645 645 From: quux
646 646 To: foo
647 647 Cc: bar
648 648
649 649 --===
650 650 Content-Type: text/x-patch; charset="us-ascii"
651 651 MIME-Version: 1.0
652 652 Content-Transfer-Encoding: 7bit
653 653 Content-Disposition: inline; filename=t2-1.patch
654 654
655 655 # HG changeset patch
656 656 # User test
657 657 # Date 1 0
658 658 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
659 659 # Parent 0000000000000000000000000000000000000000
660 660 a
661 661
662 662 diff -r 000000000000 -r 8580ff50825a a
663 663 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
664 664 +++ b/a Thu Jan 01 00:00:01 1970 +0000
665 665 @@ -0,0 +1,1 @@
666 666 +a
667 667
668 668 --===
669 669 Displaying [PATCH 2 of 3] b ...
670 670 Content-Type: multipart/mixed; boundary="===
671 671 MIME-Version: 1.0
672 672 Subject: [PATCH 2 of 3] b
673 673 X-Mercurial-Node: 97d72e5f12c7e84f85064aa72e5a297142c36ed9
674 674 Message-Id: <97d72e5f12c7e84f8506.62@
675 675 In-Reply-To: <patchbomb.60@
676 676 References: <patchbomb.60@
677 677 User-Agent: Mercurial-patchbomb
678 678 Date: Thu, 01 Jan 1970 00:01:02 +0000
679 679 From: quux
680 680 To: foo
681 681 Cc: bar
682 682
683 683 --===
684 684 Content-Type: text/x-patch; charset="us-ascii"
685 685 MIME-Version: 1.0
686 686 Content-Transfer-Encoding: 7bit
687 687 Content-Disposition: inline; filename=t2-2.patch
688 688
689 689 # HG changeset patch
690 690 # User test
691 691 # Date 2 0
692 692 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9
693 693 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab
694 694 b
695 695
696 696 diff -r 8580ff50825a -r 97d72e5f12c7 b
697 697 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
698 698 +++ b/b Thu Jan 01 00:00:02 1970 +0000
699 699 @@ -0,0 +1,1 @@
700 700 +b
701 701
702 702 --===
703 703 Displaying [PATCH 3 of 3] charset=utf-8; content-transfer-encoding: quoted-printable ...
704 704 Content-Type: multipart/mixed; boundary="===
705 705 MIME-Version: 1.0
706 706 Subject: [PATCH 3 of 3] charset=utf-8;
707 707 content-transfer-encoding: quoted-printable
708 708 X-Mercurial-Node: c655633f8c87700bb38cc6a59a2753bdc5a6c376
709 709 Message-Id: <c655633f8c87700bb38c.63@
710 710 In-Reply-To: <patchbomb.60@
711 711 References: <patchbomb.60@
712 712 User-Agent: Mercurial-patchbomb
713 713 Date: Thu, 01 Jan 1970 00:01:03 +0000
714 714 From: quux
715 715 To: foo
716 716 Cc: bar
717 717
718 718 --===
719 719 Content-Type: text/x-patch; charset="us-ascii"
720 720 MIME-Version: 1.0
721 721 Content-Transfer-Encoding: quoted-printable
722 722 Content-Disposition: inline; filename=t2-3.patch
723 723
724 724 # HG changeset patch
725 725 # User test
726 726 # Date 4 0
727 727 # Node ID c655633f8c87700bb38cc6a59a2753bdc5a6c376
728 728 # Parent c3c9e37db9f4fe4882cda39baf42fed6bad8b15a
729 729 charset=3Dutf-8; content-transfer-encoding: quoted-printable
730 730
731 731 diff -r c3c9e37db9f4 -r c655633f8c87 qp
732 732 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
733 733 +++ b/qp Thu Jan 01 00:00:04 1970 +0000
734 734 @@ -0,0 +1,4 @@
735 735 +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
736 736 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
737 737 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
738 738 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
739 739 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
740 740 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
741 741 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
742 742 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
743 743 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
744 744 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
745 745 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
746 746 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
747 747 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
748 748 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
749 749 +foo
750 750 +
751 751 +bar
752 752
753 753 --===
754 754
755 755 test attach for single patch:
756 756 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -a -r 2 | \
757 757 > fixheaders
758 758 This patch series consists of 1 patches.
759 759
760 760
761 761 Displaying [PATCH] test ...
762 762 Content-Type: multipart/mixed; boundary="===
763 763 MIME-Version: 1.0
764 764 Subject: [PATCH] test
765 765 X-Mercurial-Node: ff2c9fa2018b15fa74b33363bda9527323e2a99f
766 766 Message-Id: <ff2c9fa2018b15fa74b3.60@
767 767 User-Agent: Mercurial-patchbomb
768 768 Date: Thu, 01 Jan 1970 00:01:00 +0000
769 769 From: quux
770 770 To: foo
771 771 Cc: bar
772 772
773 773 --===
774 774 Content-Type: text/plain; charset="us-ascii"
775 775 MIME-Version: 1.0
776 776 Content-Transfer-Encoding: 7bit
777 777
778 778 Patch subject is complete summary.
779 779
780 780
781 781
782 782 --===
783 783 Content-Type: text/x-patch; charset="us-ascii"
784 784 MIME-Version: 1.0
785 785 Content-Transfer-Encoding: 7bit
786 786 Content-Disposition: attachment; filename=t2.patch
787 787
788 788 # HG changeset patch
789 789 # User test
790 790 # Date 3 0
791 791 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f
792 792 # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
793 793 c
794 794
795 795 diff -r 97d72e5f12c7 -r ff2c9fa2018b c
796 796 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
797 797 +++ b/c Thu Jan 01 00:00:03 1970 +0000
798 798 @@ -0,0 +1,1 @@
799 799 +c
800 800
801 801 --===
802 802
803 803 test attach for single patch (quoted-printable):
804 804 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -a -r 4 | \
805 805 > fixheaders
806 806 This patch series consists of 1 patches.
807 807
808 808
809 809 Displaying [PATCH] test ...
810 810 Content-Type: multipart/mixed; boundary="===
811 811 MIME-Version: 1.0
812 812 Subject: [PATCH] test
813 813 X-Mercurial-Node: c655633f8c87700bb38cc6a59a2753bdc5a6c376
814 814 Message-Id: <c655633f8c87700bb38c.60@
815 815 User-Agent: Mercurial-patchbomb
816 816 Date: Thu, 01 Jan 1970 00:01:00 +0000
817 817 From: quux
818 818 To: foo
819 819 Cc: bar
820 820
821 821 --===
822 822 Content-Type: text/plain; charset="us-ascii"
823 823 MIME-Version: 1.0
824 824 Content-Transfer-Encoding: 7bit
825 825
826 826 Patch subject is complete summary.
827 827
828 828
829 829
830 830 --===
831 831 Content-Type: text/x-patch; charset="us-ascii"
832 832 MIME-Version: 1.0
833 833 Content-Transfer-Encoding: quoted-printable
834 834 Content-Disposition: attachment; filename=t2.patch
835 835
836 836 # HG changeset patch
837 837 # User test
838 838 # Date 4 0
839 839 # Node ID c655633f8c87700bb38cc6a59a2753bdc5a6c376
840 840 # Parent c3c9e37db9f4fe4882cda39baf42fed6bad8b15a
841 841 charset=3Dutf-8; content-transfer-encoding: quoted-printable
842 842
843 843 diff -r c3c9e37db9f4 -r c655633f8c87 qp
844 844 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
845 845 +++ b/qp Thu Jan 01 00:00:04 1970 +0000
846 846 @@ -0,0 +1,4 @@
847 847 +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
848 848 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
849 849 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
850 850 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
851 851 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
852 852 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
853 853 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
854 854 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
855 855 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
856 856 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
857 857 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
858 858 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
859 859 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
860 860 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
861 861 +foo
862 862 +
863 863 +bar
864 864
865 865 --===
866 866
867 867 test attach for multiple patches:
868 868 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -a \
869 869 > -r 0:1 -r 4 | fixheaders
870 870 This patch series consists of 3 patches.
871 871
872 872
873 873 Write the introductory message for the patch series.
874 874
875 875
876 876 Displaying [PATCH 0 of 3] test ...
877 877 Content-Type: text/plain; charset="us-ascii"
878 878 MIME-Version: 1.0
879 879 Content-Transfer-Encoding: 7bit
880 880 Subject: [PATCH 0 of 3] test
881 881 Message-Id: <patchbomb.60@
882 882 User-Agent: Mercurial-patchbomb
883 883 Date: Thu, 01 Jan 1970 00:01:00 +0000
884 884 From: quux
885 885 To: foo
886 886 Cc: bar
887 887
888 888
889 889 Displaying [PATCH 1 of 3] a ...
890 890 Content-Type: multipart/mixed; boundary="===
891 891 MIME-Version: 1.0
892 892 Subject: [PATCH 1 of 3] a
893 893 X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
894 894 Message-Id: <8580ff50825a50c8f716.61@
895 895 In-Reply-To: <patchbomb.60@
896 896 References: <patchbomb.60@
897 897 User-Agent: Mercurial-patchbomb
898 898 Date: Thu, 01 Jan 1970 00:01:01 +0000
899 899 From: quux
900 900 To: foo
901 901 Cc: bar
902 902
903 903 --===
904 904 Content-Type: text/plain; charset="us-ascii"
905 905 MIME-Version: 1.0
906 906 Content-Transfer-Encoding: 7bit
907 907
908 908 Patch subject is complete summary.
909 909
910 910
911 911
912 912 --===
913 913 Content-Type: text/x-patch; charset="us-ascii"
914 914 MIME-Version: 1.0
915 915 Content-Transfer-Encoding: 7bit
916 916 Content-Disposition: attachment; filename=t2-1.patch
917 917
918 918 # HG changeset patch
919 919 # User test
920 920 # Date 1 0
921 921 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
922 922 # Parent 0000000000000000000000000000000000000000
923 923 a
924 924
925 925 diff -r 000000000000 -r 8580ff50825a a
926 926 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
927 927 +++ b/a Thu Jan 01 00:00:01 1970 +0000
928 928 @@ -0,0 +1,1 @@
929 929 +a
930 930
931 931 --===
932 932 Displaying [PATCH 2 of 3] b ...
933 933 Content-Type: multipart/mixed; boundary="===
934 934 MIME-Version: 1.0
935 935 Subject: [PATCH 2 of 3] b
936 936 X-Mercurial-Node: 97d72e5f12c7e84f85064aa72e5a297142c36ed9
937 937 Message-Id: <97d72e5f12c7e84f8506.62@
938 938 In-Reply-To: <patchbomb.60@
939 939 References: <patchbomb.60@
940 940 User-Agent: Mercurial-patchbomb
941 941 Date: Thu, 01 Jan 1970 00:01:02 +0000
942 942 From: quux
943 943 To: foo
944 944 Cc: bar
945 945
946 946 --===
947 947 Content-Type: text/plain; charset="us-ascii"
948 948 MIME-Version: 1.0
949 949 Content-Transfer-Encoding: 7bit
950 950
951 951 Patch subject is complete summary.
952 952
953 953
954 954
955 955 --===
956 956 Content-Type: text/x-patch; charset="us-ascii"
957 957 MIME-Version: 1.0
958 958 Content-Transfer-Encoding: 7bit
959 959 Content-Disposition: attachment; filename=t2-2.patch
960 960
961 961 # HG changeset patch
962 962 # User test
963 963 # Date 2 0
964 964 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9
965 965 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab
966 966 b
967 967
968 968 diff -r 8580ff50825a -r 97d72e5f12c7 b
969 969 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
970 970 +++ b/b Thu Jan 01 00:00:02 1970 +0000
971 971 @@ -0,0 +1,1 @@
972 972 +b
973 973
974 974 --===
975 975 Displaying [PATCH 3 of 3] charset=utf-8; content-transfer-encoding: quoted-printable ...
976 976 Content-Type: multipart/mixed; boundary="===
977 977 MIME-Version: 1.0
978 978 Subject: [PATCH 3 of 3] charset=utf-8;
979 979 content-transfer-encoding: quoted-printable
980 980 X-Mercurial-Node: c655633f8c87700bb38cc6a59a2753bdc5a6c376
981 981 Message-Id: <c655633f8c87700bb38c.63@
982 982 In-Reply-To: <patchbomb.60@
983 983 References: <patchbomb.60@
984 984 User-Agent: Mercurial-patchbomb
985 985 Date: Thu, 01 Jan 1970 00:01:03 +0000
986 986 From: quux
987 987 To: foo
988 988 Cc: bar
989 989
990 990 --===
991 991 Content-Type: text/plain; charset="us-ascii"
992 992 MIME-Version: 1.0
993 993 Content-Transfer-Encoding: 7bit
994 994
995 995 Patch subject is complete summary.
996 996
997 997
998 998
999 999 --===
1000 1000 Content-Type: text/x-patch; charset="us-ascii"
1001 1001 MIME-Version: 1.0
1002 1002 Content-Transfer-Encoding: quoted-printable
1003 1003 Content-Disposition: attachment; filename=t2-3.patch
1004 1004
1005 1005 # HG changeset patch
1006 1006 # User test
1007 1007 # Date 4 0
1008 1008 # Node ID c655633f8c87700bb38cc6a59a2753bdc5a6c376
1009 1009 # Parent c3c9e37db9f4fe4882cda39baf42fed6bad8b15a
1010 1010 charset=3Dutf-8; content-transfer-encoding: quoted-printable
1011 1011
1012 1012 diff -r c3c9e37db9f4 -r c655633f8c87 qp
1013 1013 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1014 1014 +++ b/qp Thu Jan 01 00:00:04 1970 +0000
1015 1015 @@ -0,0 +1,4 @@
1016 1016 +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1017 1017 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1018 1018 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1019 1019 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1020 1020 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1021 1021 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1022 1022 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1023 1023 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1024 1024 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1025 1025 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1026 1026 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1027 1027 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1028 1028 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1029 1029 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
1030 1030 +foo
1031 1031 +
1032 1032 +bar
1033 1033
1034 1034 --===
1035 1035
1036 1036 test intro for single patch:
1037 1037 $ hg email --date '1970-1-1 0:1' -n --intro -f quux -t foo -c bar -s test \
1038 1038 > -r 2 | fixheaders
1039 1039 This patch series consists of 1 patches.
1040 1040
1041 1041
1042 1042 Write the introductory message for the patch series.
1043 1043
1044 1044
1045 1045 Displaying [PATCH 0 of 1] test ...
1046 1046 Content-Type: text/plain; charset="us-ascii"
1047 1047 MIME-Version: 1.0
1048 1048 Content-Transfer-Encoding: 7bit
1049 1049 Subject: [PATCH 0 of 1] test
1050 1050 Message-Id: <patchbomb.60@
1051 1051 User-Agent: Mercurial-patchbomb
1052 1052 Date: Thu, 01 Jan 1970 00:01:00 +0000
1053 1053 From: quux
1054 1054 To: foo
1055 1055 Cc: bar
1056 1056
1057 1057
1058 1058 Displaying [PATCH 1 of 1] c ...
1059 1059 Content-Type: text/plain; charset="us-ascii"
1060 1060 MIME-Version: 1.0
1061 1061 Content-Transfer-Encoding: 7bit
1062 1062 Subject: [PATCH 1 of 1] c
1063 1063 X-Mercurial-Node: ff2c9fa2018b15fa74b33363bda9527323e2a99f
1064 1064 Message-Id: <ff2c9fa2018b15fa74b3.61@
1065 1065 In-Reply-To: <patchbomb.60@
1066 1066 References: <patchbomb.60@
1067 1067 User-Agent: Mercurial-patchbomb
1068 1068 Date: Thu, 01 Jan 1970 00:01:01 +0000
1069 1069 From: quux
1070 1070 To: foo
1071 1071 Cc: bar
1072 1072
1073 1073 # HG changeset patch
1074 1074 # User test
1075 1075 # Date 3 0
1076 1076 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f
1077 1077 # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1078 1078 c
1079 1079
1080 1080 diff -r 97d72e5f12c7 -r ff2c9fa2018b c
1081 1081 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1082 1082 +++ b/c Thu Jan 01 00:00:03 1970 +0000
1083 1083 @@ -0,0 +1,1 @@
1084 1084 +c
1085 1085
1086 1086
1087 1087 test --desc without --intro for a single patch:
1088 1088 $ echo foo > intro.text
1089 1089 $ hg email --date '1970-1-1 0:1' -n --desc intro.text -f quux -t foo -c bar \
1090 1090 > -s test -r 2 | fixheaders
1091 1091 This patch series consists of 1 patches.
1092 1092
1093 1093
1094 1094 Displaying [PATCH 0 of 1] test ...
1095 1095 Content-Type: text/plain; charset="us-ascii"
1096 1096 MIME-Version: 1.0
1097 1097 Content-Transfer-Encoding: 7bit
1098 1098 Subject: [PATCH 0 of 1] test
1099 1099 Message-Id: <patchbomb.60@
1100 1100 User-Agent: Mercurial-patchbomb
1101 1101 Date: Thu, 01 Jan 1970 00:01:00 +0000
1102 1102 From: quux
1103 1103 To: foo
1104 1104 Cc: bar
1105 1105
1106 1106 foo
1107 1107
1108 1108 Displaying [PATCH 1 of 1] c ...
1109 1109 Content-Type: text/plain; charset="us-ascii"
1110 1110 MIME-Version: 1.0
1111 1111 Content-Transfer-Encoding: 7bit
1112 1112 Subject: [PATCH 1 of 1] c
1113 1113 X-Mercurial-Node: ff2c9fa2018b15fa74b33363bda9527323e2a99f
1114 1114 Message-Id: <ff2c9fa2018b15fa74b3.61@
1115 1115 In-Reply-To: <patchbomb.60@
1116 1116 References: <patchbomb.60@
1117 1117 User-Agent: Mercurial-patchbomb
1118 1118 Date: Thu, 01 Jan 1970 00:01:01 +0000
1119 1119 From: quux
1120 1120 To: foo
1121 1121 Cc: bar
1122 1122
1123 1123 # HG changeset patch
1124 1124 # User test
1125 1125 # Date 3 0
1126 1126 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f
1127 1127 # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1128 1128 c
1129 1129
1130 1130 diff -r 97d72e5f12c7 -r ff2c9fa2018b c
1131 1131 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1132 1132 +++ b/c Thu Jan 01 00:00:03 1970 +0000
1133 1133 @@ -0,0 +1,1 @@
1134 1134 +c
1135 1135
1136 1136
1137 1137 test intro for multiple patches:
1138 1138 $ hg email --date '1970-1-1 0:1' -n --intro -f quux -t foo -c bar -s test \
1139 1139 > -r 0:1 | fixheaders
1140 1140 This patch series consists of 2 patches.
1141 1141
1142 1142
1143 1143 Write the introductory message for the patch series.
1144 1144
1145 1145
1146 1146 Displaying [PATCH 0 of 2] test ...
1147 1147 Content-Type: text/plain; charset="us-ascii"
1148 1148 MIME-Version: 1.0
1149 1149 Content-Transfer-Encoding: 7bit
1150 1150 Subject: [PATCH 0 of 2] test
1151 1151 Message-Id: <patchbomb.60@
1152 1152 User-Agent: Mercurial-patchbomb
1153 1153 Date: Thu, 01 Jan 1970 00:01:00 +0000
1154 1154 From: quux
1155 1155 To: foo
1156 1156 Cc: bar
1157 1157
1158 1158
1159 1159 Displaying [PATCH 1 of 2] a ...
1160 1160 Content-Type: text/plain; charset="us-ascii"
1161 1161 MIME-Version: 1.0
1162 1162 Content-Transfer-Encoding: 7bit
1163 1163 Subject: [PATCH 1 of 2] a
1164 1164 X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
1165 1165 Message-Id: <8580ff50825a50c8f716.61@
1166 1166 In-Reply-To: <patchbomb.60@
1167 1167 References: <patchbomb.60@
1168 1168 User-Agent: Mercurial-patchbomb
1169 1169 Date: Thu, 01 Jan 1970 00:01:01 +0000
1170 1170 From: quux
1171 1171 To: foo
1172 1172 Cc: bar
1173 1173
1174 1174 # HG changeset patch
1175 1175 # User test
1176 1176 # Date 1 0
1177 1177 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
1178 1178 # Parent 0000000000000000000000000000000000000000
1179 1179 a
1180 1180
1181 1181 diff -r 000000000000 -r 8580ff50825a a
1182 1182 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1183 1183 +++ b/a Thu Jan 01 00:00:01 1970 +0000
1184 1184 @@ -0,0 +1,1 @@
1185 1185 +a
1186 1186
1187 1187 Displaying [PATCH 2 of 2] b ...
1188 1188 Content-Type: text/plain; charset="us-ascii"
1189 1189 MIME-Version: 1.0
1190 1190 Content-Transfer-Encoding: 7bit
1191 1191 Subject: [PATCH 2 of 2] b
1192 1192 X-Mercurial-Node: 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1193 1193 Message-Id: <97d72e5f12c7e84f8506.62@
1194 1194 In-Reply-To: <patchbomb.60@
1195 1195 References: <patchbomb.60@
1196 1196 User-Agent: Mercurial-patchbomb
1197 1197 Date: Thu, 01 Jan 1970 00:01:02 +0000
1198 1198 From: quux
1199 1199 To: foo
1200 1200 Cc: bar
1201 1201
1202 1202 # HG changeset patch
1203 1203 # User test
1204 1204 # Date 2 0
1205 1205 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1206 1206 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab
1207 1207 b
1208 1208
1209 1209 diff -r 8580ff50825a -r 97d72e5f12c7 b
1210 1210 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1211 1211 +++ b/b Thu Jan 01 00:00:02 1970 +0000
1212 1212 @@ -0,0 +1,1 @@
1213 1213 +b
1214 1214
1215 1215
1216 1216 test reply-to via config:
1217 1217 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -r 2 \
1218 1218 > --config patchbomb.reply-to='baz@example.com' | fixheaders
1219 1219 This patch series consists of 1 patches.
1220 1220
1221 1221
1222 1222 Displaying [PATCH] test ...
1223 1223 Content-Type: text/plain; charset="us-ascii"
1224 1224 MIME-Version: 1.0
1225 1225 Content-Transfer-Encoding: 7bit
1226 1226 Subject: [PATCH] test
1227 1227 X-Mercurial-Node: ff2c9fa2018b15fa74b33363bda9527323e2a99f
1228 1228 Message-Id: <ff2c9fa2018b15fa74b3.60@
1229 1229 User-Agent: Mercurial-patchbomb
1230 1230 Date: Thu, 01 Jan 1970 00:01:00 +0000
1231 1231 From: quux
1232 1232 To: foo
1233 1233 Cc: bar
1234 1234 Reply-To: baz@example.com
1235 1235
1236 1236 # HG changeset patch
1237 1237 # User test
1238 1238 # Date 3 0
1239 1239 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f
1240 1240 # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1241 1241 c
1242 1242
1243 1243 diff -r 97d72e5f12c7 -r ff2c9fa2018b c
1244 1244 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1245 1245 +++ b/c Thu Jan 01 00:00:03 1970 +0000
1246 1246 @@ -0,0 +1,1 @@
1247 1247 +c
1248 1248
1249 1249
1250 1250 test reply-to via command line:
1251 1251 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -r 2 \
1252 1252 > --reply-to baz --reply-to fred | fixheaders
1253 1253 This patch series consists of 1 patches.
1254 1254
1255 1255
1256 1256 Displaying [PATCH] test ...
1257 1257 Content-Type: text/plain; charset="us-ascii"
1258 1258 MIME-Version: 1.0
1259 1259 Content-Transfer-Encoding: 7bit
1260 1260 Subject: [PATCH] test
1261 1261 X-Mercurial-Node: ff2c9fa2018b15fa74b33363bda9527323e2a99f
1262 1262 Message-Id: <ff2c9fa2018b15fa74b3.60@
1263 1263 User-Agent: Mercurial-patchbomb
1264 1264 Date: Thu, 01 Jan 1970 00:01:00 +0000
1265 1265 From: quux
1266 1266 To: foo
1267 1267 Cc: bar
1268 1268 Reply-To: baz, fred
1269 1269
1270 1270 # HG changeset patch
1271 1271 # User test
1272 1272 # Date 3 0
1273 1273 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f
1274 1274 # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1275 1275 c
1276 1276
1277 1277 diff -r 97d72e5f12c7 -r ff2c9fa2018b c
1278 1278 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1279 1279 +++ b/c Thu Jan 01 00:00:03 1970 +0000
1280 1280 @@ -0,0 +1,1 @@
1281 1281 +c
1282 1282
1283 1283
1284 1284 tagging csets:
1285 1285 $ hg tag -r0 zero zero.foo
1286 1286 $ hg tag -r1 one one.patch
1287 1287 $ hg tag -r2 two two.diff
1288 1288
1289 1289 test inline for single named patch:
1290 1290 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -i -r 2 | \
1291 1291 > fixheaders
1292 1292 This patch series consists of 1 patches.
1293 1293
1294 1294
1295 1295 Displaying [PATCH] test ...
1296 1296 Content-Type: multipart/mixed; boundary="===
1297 1297 MIME-Version: 1.0
1298 1298 Subject: [PATCH] test
1299 1299 X-Mercurial-Node: ff2c9fa2018b15fa74b33363bda9527323e2a99f
1300 1300 Message-Id: <ff2c9fa2018b15fa74b3.60@
1301 1301 User-Agent: Mercurial-patchbomb
1302 1302 Date: Thu, 01 Jan 1970 00:01:00 +0000
1303 1303 From: quux
1304 1304 To: foo
1305 1305 Cc: bar
1306 1306
1307 1307 --===
1308 1308 Content-Type: text/x-patch; charset="us-ascii"
1309 1309 MIME-Version: 1.0
1310 1310 Content-Transfer-Encoding: 7bit
1311 1311 Content-Disposition: inline; filename=two.diff
1312 1312
1313 1313 # HG changeset patch
1314 1314 # User test
1315 1315 # Date 3 0
1316 1316 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f
1317 1317 # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1318 1318 c
1319 1319
1320 1320 diff -r 97d72e5f12c7 -r ff2c9fa2018b c
1321 1321 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1322 1322 +++ b/c Thu Jan 01 00:00:03 1970 +0000
1323 1323 @@ -0,0 +1,1 @@
1324 1324 +c
1325 1325
1326 1326 --===
1327 1327
1328 1328 test inline for multiple named/unnamed patches:
1329 1329 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar -s test -i -r 0:1 | \
1330 1330 > fixheaders
1331 1331 This patch series consists of 2 patches.
1332 1332
1333 1333
1334 1334 Write the introductory message for the patch series.
1335 1335
1336 1336
1337 1337 Displaying [PATCH 0 of 2] test ...
1338 1338 Content-Type: text/plain; charset="us-ascii"
1339 1339 MIME-Version: 1.0
1340 1340 Content-Transfer-Encoding: 7bit
1341 1341 Subject: [PATCH 0 of 2] test
1342 1342 Message-Id: <patchbomb.60@
1343 1343 User-Agent: Mercurial-patchbomb
1344 1344 Date: Thu, 01 Jan 1970 00:01:00 +0000
1345 1345 From: quux
1346 1346 To: foo
1347 1347 Cc: bar
1348 1348
1349 1349
1350 1350 Displaying [PATCH 1 of 2] a ...
1351 1351 Content-Type: multipart/mixed; boundary="===
1352 1352 MIME-Version: 1.0
1353 1353 Subject: [PATCH 1 of 2] a
1354 1354 X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
1355 1355 Message-Id: <8580ff50825a50c8f716.61@
1356 1356 In-Reply-To: <patchbomb.60@
1357 1357 References: <patchbomb.60@
1358 1358 User-Agent: Mercurial-patchbomb
1359 1359 Date: Thu, 01 Jan 1970 00:01:01 +0000
1360 1360 From: quux
1361 1361 To: foo
1362 1362 Cc: bar
1363 1363
1364 1364 --===
1365 1365 Content-Type: text/x-patch; charset="us-ascii"
1366 1366 MIME-Version: 1.0
1367 1367 Content-Transfer-Encoding: 7bit
1368 1368 Content-Disposition: inline; filename=t2-1.patch
1369 1369
1370 1370 # HG changeset patch
1371 1371 # User test
1372 1372 # Date 1 0
1373 1373 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
1374 1374 # Parent 0000000000000000000000000000000000000000
1375 1375 a
1376 1376
1377 1377 diff -r 000000000000 -r 8580ff50825a a
1378 1378 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1379 1379 +++ b/a Thu Jan 01 00:00:01 1970 +0000
1380 1380 @@ -0,0 +1,1 @@
1381 1381 +a
1382 1382
1383 1383 --===
1384 1384 Displaying [PATCH 2 of 2] b ...
1385 1385 Content-Type: multipart/mixed; boundary="===
1386 1386 MIME-Version: 1.0
1387 1387 Subject: [PATCH 2 of 2] b
1388 1388 X-Mercurial-Node: 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1389 1389 Message-Id: <97d72e5f12c7e84f8506.62@
1390 1390 In-Reply-To: <patchbomb.60@
1391 1391 References: <patchbomb.60@
1392 1392 User-Agent: Mercurial-patchbomb
1393 1393 Date: Thu, 01 Jan 1970 00:01:02 +0000
1394 1394 From: quux
1395 1395 To: foo
1396 1396 Cc: bar
1397 1397
1398 1398 --===
1399 1399 Content-Type: text/x-patch; charset="us-ascii"
1400 1400 MIME-Version: 1.0
1401 1401 Content-Transfer-Encoding: 7bit
1402 1402 Content-Disposition: inline; filename=one.patch
1403 1403
1404 1404 # HG changeset patch
1405 1405 # User test
1406 1406 # Date 2 0
1407 1407 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1408 1408 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab
1409 1409 b
1410 1410
1411 1411 diff -r 8580ff50825a -r 97d72e5f12c7 b
1412 1412 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1413 1413 +++ b/b Thu Jan 01 00:00:02 1970 +0000
1414 1414 @@ -0,0 +1,1 @@
1415 1415 +b
1416 1416
1417 1417 --===
1418 1418
1419 1419
1420 1420 test inreplyto:
1421 1421 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar --in-reply-to baz \
1422 1422 > -r tip | fixheaders
1423 1423 This patch series consists of 1 patches.
1424 1424
1425 1425
1426 1426 Displaying [PATCH] Added tag two, two.diff for changeset ff2c9fa2018b ...
1427 1427 Content-Type: text/plain; charset="us-ascii"
1428 1428 MIME-Version: 1.0
1429 1429 Content-Transfer-Encoding: 7bit
1430 1430 Subject: [PATCH] Added tag two, two.diff for changeset ff2c9fa2018b
1431 1431 X-Mercurial-Node: e317db6a6f288748d1f6cb064f3810fcba66b1b6
1432 1432 Message-Id: <e317db6a6f288748d1f6.60@
1433 1433 In-Reply-To: <baz>
1434 1434 References: <baz>
1435 1435 User-Agent: Mercurial-patchbomb
1436 1436 Date: Thu, 01 Jan 1970 00:01:00 +0000
1437 1437 From: quux
1438 1438 To: foo
1439 1439 Cc: bar
1440 1440
1441 1441 # HG changeset patch
1442 1442 # User test
1443 1443 # Date 0 0
1444 1444 # Node ID e317db6a6f288748d1f6cb064f3810fcba66b1b6
1445 1445 # Parent eae5fcf795eee29d0e45ffc9f519a91cd79fc9ff
1446 1446 Added tag two, two.diff for changeset ff2c9fa2018b
1447 1447
1448 1448 diff -r eae5fcf795ee -r e317db6a6f28 .hgtags
1449 1449 --- a/.hgtags Thu Jan 01 00:00:00 1970 +0000
1450 1450 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1451 1451 @@ -2,3 +2,5 @@
1452 1452 8580ff50825a50c8f716709acdf8de0deddcd6ab zero.foo
1453 1453 97d72e5f12c7e84f85064aa72e5a297142c36ed9 one
1454 1454 97d72e5f12c7e84f85064aa72e5a297142c36ed9 one.patch
1455 1455 +ff2c9fa2018b15fa74b33363bda9527323e2a99f two
1456 1456 +ff2c9fa2018b15fa74b33363bda9527323e2a99f two.diff
1457 1457
1458 1458
1459 1459 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar --in-reply-to baz \
1460 1460 > -r 0:1
1461 1461 This patch series consists of 2 patches.
1462 1462
1463 1463 abort: Subject: [PATCH 0 of 2] Please enter a valid value
1464 1464 [255]
1465 1465
1466 1466 $ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar --in-reply-to baz \
1467 1467 > -s test -r 0:1 | fixheaders
1468 1468 This patch series consists of 2 patches.
1469 1469
1470 1470
1471 1471 Write the introductory message for the patch series.
1472 1472
1473 1473
1474 1474 Displaying [PATCH 0 of 2] test ...
1475 1475 Content-Type: text/plain; charset="us-ascii"
1476 1476 MIME-Version: 1.0
1477 1477 Content-Transfer-Encoding: 7bit
1478 1478 Subject: [PATCH 0 of 2] test
1479 1479 Message-Id: <patchbomb.60@
1480 1480 In-Reply-To: <baz>
1481 1481 References: <baz>
1482 1482 User-Agent: Mercurial-patchbomb
1483 1483 Date: Thu, 01 Jan 1970 00:01:00 +0000
1484 1484 From: quux
1485 1485 To: foo
1486 1486 Cc: bar
1487 1487
1488 1488
1489 1489 Displaying [PATCH 1 of 2] a ...
1490 1490 Content-Type: text/plain; charset="us-ascii"
1491 1491 MIME-Version: 1.0
1492 1492 Content-Transfer-Encoding: 7bit
1493 1493 Subject: [PATCH 1 of 2] a
1494 1494 X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
1495 1495 Message-Id: <8580ff50825a50c8f716.61@
1496 1496 In-Reply-To: <patchbomb.60@
1497 1497 References: <patchbomb.60@
1498 1498 User-Agent: Mercurial-patchbomb
1499 1499 Date: Thu, 01 Jan 1970 00:01:01 +0000
1500 1500 From: quux
1501 1501 To: foo
1502 1502 Cc: bar
1503 1503
1504 1504 # HG changeset patch
1505 1505 # User test
1506 1506 # Date 1 0
1507 1507 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
1508 1508 # Parent 0000000000000000000000000000000000000000
1509 1509 a
1510 1510
1511 1511 diff -r 000000000000 -r 8580ff50825a a
1512 1512 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1513 1513 +++ b/a Thu Jan 01 00:00:01 1970 +0000
1514 1514 @@ -0,0 +1,1 @@
1515 1515 +a
1516 1516
1517 1517 Displaying [PATCH 2 of 2] b ...
1518 1518 Content-Type: text/plain; charset="us-ascii"
1519 1519 MIME-Version: 1.0
1520 1520 Content-Transfer-Encoding: 7bit
1521 1521 Subject: [PATCH 2 of 2] b
1522 1522 X-Mercurial-Node: 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1523 1523 Message-Id: <97d72e5f12c7e84f8506.62@
1524 1524 In-Reply-To: <patchbomb.60@
1525 1525 References: <patchbomb.60@
1526 1526 User-Agent: Mercurial-patchbomb
1527 1527 Date: Thu, 01 Jan 1970 00:01:02 +0000
1528 1528 From: quux
1529 1529 To: foo
1530 1530 Cc: bar
1531 1531
1532 1532 # HG changeset patch
1533 1533 # User test
1534 1534 # Date 2 0
1535 1535 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1536 1536 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab
1537 1537 b
1538 1538
1539 1539 diff -r 8580ff50825a -r 97d72e5f12c7 b
1540 1540 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1541 1541 +++ b/b Thu Jan 01 00:00:02 1970 +0000
1542 1542 @@ -0,0 +1,1 @@
1543 1543 +b
1544 1544
1545 1545
1546 1546 test single flag for single patch:
1547 1547 $ hg email --date '1970-1-1 0:1' -n --flag fooFlag -f quux -t foo -c bar -s test \
1548 1548 > -r 2 | fixheaders
1549 1549 This patch series consists of 1 patches.
1550 1550
1551 1551
1552 1552 Displaying [PATCH fooFlag] test ...
1553 1553 Content-Type: text/plain; charset="us-ascii"
1554 1554 MIME-Version: 1.0
1555 1555 Content-Transfer-Encoding: 7bit
1556 1556 Subject: [PATCH fooFlag] test
1557 1557 X-Mercurial-Node: ff2c9fa2018b15fa74b33363bda9527323e2a99f
1558 1558 Message-Id: <ff2c9fa2018b15fa74b3.60@
1559 1559 User-Agent: Mercurial-patchbomb
1560 1560 Date: Thu, 01 Jan 1970 00:01:00 +0000
1561 1561 From: quux
1562 1562 To: foo
1563 1563 Cc: bar
1564 1564
1565 1565 # HG changeset patch
1566 1566 # User test
1567 1567 # Date 3 0
1568 1568 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f
1569 1569 # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1570 1570 c
1571 1571
1572 1572 diff -r 97d72e5f12c7 -r ff2c9fa2018b c
1573 1573 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1574 1574 +++ b/c Thu Jan 01 00:00:03 1970 +0000
1575 1575 @@ -0,0 +1,1 @@
1576 1576 +c
1577 1577
1578 1578
1579 1579 test single flag for multiple patches:
1580 1580 $ hg email --date '1970-1-1 0:1' -n --flag fooFlag -f quux -t foo -c bar -s test \
1581 1581 > -r 0:1 | fixheaders
1582 1582 This patch series consists of 2 patches.
1583 1583
1584 1584
1585 1585 Write the introductory message for the patch series.
1586 1586
1587 1587
1588 1588 Displaying [PATCH 0 of 2 fooFlag] test ...
1589 1589 Content-Type: text/plain; charset="us-ascii"
1590 1590 MIME-Version: 1.0
1591 1591 Content-Transfer-Encoding: 7bit
1592 1592 Subject: [PATCH 0 of 2 fooFlag] test
1593 1593 Message-Id: <patchbomb.60@
1594 1594 User-Agent: Mercurial-patchbomb
1595 1595 Date: Thu, 01 Jan 1970 00:01:00 +0000
1596 1596 From: quux
1597 1597 To: foo
1598 1598 Cc: bar
1599 1599
1600 1600
1601 1601 Displaying [PATCH 1 of 2 fooFlag] a ...
1602 1602 Content-Type: text/plain; charset="us-ascii"
1603 1603 MIME-Version: 1.0
1604 1604 Content-Transfer-Encoding: 7bit
1605 1605 Subject: [PATCH 1 of 2 fooFlag] a
1606 1606 X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
1607 1607 Message-Id: <8580ff50825a50c8f716.61@
1608 1608 In-Reply-To: <patchbomb.60@
1609 1609 References: <patchbomb.60@
1610 1610 User-Agent: Mercurial-patchbomb
1611 1611 Date: Thu, 01 Jan 1970 00:01:01 +0000
1612 1612 From: quux
1613 1613 To: foo
1614 1614 Cc: bar
1615 1615
1616 1616 # HG changeset patch
1617 1617 # User test
1618 1618 # Date 1 0
1619 1619 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
1620 1620 # Parent 0000000000000000000000000000000000000000
1621 1621 a
1622 1622
1623 1623 diff -r 000000000000 -r 8580ff50825a a
1624 1624 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1625 1625 +++ b/a Thu Jan 01 00:00:01 1970 +0000
1626 1626 @@ -0,0 +1,1 @@
1627 1627 +a
1628 1628
1629 1629 Displaying [PATCH 2 of 2 fooFlag] b ...
1630 1630 Content-Type: text/plain; charset="us-ascii"
1631 1631 MIME-Version: 1.0
1632 1632 Content-Transfer-Encoding: 7bit
1633 1633 Subject: [PATCH 2 of 2 fooFlag] b
1634 1634 X-Mercurial-Node: 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1635 1635 Message-Id: <97d72e5f12c7e84f8506.62@
1636 1636 In-Reply-To: <patchbomb.60@
1637 1637 References: <patchbomb.60@
1638 1638 User-Agent: Mercurial-patchbomb
1639 1639 Date: Thu, 01 Jan 1970 00:01:02 +0000
1640 1640 From: quux
1641 1641 To: foo
1642 1642 Cc: bar
1643 1643
1644 1644 # HG changeset patch
1645 1645 # User test
1646 1646 # Date 2 0
1647 1647 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1648 1648 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab
1649 1649 b
1650 1650
1651 1651 diff -r 8580ff50825a -r 97d72e5f12c7 b
1652 1652 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1653 1653 +++ b/b Thu Jan 01 00:00:02 1970 +0000
1654 1654 @@ -0,0 +1,1 @@
1655 1655 +b
1656 1656
1657 1657
1658 1658 test mutiple flags for single patch:
1659 1659 $ hg email --date '1970-1-1 0:1' -n --flag fooFlag --flag barFlag -f quux -t foo \
1660 1660 > -c bar -s test -r 2 | fixheaders
1661 1661 This patch series consists of 1 patches.
1662 1662
1663 1663
1664 1664 Displaying [PATCH fooFlag barFlag] test ...
1665 1665 Content-Type: text/plain; charset="us-ascii"
1666 1666 MIME-Version: 1.0
1667 1667 Content-Transfer-Encoding: 7bit
1668 1668 Subject: [PATCH fooFlag barFlag] test
1669 1669 X-Mercurial-Node: ff2c9fa2018b15fa74b33363bda9527323e2a99f
1670 1670 Message-Id: <ff2c9fa2018b15fa74b3.60@
1671 1671 User-Agent: Mercurial-patchbomb
1672 1672 Date: Thu, 01 Jan 1970 00:01:00 +0000
1673 1673 From: quux
1674 1674 To: foo
1675 1675 Cc: bar
1676 1676
1677 1677 # HG changeset patch
1678 1678 # User test
1679 1679 # Date 3 0
1680 1680 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f
1681 1681 # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1682 1682 c
1683 1683
1684 1684 diff -r 97d72e5f12c7 -r ff2c9fa2018b c
1685 1685 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1686 1686 +++ b/c Thu Jan 01 00:00:03 1970 +0000
1687 1687 @@ -0,0 +1,1 @@
1688 1688 +c
1689 1689
1690 1690
1691 1691 test multiple flags for multiple patches:
1692 1692 $ hg email --date '1970-1-1 0:1' -n --flag fooFlag --flag barFlag -f quux -t foo \
1693 1693 > -c bar -s test -r 0:1 | fixheaders
1694 1694 This patch series consists of 2 patches.
1695 1695
1696 1696
1697 1697 Write the introductory message for the patch series.
1698 1698
1699 1699
1700 1700 Displaying [PATCH 0 of 2 fooFlag barFlag] test ...
1701 1701 Content-Type: text/plain; charset="us-ascii"
1702 1702 MIME-Version: 1.0
1703 1703 Content-Transfer-Encoding: 7bit
1704 1704 Subject: [PATCH 0 of 2 fooFlag barFlag] test
1705 1705 Message-Id: <patchbomb.60@
1706 1706 User-Agent: Mercurial-patchbomb
1707 1707 Date: Thu, 01 Jan 1970 00:01:00 +0000
1708 1708 From: quux
1709 1709 To: foo
1710 1710 Cc: bar
1711 1711
1712 1712
1713 1713 Displaying [PATCH 1 of 2 fooFlag barFlag] a ...
1714 1714 Content-Type: text/plain; charset="us-ascii"
1715 1715 MIME-Version: 1.0
1716 1716 Content-Transfer-Encoding: 7bit
1717 1717 Subject: [PATCH 1 of 2 fooFlag barFlag] a
1718 1718 X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
1719 1719 Message-Id: <8580ff50825a50c8f716.61@
1720 1720 In-Reply-To: <patchbomb.60@
1721 1721 References: <patchbomb.60@
1722 1722 User-Agent: Mercurial-patchbomb
1723 1723 Date: Thu, 01 Jan 1970 00:01:01 +0000
1724 1724 From: quux
1725 1725 To: foo
1726 1726 Cc: bar
1727 1727
1728 1728 # HG changeset patch
1729 1729 # User test
1730 1730 # Date 1 0
1731 1731 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
1732 1732 # Parent 0000000000000000000000000000000000000000
1733 1733 a
1734 1734
1735 1735 diff -r 000000000000 -r 8580ff50825a a
1736 1736 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1737 1737 +++ b/a Thu Jan 01 00:00:01 1970 +0000
1738 1738 @@ -0,0 +1,1 @@
1739 1739 +a
1740 1740
1741 1741 Displaying [PATCH 2 of 2 fooFlag barFlag] b ...
1742 1742 Content-Type: text/plain; charset="us-ascii"
1743 1743 MIME-Version: 1.0
1744 1744 Content-Transfer-Encoding: 7bit
1745 1745 Subject: [PATCH 2 of 2 fooFlag barFlag] b
1746 1746 X-Mercurial-Node: 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1747 1747 Message-Id: <97d72e5f12c7e84f8506.62@
1748 1748 In-Reply-To: <patchbomb.60@
1749 1749 References: <patchbomb.60@
1750 1750 User-Agent: Mercurial-patchbomb
1751 1751 Date: Thu, 01 Jan 1970 00:01:02 +0000
1752 1752 From: quux
1753 1753 To: foo
1754 1754 Cc: bar
1755 1755
1756 1756 # HG changeset patch
1757 1757 # User test
1758 1758 # Date 2 0
1759 1759 # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1760 1760 # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab
1761 1761 b
1762 1762
1763 1763 diff -r 8580ff50825a -r 97d72e5f12c7 b
1764 1764 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1765 1765 +++ b/b Thu Jan 01 00:00:02 1970 +0000
1766 1766 @@ -0,0 +1,1 @@
1767 1767 +b
1768 1768
1769 1769
1770 1770 test multi-address parsing:
1771 1771 $ hg email --date '1980-1-1 0:1' -m tmp.mbox -f quux -t 'spam<spam><eggs>' \
1772 1772 > -t toast -c 'foo,bar@example.com' -c '"A, B <>" <a@example.com>' -s test -r 0 \
1773 1773 > --config email.bcc='"Quux, A." <quux>'
1774 1774 This patch series consists of 1 patches.
1775 1775
1776 1776
1777 1777 Writing [PATCH] test ...
1778 1778 $ fixheaders < tmp.mbox
1779 1779 From quux Tue Jan 01 00:01:01 1980
1780 1780 Content-Type: text/plain; charset="us-ascii"
1781 1781 MIME-Version: 1.0
1782 1782 Content-Transfer-Encoding: 7bit
1783 1783 Subject: [PATCH] test
1784 1784 X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
1785 1785 Message-Id: <8580ff50825a50c8f716.315532860@
1786 1786 User-Agent: Mercurial-patchbomb
1787 1787 Date: Tue, 01 Jan 1980 00:01:00 +0000
1788 1788 From: quux
1789 1789 To: spam <spam>, eggs, toast
1790 1790 Cc: foo, bar@example.com, "A, B <>" <a@example.com>
1791 1791 Bcc: "Quux, A." <quux>
1792 1792
1793 1793 # HG changeset patch
1794 1794 # User test
1795 1795 # Date 1 0
1796 1796 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
1797 1797 # Parent 0000000000000000000000000000000000000000
1798 1798 a
1799 1799
1800 1800 diff -r 000000000000 -r 8580ff50825a a
1801 1801 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1802 1802 +++ b/a Thu Jan 01 00:00:01 1970 +0000
1803 1803 @@ -0,0 +1,1 @@
1804 1804 +a
1805 1805
1806 1806
1807 1807
1808 1808 test multi-byte domain parsing:
1809 1809 $ UUML=`python -c 'import sys; sys.stdout.write("\374")'`
1810 1810 $ HGENCODING=iso-8859-1
1811 1811 $ export HGENCODING
1812 1812 $ hg email --date '1980-1-1 0:1' -m tmp.mbox -f quux -t "bar@${UUML}nicode.com" -s test -r 0
1813 1813 This patch series consists of 1 patches.
1814 1814
1815 1815 Cc:
1816 1816
1817 1817 Writing [PATCH] test ...
1818 1818
1819 1819 $ cat tmp.mbox
1820 1820 From quux Tue Jan 01 00:01:01 1980
1821 1821 Content-Type: text/plain; charset="us-ascii"
1822 1822 MIME-Version: 1.0
1823 1823 Content-Transfer-Encoding: 7bit
1824 1824 Subject: [PATCH] test
1825 1825 X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
1826 1826 Message-Id: <8580ff50825a50c8f716.315532860@* (glob)
1827 1827 User-Agent: Mercurial-patchbomb/* (glob)
1828 1828 Date: Tue, 01 Jan 1980 00:01:00 +0000
1829 1829 From: quux
1830 1830 To: bar@xn--nicode-2ya.com
1831 1831
1832 1832 # HG changeset patch
1833 1833 # User test
1834 1834 # Date 1 0
1835 1835 # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
1836 1836 # Parent 0000000000000000000000000000000000000000
1837 1837 a
1838 1838
1839 1839 diff -r 000000000000 -r 8580ff50825a a
1840 1840 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1841 1841 +++ b/a Thu Jan 01 00:00:01 1970 +0000
1842 1842 @@ -0,0 +1,1 @@
1843 1843 +a
1844 1844
1845 1845
1846 1846
1847 1847 test outgoing:
1848 1848 $ hg up 1
1849 1849 0 files updated, 0 files merged, 6 files removed, 0 files unresolved
1850 1850
1851 1851 $ hg branch test
1852 1852 marked working directory as branch test
1853 1853
1854 1854 $ echo d > d
1855 1855 $ hg add d
1856 1856 $ hg ci -md -d '4 0'
1857 1857 $ hg email --date '1980-1-1 0:1' -n -t foo -s test -o ../t
1858 1858 comparing with ../t
1859 1859 searching for changes
1860 1860 From [test]: test
1861 1861 This patch series consists of 8 patches.
1862 1862
1863 1863
1864 1864 Write the introductory message for the patch series.
1865 1865
1866 1866 Cc:
1867 1867
1868 1868 Displaying [PATCH 0 of 8] test ...
1869 1869 Content-Type: text/plain; charset="us-ascii"
1870 1870 MIME-Version: 1.0
1871 1871 Content-Transfer-Encoding: 7bit
1872 1872 Subject: [PATCH 0 of 8] test
1873 1873 Message-Id: <patchbomb.315532860@* (glob)
1874 1874 User-Agent: Mercurial-patchbomb/* (glob)
1875 1875 Date: Tue, 01 Jan 1980 00:01:00 +0000
1876 1876 From: test
1877 1877 To: foo
1878 1878
1879 1879
1880 1880 Displaying [PATCH 1 of 8] c ...
1881 1881 Content-Type: text/plain; charset="us-ascii"
1882 1882 MIME-Version: 1.0
1883 1883 Content-Transfer-Encoding: 7bit
1884 1884 Subject: [PATCH 1 of 8] c
1885 1885 X-Mercurial-Node: ff2c9fa2018b15fa74b33363bda9527323e2a99f
1886 1886 Message-Id: <ff2c9fa2018b15fa74b3.315532861@* (glob)
1887 1887 In-Reply-To: <patchbomb\.315532860@[^>]*> (re)
1888 1888 References: <patchbomb\.315532860@[^>]*> (re)
1889 1889 User-Agent: Mercurial-patchbomb/* (glob)
1890 1890 Date: Tue, 01 Jan 1980 00:01:01 +0000
1891 1891 From: test
1892 1892 To: foo
1893 1893
1894 1894 # HG changeset patch
1895 1895 # User test
1896 1896 # Date 3 0
1897 1897 # Node ID ff2c9fa2018b15fa74b33363bda9527323e2a99f
1898 1898 # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
1899 1899 c
1900 1900
1901 1901 diff -r 97d72e5f12c7 -r ff2c9fa2018b c
1902 1902 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1903 1903 +++ b/c Thu Jan 01 00:00:03 1970 +0000
1904 1904 @@ -0,0 +1,1 @@
1905 1905 +c
1906 1906
1907 1907 Displaying [PATCH 2 of 8] charset=utf-8; content-transfer-encoding: base64 ...
1908 1908 Content-Type: text/plain; charset="us-ascii"
1909 1909 MIME-Version: 1.0
1910 1910 Content-Transfer-Encoding: 8bit
1911 1911 Subject: [PATCH 2 of 8] charset=utf-8; content-transfer-encoding: base64
1912 1912 X-Mercurial-Node: c3c9e37db9f4fe4882cda39baf42fed6bad8b15a
1913 1913 Message-Id: <c3c9e37db9f4fe4882cd.315532862@* (glob)
1914 1914 In-Reply-To: <patchbomb\.315532860@[^>]*> (re)
1915 1915 References: <patchbomb\.315532860@[^>]*> (re)
1916 1916 User-Agent: Mercurial-patchbomb/* (glob)
1917 1917 Date: Tue, 01 Jan 1980 00:01:02 +0000
1918 1918 From: test
1919 1919 To: foo
1920 1920
1921 1921 # HG changeset patch
1922 1922 # User test
1923 1923 # Date 4 0
1924 1924 # Node ID c3c9e37db9f4fe4882cda39baf42fed6bad8b15a
1925 1925 # Parent ff2c9fa2018b15fa74b33363bda9527323e2a99f
1926 1926 charset=utf-8; content-transfer-encoding: base64
1927 1927
1928 1928 diff -r ff2c9fa2018b -r c3c9e37db9f4 description
1929 1929 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1930 1930 +++ b/description Thu Jan 01 00:00:04 1970 +0000
1931 1931 @@ -0,0 +1,3 @@
1932 1932 +a multiline
1933 1933 +
1934 1934 +description
1935 1935 diff -r ff2c9fa2018b -r c3c9e37db9f4 utf
1936 1936 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1937 1937 +++ b/utf Thu Jan 01 00:00:04 1970 +0000
1938 1938 @@ -0,0 +1,1 @@
1939 1939 +h\xc3\xb6mma! (esc)
1940 1940
1941 1941 Displaying [PATCH 3 of 8] charset=utf-8; content-transfer-encoding: quoted-printable ...
1942 1942 Content-Type: text/plain; charset="us-ascii"
1943 1943 MIME-Version: 1.0
1944 1944 Content-Transfer-Encoding: quoted-printable
1945 1945 Subject: [PATCH 3 of 8] charset=utf-8;
1946 1946 content-transfer-encoding: quoted-printable
1947 1947 X-Mercurial-Node: c655633f8c87700bb38cc6a59a2753bdc5a6c376
1948 1948 Message-Id: <c655633f8c87700bb38c.315532863@* (glob)
1949 1949 In-Reply-To: <patchbomb\.315532860@[^>]*> (re)
1950 1950 References: <patchbomb\.315532860@[^>]*> (re)
1951 1951 User-Agent: Mercurial-patchbomb/* (glob)
1952 1952 Date: Tue, 01 Jan 1980 00:01:03 +0000
1953 1953 From: test
1954 1954 To: foo
1955 1955
1956 1956 # HG changeset patch
1957 1957 # User test
1958 1958 # Date 4 0
1959 1959 # Node ID c655633f8c87700bb38cc6a59a2753bdc5a6c376
1960 1960 # Parent c3c9e37db9f4fe4882cda39baf42fed6bad8b15a
1961 1961 charset=3Dutf-8; content-transfer-encoding: quoted-printable
1962 1962
1963 1963 diff -r c3c9e37db9f4 -r c655633f8c87 qp
1964 1964 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1965 1965 +++ b/qp Thu Jan 01 00:00:04 1970 +0000
1966 1966 @@ -0,0 +1,4 @@
1967 1967 +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1968 1968 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1969 1969 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1970 1970 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1971 1971 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1972 1972 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1973 1973 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1974 1974 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1975 1975 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1976 1976 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1977 1977 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1978 1978 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1979 1979 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
1980 1980 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
1981 1981 +foo
1982 1982 +
1983 1983 +bar
1984 1984
1985 1985 Displaying [PATCH 4 of 8] charset=us-ascii; content-transfer-encoding: 8bit ...
1986 1986 Content-Type: text/plain; charset="us-ascii"
1987 1987 MIME-Version: 1.0
1988 1988 Content-Transfer-Encoding: 8bit
1989 1989 Subject: [PATCH 4 of 8] charset=us-ascii; content-transfer-encoding: 8bit
1990 1990 X-Mercurial-Node: 22d0f96be12f5945fd67d101af58f7bc8263c835
1991 1991 Message-Id: <22d0f96be12f5945fd67.315532864@* (glob)
1992 1992 In-Reply-To: <patchbomb\.315532860@[^>]*> (re)
1993 1993 References: <patchbomb\.315532860@[^>]*> (re)
1994 1994 User-Agent: Mercurial-patchbomb/* (glob)
1995 1995 Date: Tue, 01 Jan 1980 00:01:04 +0000
1996 1996 From: test
1997 1997 To: foo
1998 1998
1999 1999 # HG changeset patch
2000 2000 # User test
2001 2001 # Date 5 0
2002 2002 # Node ID 22d0f96be12f5945fd67d101af58f7bc8263c835
2003 2003 # Parent c655633f8c87700bb38cc6a59a2753bdc5a6c376
2004 2004 charset=us-ascii; content-transfer-encoding: 8bit
2005 2005
2006 2006 diff -r c655633f8c87 -r 22d0f96be12f isolatin
2007 2007 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2008 2008 +++ b/isolatin Thu Jan 01 00:00:05 1970 +0000
2009 2009 @@ -0,0 +1,1 @@
2010 2010 +h\xf6mma! (esc)
2011 2011
2012 2012 Displaying [PATCH 5 of 8] Added tag zero, zero.foo for changeset 8580ff50825a ...
2013 2013 Content-Type: text/plain; charset="us-ascii"
2014 2014 MIME-Version: 1.0
2015 2015 Content-Transfer-Encoding: 7bit
2016 2016 Subject: [PATCH 5 of 8] Added tag zero, zero.foo for changeset 8580ff50825a
2017 2017 X-Mercurial-Node: dd9c2b4b8a8a0934d5523c15f2c119b362360903
2018 2018 Message-Id: <dd9c2b4b8a8a0934d552.315532865@* (glob)
2019 2019 In-Reply-To: <patchbomb\.315532860@[^>]*> (re)
2020 2020 References: <patchbomb\.315532860@[^>]*> (re)
2021 2021 User-Agent: Mercurial-patchbomb/* (glob)
2022 2022 Date: Tue, 01 Jan 1980 00:01:05 +0000
2023 2023 From: test
2024 2024 To: foo
2025 2025
2026 2026 # HG changeset patch
2027 2027 # User test
2028 2028 # Date 0 0
2029 2029 # Node ID dd9c2b4b8a8a0934d5523c15f2c119b362360903
2030 2030 # Parent 22d0f96be12f5945fd67d101af58f7bc8263c835
2031 2031 Added tag zero, zero.foo for changeset 8580ff50825a
2032 2032
2033 2033 diff -r 22d0f96be12f -r dd9c2b4b8a8a .hgtags
2034 2034 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2035 2035 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2036 2036 @@ -0,0 +1,2 @@
2037 2037 +8580ff50825a50c8f716709acdf8de0deddcd6ab zero
2038 2038 +8580ff50825a50c8f716709acdf8de0deddcd6ab zero.foo
2039 2039
2040 2040 Displaying [PATCH 6 of 8] Added tag one, one.patch for changeset 97d72e5f12c7 ...
2041 2041 Content-Type: text/plain; charset="us-ascii"
2042 2042 MIME-Version: 1.0
2043 2043 Content-Transfer-Encoding: 7bit
2044 2044 Subject: [PATCH 6 of 8] Added tag one, one.patch for changeset 97d72e5f12c7
2045 2045 X-Mercurial-Node: eae5fcf795eee29d0e45ffc9f519a91cd79fc9ff
2046 2046 Message-Id: <eae5fcf795eee29d0e45.315532866@* (glob)
2047 2047 In-Reply-To: <patchbomb\.315532860@[^>]*> (re)
2048 2048 References: <patchbomb\.315532860@[^>]*> (re)
2049 2049 User-Agent: Mercurial-patchbomb/* (glob)
2050 2050 Date: Tue, 01 Jan 1980 00:01:06 +0000
2051 2051 From: test
2052 2052 To: foo
2053 2053
2054 2054 # HG changeset patch
2055 2055 # User test
2056 2056 # Date 0 0
2057 2057 # Node ID eae5fcf795eee29d0e45ffc9f519a91cd79fc9ff
2058 2058 # Parent dd9c2b4b8a8a0934d5523c15f2c119b362360903
2059 2059 Added tag one, one.patch for changeset 97d72e5f12c7
2060 2060
2061 2061 diff -r dd9c2b4b8a8a -r eae5fcf795ee .hgtags
2062 2062 --- a/.hgtags Thu Jan 01 00:00:00 1970 +0000
2063 2063 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2064 2064 @@ -1,2 +1,4 @@
2065 2065 8580ff50825a50c8f716709acdf8de0deddcd6ab zero
2066 2066 8580ff50825a50c8f716709acdf8de0deddcd6ab zero.foo
2067 2067 +97d72e5f12c7e84f85064aa72e5a297142c36ed9 one
2068 2068 +97d72e5f12c7e84f85064aa72e5a297142c36ed9 one.patch
2069 2069
2070 2070 Displaying [PATCH 7 of 8] Added tag two, two.diff for changeset ff2c9fa2018b ...
2071 2071 Content-Type: text/plain; charset="us-ascii"
2072 2072 MIME-Version: 1.0
2073 2073 Content-Transfer-Encoding: 7bit
2074 2074 Subject: [PATCH 7 of 8] Added tag two, two.diff for changeset ff2c9fa2018b
2075 2075 X-Mercurial-Node: e317db6a6f288748d1f6cb064f3810fcba66b1b6
2076 2076 Message-Id: <e317db6a6f288748d1f6.315532867@* (glob)
2077 2077 In-Reply-To: <patchbomb\.315532860@[^>]*> (re)
2078 2078 References: <patchbomb\.315532860@[^>]*> (re)
2079 2079 User-Agent: Mercurial-patchbomb/* (glob)
2080 2080 Date: Tue, 01 Jan 1980 00:01:07 +0000
2081 2081 From: test
2082 2082 To: foo
2083 2083
2084 2084 # HG changeset patch
2085 2085 # User test
2086 2086 # Date 0 0
2087 2087 # Node ID e317db6a6f288748d1f6cb064f3810fcba66b1b6
2088 2088 # Parent eae5fcf795eee29d0e45ffc9f519a91cd79fc9ff
2089 2089 Added tag two, two.diff for changeset ff2c9fa2018b
2090 2090
2091 2091 diff -r eae5fcf795ee -r e317db6a6f28 .hgtags
2092 2092 --- a/.hgtags Thu Jan 01 00:00:00 1970 +0000
2093 2093 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2094 2094 @@ -2,3 +2,5 @@
2095 2095 8580ff50825a50c8f716709acdf8de0deddcd6ab zero.foo
2096 2096 97d72e5f12c7e84f85064aa72e5a297142c36ed9 one
2097 2097 97d72e5f12c7e84f85064aa72e5a297142c36ed9 one.patch
2098 2098 +ff2c9fa2018b15fa74b33363bda9527323e2a99f two
2099 2099 +ff2c9fa2018b15fa74b33363bda9527323e2a99f two.diff
2100 2100
2101 2101 Displaying [PATCH 8 of 8] d ...
2102 2102 Content-Type: text/plain; charset="us-ascii"
2103 2103 MIME-Version: 1.0
2104 2104 Content-Transfer-Encoding: 7bit
2105 2105 Subject: [PATCH 8 of 8] d
2106 2106 X-Mercurial-Node: 2f9fa9b998c5fe3ac2bd9a2b14bfcbeecbc7c268
2107 2107 Message-Id: <2f9fa9b998c5fe3ac2bd\.315532868[^>]*> (re)
2108 2108 In-Reply-To: <patchbomb\.315532860@[^>]*> (re)
2109 2109 References: <patchbomb\.315532860@[^>]*> (re)
2110 2110 User-Agent: Mercurial-patchbomb/* (glob)
2111 2111 Date: Tue, 01 Jan 1980 00:01:08 +0000
2112 2112 From: test
2113 2113 To: foo
2114 2114
2115 2115 # HG changeset patch
2116 2116 # User test
2117 2117 # Date 4 0
2118 2118 # Branch test
2119 2119 # Node ID 2f9fa9b998c5fe3ac2bd9a2b14bfcbeecbc7c268
2120 2120 # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
2121 2121 d
2122 2122
2123 2123 diff -r 97d72e5f12c7 -r 2f9fa9b998c5 d
2124 2124 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2125 2125 +++ b/d Thu Jan 01 00:00:04 1970 +0000
2126 2126 @@ -0,0 +1,1 @@
2127 2127 +d
2128 2128
2129 2129
2130 2130 dest#branch URIs:
2131 2131 $ hg email --date '1980-1-1 0:1' -n -t foo -s test -o ../t#test
2132 2132 comparing with ../t
2133 2133 searching for changes
2134 2134 From [test]: test
2135 2135 This patch series consists of 1 patches.
2136 2136
2137 2137 Cc:
2138 2138
2139 2139 Displaying [PATCH] test ...
2140 2140 Content-Type: text/plain; charset="us-ascii"
2141 2141 MIME-Version: 1.0
2142 2142 Content-Transfer-Encoding: 7bit
2143 2143 Subject: [PATCH] test
2144 2144 X-Mercurial-Node: 2f9fa9b998c5fe3ac2bd9a2b14bfcbeecbc7c268
2145 2145 Message-Id: <2f9fa9b998c5fe3ac2bd.315532860@* (glob)
2146 2146 User-Agent: Mercurial-patchbomb/* (glob)
2147 2147 Date: Tue, 01 Jan 1980 00:01:00 +0000
2148 2148 From: test
2149 2149 To: foo
2150 2150
2151 2151 # HG changeset patch
2152 2152 # User test
2153 2153 # Date 4 0
2154 2154 # Branch test
2155 2155 # Node ID 2f9fa9b998c5fe3ac2bd9a2b14bfcbeecbc7c268
2156 2156 # Parent 97d72e5f12c7e84f85064aa72e5a297142c36ed9
2157 2157 d
2158 2158
2159 2159 diff -r 97d72e5f12c7 -r 2f9fa9b998c5 d
2160 2160 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2161 2161 +++ b/d Thu Jan 01 00:00:04 1970 +0000
2162 2162 @@ -0,0 +1,1 @@
2163 2163 +d
2164 2164
@@ -1,69 +1,69
1 1 Corrupt an hg repo with two pulls.
2 2 create one repo with a long history
3 3
4 4 $ hg init source1
5 5 $ cd source1
6 6 $ touch foo
7 7 $ hg add foo
8 8 $ for i in 1 2 3 4 5 6 7 8 9 10; do
9 9 > echo $i >> foo
10 10 > hg ci -m $i
11 11 > done
12 12 $ cd ..
13 13
14 14 create one repo with a shorter history
15 15
16 16 $ hg clone -r 0 source1 source2
17 17 adding changesets
18 18 adding manifests
19 19 adding file changes
20 20 added 1 changesets with 1 changes to 1 files
21 21 updating to branch default
22 22 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 23 $ cd source2
24 24 $ echo a >> foo
25 25 $ hg ci -m a
26 26 $ cd ..
27 27
28 28 create a third repo to pull both other repos into it
29 29
30 30 $ hg init corrupted
31 31 $ cd corrupted
32 32
33 33 use a hook to make the second pull start while the first one is still running
34 34
35 35 $ echo '[hooks]' >> .hg/hgrc
36 36 $ echo 'prechangegroup = sleep 5' >> .hg/hgrc
37 37
38 38 start a pull...
39 39
40 40 $ hg pull ../source1 &
41 41
42 42 ... and start another pull before the first one has finished
43 43
44 44 $ sleep 1
45 pulling from ../source1
46 requesting all changes
45 47 $ hg pull ../source2 2>/dev/null
46 48 pulling from ../source2
47 pulling from ../source1
48 requesting all changes
49 49 adding changesets
50 50 adding manifests
51 51 adding file changes
52 52 added 10 changesets with 10 changes to 1 files
53 53 (run 'hg update' to get a working copy)
54 54 searching for changes
55 55 adding changesets
56 56 adding manifests
57 57 adding file changes
58 58 added 1 changesets with 1 changes to 1 files (+1 heads)
59 59 (run 'hg heads' to see heads, 'hg merge' to merge)
60 60
61 61 see the result
62 62
63 63 $ wait
64 64 $ hg verify
65 65 checking changesets
66 66 checking manifests
67 67 crosschecking files in changesets and manifests
68 68 checking files
69 69 1 files, 11 changesets, 11 total revisions
@@ -1,156 +1,156
1 1
2 2 $ hg clone http://localhost:$HGPORT/ copy
3 3 abort: error: Connection refused
4 4 [255]
5 5 $ test -d copy
6 6 [1]
7 7
8 8 This server doesn't do range requests so it's basically only good for
9 9 one pull
10 10
11 11 $ cat > dumb.py <<EOF
12 12 > import BaseHTTPServer, SimpleHTTPServer, os, signal, sys
13 13 >
14 14 > def run(server_class=BaseHTTPServer.HTTPServer,
15 15 > handler_class=SimpleHTTPServer.SimpleHTTPRequestHandler):
16 16 > server_address = ('localhost', int(os.environ['HGPORT']))
17 17 > httpd = server_class(server_address, handler_class)
18 18 > httpd.serve_forever()
19 19 >
20 20 > signal.signal(signal.SIGTERM, lambda x, y: sys.exit(0))
21 21 > run()
22 22 > EOF
23 23 $ python dumb.py 2>/dev/null &
24 24 $ echo $! >> $DAEMON_PIDS
25 25 $ mkdir remote
26 26 $ cd remote
27 27 $ hg init
28 28 $ echo foo > bar
29 29 $ echo c2 > '.dotfile with spaces'
30 30 $ hg add
31 31 adding .dotfile with spaces
32 32 adding bar
33 33 $ hg commit -m"test"
34 34 $ hg tip
35 35 changeset: 0:02770d679fb8
36 36 tag: tip
37 37 user: test
38 38 date: Thu Jan 01 00:00:00 1970 +0000
39 39 summary: test
40 40
41 41 $ cd ..
42 42 $ hg clone static-http://localhost:$HGPORT/remote local
43 43 requesting all changes
44 44 adding changesets
45 45 adding manifests
46 46 adding file changes
47 47 added 1 changesets with 2 changes to 2 files
48 48 updating to branch default
49 49 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
50 50 $ cd local
51 51 $ hg verify
52 52 checking changesets
53 53 checking manifests
54 54 crosschecking files in changesets and manifests
55 55 checking files
56 56 2 files, 1 changesets, 2 total revisions
57 57 $ cat bar
58 58 foo
59 59 $ cd ../remote
60 60 $ echo baz > quux
61 61 $ hg commit -A -mtest2
62 62 adding quux
63 63
64 64 check for HTTP opener failures when cachefile does not exist
65 65
66 66 $ rm .hg/cache/*
67 67 $ cd ../local
68 68 $ echo '[hooks]' >> .hg/hgrc
69 69 $ echo 'changegroup = python "$TESTDIR"/printenv.py changegroup' >> .hg/hgrc
70 70 $ hg pull
71 changegroup hook: HG_NODE=4ac2e3648604439c580c69b09ec9d93a88d93432 HG_SOURCE=pull HG_URL=http://localhost:$HGPORT/remote
72 71 pulling from static-http://localhost:$HGPORT/remote
73 72 searching for changes
74 73 adding changesets
75 74 adding manifests
76 75 adding file changes
77 76 added 1 changesets with 1 changes to 1 files
77 changegroup hook: HG_NODE=4ac2e3648604439c580c69b09ec9d93a88d93432 HG_SOURCE=pull HG_URL=http://localhost:$HGPORT/remote
78 78 (run 'hg update' to get a working copy)
79 79
80 80 trying to push
81 81
82 82 $ hg update
83 83 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 84 $ echo more foo >> bar
85 85 $ hg commit -m"test"
86 86 $ hg push
87 87 pushing to static-http://localhost:$HGPORT/remote
88 88 abort: cannot lock static-http repository
89 89 [255]
90 90
91 91 trying clone -r
92 92
93 93 $ cd ..
94 94 $ hg clone -r donotexist static-http://localhost:$HGPORT/remote local0
95 95 abort: unknown revision 'donotexist'!
96 96 [255]
97 97 $ hg clone -r 0 static-http://localhost:$HGPORT/remote local0
98 98 adding changesets
99 99 adding manifests
100 100 adding file changes
101 101 added 1 changesets with 2 changes to 2 files
102 102 updating to branch default
103 103 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
104 104
105 105 test with "/" URI (issue 747)
106 106
107 107 $ hg init
108 108 $ echo a > a
109 109 $ hg add a
110 110 $ hg ci -ma
111 111 $ hg clone static-http://localhost:$HGPORT/ local2
112 112 requesting all changes
113 113 adding changesets
114 114 adding manifests
115 115 adding file changes
116 116 added 1 changesets with 1 changes to 1 files
117 117 updating to branch default
118 118 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
119 119 $ cd local2
120 120 $ hg verify
121 121 checking changesets
122 122 checking manifests
123 123 crosschecking files in changesets and manifests
124 124 checking files
125 125 1 files, 1 changesets, 1 total revisions
126 126 $ cat a
127 127 a
128 128 $ hg paths
129 129 default = static-http://localhost:$HGPORT/
130 130
131 131 test with empty repo (issue965)
132 132
133 133 $ cd ..
134 134 $ hg init remotempty
135 135 $ hg clone static-http://localhost:$HGPORT/remotempty local3
136 136 no changes found
137 137 updating to branch default
138 138 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
139 139 $ cd local3
140 140 $ hg verify
141 141 checking changesets
142 142 checking manifests
143 143 crosschecking files in changesets and manifests
144 144 checking files
145 145 0 files, 0 changesets, 0 total revisions
146 146 $ hg paths
147 147 default = static-http://localhost:$HGPORT/remotempty
148 148
149 149 test with non-repo
150 150
151 151 $ cd ..
152 152 $ mkdir notarepo
153 153 $ hg clone static-http://localhost:$HGPORT/notarepo local3
154 154 abort: 'http://localhost:$HGPORT/notarepo' does not appear to be an hg repository!
155 155 [255]
156 156 $ kill $!
General Comments 0
You need to be logged in to leave comments. Login now