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