##// END OF EJS Templates
alias: keep error message in "badalias" so that help can see it...
Yuya Nishihara -
r22160:645457f7 default
parent child Browse files
Show More
@@ -1,929 +1,917 b''
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', '--traceback')
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 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
66 66 starttime = time.time()
67 67 ret = None
68 68 try:
69 69 ret = _runcatch(req)
70 70 return ret
71 71 finally:
72 72 duration = time.time() - starttime
73 73 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
74 74 msg, ret or 0, duration)
75 75
76 76 def _runcatch(req):
77 77 def catchterm(*args):
78 78 raise error.SignalInterrupt
79 79
80 80 ui = req.ui
81 81 try:
82 82 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
83 83 num = getattr(signal, name, None)
84 84 if num:
85 85 signal.signal(num, catchterm)
86 86 except ValueError:
87 87 pass # happens if called in a thread
88 88
89 89 try:
90 90 try:
91 91 debugger = 'pdb'
92 92 debugtrace = {
93 93 'pdb' : pdb.set_trace
94 94 }
95 95 debugmortem = {
96 96 'pdb' : pdb.post_mortem
97 97 }
98 98
99 99 # read --config before doing anything else
100 100 # (e.g. to change trust settings for reading .hg/hgrc)
101 101 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
102 102
103 103 if req.repo:
104 104 # copy configs that were passed on the cmdline (--config) to
105 105 # the repo ui
106 106 for sec, name, val in cfgs:
107 107 req.repo.ui.setconfig(sec, name, val, source='--config')
108 108
109 109 # if we are in HGPLAIN mode, then disable custom debugging
110 110 debugger = ui.config("ui", "debugger")
111 111 debugmod = pdb
112 112 if not debugger or ui.plain():
113 113 debugger = 'pdb'
114 114 elif '--debugger' in req.args:
115 115 # This import can be slow for fancy debuggers, so only
116 116 # do it when absolutely necessary, i.e. when actual
117 117 # debugging has been requested
118 118 try:
119 119 debugmod = __import__(debugger)
120 120 except ImportError:
121 121 pass # Leave debugmod = pdb
122 122
123 123 debugtrace[debugger] = debugmod.set_trace
124 124 debugmortem[debugger] = debugmod.post_mortem
125 125
126 126 # enter the debugger before command execution
127 127 if '--debugger' in req.args:
128 128 ui.warn(_("entering debugger - "
129 129 "type c to continue starting hg or h for help\n"))
130 130
131 131 if (debugger != 'pdb' and
132 132 debugtrace[debugger] == debugtrace['pdb']):
133 133 ui.warn(_("%s debugger specified "
134 134 "but its module was not found\n") % debugger)
135 135
136 136 debugtrace[debugger]()
137 137 try:
138 138 return _dispatch(req)
139 139 finally:
140 140 ui.flush()
141 141 except: # re-raises
142 142 # enter the debugger when we hit an exception
143 143 if '--debugger' in req.args:
144 144 traceback.print_exc()
145 145 debugmortem[debugger](sys.exc_info()[2])
146 146 ui.traceback()
147 147 raise
148 148
149 149 # Global exception handling, alphabetically
150 150 # Mercurial-specific first, followed by built-in and library exceptions
151 151 except error.AmbiguousCommand, inst:
152 152 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
153 153 (inst.args[0], " ".join(inst.args[1])))
154 154 except error.ParseError, inst:
155 155 if len(inst.args) > 1:
156 156 ui.warn(_("hg: parse error at %s: %s\n") %
157 157 (inst.args[1], inst.args[0]))
158 158 else:
159 159 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
160 160 return -1
161 161 except error.LockHeld, inst:
162 162 if inst.errno == errno.ETIMEDOUT:
163 163 reason = _('timed out waiting for lock held by %s') % inst.locker
164 164 else:
165 165 reason = _('lock held by %s') % inst.locker
166 166 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
167 167 except error.LockUnavailable, inst:
168 168 ui.warn(_("abort: could not lock %s: %s\n") %
169 169 (inst.desc or inst.filename, inst.strerror))
170 170 except error.CommandError, inst:
171 171 if inst.args[0]:
172 172 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
173 173 commands.help_(ui, inst.args[0], full=False, command=True)
174 174 else:
175 175 ui.warn(_("hg: %s\n") % inst.args[1])
176 176 commands.help_(ui, 'shortlist')
177 177 except error.OutOfBandError, inst:
178 178 ui.warn(_("abort: remote error:\n"))
179 179 ui.warn(''.join(inst.args))
180 180 except error.RepoError, inst:
181 181 ui.warn(_("abort: %s!\n") % inst)
182 182 if inst.hint:
183 183 ui.warn(_("(%s)\n") % inst.hint)
184 184 except error.ResponseError, inst:
185 185 ui.warn(_("abort: %s") % inst.args[0])
186 186 if not isinstance(inst.args[1], basestring):
187 187 ui.warn(" %r\n" % (inst.args[1],))
188 188 elif not inst.args[1]:
189 189 ui.warn(_(" empty string\n"))
190 190 else:
191 191 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
192 192 except error.RevlogError, inst:
193 193 ui.warn(_("abort: %s!\n") % inst)
194 194 except error.SignalInterrupt:
195 195 ui.warn(_("killed!\n"))
196 196 except error.UnknownCommand, inst:
197 197 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
198 198 try:
199 199 # check if the command is in a disabled extension
200 200 # (but don't check for extensions themselves)
201 201 commands.help_(ui, inst.args[0], unknowncmd=True)
202 202 except error.UnknownCommand:
203 203 commands.help_(ui, 'shortlist')
204 204 except error.InterventionRequired, inst:
205 205 ui.warn("%s\n" % inst)
206 206 return 1
207 207 except util.Abort, inst:
208 208 ui.warn(_("abort: %s\n") % inst)
209 209 if inst.hint:
210 210 ui.warn(_("(%s)\n") % inst.hint)
211 211 except ImportError, inst:
212 212 ui.warn(_("abort: %s!\n") % inst)
213 213 m = str(inst).split()[-1]
214 214 if m in "mpatch bdiff".split():
215 215 ui.warn(_("(did you forget to compile extensions?)\n"))
216 216 elif m in "zlib".split():
217 217 ui.warn(_("(is your Python install correct?)\n"))
218 218 except IOError, inst:
219 219 if util.safehasattr(inst, "code"):
220 220 ui.warn(_("abort: %s\n") % inst)
221 221 elif util.safehasattr(inst, "reason"):
222 222 try: # usually it is in the form (errno, strerror)
223 223 reason = inst.reason.args[1]
224 224 except (AttributeError, IndexError):
225 225 # it might be anything, for example a string
226 226 reason = inst.reason
227 227 ui.warn(_("abort: error: %s\n") % reason)
228 228 elif (util.safehasattr(inst, "args")
229 229 and inst.args and inst.args[0] == errno.EPIPE):
230 230 if ui.debugflag:
231 231 ui.warn(_("broken pipe\n"))
232 232 elif getattr(inst, "strerror", None):
233 233 if getattr(inst, "filename", None):
234 234 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
235 235 else:
236 236 ui.warn(_("abort: %s\n") % inst.strerror)
237 237 else:
238 238 raise
239 239 except OSError, inst:
240 240 if getattr(inst, "filename", None) is not None:
241 241 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
242 242 else:
243 243 ui.warn(_("abort: %s\n") % inst.strerror)
244 244 except KeyboardInterrupt:
245 245 try:
246 246 ui.warn(_("interrupted!\n"))
247 247 except IOError, inst:
248 248 if inst.errno == errno.EPIPE:
249 249 if ui.debugflag:
250 250 ui.warn(_("\nbroken pipe\n"))
251 251 else:
252 252 raise
253 253 except MemoryError:
254 254 ui.warn(_("abort: out of memory\n"))
255 255 except SystemExit, inst:
256 256 # Commands shouldn't sys.exit directly, but give a return code.
257 257 # Just in case catch this and and pass exit code to caller.
258 258 return inst.code
259 259 except socket.error, inst:
260 260 ui.warn(_("abort: %s\n") % inst.args[-1])
261 261 except: # re-raises
262 262 myver = util.version()
263 263 # For compatibility checking, we discard the portion of the hg
264 264 # version after the + on the assumption that if a "normal
265 265 # user" is running a build with a + in it the packager
266 266 # probably built from fairly close to a tag and anyone with a
267 267 # 'make local' copy of hg (where the version number can be out
268 268 # of date) will be clueful enough to notice the implausible
269 269 # version number and try updating.
270 270 compare = myver.split('+')[0]
271 271 ct = tuplever(compare)
272 272 worst = None, ct, ''
273 273 for name, mod in extensions.extensions():
274 274 testedwith = getattr(mod, 'testedwith', '')
275 275 report = getattr(mod, 'buglink', _('the extension author.'))
276 276 if not testedwith.strip():
277 277 # We found an untested extension. It's likely the culprit.
278 278 worst = name, 'unknown', report
279 279 break
280 280 if compare not in testedwith.split() and testedwith != 'internal':
281 281 tested = [tuplever(v) for v in testedwith.split()]
282 282 lower = [t for t in tested if t < ct]
283 283 nearest = max(lower or tested)
284 284 if worst[0] is None or nearest < worst[1]:
285 285 worst = name, nearest, report
286 286 if worst[0] is not None:
287 287 name, testedwith, report = worst
288 288 if not isinstance(testedwith, str):
289 289 testedwith = '.'.join([str(c) for c in testedwith])
290 290 warning = (_('** Unknown exception encountered with '
291 291 'possibly-broken third-party extension %s\n'
292 292 '** which supports versions %s of Mercurial.\n'
293 293 '** Please disable %s and try your action again.\n'
294 294 '** If that fixes the bug please report it to %s\n')
295 295 % (name, testedwith, name, report))
296 296 else:
297 297 warning = (_("** unknown exception encountered, "
298 298 "please report by visiting\n") +
299 299 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
300 300 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
301 301 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
302 302 (_("** Extensions loaded: %s\n") %
303 303 ", ".join([x[0] for x in extensions.extensions()])))
304 304 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
305 305 ui.warn(warning)
306 306 raise
307 307
308 308 return -1
309 309
310 310 def tuplever(v):
311 311 try:
312 312 return tuple([int(i) for i in v.split('.')])
313 313 except ValueError:
314 314 return tuple()
315 315
316 316 def aliasargs(fn, givenargs):
317 317 args = getattr(fn, 'args', [])
318 318 if args:
319 319 cmd = ' '.join(map(util.shellquote, args))
320 320
321 321 nums = []
322 322 def replacer(m):
323 323 num = int(m.group(1)) - 1
324 324 nums.append(num)
325 325 if num < len(givenargs):
326 326 return givenargs[num]
327 327 raise util.Abort(_('too few arguments for command alias'))
328 328 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
329 329 givenargs = [x for i, x in enumerate(givenargs)
330 330 if i not in nums]
331 331 args = shlex.split(cmd)
332 332 return args + givenargs
333 333
334 334 def aliasinterpolate(name, args, cmd):
335 335 '''interpolate args into cmd for shell aliases
336 336
337 337 This also handles $0, $@ and "$@".
338 338 '''
339 339 # util.interpolate can't deal with "$@" (with quotes) because it's only
340 340 # built to match prefix + patterns.
341 341 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
342 342 replacemap['$0'] = name
343 343 replacemap['$$'] = '$'
344 344 replacemap['$@'] = ' '.join(args)
345 345 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
346 346 # parameters, separated out into words. Emulate the same behavior here by
347 347 # quoting the arguments individually. POSIX shells will then typically
348 348 # tokenize each argument into exactly one word.
349 349 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
350 350 # escape '\$' for regex
351 351 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
352 352 r = re.compile(regex)
353 353 return r.sub(lambda x: replacemap[x.group()], cmd)
354 354
355 355 class cmdalias(object):
356 356 def __init__(self, name, definition, cmdtable):
357 357 self.name = self.cmd = name
358 358 self.cmdname = ''
359 359 self.definition = definition
360 self.fn = None
360 361 self.args = []
361 362 self.opts = []
362 363 self.help = ''
363 364 self.norepo = True
364 365 self.optionalrepo = False
365 self.badalias = False
366 self.badalias = None
366 367
367 368 try:
368 369 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
369 370 for alias, e in cmdtable.iteritems():
370 371 if e is entry:
371 372 self.cmd = alias
372 373 break
373 374 self.shadows = True
374 375 except error.UnknownCommand:
375 376 self.shadows = False
376 377
377 378 if not self.definition:
378 def fn(ui, *args):
379 ui.warn(_("no definition for alias '%s'\n") % self.name)
380 return -1
381 self.fn = fn
382 self.badalias = True
379 self.badalias = _("no definition for alias '%s'") % self.name
383 380 return
384 381
385 382 if self.definition.startswith('!'):
386 383 self.shell = True
387 384 def fn(ui, *args):
388 385 env = {'HG_ARGS': ' '.join((self.name,) + args)}
389 386 def _checkvar(m):
390 387 if m.groups()[0] == '$':
391 388 return m.group()
392 389 elif int(m.groups()[0]) <= len(args):
393 390 return m.group()
394 391 else:
395 392 ui.debug("No argument found for substitution "
396 393 "of %i variable in alias '%s' definition."
397 394 % (int(m.groups()[0]), self.name))
398 395 return ''
399 396 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
400 397 cmd = aliasinterpolate(self.name, args, cmd)
401 398 return util.system(cmd, environ=env, out=ui.fout)
402 399 self.fn = fn
403 400 return
404 401
405 402 try:
406 403 args = shlex.split(self.definition)
407 404 except ValueError, inst:
408 def fn(ui, *args):
409 ui.warn(_("error in definition for alias '%s': %s\n")
405 self.badalias = (_("error in definition for alias '%s': %s")
410 406 % (self.name, inst))
411 return -1
412 self.fn = fn
413 self.badalias = True
414 407 return
415 408 self.cmdname = cmd = args.pop(0)
416 409 args = map(util.expandpath, args)
417 410
418 411 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
419 412 if _earlygetopt([invalidarg], args):
420 def fn(ui, *args):
421 ui.warn(_("error in definition for alias '%s': %s may only "
422 "be given on the command line\n")
413 self.badalias = (_("error in definition for alias '%s': %s may "
414 "only be given on the command line")
423 415 % (self.name, invalidarg))
424 return -1
425
426 self.fn = fn
427 self.badalias = True
428 416 return
429 417
430 418 try:
431 419 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
432 420 if len(tableentry) > 2:
433 421 self.fn, self.opts, self.help = tableentry
434 422 else:
435 423 self.fn, self.opts = tableentry
436 424
437 425 self.args = aliasargs(self.fn, args)
438 426 if cmd not in commands.norepo.split(' '):
439 427 self.norepo = False
440 428 if cmd in commands.optionalrepo.split(' '):
441 429 self.optionalrepo = True
442 430 if self.help.startswith("hg " + cmd):
443 431 # drop prefix in old-style help lines so hg shows the alias
444 432 self.help = self.help[4 + len(cmd):]
445 433 self.__doc__ = self.fn.__doc__
446 434
447 435 except error.UnknownCommand:
448 436 def fn(ui, *args):
449 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
450 % (self.name, cmd))
451 437 try:
452 438 # check if the command is in a disabled extension
453 439 commands.help_(ui, cmd, unknowncmd=True)
454 440 except error.UnknownCommand:
455 441 pass
456 442 return -1
457 443 self.fn = fn
458 self.badalias = True
444 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
445 % (self.name, cmd))
459 446 except error.AmbiguousCommand:
460 def fn(ui, *args):
461 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
447 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
462 448 % (self.name, cmd))
463 return -1
464 self.fn = fn
465 self.badalias = True
466 449
467 450 def __call__(self, ui, *args, **opts):
451 if self.badalias:
452 ui.warn(self.badalias + '\n')
453 if self.fn:
454 return self.fn(ui, *args, **opts)
455 return -1
468 456 if self.shadows:
469 457 ui.debug("alias '%s' shadows command '%s'\n" %
470 458 (self.name, self.cmdname))
471 459
472 460 if util.safehasattr(self, 'shell'):
473 461 return self.fn(ui, *args, **opts)
474 462 else:
475 463 try:
476 464 return util.checksignature(self.fn)(ui, *args, **opts)
477 465 except error.SignatureError:
478 466 args = ' '.join([self.cmdname] + self.args)
479 467 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
480 468 raise
481 469
482 470 def addaliases(ui, cmdtable):
483 471 # aliases are processed after extensions have been loaded, so they
484 472 # may use extension commands. Aliases can also use other alias definitions,
485 473 # but only if they have been defined prior to the current definition.
486 474 for alias, definition in ui.configitems('alias'):
487 475 aliasdef = cmdalias(alias, definition, cmdtable)
488 476
489 477 try:
490 478 olddef = cmdtable[aliasdef.cmd][0]
491 479 if olddef.definition == aliasdef.definition:
492 480 continue
493 481 except (KeyError, AttributeError):
494 482 # definition might not exist or it might not be a cmdalias
495 483 pass
496 484
497 485 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
498 486 if aliasdef.norepo:
499 487 commands.norepo += ' %s' % alias
500 488 if aliasdef.optionalrepo:
501 489 commands.optionalrepo += ' %s' % alias
502 490
503 491 def _parse(ui, args):
504 492 options = {}
505 493 cmdoptions = {}
506 494
507 495 try:
508 496 args = fancyopts.fancyopts(args, commands.globalopts, options)
509 497 except fancyopts.getopt.GetoptError, inst:
510 498 raise error.CommandError(None, inst)
511 499
512 500 if args:
513 501 cmd, args = args[0], args[1:]
514 502 aliases, entry = cmdutil.findcmd(cmd, commands.table,
515 503 ui.configbool("ui", "strict"))
516 504 cmd = aliases[0]
517 505 args = aliasargs(entry[0], args)
518 506 defaults = ui.config("defaults", cmd)
519 507 if defaults:
520 508 args = map(util.expandpath, shlex.split(defaults)) + args
521 509 c = list(entry[1])
522 510 else:
523 511 cmd = None
524 512 c = []
525 513
526 514 # combine global options into local
527 515 for o in commands.globalopts:
528 516 c.append((o[0], o[1], options[o[1]], o[3]))
529 517
530 518 try:
531 519 args = fancyopts.fancyopts(args, c, cmdoptions, True)
532 520 except fancyopts.getopt.GetoptError, inst:
533 521 raise error.CommandError(cmd, inst)
534 522
535 523 # separate global options back out
536 524 for o in commands.globalopts:
537 525 n = o[1]
538 526 options[n] = cmdoptions[n]
539 527 del cmdoptions[n]
540 528
541 529 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
542 530
543 531 def _parseconfig(ui, config):
544 532 """parse the --config options from the command line"""
545 533 configs = []
546 534
547 535 for cfg in config:
548 536 try:
549 537 name, value = cfg.split('=', 1)
550 538 section, name = name.split('.', 1)
551 539 if not section or not name:
552 540 raise IndexError
553 541 ui.setconfig(section, name, value, '--config')
554 542 configs.append((section, name, value))
555 543 except (IndexError, ValueError):
556 544 raise util.Abort(_('malformed --config option: %r '
557 545 '(use --config section.name=value)') % cfg)
558 546
559 547 return configs
560 548
561 549 def _earlygetopt(aliases, args):
562 550 """Return list of values for an option (or aliases).
563 551
564 552 The values are listed in the order they appear in args.
565 553 The options and values are removed from args.
566 554
567 555 >>> args = ['x', '--cwd', 'foo', 'y']
568 556 >>> _earlygetopt(['--cwd'], args), args
569 557 (['foo'], ['x', 'y'])
570 558
571 559 >>> args = ['x', '--cwd=bar', 'y']
572 560 >>> _earlygetopt(['--cwd'], args), args
573 561 (['bar'], ['x', 'y'])
574 562
575 563 >>> args = ['x', '-R', 'foo', 'y']
576 564 >>> _earlygetopt(['-R'], args), args
577 565 (['foo'], ['x', 'y'])
578 566
579 567 >>> args = ['x', '-Rbar', 'y']
580 568 >>> _earlygetopt(['-R'], args), args
581 569 (['bar'], ['x', 'y'])
582 570 """
583 571 try:
584 572 argcount = args.index("--")
585 573 except ValueError:
586 574 argcount = len(args)
587 575 shortopts = [opt for opt in aliases if len(opt) == 2]
588 576 values = []
589 577 pos = 0
590 578 while pos < argcount:
591 579 fullarg = arg = args[pos]
592 580 equals = arg.find('=')
593 581 if equals > -1:
594 582 arg = arg[:equals]
595 583 if arg in aliases:
596 584 del args[pos]
597 585 if equals > -1:
598 586 values.append(fullarg[equals + 1:])
599 587 argcount -= 1
600 588 else:
601 589 if pos + 1 >= argcount:
602 590 # ignore and let getopt report an error if there is no value
603 591 break
604 592 values.append(args.pop(pos))
605 593 argcount -= 2
606 594 elif arg[:2] in shortopts:
607 595 # short option can have no following space, e.g. hg log -Rfoo
608 596 values.append(args.pop(pos)[2:])
609 597 argcount -= 1
610 598 else:
611 599 pos += 1
612 600 return values
613 601
614 602 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
615 603 # run pre-hook, and abort if it fails
616 604 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
617 605 pats=cmdpats, opts=cmdoptions)
618 606 ret = _runcommand(ui, options, cmd, d)
619 607 # run post-hook, passing command result
620 608 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
621 609 result=ret, pats=cmdpats, opts=cmdoptions)
622 610 return ret
623 611
624 612 def _getlocal(ui, rpath):
625 613 """Return (path, local ui object) for the given target path.
626 614
627 615 Takes paths in [cwd]/.hg/hgrc into account."
628 616 """
629 617 try:
630 618 wd = os.getcwd()
631 619 except OSError, e:
632 620 raise util.Abort(_("error getting current working directory: %s") %
633 621 e.strerror)
634 622 path = cmdutil.findrepo(wd) or ""
635 623 if not path:
636 624 lui = ui
637 625 else:
638 626 lui = ui.copy()
639 627 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
640 628
641 629 if rpath and rpath[-1]:
642 630 path = lui.expandpath(rpath[-1])
643 631 lui = ui.copy()
644 632 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
645 633
646 634 return path, lui
647 635
648 636 def _checkshellalias(lui, ui, args):
649 637 options = {}
650 638
651 639 try:
652 640 args = fancyopts.fancyopts(args, commands.globalopts, options)
653 641 except fancyopts.getopt.GetoptError:
654 642 return
655 643
656 644 if not args:
657 645 return
658 646
659 647 norepo = commands.norepo
660 648 optionalrepo = commands.optionalrepo
661 649 def restorecommands():
662 650 commands.norepo = norepo
663 651 commands.optionalrepo = optionalrepo
664 652
665 653 cmdtable = commands.table.copy()
666 654 addaliases(lui, cmdtable)
667 655
668 656 cmd = args[0]
669 657 try:
670 658 aliases, entry = cmdutil.findcmd(cmd, cmdtable)
671 659 except (error.AmbiguousCommand, error.UnknownCommand):
672 660 restorecommands()
673 661 return
674 662
675 663 cmd = aliases[0]
676 664 fn = entry[0]
677 665
678 666 if cmd and util.safehasattr(fn, 'shell'):
679 667 d = lambda: fn(ui, *args[1:])
680 668 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
681 669 [], {})
682 670
683 671 restorecommands()
684 672
685 673 _loaded = set()
686 674 def _dispatch(req):
687 675 args = req.args
688 676 ui = req.ui
689 677
690 678 # check for cwd
691 679 cwd = _earlygetopt(['--cwd'], args)
692 680 if cwd:
693 681 os.chdir(cwd[-1])
694 682
695 683 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
696 684 path, lui = _getlocal(ui, rpath)
697 685
698 686 # Now that we're operating in the right directory/repository with
699 687 # the right config settings, check for shell aliases
700 688 shellaliasfn = _checkshellalias(lui, ui, args)
701 689 if shellaliasfn:
702 690 return shellaliasfn()
703 691
704 692 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
705 693 # reposetup. Programs like TortoiseHg will call _dispatch several
706 694 # times so we keep track of configured extensions in _loaded.
707 695 extensions.loadall(lui)
708 696 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
709 697 # Propagate any changes to lui.__class__ by extensions
710 698 ui.__class__ = lui.__class__
711 699
712 700 # (uisetup and extsetup are handled in extensions.loadall)
713 701
714 702 for name, module in exts:
715 703 cmdtable = getattr(module, 'cmdtable', {})
716 704 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
717 705 if overrides:
718 706 ui.warn(_("extension '%s' overrides commands: %s\n")
719 707 % (name, " ".join(overrides)))
720 708 commands.table.update(cmdtable)
721 709 _loaded.add(name)
722 710
723 711 # (reposetup is handled in hg.repository)
724 712
725 713 addaliases(lui, commands.table)
726 714
727 715 # check for fallback encoding
728 716 fallback = lui.config('ui', 'fallbackencoding')
729 717 if fallback:
730 718 encoding.fallbackencoding = fallback
731 719
732 720 fullargs = args
733 721 cmd, func, args, options, cmdoptions = _parse(lui, args)
734 722
735 723 if options["config"]:
736 724 raise util.Abort(_("option --config may not be abbreviated!"))
737 725 if options["cwd"]:
738 726 raise util.Abort(_("option --cwd may not be abbreviated!"))
739 727 if options["repository"]:
740 728 raise util.Abort(_(
741 729 "option -R has to be separated from other options (e.g. not -qR) "
742 730 "and --repository may only be abbreviated as --repo!"))
743 731
744 732 if options["encoding"]:
745 733 encoding.encoding = options["encoding"]
746 734 if options["encodingmode"]:
747 735 encoding.encodingmode = options["encodingmode"]
748 736 if options["time"]:
749 737 def get_times():
750 738 t = os.times()
751 739 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
752 740 t = (t[0], t[1], t[2], t[3], time.clock())
753 741 return t
754 742 s = get_times()
755 743 def print_time():
756 744 t = get_times()
757 745 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
758 746 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
759 747 atexit.register(print_time)
760 748
761 749 uis = set([ui, lui])
762 750
763 751 if req.repo:
764 752 uis.add(req.repo.ui)
765 753
766 754 if options['verbose'] or options['debug'] or options['quiet']:
767 755 for opt in ('verbose', 'debug', 'quiet'):
768 756 val = str(bool(options[opt]))
769 757 for ui_ in uis:
770 758 ui_.setconfig('ui', opt, val, '--' + opt)
771 759
772 760 if options['traceback']:
773 761 for ui_ in uis:
774 762 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
775 763
776 764 if options['noninteractive']:
777 765 for ui_ in uis:
778 766 ui_.setconfig('ui', 'interactive', 'off', '-y')
779 767
780 768 if cmdoptions.get('insecure', False):
781 769 for ui_ in uis:
782 770 ui_.setconfig('web', 'cacerts', '', '--insecure')
783 771
784 772 if options['version']:
785 773 return commands.version_(ui)
786 774 if options['help']:
787 775 return commands.help_(ui, cmd, command=True)
788 776 elif not cmd:
789 777 return commands.help_(ui, 'shortlist')
790 778
791 779 repo = None
792 780 cmdpats = args[:]
793 781 if cmd not in commands.norepo.split():
794 782 # use the repo from the request only if we don't have -R
795 783 if not rpath and not cwd:
796 784 repo = req.repo
797 785
798 786 if repo:
799 787 # set the descriptors of the repo ui to those of ui
800 788 repo.ui.fin = ui.fin
801 789 repo.ui.fout = ui.fout
802 790 repo.ui.ferr = ui.ferr
803 791 else:
804 792 try:
805 793 repo = hg.repository(ui, path=path)
806 794 if not repo.local():
807 795 raise util.Abort(_("repository '%s' is not local") % path)
808 796 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
809 797 except error.RequirementError:
810 798 raise
811 799 except error.RepoError:
812 800 if cmd not in commands.optionalrepo.split():
813 801 if (cmd in commands.inferrepo.split() and
814 802 args and not path): # try to infer -R from command args
815 803 repos = map(cmdutil.findrepo, args)
816 804 guess = repos[0]
817 805 if guess and repos.count(guess) == len(repos):
818 806 req.args = ['--repository', guess] + fullargs
819 807 return _dispatch(req)
820 808 if not path:
821 809 raise error.RepoError(_("no repository found in '%s'"
822 810 " (.hg not found)")
823 811 % os.getcwd())
824 812 raise
825 813 if repo:
826 814 ui = repo.ui
827 815 if options['hidden']:
828 816 repo = repo.unfiltered()
829 817 args.insert(0, repo)
830 818 elif rpath:
831 819 ui.warn(_("warning: --repository ignored\n"))
832 820
833 821 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
834 822 ui.log("command", '%s\n', msg)
835 823 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
836 824 try:
837 825 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
838 826 cmdpats, cmdoptions)
839 827 finally:
840 828 if repo and repo != req.repo:
841 829 repo.close()
842 830
843 831 def lsprofile(ui, func, fp):
844 832 format = ui.config('profiling', 'format', default='text')
845 833 field = ui.config('profiling', 'sort', default='inlinetime')
846 834 limit = ui.configint('profiling', 'limit', default=30)
847 835 climit = ui.configint('profiling', 'nested', default=5)
848 836
849 837 if format not in ['text', 'kcachegrind']:
850 838 ui.warn(_("unrecognized profiling format '%s'"
851 839 " - Ignored\n") % format)
852 840 format = 'text'
853 841
854 842 try:
855 843 from mercurial import lsprof
856 844 except ImportError:
857 845 raise util.Abort(_(
858 846 'lsprof not available - install from '
859 847 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
860 848 p = lsprof.Profiler()
861 849 p.enable(subcalls=True)
862 850 try:
863 851 return func()
864 852 finally:
865 853 p.disable()
866 854
867 855 if format == 'kcachegrind':
868 856 import lsprofcalltree
869 857 calltree = lsprofcalltree.KCacheGrind(p)
870 858 calltree.output(fp)
871 859 else:
872 860 # format == 'text'
873 861 stats = lsprof.Stats(p.getstats())
874 862 stats.sort(field)
875 863 stats.pprint(limit=limit, file=fp, climit=climit)
876 864
877 865 def statprofile(ui, func, fp):
878 866 try:
879 867 import statprof
880 868 except ImportError:
881 869 raise util.Abort(_(
882 870 'statprof not available - install using "easy_install statprof"'))
883 871
884 872 freq = ui.configint('profiling', 'freq', default=1000)
885 873 if freq > 0:
886 874 statprof.reset(freq)
887 875 else:
888 876 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
889 877
890 878 statprof.start()
891 879 try:
892 880 return func()
893 881 finally:
894 882 statprof.stop()
895 883 statprof.display(fp)
896 884
897 885 def _runcommand(ui, options, cmd, cmdfunc):
898 886 def checkargs():
899 887 try:
900 888 return cmdfunc()
901 889 except error.SignatureError:
902 890 raise error.CommandError(cmd, _("invalid arguments"))
903 891
904 892 if options['profile']:
905 893 profiler = os.getenv('HGPROF')
906 894 if profiler is None:
907 895 profiler = ui.config('profiling', 'type', default='ls')
908 896 if profiler not in ('ls', 'stat'):
909 897 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
910 898 profiler = 'ls'
911 899
912 900 output = ui.config('profiling', 'output')
913 901
914 902 if output:
915 903 path = ui.expandpath(output)
916 904 fp = open(path, 'wb')
917 905 else:
918 906 fp = sys.stderr
919 907
920 908 try:
921 909 if profiler == 'ls':
922 910 return lsprofile(ui, checkargs, fp)
923 911 else:
924 912 return statprofile(ui, checkargs, fp)
925 913 finally:
926 914 if output:
927 915 fp.close()
928 916 else:
929 917 return checkargs()
@@ -1,508 +1,508 b''
1 1 # help.py - help data for mercurial
2 2 #
3 3 # Copyright 2006 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 gettext, _
9 9 import itertools, sys, os
10 10 import error
11 11 import extensions, revset, fileset, templatekw, templatefilters, filemerge
12 12 import encoding, util, minirst
13 13 import cmdutil
14 14
15 15 def listexts(header, exts, indent=1, showdeprecated=False):
16 16 '''return a text listing of the given extensions'''
17 17 rst = []
18 18 if exts:
19 19 rst.append('\n%s\n\n' % header)
20 20 for name, desc in sorted(exts.iteritems()):
21 21 if '(DEPRECATED)' in desc and not showdeprecated:
22 22 continue
23 23 rst.append('%s:%s: %s\n' % (' ' * indent, name, desc))
24 24 return rst
25 25
26 26 def extshelp():
27 27 rst = loaddoc('extensions')().splitlines(True)
28 28 rst.extend(listexts(
29 29 _('enabled extensions:'), extensions.enabled(), showdeprecated=True))
30 30 rst.extend(listexts(_('disabled extensions:'), extensions.disabled()))
31 31 doc = ''.join(rst)
32 32 return doc
33 33
34 34 def optrst(header, options, verbose):
35 35 data = []
36 36 multioccur = False
37 37 for option in options:
38 38 if len(option) == 5:
39 39 shortopt, longopt, default, desc, optlabel = option
40 40 else:
41 41 shortopt, longopt, default, desc = option
42 42 optlabel = _("VALUE") # default label
43 43
44 44 if not verbose and ("DEPRECATED" in desc or _("DEPRECATED") in desc):
45 45 continue
46 46
47 47 so = ''
48 48 if shortopt:
49 49 so = '-' + shortopt
50 50 lo = '--' + longopt
51 51 if default:
52 52 desc += _(" (default: %s)") % default
53 53
54 54 if isinstance(default, list):
55 55 lo += " %s [+]" % optlabel
56 56 multioccur = True
57 57 elif (default is not None) and not isinstance(default, bool):
58 58 lo += " %s" % optlabel
59 59
60 60 data.append((so, lo, desc))
61 61
62 62 if multioccur:
63 63 header += (_(" ([+] can be repeated)"))
64 64
65 65 rst = ['\n%s:\n\n' % header]
66 66 rst.extend(minirst.maketable(data, 1))
67 67
68 68 return ''.join(rst)
69 69
70 70 def indicateomitted(rst, omitted, notomitted=None):
71 71 rst.append('\n\n.. container:: omitted\n\n %s\n\n' % omitted)
72 72 if notomitted:
73 73 rst.append('\n\n.. container:: notomitted\n\n %s\n\n' % notomitted)
74 74
75 75 def topicmatch(kw):
76 76 """Return help topics matching kw.
77 77
78 78 Returns {'section': [(name, summary), ...], ...} where section is
79 79 one of topics, commands, extensions, or extensioncommands.
80 80 """
81 81 kw = encoding.lower(kw)
82 82 def lowercontains(container):
83 83 return kw in encoding.lower(container) # translated in helptable
84 84 results = {'topics': [],
85 85 'commands': [],
86 86 'extensions': [],
87 87 'extensioncommands': [],
88 88 }
89 89 for names, header, doc in helptable:
90 90 if (sum(map(lowercontains, names))
91 91 or lowercontains(header)
92 92 or lowercontains(doc())):
93 93 results['topics'].append((names[0], header))
94 94 import commands # avoid cycle
95 95 for cmd, entry in commands.table.iteritems():
96 96 if len(entry) == 3:
97 97 summary = entry[2]
98 98 else:
99 99 summary = ''
100 100 # translate docs *before* searching there
101 101 docs = _(getattr(entry[0], '__doc__', None)) or ''
102 102 if kw in cmd or lowercontains(summary) or lowercontains(docs):
103 103 doclines = docs.splitlines()
104 104 if doclines:
105 105 summary = doclines[0]
106 106 cmdname = cmd.split('|')[0].lstrip('^')
107 107 results['commands'].append((cmdname, summary))
108 108 for name, docs in itertools.chain(
109 109 extensions.enabled(False).iteritems(),
110 110 extensions.disabled().iteritems()):
111 111 # extensions.load ignores the UI argument
112 112 mod = extensions.load(None, name, '')
113 113 name = name.split('.')[-1]
114 114 if lowercontains(name) or lowercontains(docs):
115 115 # extension docs are already translated
116 116 results['extensions'].append((name, docs.splitlines()[0]))
117 117 for cmd, entry in getattr(mod, 'cmdtable', {}).iteritems():
118 118 if kw in cmd or (len(entry) > 2 and lowercontains(entry[2])):
119 119 cmdname = cmd.split('|')[0].lstrip('^')
120 120 if entry[0].__doc__:
121 121 cmddoc = gettext(entry[0].__doc__).splitlines()[0]
122 122 else:
123 123 cmddoc = _('(no help text available)')
124 124 results['extensioncommands'].append((cmdname, cmddoc))
125 125 return results
126 126
127 127 def loaddoc(topic):
128 128 """Return a delayed loader for help/topic.txt."""
129 129
130 130 def loader():
131 131 if util.mainfrozen():
132 132 module = sys.executable
133 133 else:
134 134 module = __file__
135 135 base = os.path.dirname(module)
136 136
137 137 for dir in ('.', '..'):
138 138 docdir = os.path.join(base, dir, 'help')
139 139 if os.path.isdir(docdir):
140 140 break
141 141
142 142 path = os.path.join(docdir, topic + ".txt")
143 143 doc = gettext(util.readfile(path))
144 144 for rewriter in helphooks.get(topic, []):
145 145 doc = rewriter(topic, doc)
146 146 return doc
147 147
148 148 return loader
149 149
150 150 helptable = sorted([
151 151 (["config", "hgrc"], _("Configuration Files"), loaddoc('config')),
152 152 (["dates"], _("Date Formats"), loaddoc('dates')),
153 153 (["patterns"], _("File Name Patterns"), loaddoc('patterns')),
154 154 (['environment', 'env'], _('Environment Variables'),
155 155 loaddoc('environment')),
156 156 (['revisions', 'revs'], _('Specifying Single Revisions'),
157 157 loaddoc('revisions')),
158 158 (['multirevs', 'mrevs'], _('Specifying Multiple Revisions'),
159 159 loaddoc('multirevs')),
160 160 (['revsets', 'revset'], _("Specifying Revision Sets"), loaddoc('revsets')),
161 161 (['filesets', 'fileset'], _("Specifying File Sets"), loaddoc('filesets')),
162 162 (['diffs'], _('Diff Formats'), loaddoc('diffs')),
163 163 (['merge-tools', 'mergetools'], _('Merge Tools'), loaddoc('merge-tools')),
164 164 (['templating', 'templates', 'template', 'style'], _('Template Usage'),
165 165 loaddoc('templates')),
166 166 (['urls'], _('URL Paths'), loaddoc('urls')),
167 167 (["extensions"], _("Using Additional Features"), extshelp),
168 168 (["subrepos", "subrepo"], _("Subrepositories"), loaddoc('subrepos')),
169 169 (["hgweb"], _("Configuring hgweb"), loaddoc('hgweb')),
170 170 (["glossary"], _("Glossary"), loaddoc('glossary')),
171 171 (["hgignore", "ignore"], _("Syntax for Mercurial Ignore Files"),
172 172 loaddoc('hgignore')),
173 173 (["phases"], _("Working with Phases"), loaddoc('phases')),
174 174 ])
175 175
176 176 # Map topics to lists of callable taking the current topic help and
177 177 # returning the updated version
178 178 helphooks = {}
179 179
180 180 def addtopichook(topic, rewriter):
181 181 helphooks.setdefault(topic, []).append(rewriter)
182 182
183 183 def makeitemsdoc(topic, doc, marker, items):
184 184 """Extract docstring from the items key to function mapping, build a
185 185 .single documentation block and use it to overwrite the marker in doc
186 186 """
187 187 entries = []
188 188 for name in sorted(items):
189 189 text = (items[name].__doc__ or '').rstrip()
190 190 if not text:
191 191 continue
192 192 text = gettext(text)
193 193 lines = text.splitlines()
194 194 doclines = [(lines[0])]
195 195 for l in lines[1:]:
196 196 # Stop once we find some Python doctest
197 197 if l.strip().startswith('>>>'):
198 198 break
199 199 doclines.append(' ' + l.strip())
200 200 entries.append('\n'.join(doclines))
201 201 entries = '\n\n'.join(entries)
202 202 return doc.replace(marker, entries)
203 203
204 204 def addtopicsymbols(topic, marker, symbols):
205 205 def add(topic, doc):
206 206 return makeitemsdoc(topic, doc, marker, symbols)
207 207 addtopichook(topic, add)
208 208
209 209 addtopicsymbols('filesets', '.. predicatesmarker', fileset.symbols)
210 210 addtopicsymbols('merge-tools', '.. internaltoolsmarker', filemerge.internals)
211 211 addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols)
212 212 addtopicsymbols('templates', '.. keywordsmarker', templatekw.dockeywords)
213 213 addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters)
214 214
215 215 def help_(ui, name, unknowncmd=False, full=True, **opts):
216 216 '''
217 217 Generate the help for 'name' as unformatted restructured text. If
218 218 'name' is None, describe the commands available.
219 219 '''
220 220
221 221 import commands # avoid cycle
222 222
223 223 def helpcmd(name):
224 224 try:
225 225 aliases, entry = cmdutil.findcmd(name, commands.table,
226 226 strict=unknowncmd)
227 227 except error.AmbiguousCommand, inst:
228 228 # py3k fix: except vars can't be used outside the scope of the
229 229 # except block, nor can be used inside a lambda. python issue4617
230 230 prefix = inst.args[0]
231 231 select = lambda c: c.lstrip('^').startswith(prefix)
232 232 rst = helplist(select)
233 233 return rst
234 234
235 235 rst = []
236 236
237 237 # check if it's an invalid alias and display its error if it is
238 if getattr(entry[0], 'badalias', False):
238 if getattr(entry[0], 'badalias', None):
239 239 if not unknowncmd:
240 240 ui.pushbuffer()
241 241 entry[0](ui)
242 242 rst.append(ui.popbuffer())
243 243 return rst
244 244
245 245 # synopsis
246 246 if len(entry) > 2:
247 247 if entry[2].startswith('hg'):
248 248 rst.append("%s\n" % entry[2])
249 249 else:
250 250 rst.append('hg %s %s\n' % (aliases[0], entry[2]))
251 251 else:
252 252 rst.append('hg %s\n' % aliases[0])
253 253 # aliases
254 254 if full and not ui.quiet and len(aliases) > 1:
255 255 rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:]))
256 256 rst.append('\n')
257 257
258 258 # description
259 259 doc = gettext(entry[0].__doc__)
260 260 if not doc:
261 261 doc = _("(no help text available)")
262 262 if util.safehasattr(entry[0], 'definition'): # aliased command
263 263 if entry[0].definition.startswith('!'): # shell alias
264 264 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
265 265 else:
266 266 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
267 267 doc = doc.splitlines(True)
268 268 if ui.quiet or not full:
269 269 rst.append(doc[0])
270 270 else:
271 271 rst.extend(doc)
272 272 rst.append('\n')
273 273
274 274 # check if this command shadows a non-trivial (multi-line)
275 275 # extension help text
276 276 try:
277 277 mod = extensions.find(name)
278 278 doc = gettext(mod.__doc__) or ''
279 279 if '\n' in doc.strip():
280 280 msg = _('(use "hg help -e %s" to show help for '
281 281 'the %s extension)') % (name, name)
282 282 rst.append('\n%s\n' % msg)
283 283 except KeyError:
284 284 pass
285 285
286 286 # options
287 287 if not ui.quiet and entry[1]:
288 288 rst.append(optrst(_("options"), entry[1], ui.verbose))
289 289
290 290 if ui.verbose:
291 291 rst.append(optrst(_("global options"),
292 292 commands.globalopts, ui.verbose))
293 293
294 294 if not ui.verbose:
295 295 if not full:
296 296 rst.append(_('\n(use "hg %s -h" to show more help)\n')
297 297 % name)
298 298 elif not ui.quiet:
299 299 rst.append(_('\n(some details hidden, use --verbose '
300 300 'to show complete help)'))
301 301
302 302 return rst
303 303
304 304
305 305 def helplist(select=None):
306 306 # list of commands
307 307 if name == "shortlist":
308 308 header = _('basic commands:\n\n')
309 309 elif name == "debug":
310 310 header = _('debug commands (internal and unsupported):\n\n')
311 311 else:
312 312 header = _('list of commands:\n\n')
313 313
314 314 h = {}
315 315 cmds = {}
316 316 for c, e in commands.table.iteritems():
317 317 f = c.split("|", 1)[0]
318 318 if select and not select(f):
319 319 continue
320 320 if (not select and name != 'shortlist' and
321 321 e[0].__module__ != commands.__name__):
322 322 continue
323 323 if name == "shortlist" and not f.startswith("^"):
324 324 continue
325 325 f = f.lstrip("^")
326 326 if not ui.debugflag and f.startswith("debug") and name != "debug":
327 327 continue
328 328 doc = e[0].__doc__
329 329 if doc and 'DEPRECATED' in doc and not ui.verbose:
330 330 continue
331 331 doc = gettext(doc)
332 332 if not doc:
333 333 doc = _("(no help text available)")
334 334 h[f] = doc.splitlines()[0].rstrip()
335 335 cmds[f] = c.lstrip("^")
336 336
337 337 rst = []
338 338 if not h:
339 339 if not ui.quiet:
340 340 rst.append(_('no commands defined\n'))
341 341 return rst
342 342
343 343 if not ui.quiet:
344 344 rst.append(header)
345 345 fns = sorted(h)
346 346 for f in fns:
347 347 if ui.verbose:
348 348 commacmds = cmds[f].replace("|",", ")
349 349 rst.append(" :%s: %s\n" % (commacmds, h[f]))
350 350 else:
351 351 rst.append(' :%s: %s\n' % (f, h[f]))
352 352
353 353 if not name:
354 354 exts = listexts(_('enabled extensions:'), extensions.enabled())
355 355 if exts:
356 356 rst.append('\n')
357 357 rst.extend(exts)
358 358
359 359 rst.append(_("\nadditional help topics:\n\n"))
360 360 topics = []
361 361 for names, header, doc in helptable:
362 362 topics.append((names[0], header))
363 363 for t, desc in topics:
364 364 rst.append(" :%s: %s\n" % (t, desc))
365 365
366 366 if ui.quiet:
367 367 pass
368 368 elif ui.verbose:
369 369 rst.append('\n%s\n' % optrst(_("global options"),
370 370 commands.globalopts, ui.verbose))
371 371 if name == 'shortlist':
372 372 rst.append(_('\n(use "hg help" for the full list '
373 373 'of commands)\n'))
374 374 else:
375 375 if name == 'shortlist':
376 376 rst.append(_('\n(use "hg help" for the full list of commands '
377 377 'or "hg -v" for details)\n'))
378 378 elif name and not full:
379 379 rst.append(_('\n(use "hg help %s" to show the full help '
380 380 'text)\n') % name)
381 381 else:
382 382 rst.append(_('\n(use "hg help -v%s" to show built-in aliases '
383 383 'and global options)\n')
384 384 % (name and " " + name or ""))
385 385 return rst
386 386
387 387 def helptopic(name):
388 388 for names, header, doc in helptable:
389 389 if name in names:
390 390 break
391 391 else:
392 392 raise error.UnknownCommand(name)
393 393
394 394 rst = [minirst.section(header)]
395 395
396 396 # description
397 397 if not doc:
398 398 rst.append(" %s\n" % _("(no help text available)"))
399 399 if callable(doc):
400 400 rst += [" %s\n" % l for l in doc().splitlines()]
401 401
402 402 if not ui.verbose:
403 403 omitted = _('(some details hidden, use --verbose'
404 404 ' to show complete help)')
405 405 indicateomitted(rst, omitted)
406 406
407 407 try:
408 408 cmdutil.findcmd(name, commands.table)
409 409 rst.append(_('\nuse "hg help -c %s" to see help for '
410 410 'the %s command\n') % (name, name))
411 411 except error.UnknownCommand:
412 412 pass
413 413 return rst
414 414
415 415 def helpext(name):
416 416 try:
417 417 mod = extensions.find(name)
418 418 doc = gettext(mod.__doc__) or _('no help text available')
419 419 except KeyError:
420 420 mod = None
421 421 doc = extensions.disabledext(name)
422 422 if not doc:
423 423 raise error.UnknownCommand(name)
424 424
425 425 if '\n' not in doc:
426 426 head, tail = doc, ""
427 427 else:
428 428 head, tail = doc.split('\n', 1)
429 429 rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)]
430 430 if tail:
431 431 rst.extend(tail.splitlines(True))
432 432 rst.append('\n')
433 433
434 434 if not ui.verbose:
435 435 omitted = _('(some details hidden, use --verbose'
436 436 ' to show complete help)')
437 437 indicateomitted(rst, omitted)
438 438
439 439 if mod:
440 440 try:
441 441 ct = mod.cmdtable
442 442 except AttributeError:
443 443 ct = {}
444 444 modcmds = set([c.split('|', 1)[0] for c in ct])
445 445 rst.extend(helplist(modcmds.__contains__))
446 446 else:
447 447 rst.append(_('(use "hg help extensions" for information on enabling'
448 448 ' extensions)\n'))
449 449 return rst
450 450
451 451 def helpextcmd(name):
452 452 cmd, ext, mod = extensions.disabledcmd(ui, name,
453 453 ui.configbool('ui', 'strict'))
454 454 doc = gettext(mod.__doc__).splitlines()[0]
455 455
456 456 rst = listexts(_("'%s' is provided by the following "
457 457 "extension:") % cmd, {ext: doc}, indent=4)
458 458 rst.append('\n')
459 459 rst.append(_('(use "hg help extensions" for information on enabling '
460 460 'extensions)\n'))
461 461 return rst
462 462
463 463
464 464 rst = []
465 465 kw = opts.get('keyword')
466 466 if kw:
467 467 matches = topicmatch(kw)
468 468 for t, title in (('topics', _('Topics')),
469 469 ('commands', _('Commands')),
470 470 ('extensions', _('Extensions')),
471 471 ('extensioncommands', _('Extension Commands'))):
472 472 if matches[t]:
473 473 rst.append('%s:\n\n' % title)
474 474 rst.extend(minirst.maketable(sorted(matches[t]), 1))
475 475 rst.append('\n')
476 476 if not rst:
477 477 msg = _('no matches')
478 478 hint = _('try "hg help" for a list of topics')
479 479 raise util.Abort(msg, hint=hint)
480 480 elif name and name != 'shortlist':
481 481 if unknowncmd:
482 482 queries = (helpextcmd,)
483 483 elif opts.get('extension'):
484 484 queries = (helpext,)
485 485 elif opts.get('command'):
486 486 queries = (helpcmd,)
487 487 else:
488 488 queries = (helptopic, helpcmd, helpext, helpextcmd)
489 489 for f in queries:
490 490 try:
491 491 rst = f(name)
492 492 break
493 493 except error.UnknownCommand:
494 494 pass
495 495 else:
496 496 if unknowncmd:
497 497 raise error.UnknownCommand(name)
498 498 else:
499 499 msg = _('no such help topic: %s') % name
500 500 hint = _('try "hg help --keyword %s"') % name
501 501 raise util.Abort(msg, hint=hint)
502 502 else:
503 503 # program name
504 504 if not ui.quiet:
505 505 rst = [_("Mercurial Distributed SCM\n"), '\n']
506 506 rst.extend(helplist())
507 507
508 508 return ''.join(rst)
General Comments 0
You need to be logged in to leave comments. Login now