##// END OF EJS Templates
blackbox: fix recording exit codes (issue3938)...
Durham Goode -
r19229:41e39a02 stable
parent child Browse files
Show More
@@ -1,866 +1,869
1 1 # dispatch.py - command dispatching for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re
10 10 import util, commands, hg, fancyopts, extensions, hook, error
11 11 import cmdutil, encoding
12 12 import ui as uimod
13 13
14 14 class request(object):
15 15 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
16 16 ferr=None):
17 17 self.args = args
18 18 self.ui = ui
19 19 self.repo = repo
20 20
21 21 # input/output/error streams
22 22 self.fin = fin
23 23 self.fout = fout
24 24 self.ferr = ferr
25 25
26 26 def run():
27 27 "run the command in sys.argv"
28 28 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
29 29
30 30 def dispatch(req):
31 31 "run the command specified in req.args"
32 32 if req.ferr:
33 33 ferr = req.ferr
34 34 elif req.ui:
35 35 ferr = req.ui.ferr
36 36 else:
37 37 ferr = sys.stderr
38 38
39 39 try:
40 40 if not req.ui:
41 41 req.ui = uimod.ui()
42 42 if '--traceback' in req.args:
43 43 req.ui.setconfig('ui', 'traceback', 'on')
44 44
45 45 # set ui streams from the request
46 46 if req.fin:
47 47 req.ui.fin = req.fin
48 48 if req.fout:
49 49 req.ui.fout = req.fout
50 50 if req.ferr:
51 51 req.ui.ferr = req.ferr
52 52 except util.Abort, inst:
53 53 ferr.write(_("abort: %s\n") % inst)
54 54 if inst.hint:
55 55 ferr.write(_("(%s)\n") % inst.hint)
56 56 return -1
57 57 except error.ParseError, inst:
58 58 if len(inst.args) > 1:
59 59 ferr.write(_("hg: parse error at %s: %s\n") %
60 60 (inst.args[1], inst.args[0]))
61 61 else:
62 62 ferr.write(_("hg: parse error: %s\n") % inst.args[0])
63 63 return -1
64 64
65 return _runcatch(req)
65 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
66 starttime = time.time()
67 ret = None
68 try:
69 ret = _runcatch(req)
70 return ret
71 finally:
72 duration = time.time() - starttime
73 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
74 msg, ret or 0, duration)
66 75
67 76 def _runcatch(req):
68 77 def catchterm(*args):
69 78 raise error.SignalInterrupt
70 79
71 80 ui = req.ui
72 81 try:
73 82 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
74 83 num = getattr(signal, name, None)
75 84 if num:
76 85 signal.signal(num, catchterm)
77 86 except ValueError:
78 87 pass # happens if called in a thread
79 88
80 89 try:
81 90 try:
82 91 # enter the debugger before command execution
83 92 if '--debugger' in req.args:
84 93 ui.warn(_("entering debugger - "
85 94 "type c to continue starting hg or h for help\n"))
86 95 pdb.set_trace()
87 96 try:
88 97 return _dispatch(req)
89 98 finally:
90 99 ui.flush()
91 100 except: # re-raises
92 101 # enter the debugger when we hit an exception
93 102 if '--debugger' in req.args:
94 103 traceback.print_exc()
95 104 pdb.post_mortem(sys.exc_info()[2])
96 105 ui.traceback()
97 106 raise
98 107
99 108 # Global exception handling, alphabetically
100 109 # Mercurial-specific first, followed by built-in and library exceptions
101 110 except error.AmbiguousCommand, inst:
102 111 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
103 112 (inst.args[0], " ".join(inst.args[1])))
104 113 except error.ParseError, inst:
105 114 if len(inst.args) > 1:
106 115 ui.warn(_("hg: parse error at %s: %s\n") %
107 116 (inst.args[1], inst.args[0]))
108 117 else:
109 118 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
110 119 return -1
111 120 except error.LockHeld, inst:
112 121 if inst.errno == errno.ETIMEDOUT:
113 122 reason = _('timed out waiting for lock held by %s') % inst.locker
114 123 else:
115 124 reason = _('lock held by %s') % inst.locker
116 125 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
117 126 except error.LockUnavailable, inst:
118 127 ui.warn(_("abort: could not lock %s: %s\n") %
119 128 (inst.desc or inst.filename, inst.strerror))
120 129 except error.CommandError, inst:
121 130 if inst.args[0]:
122 131 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
123 132 commands.help_(ui, inst.args[0], full=False, command=True)
124 133 else:
125 134 ui.warn(_("hg: %s\n") % inst.args[1])
126 135 commands.help_(ui, 'shortlist')
127 136 except error.OutOfBandError, inst:
128 137 ui.warn(_("abort: remote error:\n"))
129 138 ui.warn(''.join(inst.args))
130 139 except error.RepoError, inst:
131 140 ui.warn(_("abort: %s!\n") % inst)
132 141 if inst.hint:
133 142 ui.warn(_("(%s)\n") % inst.hint)
134 143 except error.ResponseError, inst:
135 144 ui.warn(_("abort: %s") % inst.args[0])
136 145 if not isinstance(inst.args[1], basestring):
137 146 ui.warn(" %r\n" % (inst.args[1],))
138 147 elif not inst.args[1]:
139 148 ui.warn(_(" empty string\n"))
140 149 else:
141 150 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
142 151 except error.RevlogError, inst:
143 152 ui.warn(_("abort: %s!\n") % inst)
144 153 except error.SignalInterrupt:
145 154 ui.warn(_("killed!\n"))
146 155 except error.UnknownCommand, inst:
147 156 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
148 157 try:
149 158 # check if the command is in a disabled extension
150 159 # (but don't check for extensions themselves)
151 160 commands.help_(ui, inst.args[0], unknowncmd=True)
152 161 except error.UnknownCommand:
153 162 commands.help_(ui, 'shortlist')
154 163 except error.InterventionRequired, inst:
155 164 ui.warn("%s\n" % inst)
156 165 return 1
157 166 except util.Abort, inst:
158 167 ui.warn(_("abort: %s\n") % inst)
159 168 if inst.hint:
160 169 ui.warn(_("(%s)\n") % inst.hint)
161 170 except ImportError, inst:
162 171 ui.warn(_("abort: %s!\n") % inst)
163 172 m = str(inst).split()[-1]
164 173 if m in "mpatch bdiff".split():
165 174 ui.warn(_("(did you forget to compile extensions?)\n"))
166 175 elif m in "zlib".split():
167 176 ui.warn(_("(is your Python install correct?)\n"))
168 177 except IOError, inst:
169 178 if util.safehasattr(inst, "code"):
170 179 ui.warn(_("abort: %s\n") % inst)
171 180 elif util.safehasattr(inst, "reason"):
172 181 try: # usually it is in the form (errno, strerror)
173 182 reason = inst.reason.args[1]
174 183 except (AttributeError, IndexError):
175 184 # it might be anything, for example a string
176 185 reason = inst.reason
177 186 ui.warn(_("abort: error: %s\n") % reason)
178 187 elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
179 188 if ui.debugflag:
180 189 ui.warn(_("broken pipe\n"))
181 190 elif getattr(inst, "strerror", None):
182 191 if getattr(inst, "filename", None):
183 192 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
184 193 else:
185 194 ui.warn(_("abort: %s\n") % inst.strerror)
186 195 else:
187 196 raise
188 197 except OSError, inst:
189 198 if getattr(inst, "filename", None) is not None:
190 199 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
191 200 else:
192 201 ui.warn(_("abort: %s\n") % inst.strerror)
193 202 except KeyboardInterrupt:
194 203 try:
195 204 ui.warn(_("interrupted!\n"))
196 205 except IOError, inst:
197 206 if inst.errno == errno.EPIPE:
198 207 if ui.debugflag:
199 208 ui.warn(_("\nbroken pipe\n"))
200 209 else:
201 210 raise
202 211 except MemoryError:
203 212 ui.warn(_("abort: out of memory\n"))
204 213 except SystemExit, inst:
205 214 # Commands shouldn't sys.exit directly, but give a return code.
206 215 # Just in case catch this and and pass exit code to caller.
207 216 return inst.code
208 217 except socket.error, inst:
209 218 ui.warn(_("abort: %s\n") % inst.args[-1])
210 219 except: # re-raises
211 220 myver = util.version()
212 221 # For compatibility checking, we discard the portion of the hg
213 222 # version after the + on the assumption that if a "normal
214 223 # user" is running a build with a + in it the packager
215 224 # probably built from fairly close to a tag and anyone with a
216 225 # 'make local' copy of hg (where the version number can be out
217 226 # of date) will be clueful enough to notice the implausible
218 227 # version number and try updating.
219 228 compare = myver.split('+')[0]
220 229 ct = tuplever(compare)
221 230 worst = None, ct, ''
222 231 for name, mod in extensions.extensions():
223 232 testedwith = getattr(mod, 'testedwith', '')
224 233 report = getattr(mod, 'buglink', _('the extension author.'))
225 234 if not testedwith.strip():
226 235 # We found an untested extension. It's likely the culprit.
227 236 worst = name, 'unknown', report
228 237 break
229 238 if compare not in testedwith.split() and testedwith != 'internal':
230 239 tested = [tuplever(v) for v in testedwith.split()]
231 240 lower = [t for t in tested if t < ct]
232 241 nearest = max(lower or tested)
233 242 if worst[0] is None or nearest < worst[1]:
234 243 worst = name, nearest, report
235 244 if worst[0] is not None:
236 245 name, testedwith, report = worst
237 246 if not isinstance(testedwith, str):
238 247 testedwith = '.'.join([str(c) for c in testedwith])
239 248 warning = (_('** Unknown exception encountered with '
240 249 'possibly-broken third-party extension %s\n'
241 250 '** which supports versions %s of Mercurial.\n'
242 251 '** Please disable %s and try your action again.\n'
243 252 '** If that fixes the bug please report it to %s\n')
244 253 % (name, testedwith, name, report))
245 254 else:
246 255 warning = (_("** unknown exception encountered, "
247 256 "please report by visiting\n") +
248 257 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
249 258 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
250 259 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
251 260 (_("** Extensions loaded: %s\n") %
252 261 ", ".join([x[0] for x in extensions.extensions()])))
253 262 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
254 263 ui.warn(warning)
255 264 raise
256 265
257 266 return -1
258 267
259 268 def tuplever(v):
260 269 try:
261 270 return tuple([int(i) for i in v.split('.')])
262 271 except ValueError:
263 272 return tuple()
264 273
265 274 def aliasargs(fn, givenargs):
266 275 args = getattr(fn, 'args', [])
267 276 if args:
268 277 cmd = ' '.join(map(util.shellquote, args))
269 278
270 279 nums = []
271 280 def replacer(m):
272 281 num = int(m.group(1)) - 1
273 282 nums.append(num)
274 283 if num < len(givenargs):
275 284 return givenargs[num]
276 285 raise util.Abort(_('too few arguments for command alias'))
277 286 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
278 287 givenargs = [x for i, x in enumerate(givenargs)
279 288 if i not in nums]
280 289 args = shlex.split(cmd)
281 290 return args + givenargs
282 291
283 292 class cmdalias(object):
284 293 def __init__(self, name, definition, cmdtable):
285 294 self.name = self.cmd = name
286 295 self.cmdname = ''
287 296 self.definition = definition
288 297 self.args = []
289 298 self.opts = []
290 299 self.help = ''
291 300 self.norepo = True
292 301 self.optionalrepo = False
293 302 self.badalias = False
294 303
295 304 try:
296 305 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
297 306 for alias, e in cmdtable.iteritems():
298 307 if e is entry:
299 308 self.cmd = alias
300 309 break
301 310 self.shadows = True
302 311 except error.UnknownCommand:
303 312 self.shadows = False
304 313
305 314 if not self.definition:
306 315 def fn(ui, *args):
307 316 ui.warn(_("no definition for alias '%s'\n") % self.name)
308 317 return 1
309 318 self.fn = fn
310 319 self.badalias = True
311 320 return
312 321
313 322 if self.definition.startswith('!'):
314 323 self.shell = True
315 324 def fn(ui, *args):
316 325 env = {'HG_ARGS': ' '.join((self.name,) + args)}
317 326 def _checkvar(m):
318 327 if m.groups()[0] == '$':
319 328 return m.group()
320 329 elif int(m.groups()[0]) <= len(args):
321 330 return m.group()
322 331 else:
323 332 ui.debug("No argument found for substitution "
324 333 "of %i variable in alias '%s' definition."
325 334 % (int(m.groups()[0]), self.name))
326 335 return ''
327 336 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
328 337 replace = dict((str(i + 1), arg) for i, arg in enumerate(args))
329 338 replace['0'] = self.name
330 339 replace['@'] = ' '.join(args)
331 340 cmd = util.interpolate(r'\$', replace, cmd, escape_prefix=True)
332 341 return util.system(cmd, environ=env, out=ui.fout)
333 342 self.fn = fn
334 343 return
335 344
336 345 args = shlex.split(self.definition)
337 346 self.cmdname = cmd = args.pop(0)
338 347 args = map(util.expandpath, args)
339 348
340 349 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
341 350 if _earlygetopt([invalidarg], args):
342 351 def fn(ui, *args):
343 352 ui.warn(_("error in definition for alias '%s': %s may only "
344 353 "be given on the command line\n")
345 354 % (self.name, invalidarg))
346 355 return 1
347 356
348 357 self.fn = fn
349 358 self.badalias = True
350 359 return
351 360
352 361 try:
353 362 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
354 363 if len(tableentry) > 2:
355 364 self.fn, self.opts, self.help = tableentry
356 365 else:
357 366 self.fn, self.opts = tableentry
358 367
359 368 self.args = aliasargs(self.fn, args)
360 369 if cmd not in commands.norepo.split(' '):
361 370 self.norepo = False
362 371 if cmd in commands.optionalrepo.split(' '):
363 372 self.optionalrepo = True
364 373 if self.help.startswith("hg " + cmd):
365 374 # drop prefix in old-style help lines so hg shows the alias
366 375 self.help = self.help[4 + len(cmd):]
367 376 self.__doc__ = self.fn.__doc__
368 377
369 378 except error.UnknownCommand:
370 379 def fn(ui, *args):
371 380 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
372 381 % (self.name, cmd))
373 382 try:
374 383 # check if the command is in a disabled extension
375 384 commands.help_(ui, cmd, unknowncmd=True)
376 385 except error.UnknownCommand:
377 386 pass
378 387 return 1
379 388 self.fn = fn
380 389 self.badalias = True
381 390 except error.AmbiguousCommand:
382 391 def fn(ui, *args):
383 392 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
384 393 % (self.name, cmd))
385 394 return 1
386 395 self.fn = fn
387 396 self.badalias = True
388 397
389 398 def __call__(self, ui, *args, **opts):
390 399 if self.shadows:
391 400 ui.debug("alias '%s' shadows command '%s'\n" %
392 401 (self.name, self.cmdname))
393 402
394 403 if util.safehasattr(self, 'shell'):
395 404 return self.fn(ui, *args, **opts)
396 405 else:
397 406 try:
398 407 util.checksignature(self.fn)(ui, *args, **opts)
399 408 except error.SignatureError:
400 409 args = ' '.join([self.cmdname] + self.args)
401 410 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
402 411 raise
403 412
404 413 def addaliases(ui, cmdtable):
405 414 # aliases are processed after extensions have been loaded, so they
406 415 # may use extension commands. Aliases can also use other alias definitions,
407 416 # but only if they have been defined prior to the current definition.
408 417 for alias, definition in ui.configitems('alias'):
409 418 aliasdef = cmdalias(alias, definition, cmdtable)
410 419
411 420 try:
412 421 olddef = cmdtable[aliasdef.cmd][0]
413 422 if olddef.definition == aliasdef.definition:
414 423 continue
415 424 except (KeyError, AttributeError):
416 425 # definition might not exist or it might not be a cmdalias
417 426 pass
418 427
419 428 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
420 429 if aliasdef.norepo:
421 430 commands.norepo += ' %s' % alias
422 431 if aliasdef.optionalrepo:
423 432 commands.optionalrepo += ' %s' % alias
424 433
425 434 def _parse(ui, args):
426 435 options = {}
427 436 cmdoptions = {}
428 437
429 438 try:
430 439 args = fancyopts.fancyopts(args, commands.globalopts, options)
431 440 except fancyopts.getopt.GetoptError, inst:
432 441 raise error.CommandError(None, inst)
433 442
434 443 if args:
435 444 cmd, args = args[0], args[1:]
436 445 aliases, entry = cmdutil.findcmd(cmd, commands.table,
437 446 ui.configbool("ui", "strict"))
438 447 cmd = aliases[0]
439 448 args = aliasargs(entry[0], args)
440 449 defaults = ui.config("defaults", cmd)
441 450 if defaults:
442 451 args = map(util.expandpath, shlex.split(defaults)) + args
443 452 c = list(entry[1])
444 453 else:
445 454 cmd = None
446 455 c = []
447 456
448 457 # combine global options into local
449 458 for o in commands.globalopts:
450 459 c.append((o[0], o[1], options[o[1]], o[3]))
451 460
452 461 try:
453 462 args = fancyopts.fancyopts(args, c, cmdoptions, True)
454 463 except fancyopts.getopt.GetoptError, inst:
455 464 raise error.CommandError(cmd, inst)
456 465
457 466 # separate global options back out
458 467 for o in commands.globalopts:
459 468 n = o[1]
460 469 options[n] = cmdoptions[n]
461 470 del cmdoptions[n]
462 471
463 472 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
464 473
465 474 def _parseconfig(ui, config):
466 475 """parse the --config options from the command line"""
467 476 configs = []
468 477
469 478 for cfg in config:
470 479 try:
471 480 name, value = cfg.split('=', 1)
472 481 section, name = name.split('.', 1)
473 482 if not section or not name:
474 483 raise IndexError
475 484 ui.setconfig(section, name, value)
476 485 configs.append((section, name, value))
477 486 except (IndexError, ValueError):
478 487 raise util.Abort(_('malformed --config option: %r '
479 488 '(use --config section.name=value)') % cfg)
480 489
481 490 return configs
482 491
483 492 def _earlygetopt(aliases, args):
484 493 """Return list of values for an option (or aliases).
485 494
486 495 The values are listed in the order they appear in args.
487 496 The options and values are removed from args.
488 497
489 498 >>> args = ['x', '--cwd', 'foo', 'y']
490 499 >>> _earlygetopt(['--cwd'], args), args
491 500 (['foo'], ['x', 'y'])
492 501
493 502 >>> args = ['x', '--cwd=bar', 'y']
494 503 >>> _earlygetopt(['--cwd'], args), args
495 504 (['bar'], ['x', 'y'])
496 505
497 506 >>> args = ['x', '-R', 'foo', 'y']
498 507 >>> _earlygetopt(['-R'], args), args
499 508 (['foo'], ['x', 'y'])
500 509
501 510 >>> args = ['x', '-Rbar', 'y']
502 511 >>> _earlygetopt(['-R'], args), args
503 512 (['bar'], ['x', 'y'])
504 513 """
505 514 try:
506 515 argcount = args.index("--")
507 516 except ValueError:
508 517 argcount = len(args)
509 518 shortopts = [opt for opt in aliases if len(opt) == 2]
510 519 values = []
511 520 pos = 0
512 521 while pos < argcount:
513 522 fullarg = arg = args[pos]
514 523 equals = arg.find('=')
515 524 if equals > -1:
516 525 arg = arg[:equals]
517 526 if arg in aliases:
518 527 del args[pos]
519 528 if equals > -1:
520 529 values.append(fullarg[equals + 1:])
521 530 argcount -= 1
522 531 else:
523 532 if pos + 1 >= argcount:
524 533 # ignore and let getopt report an error if there is no value
525 534 break
526 535 values.append(args.pop(pos))
527 536 argcount -= 2
528 537 elif arg[:2] in shortopts:
529 538 # short option can have no following space, e.g. hg log -Rfoo
530 539 values.append(args.pop(pos)[2:])
531 540 argcount -= 1
532 541 else:
533 542 pos += 1
534 543 return values
535 544
536 545 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
537 546 # run pre-hook, and abort if it fails
538 547 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
539 548 pats=cmdpats, opts=cmdoptions)
540 549 ret = _runcommand(ui, options, cmd, d)
541 550 # run post-hook, passing command result
542 551 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
543 552 result=ret, pats=cmdpats, opts=cmdoptions)
544 553 return ret
545 554
546 555 def _getlocal(ui, rpath):
547 556 """Return (path, local ui object) for the given target path.
548 557
549 558 Takes paths in [cwd]/.hg/hgrc into account."
550 559 """
551 560 try:
552 561 wd = os.getcwd()
553 562 except OSError, e:
554 563 raise util.Abort(_("error getting current working directory: %s") %
555 564 e.strerror)
556 565 path = cmdutil.findrepo(wd) or ""
557 566 if not path:
558 567 lui = ui
559 568 else:
560 569 lui = ui.copy()
561 570 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
562 571
563 572 if rpath and rpath[-1]:
564 573 path = lui.expandpath(rpath[-1])
565 574 lui = ui.copy()
566 575 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
567 576
568 577 return path, lui
569 578
570 579 def _checkshellalias(lui, ui, args):
571 580 options = {}
572 581
573 582 try:
574 583 args = fancyopts.fancyopts(args, commands.globalopts, options)
575 584 except fancyopts.getopt.GetoptError:
576 585 return
577 586
578 587 if not args:
579 588 return
580 589
581 590 norepo = commands.norepo
582 591 optionalrepo = commands.optionalrepo
583 592 def restorecommands():
584 593 commands.norepo = norepo
585 594 commands.optionalrepo = optionalrepo
586 595
587 596 cmdtable = commands.table.copy()
588 597 addaliases(lui, cmdtable)
589 598
590 599 cmd = args[0]
591 600 try:
592 601 aliases, entry = cmdutil.findcmd(cmd, cmdtable,
593 602 lui.configbool("ui", "strict"))
594 603 except (error.AmbiguousCommand, error.UnknownCommand):
595 604 restorecommands()
596 605 return
597 606
598 607 cmd = aliases[0]
599 608 fn = entry[0]
600 609
601 610 if cmd and util.safehasattr(fn, 'shell'):
602 611 d = lambda: fn(ui, *args[1:])
603 612 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
604 613 [], {})
605 614
606 615 restorecommands()
607 616
608 617 _loaded = set()
609 618 def _dispatch(req):
610 619 args = req.args
611 620 ui = req.ui
612 621
613 622 # read --config before doing anything else
614 623 # (e.g. to change trust settings for reading .hg/hgrc)
615 624 cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
616 625
617 626 # check for cwd
618 627 cwd = _earlygetopt(['--cwd'], args)
619 628 if cwd:
620 629 os.chdir(cwd[-1])
621 630
622 631 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
623 632 path, lui = _getlocal(ui, rpath)
624 633
625 634 # Now that we're operating in the right directory/repository with
626 635 # the right config settings, check for shell aliases
627 636 shellaliasfn = _checkshellalias(lui, ui, args)
628 637 if shellaliasfn:
629 638 return shellaliasfn()
630 639
631 640 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
632 641 # reposetup. Programs like TortoiseHg will call _dispatch several
633 642 # times so we keep track of configured extensions in _loaded.
634 643 extensions.loadall(lui)
635 644 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
636 645 # Propagate any changes to lui.__class__ by extensions
637 646 ui.__class__ = lui.__class__
638 647
639 648 # (uisetup and extsetup are handled in extensions.loadall)
640 649
641 650 for name, module in exts:
642 651 cmdtable = getattr(module, 'cmdtable', {})
643 652 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
644 653 if overrides:
645 654 ui.warn(_("extension '%s' overrides commands: %s\n")
646 655 % (name, " ".join(overrides)))
647 656 commands.table.update(cmdtable)
648 657 _loaded.add(name)
649 658
650 659 # (reposetup is handled in hg.repository)
651 660
652 661 addaliases(lui, commands.table)
653 662
654 663 # check for fallback encoding
655 664 fallback = lui.config('ui', 'fallbackencoding')
656 665 if fallback:
657 666 encoding.fallbackencoding = fallback
658 667
659 668 fullargs = args
660 669 cmd, func, args, options, cmdoptions = _parse(lui, args)
661 670
662 671 if options["config"]:
663 672 raise util.Abort(_("option --config may not be abbreviated!"))
664 673 if options["cwd"]:
665 674 raise util.Abort(_("option --cwd may not be abbreviated!"))
666 675 if options["repository"]:
667 676 raise util.Abort(_(
668 677 "option -R has to be separated from other options (e.g. not -qR) "
669 678 "and --repository may only be abbreviated as --repo!"))
670 679
671 680 if options["encoding"]:
672 681 encoding.encoding = options["encoding"]
673 682 if options["encodingmode"]:
674 683 encoding.encodingmode = options["encodingmode"]
675 684 if options["time"]:
676 685 def get_times():
677 686 t = os.times()
678 687 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
679 688 t = (t[0], t[1], t[2], t[3], time.clock())
680 689 return t
681 690 s = get_times()
682 691 def print_time():
683 692 t = get_times()
684 693 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
685 694 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
686 695 atexit.register(print_time)
687 696
688 697 uis = set([ui, lui])
689 698
690 699 if req.repo:
691 700 uis.add(req.repo.ui)
692 701
693 702 # copy configs that were passed on the cmdline (--config) to the repo ui
694 703 for cfg in cfgs:
695 704 req.repo.ui.setconfig(*cfg)
696 705
697 706 if options['verbose'] or options['debug'] or options['quiet']:
698 707 for opt in ('verbose', 'debug', 'quiet'):
699 708 val = str(bool(options[opt]))
700 709 for ui_ in uis:
701 710 ui_.setconfig('ui', opt, val)
702 711
703 712 if options['traceback']:
704 713 for ui_ in uis:
705 714 ui_.setconfig('ui', 'traceback', 'on')
706 715
707 716 if options['noninteractive']:
708 717 for ui_ in uis:
709 718 ui_.setconfig('ui', 'interactive', 'off')
710 719
711 720 if cmdoptions.get('insecure', False):
712 721 for ui_ in uis:
713 722 ui_.setconfig('web', 'cacerts', '')
714 723
715 724 if options['version']:
716 725 return commands.version_(ui)
717 726 if options['help']:
718 727 return commands.help_(ui, cmd)
719 728 elif not cmd:
720 729 return commands.help_(ui, 'shortlist')
721 730
722 731 repo = None
723 732 cmdpats = args[:]
724 733 if cmd not in commands.norepo.split():
725 734 # use the repo from the request only if we don't have -R
726 735 if not rpath and not cwd:
727 736 repo = req.repo
728 737
729 738 if repo:
730 739 # set the descriptors of the repo ui to those of ui
731 740 repo.ui.fin = ui.fin
732 741 repo.ui.fout = ui.fout
733 742 repo.ui.ferr = ui.ferr
734 743 else:
735 744 try:
736 745 repo = hg.repository(ui, path=path)
737 746 if not repo.local():
738 747 raise util.Abort(_("repository '%s' is not local") % path)
739 748 if options['hidden']:
740 749 repo = repo.unfiltered()
741 750 repo.ui.setconfig("bundle", "mainreporoot", repo.root)
742 751 except error.RequirementError:
743 752 raise
744 753 except error.RepoError:
745 754 if cmd not in commands.optionalrepo.split():
746 755 if (cmd in commands.inferrepo.split() and
747 756 args and not path): # try to infer -R from command args
748 757 repos = map(cmdutil.findrepo, args)
749 758 guess = repos[0]
750 759 if guess and repos.count(guess) == len(repos):
751 760 req.args = ['--repository', guess] + fullargs
752 761 return _dispatch(req)
753 762 if not path:
754 763 raise error.RepoError(_("no repository found in '%s'"
755 764 " (.hg not found)")
756 765 % os.getcwd())
757 766 raise
758 767 if repo:
759 768 ui = repo.ui
760 769 args.insert(0, repo)
761 770 elif rpath:
762 771 ui.warn(_("warning: --repository ignored\n"))
763 772
764 773 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
765 774 ui.log("command", '%s\n', msg)
766 775 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
767 starttime = time.time()
768 ret = None
769 776 try:
770 ret = runcommand(lui, repo, cmd, fullargs, ui, options, d,
777 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
771 778 cmdpats, cmdoptions)
772 return ret
773 779 finally:
774 duration = time.time() - starttime
775 ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
776 cmd, ret, duration)
777 780 if repo and repo != req.repo:
778 781 repo.close()
779 782
780 783 def lsprofile(ui, func, fp):
781 784 format = ui.config('profiling', 'format', default='text')
782 785 field = ui.config('profiling', 'sort', default='inlinetime')
783 786 limit = ui.configint('profiling', 'limit', default=30)
784 787 climit = ui.configint('profiling', 'nested', default=5)
785 788
786 789 if format not in ['text', 'kcachegrind']:
787 790 ui.warn(_("unrecognized profiling format '%s'"
788 791 " - Ignored\n") % format)
789 792 format = 'text'
790 793
791 794 try:
792 795 from mercurial import lsprof
793 796 except ImportError:
794 797 raise util.Abort(_(
795 798 'lsprof not available - install from '
796 799 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
797 800 p = lsprof.Profiler()
798 801 p.enable(subcalls=True)
799 802 try:
800 803 return func()
801 804 finally:
802 805 p.disable()
803 806
804 807 if format == 'kcachegrind':
805 808 import lsprofcalltree
806 809 calltree = lsprofcalltree.KCacheGrind(p)
807 810 calltree.output(fp)
808 811 else:
809 812 # format == 'text'
810 813 stats = lsprof.Stats(p.getstats())
811 814 stats.sort(field)
812 815 stats.pprint(limit=limit, file=fp, climit=climit)
813 816
814 817 def statprofile(ui, func, fp):
815 818 try:
816 819 import statprof
817 820 except ImportError:
818 821 raise util.Abort(_(
819 822 'statprof not available - install using "easy_install statprof"'))
820 823
821 824 freq = ui.configint('profiling', 'freq', default=1000)
822 825 if freq > 0:
823 826 statprof.reset(freq)
824 827 else:
825 828 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
826 829
827 830 statprof.start()
828 831 try:
829 832 return func()
830 833 finally:
831 834 statprof.stop()
832 835 statprof.display(fp)
833 836
834 837 def _runcommand(ui, options, cmd, cmdfunc):
835 838 def checkargs():
836 839 try:
837 840 return cmdfunc()
838 841 except error.SignatureError:
839 842 raise error.CommandError(cmd, _("invalid arguments"))
840 843
841 844 if options['profile']:
842 845 profiler = os.getenv('HGPROF')
843 846 if profiler is None:
844 847 profiler = ui.config('profiling', 'type', default='ls')
845 848 if profiler not in ('ls', 'stat'):
846 849 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
847 850 profiler = 'ls'
848 851
849 852 output = ui.config('profiling', 'output')
850 853
851 854 if output:
852 855 path = ui.expandpath(output)
853 856 fp = open(path, 'wb')
854 857 else:
855 858 fp = sys.stderr
856 859
857 860 try:
858 861 if profiler == 'ls':
859 862 return lsprofile(ui, checkargs, fp)
860 863 else:
861 864 return statprofile(ui, checkargs, fp)
862 865 finally:
863 866 if output:
864 867 fp.close()
865 868 else:
866 869 return checkargs()
@@ -1,154 +1,154
1 1 setup
2 2 $ cat > mock.py <<EOF
3 3 > from mercurial import util
4 4 >
5 5 > def makedate():
6 6 > return 0, 0
7 7 > def getuser():
8 8 > return 'bob'
9 9 > # mock the date and user apis so the output is always the same
10 10 > def uisetup(ui):
11 11 > util.makedate = makedate
12 12 > util.getuser = getuser
13 13 > EOF
14 14 $ cat >> $HGRCPATH <<EOF
15 15 > [extensions]
16 16 > blackbox=
17 17 > mock=`pwd`/mock.py
18 18 > mq=
19 19 > EOF
20 20 $ hg init blackboxtest
21 21 $ cd blackboxtest
22 22
23 23 command, exit codes, and duration
24 24
25 25 $ echo a > a
26 26 $ hg add a
27 27 $ hg blackbox
28 28 1970/01/01 00:00:00 bob> add a
29 1970/01/01 00:00:00 bob> add exited 0 after * seconds (glob)
29 1970/01/01 00:00:00 bob> add a exited 0 after * seconds (glob)
30 30
31 31 incoming change tracking
32 32
33 33 create two heads to verify that we only see one change in the log later
34 34 $ hg commit -ma
35 35 $ hg up null
36 36 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
37 37 $ echo b > b
38 38 $ hg commit -Amb
39 39 adding b
40 40 created new head
41 41
42 42 clone, commit, pull
43 43 $ hg clone . ../blackboxtest2
44 44 updating to branch default
45 45 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 46 $ echo c > c
47 47 $ hg commit -Amc
48 48 adding c
49 49 $ cd ../blackboxtest2
50 50 $ hg pull
51 51 pulling from $TESTTMP/blackboxtest (glob)
52 52 searching for changes
53 53 adding changesets
54 54 adding manifests
55 55 adding file changes
56 56 added 1 changesets with 1 changes to 1 files
57 57 (run 'hg update' to get a working copy)
58 58 $ hg blackbox -l 3
59 59 1970/01/01 00:00:00 bob> pull
60 60 1970/01/01 00:00:00 bob> 1 incoming changes - new heads: d02f48003e62
61 1970/01/01 00:00:00 bob> pull exited None after * seconds (glob)
61 1970/01/01 00:00:00 bob> pull exited 0 after * seconds (glob)
62 62
63 63 we must not cause a failure if we cannot write to the log
64 64
65 65 $ hg rollback
66 66 repository tip rolled back to revision 1 (undo pull)
67 67
68 68 #if unix-permissions
69 69 $ chmod 000 .hg/blackbox.log
70 70 $ hg --debug incoming
71 71 warning: cannot write to blackbox.log: Permission denied
72 72 comparing with $TESTTMP/blackboxtest (glob)
73 73 query 1; heads
74 74 searching for changes
75 75 all local heads known remotely
76 76 changeset: 2:d02f48003e62c24e2659d97d30f2a83abe5d5d51
77 77 tag: tip
78 78 phase: draft
79 79 parent: 1:6563da9dcf87b1949716e38ff3e3dfaa3198eb06
80 80 parent: -1:0000000000000000000000000000000000000000
81 81 manifest: 2:ab9d46b053ebf45b7996f2922b9893ff4b63d892
82 82 user: test
83 83 date: Thu Jan 01 00:00:00 1970 +0000
84 84 files+: c
85 85 extra: branch=default
86 86 description:
87 87 c
88 88
89 89
90 90 #endif
91 91 $ hg pull
92 92 pulling from $TESTTMP/blackboxtest (glob)
93 93 searching for changes
94 94 adding changesets
95 95 adding manifests
96 96 adding file changes
97 97 added 1 changesets with 1 changes to 1 files
98 98 (run 'hg update' to get a working copy)
99 99
100 100 a failure reading from the log is fine
101 101 #if unix-permissions
102 102 $ hg blackbox -l 3
103 103 abort: Permission denied: $TESTTMP/blackboxtest2/.hg/blackbox.log
104 104 [255]
105 105
106 106 $ chmod 600 .hg/blackbox.log
107 107 #endif
108 108
109 109 backup bundles get logged
110 110
111 111 $ touch d
112 112 $ hg commit -Amd
113 113 adding d
114 114 created new head
115 115 $ hg strip tip
116 116 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
117 117 saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
118 118 $ hg blackbox -l 3
119 119 1970/01/01 00:00:00 bob> strip tip
120 120 1970/01/01 00:00:00 bob> saved backup bundle to $TESTTMP/blackboxtest2/.hg/strip-backup/*-backup.hg (glob)
121 1970/01/01 00:00:00 bob> strip exited 0 after * seconds (glob)
121 1970/01/01 00:00:00 bob> strip tip exited 0 after * seconds (glob)
122 122
123 123 extension and python hooks - use the eol extension for a pythonhook
124 124
125 125 $ echo '[extensions]' >> .hg/hgrc
126 126 $ echo 'eol=' >> .hg/hgrc
127 127 $ echo '[hooks]' >> .hg/hgrc
128 128 $ echo 'update = echo hooked' >> .hg/hgrc
129 129 $ hg update
130 130 hooked
131 131 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
132 132 $ hg blackbox -l 4
133 133 1970/01/01 00:00:00 bob> update
134 134 1970/01/01 00:00:00 bob> pythonhook-preupdate: hgext.eol.preupdate finished in * seconds (glob)
135 135 1970/01/01 00:00:00 bob> exthook-update: echo hooked finished in * seconds (glob)
136 1970/01/01 00:00:00 bob> update exited False after * seconds (glob)
136 1970/01/01 00:00:00 bob> update exited 0 after * seconds (glob)
137 137
138 138 log rotation
139 139
140 140 $ echo '[blackbox]' >> .hg/hgrc
141 141 $ echo 'maxsize = 20 b' >> .hg/hgrc
142 142 $ echo 'maxfiles = 3' >> .hg/hgrc
143 143 $ hg status
144 144 $ hg status
145 145 $ hg status
146 146 $ hg tip -q
147 147 2:d02f48003e62
148 148 $ ls .hg/blackbox.log*
149 149 .hg/blackbox.log
150 150 .hg/blackbox.log.1
151 151 .hg/blackbox.log.2
152 152
153 153 cleanup
154 154 $ cd ..
@@ -1,210 +1,208
1 1
2 2 $ cat > loop.py <<EOF
3 3 > from mercurial import commands
4 4 >
5 5 > def loop(ui, loops, **opts):
6 6 > loops = int(loops)
7 7 > total = None
8 8 > if loops >= 0:
9 9 > total = loops
10 10 > if opts.get('total', None):
11 11 > total = int(opts.get('total'))
12 12 > nested = False
13 13 > if opts.get('nested', None):
14 14 > nested = True
15 15 > loops = abs(loops)
16 16 >
17 17 > for i in range(loops):
18 18 > ui.progress('loop', i, 'loop.%d' % i, 'loopnum', total)
19 19 > if opts.get('parallel'):
20 20 > ui.progress('other', i, 'other.%d' % i, 'othernum', total)
21 21 > if nested:
22 22 > for j in range(2):
23 23 > ui.progress('nested', j, 'nested.%d' % j, 'nestnum', 2)
24 24 > ui.progress('nested', None, 'nested.done', 'nestnum', 2)
25 25 > ui.progress('loop', None, 'loop.done', 'loopnum', total)
26 26 >
27 27 > commands.norepo += " loop"
28 28 >
29 29 > cmdtable = {
30 30 > "loop": (loop, [('', 'total', '', 'override for total'),
31 31 > ('', 'nested', False, 'show nested results'),
32 32 > ('', 'parallel', False, 'show parallel sets of results'),
33 33 > ],
34 34 > 'hg loop LOOPS'),
35 35 > }
36 36 > EOF
37 37
38 38 $ cp $HGRCPATH $HGRCPATH.orig
39 39 $ echo "[extensions]" >> $HGRCPATH
40 40 $ echo "progress=" >> $HGRCPATH
41 41 $ echo "loop=`pwd`/loop.py" >> $HGRCPATH
42 42 $ echo "[progress]" >> $HGRCPATH
43 43 $ echo "format = topic bar number" >> $HGRCPATH
44 44 $ echo "assume-tty=1" >> $HGRCPATH
45 45 $ echo "width=60" >> $HGRCPATH
46 46
47 47 test default params, display nothing because of delay
48 48
49 49 $ hg -y loop 3
50 50 $ echo "delay=0" >> $HGRCPATH
51 51 $ echo "refresh=0" >> $HGRCPATH
52 52
53 53 test with delay=0, refresh=0
54 54
55 55 $ hg -y loop 3
56 56 \r (no-eol) (esc)
57 57 loop [ ] 0/3\r (no-eol) (esc)
58 58 loop [===============> ] 1/3\r (no-eol) (esc)
59 59 loop [===============================> ] 2/3\r (no-eol) (esc)
60 60 \r (no-eol) (esc)
61 61
62 62
63 63 test nested short-lived topics (which shouldn't display with nestdelay):
64 64
65 65 $ hg -y loop 3 --nested
66 66 \r (no-eol) (esc)
67 67 loop [ ] 0/3\r (no-eol) (esc)
68 68 loop [===============> ] 1/3\r (no-eol) (esc)
69 69 loop [===============================> ] 2/3\r (no-eol) (esc)
70 70 \r (no-eol) (esc)
71 71
72 72
73 73 $ hg --config progress.changedelay=0 -y loop 3 --nested
74 74 \r (no-eol) (esc)
75 75 loop [ ] 0/3\r (no-eol) (esc)
76 76 nested [ ] 0/2\r (no-eol) (esc)
77 77 nested [======================> ] 1/2\r (no-eol) (esc)
78 78 loop [===============> ] 1/3\r (no-eol) (esc)
79 79 nested [ ] 0/2\r (no-eol) (esc)
80 80 nested [======================> ] 1/2\r (no-eol) (esc)
81 81 loop [===============================> ] 2/3\r (no-eol) (esc)
82 82 nested [ ] 0/2\r (no-eol) (esc)
83 83 nested [======================> ] 1/2\r (no-eol) (esc)
84 84 \r (no-eol) (esc)
85 85
86 86
87 87 test two topics being printed in parallel (as when we're doing a local
88 88 --pull clone, where you get the unbundle and bundle progress at the
89 89 same time):
90 90 $ hg loop 3 --parallel
91 91 \r (no-eol) (esc)
92 92 loop [ ] 0/3\r (no-eol) (esc)
93 93 loop [===============> ] 1/3\r (no-eol) (esc)
94 94 loop [===============================> ] 2/3\r (no-eol) (esc)
95 95 \r (no-eol) (esc)
96 96 test refresh is taken in account
97 97
98 98 $ hg -y --config progress.refresh=100 loop 3
99 99
100 100 test format options 1
101 101
102 102 $ hg -y --config 'progress.format=number topic item+2' loop 2
103 103 \r (no-eol) (esc)
104 104 0/2 loop lo\r (no-eol) (esc)
105 105 1/2 loop lo\r (no-eol) (esc)
106 106 \r (no-eol) (esc)
107 107
108 108 test format options 2
109 109
110 110 $ hg -y --config 'progress.format=number item-3 bar' loop 2
111 111 \r (no-eol) (esc)
112 112 0/2 p.0 [ ]\r (no-eol) (esc)
113 113 1/2 p.1 [=======================> ]\r (no-eol) (esc)
114 114 \r (no-eol) (esc)
115 115
116 116 test format options and indeterminate progress
117 117
118 118 $ hg -y --config 'progress.format=number item bar' loop -- -2
119 119 \r (no-eol) (esc)
120 120 0 loop.0 [ <=> ]\r (no-eol) (esc)
121 121 1 loop.1 [ <=> ]\r (no-eol) (esc)
122 122 \r (no-eol) (esc)
123 123
124 124 make sure things don't fall over if count > total
125 125
126 126 $ hg -y loop --total 4 6
127 127 \r (no-eol) (esc)
128 128 loop [ ] 0/4\r (no-eol) (esc)
129 129 loop [===========> ] 1/4\r (no-eol) (esc)
130 130 loop [=======================> ] 2/4\r (no-eol) (esc)
131 131 loop [===================================> ] 3/4\r (no-eol) (esc)
132 132 loop [===============================================>] 4/4\r (no-eol) (esc)
133 133 loop [ <=> ] 5/4\r (no-eol) (esc)
134 134 \r (no-eol) (esc)
135 135
136 136 test immediate progress completion
137 137
138 138 $ hg -y loop 0
139 139
140 140 test delay time estimates
141 141
142 142 $ cat > mocktime.py <<EOF
143 143 > import os
144 144 > import time
145 145 >
146 146 > class mocktime(object):
147 147 > def __init__(self, increment):
148 148 > self.time = 0
149 149 > self.increment = increment
150 150 > def __call__(self):
151 151 > self.time += self.increment
152 152 > return self.time
153 153 >
154 154 > def uisetup(ui):
155 155 > time.time = mocktime(int(os.environ.get('MOCKTIME', '11')))
156 156 > EOF
157 157
158 158 $ cp $HGRCPATH.orig $HGRCPATH
159 159 $ echo "[extensions]" >> $HGRCPATH
160 160 $ echo "mocktime=`pwd`/mocktime.py" >> $HGRCPATH
161 161 $ echo "progress=" >> $HGRCPATH
162 162 $ echo "loop=`pwd`/loop.py" >> $HGRCPATH
163 163 $ echo "[progress]" >> $HGRCPATH
164 164 $ echo "assume-tty=1" >> $HGRCPATH
165 165 $ echo "delay=25" >> $HGRCPATH
166 166 $ echo "width=60" >> $HGRCPATH
167 167
168 168 $ hg -y loop 8
169 169 \r (no-eol) (esc)
170 loop [====> ] 1/8 1m18s\r (no-eol) (esc)
171 170 loop [=========> ] 2/8 1m07s\r (no-eol) (esc)
172 171 loop [===============> ] 3/8 56s\r (no-eol) (esc)
173 172 loop [=====================> ] 4/8 45s\r (no-eol) (esc)
174 173 loop [==========================> ] 5/8 34s\r (no-eol) (esc)
175 174 loop [================================> ] 6/8 23s\r (no-eol) (esc)
176 175 loop [=====================================> ] 7/8 12s\r (no-eol) (esc)
177 176 \r (no-eol) (esc)
178 177
179 178 $ MOCKTIME=10000 hg -y loop 4
180 179 \r (no-eol) (esc)
181 180 loop [ ] 0/4\r (no-eol) (esc)
182 181 loop [=========> ] 1/4 8h21m\r (no-eol) (esc)
183 182 loop [====================> ] 2/4 5h34m\r (no-eol) (esc)
184 183 loop [==============================> ] 3/4 2h47m\r (no-eol) (esc)
185 184 \r (no-eol) (esc)
186 185
187 186 $ MOCKTIME=1000000 hg -y loop 4
188 187 \r (no-eol) (esc)
189 188 loop [ ] 0/4\r (no-eol) (esc)
190 189 loop [=========> ] 1/4 5w00d\r (no-eol) (esc)
191 190 loop [====================> ] 2/4 3w03d\r (no-eol) (esc)
192 191 loop [=============================> ] 3/4 11d14h\r (no-eol) (esc)
193 192 \r (no-eol) (esc)
194 193
195 194
196 195 $ MOCKTIME=14000000 hg -y loop 4
197 196 \r (no-eol) (esc)
198 197 loop [ ] 0/4\r (no-eol) (esc)
199 198 loop [=========> ] 1/4 1y18w\r (no-eol) (esc)
200 199 loop [===================> ] 2/4 46w03d\r (no-eol) (esc)
201 200 loop [=============================> ] 3/4 23w02d\r (no-eol) (esc)
202 201 \r (no-eol) (esc)
203 202
204 203 Time estimates should not fail when there's no end point:
205 204 $ hg -y loop -- -4
206 205 \r (no-eol) (esc)
207 loop [ <=> ] 1\r (no-eol) (esc)
208 206 loop [ <=> ] 2\r (no-eol) (esc)
209 207 loop [ <=> ] 3\r (no-eol) (esc)
210 208 \r (no-eol) (esc)
General Comments 0
You need to be logged in to leave comments. Login now