##// END OF EJS Templates
dispatch: defer environment variable resolution in alias commands (BC)...
Jun Wu -
r29087:ad1bdea4 default
parent child Browse files
Show More
@@ -1,1084 +1,1088 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 __future__ import absolute_import, print_function
9 9
10 10 import atexit
11 11 import difflib
12 12 import errno
13 13 import os
14 14 import pdb
15 15 import re
16 16 import shlex
17 17 import signal
18 18 import socket
19 19 import sys
20 20 import time
21 21 import traceback
22 22
23 23
24 24 from .i18n import _
25 25
26 26 from . import (
27 27 cmdutil,
28 28 commands,
29 29 demandimport,
30 30 encoding,
31 31 error,
32 32 extensions,
33 33 fancyopts,
34 34 fileset,
35 35 hg,
36 36 hook,
37 37 revset,
38 38 templatefilters,
39 39 templatekw,
40 40 templater,
41 41 ui as uimod,
42 42 util,
43 43 )
44 44
45 45 class request(object):
46 46 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
47 47 ferr=None):
48 48 self.args = args
49 49 self.ui = ui
50 50 self.repo = repo
51 51
52 52 # input/output/error streams
53 53 self.fin = fin
54 54 self.fout = fout
55 55 self.ferr = ferr
56 56
57 57 def run():
58 58 "run the command in sys.argv"
59 59 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
60 60
61 61 def _getsimilar(symbols, value):
62 62 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
63 63 # The cutoff for similarity here is pretty arbitrary. It should
64 64 # probably be investigated and tweaked.
65 65 return [s for s in symbols if sim(s) > 0.6]
66 66
67 67 def _reportsimilar(write, similar):
68 68 if len(similar) == 1:
69 69 write(_("(did you mean %s?)\n") % similar[0])
70 70 elif similar:
71 71 ss = ", ".join(sorted(similar))
72 72 write(_("(did you mean one of %s?)\n") % ss)
73 73
74 74 def _formatparse(write, inst):
75 75 similar = []
76 76 if isinstance(inst, error.UnknownIdentifier):
77 77 # make sure to check fileset first, as revset can invoke fileset
78 78 similar = _getsimilar(inst.symbols, inst.function)
79 79 if len(inst.args) > 1:
80 80 write(_("hg: parse error at %s: %s\n") %
81 81 (inst.args[1], inst.args[0]))
82 82 if (inst.args[0][0] == ' '):
83 83 write(_("unexpected leading whitespace\n"))
84 84 else:
85 85 write(_("hg: parse error: %s\n") % inst.args[0])
86 86 _reportsimilar(write, similar)
87 87 if inst.hint:
88 88 write(_("(%s)\n") % inst.hint)
89 89
90 90 def dispatch(req):
91 91 "run the command specified in req.args"
92 92 if req.ferr:
93 93 ferr = req.ferr
94 94 elif req.ui:
95 95 ferr = req.ui.ferr
96 96 else:
97 97 ferr = sys.stderr
98 98
99 99 try:
100 100 if not req.ui:
101 101 req.ui = uimod.ui()
102 102 if '--traceback' in req.args:
103 103 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
104 104
105 105 # set ui streams from the request
106 106 if req.fin:
107 107 req.ui.fin = req.fin
108 108 if req.fout:
109 109 req.ui.fout = req.fout
110 110 if req.ferr:
111 111 req.ui.ferr = req.ferr
112 112 except error.Abort as inst:
113 113 ferr.write(_("abort: %s\n") % inst)
114 114 if inst.hint:
115 115 ferr.write(_("(%s)\n") % inst.hint)
116 116 return -1
117 117 except error.ParseError as inst:
118 118 _formatparse(ferr.write, inst)
119 119 return -1
120 120
121 121 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
122 122 starttime = time.time()
123 123 ret = None
124 124 try:
125 125 ret = _runcatch(req)
126 126 except KeyboardInterrupt:
127 127 try:
128 128 req.ui.warn(_("interrupted!\n"))
129 129 except IOError as inst:
130 130 if inst.errno != errno.EPIPE:
131 131 raise
132 132 ret = -1
133 133 finally:
134 134 duration = time.time() - starttime
135 135 req.ui.flush()
136 136 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
137 137 msg, ret or 0, duration)
138 138 return ret
139 139
140 140 def _runcatch(req):
141 141 def catchterm(*args):
142 142 raise error.SignalInterrupt
143 143
144 144 ui = req.ui
145 145 try:
146 146 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
147 147 num = getattr(signal, name, None)
148 148 if num:
149 149 signal.signal(num, catchterm)
150 150 except ValueError:
151 151 pass # happens if called in a thread
152 152
153 153 try:
154 154 try:
155 155 debugger = 'pdb'
156 156 debugtrace = {
157 157 'pdb' : pdb.set_trace
158 158 }
159 159 debugmortem = {
160 160 'pdb' : pdb.post_mortem
161 161 }
162 162
163 163 # read --config before doing anything else
164 164 # (e.g. to change trust settings for reading .hg/hgrc)
165 165 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
166 166
167 167 if req.repo:
168 168 # copy configs that were passed on the cmdline (--config) to
169 169 # the repo ui
170 170 for sec, name, val in cfgs:
171 171 req.repo.ui.setconfig(sec, name, val, source='--config')
172 172
173 173 # developer config: ui.debugger
174 174 debugger = ui.config("ui", "debugger")
175 175 debugmod = pdb
176 176 if not debugger or ui.plain():
177 177 # if we are in HGPLAIN mode, then disable custom debugging
178 178 debugger = 'pdb'
179 179 elif '--debugger' in req.args:
180 180 # This import can be slow for fancy debuggers, so only
181 181 # do it when absolutely necessary, i.e. when actual
182 182 # debugging has been requested
183 183 with demandimport.deactivated():
184 184 try:
185 185 debugmod = __import__(debugger)
186 186 except ImportError:
187 187 pass # Leave debugmod = pdb
188 188
189 189 debugtrace[debugger] = debugmod.set_trace
190 190 debugmortem[debugger] = debugmod.post_mortem
191 191
192 192 # enter the debugger before command execution
193 193 if '--debugger' in req.args:
194 194 ui.warn(_("entering debugger - "
195 195 "type c to continue starting hg or h for help\n"))
196 196
197 197 if (debugger != 'pdb' and
198 198 debugtrace[debugger] == debugtrace['pdb']):
199 199 ui.warn(_("%s debugger specified "
200 200 "but its module was not found\n") % debugger)
201 201 with demandimport.deactivated():
202 202 debugtrace[debugger]()
203 203 try:
204 204 return _dispatch(req)
205 205 finally:
206 206 ui.flush()
207 207 except: # re-raises
208 208 # enter the debugger when we hit an exception
209 209 if '--debugger' in req.args:
210 210 traceback.print_exc()
211 211 debugmortem[debugger](sys.exc_info()[2])
212 212 ui.traceback()
213 213 raise
214 214
215 215 # Global exception handling, alphabetically
216 216 # Mercurial-specific first, followed by built-in and library exceptions
217 217 except error.AmbiguousCommand as inst:
218 218 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
219 219 (inst.args[0], " ".join(inst.args[1])))
220 220 except error.ParseError as inst:
221 221 _formatparse(ui.warn, inst)
222 222 return -1
223 223 except error.LockHeld as inst:
224 224 if inst.errno == errno.ETIMEDOUT:
225 225 reason = _('timed out waiting for lock held by %s') % inst.locker
226 226 else:
227 227 reason = _('lock held by %s') % inst.locker
228 228 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
229 229 except error.LockUnavailable as inst:
230 230 ui.warn(_("abort: could not lock %s: %s\n") %
231 231 (inst.desc or inst.filename, inst.strerror))
232 232 except error.CommandError as inst:
233 233 if inst.args[0]:
234 234 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
235 235 commands.help_(ui, inst.args[0], full=False, command=True)
236 236 else:
237 237 ui.warn(_("hg: %s\n") % inst.args[1])
238 238 commands.help_(ui, 'shortlist')
239 239 except error.OutOfBandError as inst:
240 240 if inst.args:
241 241 msg = _("abort: remote error:\n")
242 242 else:
243 243 msg = _("abort: remote error\n")
244 244 ui.warn(msg)
245 245 if inst.args:
246 246 ui.warn(''.join(inst.args))
247 247 if inst.hint:
248 248 ui.warn('(%s)\n' % inst.hint)
249 249 except error.RepoError as inst:
250 250 ui.warn(_("abort: %s!\n") % inst)
251 251 if inst.hint:
252 252 ui.warn(_("(%s)\n") % inst.hint)
253 253 except error.ResponseError as inst:
254 254 ui.warn(_("abort: %s") % inst.args[0])
255 255 if not isinstance(inst.args[1], basestring):
256 256 ui.warn(" %r\n" % (inst.args[1],))
257 257 elif not inst.args[1]:
258 258 ui.warn(_(" empty string\n"))
259 259 else:
260 260 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
261 261 except error.CensoredNodeError as inst:
262 262 ui.warn(_("abort: file censored %s!\n") % inst)
263 263 except error.RevlogError as inst:
264 264 ui.warn(_("abort: %s!\n") % inst)
265 265 except error.SignalInterrupt:
266 266 ui.warn(_("killed!\n"))
267 267 except error.UnknownCommand as inst:
268 268 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
269 269 try:
270 270 # check if the command is in a disabled extension
271 271 # (but don't check for extensions themselves)
272 272 commands.help_(ui, inst.args[0], unknowncmd=True)
273 273 except (error.UnknownCommand, error.Abort):
274 274 suggested = False
275 275 if len(inst.args) == 2:
276 276 sim = _getsimilar(inst.args[1], inst.args[0])
277 277 if sim:
278 278 _reportsimilar(ui.warn, sim)
279 279 suggested = True
280 280 if not suggested:
281 281 commands.help_(ui, 'shortlist')
282 282 except error.InterventionRequired as inst:
283 283 ui.warn("%s\n" % inst)
284 284 if inst.hint:
285 285 ui.warn(_("(%s)\n") % inst.hint)
286 286 return 1
287 287 except error.Abort as inst:
288 288 ui.warn(_("abort: %s\n") % inst)
289 289 if inst.hint:
290 290 ui.warn(_("(%s)\n") % inst.hint)
291 291 except ImportError as inst:
292 292 ui.warn(_("abort: %s!\n") % inst)
293 293 m = str(inst).split()[-1]
294 294 if m in "mpatch bdiff".split():
295 295 ui.warn(_("(did you forget to compile extensions?)\n"))
296 296 elif m in "zlib".split():
297 297 ui.warn(_("(is your Python install correct?)\n"))
298 298 except IOError as inst:
299 299 if util.safehasattr(inst, "code"):
300 300 ui.warn(_("abort: %s\n") % inst)
301 301 elif util.safehasattr(inst, "reason"):
302 302 try: # usually it is in the form (errno, strerror)
303 303 reason = inst.reason.args[1]
304 304 except (AttributeError, IndexError):
305 305 # it might be anything, for example a string
306 306 reason = inst.reason
307 307 if isinstance(reason, unicode):
308 308 # SSLError of Python 2.7.9 contains a unicode
309 309 reason = reason.encode(encoding.encoding, 'replace')
310 310 ui.warn(_("abort: error: %s\n") % reason)
311 311 elif (util.safehasattr(inst, "args")
312 312 and inst.args and inst.args[0] == errno.EPIPE):
313 313 pass
314 314 elif getattr(inst, "strerror", None):
315 315 if getattr(inst, "filename", None):
316 316 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
317 317 else:
318 318 ui.warn(_("abort: %s\n") % inst.strerror)
319 319 else:
320 320 raise
321 321 except OSError as inst:
322 322 if getattr(inst, "filename", None) is not None:
323 323 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
324 324 else:
325 325 ui.warn(_("abort: %s\n") % inst.strerror)
326 326 except KeyboardInterrupt:
327 327 raise
328 328 except MemoryError:
329 329 ui.warn(_("abort: out of memory\n"))
330 330 except SystemExit as inst:
331 331 # Commands shouldn't sys.exit directly, but give a return code.
332 332 # Just in case catch this and and pass exit code to caller.
333 333 return inst.code
334 334 except socket.error as inst:
335 335 ui.warn(_("abort: %s\n") % inst.args[-1])
336 336 except: # perhaps re-raises
337 337 if not handlecommandexception(ui):
338 338 raise
339 339
340 340 return -1
341 341
342 342 def aliasargs(fn, givenargs):
343 343 args = getattr(fn, 'args', [])
344 344 if args:
345 345 cmd = ' '.join(map(util.shellquote, args))
346 346
347 347 nums = []
348 348 def replacer(m):
349 349 num = int(m.group(1)) - 1
350 350 nums.append(num)
351 351 if num < len(givenargs):
352 352 return givenargs[num]
353 353 raise error.Abort(_('too few arguments for command alias'))
354 354 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
355 355 givenargs = [x for i, x in enumerate(givenargs)
356 356 if i not in nums]
357 357 args = shlex.split(cmd)
358 358 return args + givenargs
359 359
360 360 def aliasinterpolate(name, args, cmd):
361 361 '''interpolate args into cmd for shell aliases
362 362
363 363 This also handles $0, $@ and "$@".
364 364 '''
365 365 # util.interpolate can't deal with "$@" (with quotes) because it's only
366 366 # built to match prefix + patterns.
367 367 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
368 368 replacemap['$0'] = name
369 369 replacemap['$$'] = '$'
370 370 replacemap['$@'] = ' '.join(args)
371 371 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
372 372 # parameters, separated out into words. Emulate the same behavior here by
373 373 # quoting the arguments individually. POSIX shells will then typically
374 374 # tokenize each argument into exactly one word.
375 375 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
376 376 # escape '\$' for regex
377 377 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
378 378 r = re.compile(regex)
379 379 return r.sub(lambda x: replacemap[x.group()], cmd)
380 380
381 381 class cmdalias(object):
382 382 def __init__(self, name, definition, cmdtable, source):
383 383 self.name = self.cmd = name
384 384 self.cmdname = ''
385 385 self.definition = definition
386 386 self.fn = None
387 self.args = []
387 self.givenargs = []
388 388 self.opts = []
389 389 self.help = ''
390 390 self.badalias = None
391 391 self.unknowncmd = False
392 392 self.source = source
393 393
394 394 try:
395 395 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
396 396 for alias, e in cmdtable.iteritems():
397 397 if e is entry:
398 398 self.cmd = alias
399 399 break
400 400 self.shadows = True
401 401 except error.UnknownCommand:
402 402 self.shadows = False
403 403
404 404 if not self.definition:
405 405 self.badalias = _("no definition for alias '%s'") % self.name
406 406 return
407 407
408 408 if self.definition.startswith('!'):
409 409 self.shell = True
410 410 def fn(ui, *args):
411 411 env = {'HG_ARGS': ' '.join((self.name,) + args)}
412 412 def _checkvar(m):
413 413 if m.groups()[0] == '$':
414 414 return m.group()
415 415 elif int(m.groups()[0]) <= len(args):
416 416 return m.group()
417 417 else:
418 418 ui.debug("No argument found for substitution "
419 419 "of %i variable in alias '%s' definition."
420 420 % (int(m.groups()[0]), self.name))
421 421 return ''
422 422 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
423 423 cmd = aliasinterpolate(self.name, args, cmd)
424 424 return ui.system(cmd, environ=env)
425 425 self.fn = fn
426 426 return
427 427
428 428 try:
429 429 args = shlex.split(self.definition)
430 430 except ValueError as inst:
431 431 self.badalias = (_("error in definition for alias '%s': %s")
432 432 % (self.name, inst))
433 433 return
434 434 self.cmdname = cmd = args.pop(0)
435 args = map(util.expandpath, args)
435 self.givenargs = args
436 436
437 437 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
438 438 if _earlygetopt([invalidarg], args):
439 439 self.badalias = (_("error in definition for alias '%s': %s may "
440 440 "only be given on the command line")
441 441 % (self.name, invalidarg))
442 442 return
443 443
444 444 try:
445 445 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
446 446 if len(tableentry) > 2:
447 447 self.fn, self.opts, self.help = tableentry
448 448 else:
449 449 self.fn, self.opts = tableentry
450 450
451 self.args = aliasargs(self.fn, args)
452 451 if self.help.startswith("hg " + cmd):
453 452 # drop prefix in old-style help lines so hg shows the alias
454 453 self.help = self.help[4 + len(cmd):]
455 454 self.__doc__ = self.fn.__doc__
456 455
457 456 except error.UnknownCommand:
458 457 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
459 458 % (self.name, cmd))
460 459 self.unknowncmd = True
461 460 except error.AmbiguousCommand:
462 461 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
463 462 % (self.name, cmd))
464 463
464 @property
465 def args(self):
466 args = map(util.expandpath, self.givenargs)
467 return aliasargs(self.fn, args)
468
465 469 def __getattr__(self, name):
466 470 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
467 471 if name not in adefaults:
468 472 raise AttributeError(name)
469 473 if self.badalias or util.safehasattr(self, 'shell'):
470 474 return adefaults[name]
471 475 return getattr(self.fn, name)
472 476
473 477 def __call__(self, ui, *args, **opts):
474 478 if self.badalias:
475 479 hint = None
476 480 if self.unknowncmd:
477 481 try:
478 482 # check if the command is in a disabled extension
479 483 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
480 484 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
481 485 except error.UnknownCommand:
482 486 pass
483 487 raise error.Abort(self.badalias, hint=hint)
484 488 if self.shadows:
485 489 ui.debug("alias '%s' shadows command '%s'\n" %
486 490 (self.name, self.cmdname))
487 491
488 492 if util.safehasattr(self, 'shell'):
489 493 return self.fn(ui, *args, **opts)
490 494 else:
491 495 try:
492 496 return util.checksignature(self.fn)(ui, *args, **opts)
493 497 except error.SignatureError:
494 498 args = ' '.join([self.cmdname] + self.args)
495 499 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
496 500 raise
497 501
498 502 def addaliases(ui, cmdtable):
499 503 # aliases are processed after extensions have been loaded, so they
500 504 # may use extension commands. Aliases can also use other alias definitions,
501 505 # but only if they have been defined prior to the current definition.
502 506 for alias, definition in ui.configitems('alias'):
503 507 source = ui.configsource('alias', alias)
504 508 aliasdef = cmdalias(alias, definition, cmdtable, source)
505 509
506 510 try:
507 511 olddef = cmdtable[aliasdef.cmd][0]
508 512 if olddef.definition == aliasdef.definition:
509 513 continue
510 514 except (KeyError, AttributeError):
511 515 # definition might not exist or it might not be a cmdalias
512 516 pass
513 517
514 518 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
515 519
516 520 def _parse(ui, args):
517 521 options = {}
518 522 cmdoptions = {}
519 523
520 524 try:
521 525 args = fancyopts.fancyopts(args, commands.globalopts, options)
522 526 except fancyopts.getopt.GetoptError as inst:
523 527 raise error.CommandError(None, inst)
524 528
525 529 if args:
526 530 cmd, args = args[0], args[1:]
527 531 aliases, entry = cmdutil.findcmd(cmd, commands.table,
528 532 ui.configbool("ui", "strict"))
529 533 cmd = aliases[0]
530 534 args = aliasargs(entry[0], args)
531 535 defaults = ui.config("defaults", cmd)
532 536 if defaults:
533 537 args = map(util.expandpath, shlex.split(defaults)) + args
534 538 c = list(entry[1])
535 539 else:
536 540 cmd = None
537 541 c = []
538 542
539 543 # combine global options into local
540 544 for o in commands.globalopts:
541 545 c.append((o[0], o[1], options[o[1]], o[3]))
542 546
543 547 try:
544 548 args = fancyopts.fancyopts(args, c, cmdoptions, True)
545 549 except fancyopts.getopt.GetoptError as inst:
546 550 raise error.CommandError(cmd, inst)
547 551
548 552 # separate global options back out
549 553 for o in commands.globalopts:
550 554 n = o[1]
551 555 options[n] = cmdoptions[n]
552 556 del cmdoptions[n]
553 557
554 558 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
555 559
556 560 def _parseconfig(ui, config):
557 561 """parse the --config options from the command line"""
558 562 configs = []
559 563
560 564 for cfg in config:
561 565 try:
562 566 name, value = [cfgelem.strip()
563 567 for cfgelem in cfg.split('=', 1)]
564 568 section, name = name.split('.', 1)
565 569 if not section or not name:
566 570 raise IndexError
567 571 ui.setconfig(section, name, value, '--config')
568 572 configs.append((section, name, value))
569 573 except (IndexError, ValueError):
570 574 raise error.Abort(_('malformed --config option: %r '
571 575 '(use --config section.name=value)') % cfg)
572 576
573 577 return configs
574 578
575 579 def _earlygetopt(aliases, args):
576 580 """Return list of values for an option (or aliases).
577 581
578 582 The values are listed in the order they appear in args.
579 583 The options and values are removed from args.
580 584
581 585 >>> args = ['x', '--cwd', 'foo', 'y']
582 586 >>> _earlygetopt(['--cwd'], args), args
583 587 (['foo'], ['x', 'y'])
584 588
585 589 >>> args = ['x', '--cwd=bar', 'y']
586 590 >>> _earlygetopt(['--cwd'], args), args
587 591 (['bar'], ['x', 'y'])
588 592
589 593 >>> args = ['x', '-R', 'foo', 'y']
590 594 >>> _earlygetopt(['-R'], args), args
591 595 (['foo'], ['x', 'y'])
592 596
593 597 >>> args = ['x', '-Rbar', 'y']
594 598 >>> _earlygetopt(['-R'], args), args
595 599 (['bar'], ['x', 'y'])
596 600 """
597 601 try:
598 602 argcount = args.index("--")
599 603 except ValueError:
600 604 argcount = len(args)
601 605 shortopts = [opt for opt in aliases if len(opt) == 2]
602 606 values = []
603 607 pos = 0
604 608 while pos < argcount:
605 609 fullarg = arg = args[pos]
606 610 equals = arg.find('=')
607 611 if equals > -1:
608 612 arg = arg[:equals]
609 613 if arg in aliases:
610 614 del args[pos]
611 615 if equals > -1:
612 616 values.append(fullarg[equals + 1:])
613 617 argcount -= 1
614 618 else:
615 619 if pos + 1 >= argcount:
616 620 # ignore and let getopt report an error if there is no value
617 621 break
618 622 values.append(args.pop(pos))
619 623 argcount -= 2
620 624 elif arg[:2] in shortopts:
621 625 # short option can have no following space, e.g. hg log -Rfoo
622 626 values.append(args.pop(pos)[2:])
623 627 argcount -= 1
624 628 else:
625 629 pos += 1
626 630 return values
627 631
628 632 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
629 633 # run pre-hook, and abort if it fails
630 634 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
631 635 pats=cmdpats, opts=cmdoptions)
632 636 ret = _runcommand(ui, options, cmd, d)
633 637 # run post-hook, passing command result
634 638 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
635 639 result=ret, pats=cmdpats, opts=cmdoptions)
636 640 return ret
637 641
638 642 def _getlocal(ui, rpath, wd=None):
639 643 """Return (path, local ui object) for the given target path.
640 644
641 645 Takes paths in [cwd]/.hg/hgrc into account."
642 646 """
643 647 if wd is None:
644 648 try:
645 649 wd = os.getcwd()
646 650 except OSError as e:
647 651 raise error.Abort(_("error getting current working directory: %s") %
648 652 e.strerror)
649 653 path = cmdutil.findrepo(wd) or ""
650 654 if not path:
651 655 lui = ui
652 656 else:
653 657 lui = ui.copy()
654 658 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
655 659
656 660 if rpath and rpath[-1]:
657 661 path = lui.expandpath(rpath[-1])
658 662 lui = ui.copy()
659 663 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
660 664
661 665 return path, lui
662 666
663 667 def _checkshellalias(lui, ui, args, precheck=True):
664 668 """Return the function to run the shell alias, if it is required
665 669
666 670 'precheck' is whether this function is invoked before adding
667 671 aliases or not.
668 672 """
669 673 options = {}
670 674
671 675 try:
672 676 args = fancyopts.fancyopts(args, commands.globalopts, options)
673 677 except fancyopts.getopt.GetoptError:
674 678 return
675 679
676 680 if not args:
677 681 return
678 682
679 683 if precheck:
680 684 strict = True
681 685 cmdtable = commands.table.copy()
682 686 addaliases(lui, cmdtable)
683 687 else:
684 688 strict = False
685 689 cmdtable = commands.table
686 690
687 691 cmd = args[0]
688 692 try:
689 693 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
690 694 except (error.AmbiguousCommand, error.UnknownCommand):
691 695 return
692 696
693 697 cmd = aliases[0]
694 698 fn = entry[0]
695 699
696 700 if cmd and util.safehasattr(fn, 'shell'):
697 701 d = lambda: fn(ui, *args[1:])
698 702 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
699 703 [], {})
700 704
701 705 def _cmdattr(ui, cmd, func, attr):
702 706 try:
703 707 return getattr(func, attr)
704 708 except AttributeError:
705 709 ui.deprecwarn("missing attribute '%s', use @command decorator "
706 710 "to register '%s'" % (attr, cmd), '3.8')
707 711 return False
708 712
709 713 _loaded = set()
710 714
711 715 # list of (objname, loadermod, loadername) tuple:
712 716 # - objname is the name of an object in extension module, from which
713 717 # extra information is loaded
714 718 # - loadermod is the module where loader is placed
715 719 # - loadername is the name of the function, which takes (ui, extensionname,
716 720 # extraobj) arguments
717 721 extraloaders = [
718 722 ('cmdtable', commands, 'loadcmdtable'),
719 723 ('filesetpredicate', fileset, 'loadpredicate'),
720 724 ('revsetpredicate', revset, 'loadpredicate'),
721 725 ('templatefilter', templatefilters, 'loadfilter'),
722 726 ('templatefunc', templater, 'loadfunction'),
723 727 ('templatekeyword', templatekw, 'loadkeyword'),
724 728 ]
725 729
726 730 def _dispatch(req):
727 731 args = req.args
728 732 ui = req.ui
729 733
730 734 # check for cwd
731 735 cwd = _earlygetopt(['--cwd'], args)
732 736 if cwd:
733 737 os.chdir(cwd[-1])
734 738
735 739 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
736 740 path, lui = _getlocal(ui, rpath)
737 741
738 742 # Now that we're operating in the right directory/repository with
739 743 # the right config settings, check for shell aliases
740 744 shellaliasfn = _checkshellalias(lui, ui, args)
741 745 if shellaliasfn:
742 746 return shellaliasfn()
743 747
744 748 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
745 749 # reposetup. Programs like TortoiseHg will call _dispatch several
746 750 # times so we keep track of configured extensions in _loaded.
747 751 extensions.loadall(lui)
748 752 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
749 753 # Propagate any changes to lui.__class__ by extensions
750 754 ui.__class__ = lui.__class__
751 755
752 756 # (uisetup and extsetup are handled in extensions.loadall)
753 757
754 758 for name, module in exts:
755 759 for objname, loadermod, loadername in extraloaders:
756 760 extraobj = getattr(module, objname, None)
757 761 if extraobj is not None:
758 762 getattr(loadermod, loadername)(ui, name, extraobj)
759 763 _loaded.add(name)
760 764
761 765 # (reposetup is handled in hg.repository)
762 766
763 767 addaliases(lui, commands.table)
764 768
765 769 if not lui.configbool("ui", "strict"):
766 770 # All aliases and commands are completely defined, now.
767 771 # Check abbreviation/ambiguity of shell alias again, because shell
768 772 # alias may cause failure of "_parse" (see issue4355)
769 773 shellaliasfn = _checkshellalias(lui, ui, args, precheck=False)
770 774 if shellaliasfn:
771 775 return shellaliasfn()
772 776
773 777 # check for fallback encoding
774 778 fallback = lui.config('ui', 'fallbackencoding')
775 779 if fallback:
776 780 encoding.fallbackencoding = fallback
777 781
778 782 fullargs = args
779 783 cmd, func, args, options, cmdoptions = _parse(lui, args)
780 784
781 785 if options["config"]:
782 786 raise error.Abort(_("option --config may not be abbreviated!"))
783 787 if options["cwd"]:
784 788 raise error.Abort(_("option --cwd may not be abbreviated!"))
785 789 if options["repository"]:
786 790 raise error.Abort(_(
787 791 "option -R has to be separated from other options (e.g. not -qR) "
788 792 "and --repository may only be abbreviated as --repo!"))
789 793
790 794 if options["encoding"]:
791 795 encoding.encoding = options["encoding"]
792 796 if options["encodingmode"]:
793 797 encoding.encodingmode = options["encodingmode"]
794 798 if options["time"]:
795 799 def get_times():
796 800 t = os.times()
797 801 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
798 802 t = (t[0], t[1], t[2], t[3], time.clock())
799 803 return t
800 804 s = get_times()
801 805 def print_time():
802 806 t = get_times()
803 807 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
804 808 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
805 809 atexit.register(print_time)
806 810
807 811 uis = set([ui, lui])
808 812
809 813 if req.repo:
810 814 uis.add(req.repo.ui)
811 815
812 816 if options['verbose'] or options['debug'] or options['quiet']:
813 817 for opt in ('verbose', 'debug', 'quiet'):
814 818 val = str(bool(options[opt]))
815 819 for ui_ in uis:
816 820 ui_.setconfig('ui', opt, val, '--' + opt)
817 821
818 822 if options['traceback']:
819 823 for ui_ in uis:
820 824 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
821 825
822 826 if options['noninteractive']:
823 827 for ui_ in uis:
824 828 ui_.setconfig('ui', 'interactive', 'off', '-y')
825 829
826 830 if cmdoptions.get('insecure', False):
827 831 for ui_ in uis:
828 832 ui_.setconfig('web', 'cacerts', '!', '--insecure')
829 833
830 834 if options['version']:
831 835 return commands.version_(ui)
832 836 if options['help']:
833 837 return commands.help_(ui, cmd, command=cmd is not None)
834 838 elif not cmd:
835 839 return commands.help_(ui, 'shortlist')
836 840
837 841 repo = None
838 842 cmdpats = args[:]
839 843 if not _cmdattr(ui, cmd, func, 'norepo'):
840 844 # use the repo from the request only if we don't have -R
841 845 if not rpath and not cwd:
842 846 repo = req.repo
843 847
844 848 if repo:
845 849 # set the descriptors of the repo ui to those of ui
846 850 repo.ui.fin = ui.fin
847 851 repo.ui.fout = ui.fout
848 852 repo.ui.ferr = ui.ferr
849 853 else:
850 854 try:
851 855 repo = hg.repository(ui, path=path)
852 856 if not repo.local():
853 857 raise error.Abort(_("repository '%s' is not local") % path)
854 858 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
855 859 except error.RequirementError:
856 860 raise
857 861 except error.RepoError:
858 862 if rpath and rpath[-1]: # invalid -R path
859 863 raise
860 864 if not _cmdattr(ui, cmd, func, 'optionalrepo'):
861 865 if (_cmdattr(ui, cmd, func, 'inferrepo') and
862 866 args and not path):
863 867 # try to infer -R from command args
864 868 repos = map(cmdutil.findrepo, args)
865 869 guess = repos[0]
866 870 if guess and repos.count(guess) == len(repos):
867 871 req.args = ['--repository', guess] + fullargs
868 872 return _dispatch(req)
869 873 if not path:
870 874 raise error.RepoError(_("no repository found in '%s'"
871 875 " (.hg not found)")
872 876 % os.getcwd())
873 877 raise
874 878 if repo:
875 879 ui = repo.ui
876 880 if options['hidden']:
877 881 repo = repo.unfiltered()
878 882 args.insert(0, repo)
879 883 elif rpath:
880 884 ui.warn(_("warning: --repository ignored\n"))
881 885
882 886 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
883 887 ui.log("command", '%s\n', msg)
884 888 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
885 889 try:
886 890 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
887 891 cmdpats, cmdoptions)
888 892 finally:
889 893 if repo and repo != req.repo:
890 894 repo.close()
891 895
892 896 def lsprofile(ui, func, fp):
893 897 format = ui.config('profiling', 'format', default='text')
894 898 field = ui.config('profiling', 'sort', default='inlinetime')
895 899 limit = ui.configint('profiling', 'limit', default=30)
896 900 climit = ui.configint('profiling', 'nested', default=0)
897 901
898 902 if format not in ['text', 'kcachegrind']:
899 903 ui.warn(_("unrecognized profiling format '%s'"
900 904 " - Ignored\n") % format)
901 905 format = 'text'
902 906
903 907 try:
904 908 from . import lsprof
905 909 except ImportError:
906 910 raise error.Abort(_(
907 911 'lsprof not available - install from '
908 912 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
909 913 p = lsprof.Profiler()
910 914 p.enable(subcalls=True)
911 915 try:
912 916 return func()
913 917 finally:
914 918 p.disable()
915 919
916 920 if format == 'kcachegrind':
917 921 from . import lsprofcalltree
918 922 calltree = lsprofcalltree.KCacheGrind(p)
919 923 calltree.output(fp)
920 924 else:
921 925 # format == 'text'
922 926 stats = lsprof.Stats(p.getstats())
923 927 stats.sort(field)
924 928 stats.pprint(limit=limit, file=fp, climit=climit)
925 929
926 930 def flameprofile(ui, func, fp):
927 931 try:
928 932 from flamegraph import flamegraph
929 933 except ImportError:
930 934 raise error.Abort(_(
931 935 'flamegraph not available - install from '
932 936 'https://github.com/evanhempel/python-flamegraph'))
933 937 # developer config: profiling.freq
934 938 freq = ui.configint('profiling', 'freq', default=1000)
935 939 filter_ = None
936 940 collapse_recursion = True
937 941 thread = flamegraph.ProfileThread(fp, 1.0 / freq,
938 942 filter_, collapse_recursion)
939 943 start_time = time.clock()
940 944 try:
941 945 thread.start()
942 946 func()
943 947 finally:
944 948 thread.stop()
945 949 thread.join()
946 950 print('Collected %d stack frames (%d unique) in %2.2f seconds.' % (
947 951 time.clock() - start_time, thread.num_frames(),
948 952 thread.num_frames(unique=True)))
949 953
950 954
951 955 def statprofile(ui, func, fp):
952 956 try:
953 957 import statprof
954 958 except ImportError:
955 959 raise error.Abort(_(
956 960 'statprof not available - install using "easy_install statprof"'))
957 961
958 962 freq = ui.configint('profiling', 'freq', default=1000)
959 963 if freq > 0:
960 964 statprof.reset(freq)
961 965 else:
962 966 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
963 967
964 968 statprof.start()
965 969 try:
966 970 return func()
967 971 finally:
968 972 statprof.stop()
969 973 statprof.display(fp)
970 974
971 975 def _runcommand(ui, options, cmd, cmdfunc):
972 976 """Enables the profiler if applicable.
973 977
974 978 ``profiling.enabled`` - boolean config that enables or disables profiling
975 979 """
976 980 def checkargs():
977 981 try:
978 982 return cmdfunc()
979 983 except error.SignatureError:
980 984 raise error.CommandError(cmd, _("invalid arguments"))
981 985
982 986 if options['profile'] or ui.configbool('profiling', 'enabled'):
983 987 profiler = os.getenv('HGPROF')
984 988 if profiler is None:
985 989 profiler = ui.config('profiling', 'type', default='ls')
986 990 if profiler not in ('ls', 'stat', 'flame'):
987 991 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
988 992 profiler = 'ls'
989 993
990 994 output = ui.config('profiling', 'output')
991 995
992 996 if output == 'blackbox':
993 997 fp = util.stringio()
994 998 elif output:
995 999 path = ui.expandpath(output)
996 1000 fp = open(path, 'wb')
997 1001 else:
998 1002 fp = sys.stderr
999 1003
1000 1004 try:
1001 1005 if profiler == 'ls':
1002 1006 return lsprofile(ui, checkargs, fp)
1003 1007 elif profiler == 'flame':
1004 1008 return flameprofile(ui, checkargs, fp)
1005 1009 else:
1006 1010 return statprofile(ui, checkargs, fp)
1007 1011 finally:
1008 1012 if output:
1009 1013 if output == 'blackbox':
1010 1014 val = "Profile:\n%s" % fp.getvalue()
1011 1015 # ui.log treats the input as a format string,
1012 1016 # so we need to escape any % signs.
1013 1017 val = val.replace('%', '%%')
1014 1018 ui.log('profile', val)
1015 1019 fp.close()
1016 1020 else:
1017 1021 return checkargs()
1018 1022
1019 1023 def _exceptionwarning(ui):
1020 1024 """Produce a warning message for the current active exception"""
1021 1025
1022 1026 # For compatibility checking, we discard the portion of the hg
1023 1027 # version after the + on the assumption that if a "normal
1024 1028 # user" is running a build with a + in it the packager
1025 1029 # probably built from fairly close to a tag and anyone with a
1026 1030 # 'make local' copy of hg (where the version number can be out
1027 1031 # of date) will be clueful enough to notice the implausible
1028 1032 # version number and try updating.
1029 1033 ct = util.versiontuple(n=2)
1030 1034 worst = None, ct, ''
1031 1035 if ui.config('ui', 'supportcontact', None) is None:
1032 1036 for name, mod in extensions.extensions():
1033 1037 testedwith = getattr(mod, 'testedwith', '')
1034 1038 report = getattr(mod, 'buglink', _('the extension author.'))
1035 1039 if not testedwith.strip():
1036 1040 # We found an untested extension. It's likely the culprit.
1037 1041 worst = name, 'unknown', report
1038 1042 break
1039 1043
1040 1044 # Never blame on extensions bundled with Mercurial.
1041 1045 if testedwith == 'internal':
1042 1046 continue
1043 1047
1044 1048 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
1045 1049 if ct in tested:
1046 1050 continue
1047 1051
1048 1052 lower = [t for t in tested if t < ct]
1049 1053 nearest = max(lower or tested)
1050 1054 if worst[0] is None or nearest < worst[1]:
1051 1055 worst = name, nearest, report
1052 1056 if worst[0] is not None:
1053 1057 name, testedwith, report = worst
1054 1058 if not isinstance(testedwith, str):
1055 1059 testedwith = '.'.join([str(c) for c in testedwith])
1056 1060 warning = (_('** Unknown exception encountered with '
1057 1061 'possibly-broken third-party extension %s\n'
1058 1062 '** which supports versions %s of Mercurial.\n'
1059 1063 '** Please disable %s and try your action again.\n'
1060 1064 '** If that fixes the bug please report it to %s\n')
1061 1065 % (name, testedwith, name, report))
1062 1066 else:
1063 1067 bugtracker = ui.config('ui', 'supportcontact', None)
1064 1068 if bugtracker is None:
1065 1069 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
1066 1070 warning = (_("** unknown exception encountered, "
1067 1071 "please report by visiting\n** ") + bugtracker + '\n')
1068 1072 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
1069 1073 (_("** Mercurial Distributed SCM (version %s)\n") %
1070 1074 util.version()) +
1071 1075 (_("** Extensions loaded: %s\n") %
1072 1076 ", ".join([x[0] for x in extensions.extensions()])))
1073 1077 return warning
1074 1078
1075 1079 def handlecommandexception(ui):
1076 1080 """Produce a warning message for broken commands
1077 1081
1078 1082 Called when handling an exception; the exception is reraised if
1079 1083 this function returns False, ignored otherwise.
1080 1084 """
1081 1085 warning = _exceptionwarning(ui)
1082 1086 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
1083 1087 ui.warn(warning)
1084 1088 return False # re-raise the exception
@@ -1,554 +1,572 b''
1 1 $ HGFOO=BAR; export HGFOO
2 2 $ cat >> $HGRCPATH <<EOF
3 3 > [alias]
4 4 > # should clobber ci but not commit (issue2993)
5 5 > ci = version
6 6 > myinit = init
7 7 > mycommit = commit
8 8 > optionalrepo = showconfig alias.myinit
9 9 > cleanstatus = status -c
10 10 > unknown = bargle
11 11 > ambiguous = s
12 12 > recursive = recursive
13 13 > disabled = email
14 14 > nodefinition =
15 15 > noclosingquotation = '
16 16 > no--cwd = status --cwd elsewhere
17 17 > no-R = status -R elsewhere
18 18 > no--repo = status --repo elsewhere
19 19 > no--repository = status --repository elsewhere
20 20 > no--config = status --config a.config=1
21 21 > mylog = log
22 22 > lognull = log -r null
23 23 > shortlog = log --template '{rev} {node|short} | {date|isodate}\n'
24 24 > positional = log --template '{\$2} {\$1} | {date|isodate}\n'
25 25 > dln = lognull --debug
26 26 > nousage = rollback
27 27 > put = export -r 0 -o "\$FOO/%R.diff"
28 28 > blank = !printf '\n'
29 29 > self = !printf '\$0\n'
30 30 > echoall = !printf '\$@\n'
31 31 > echo1 = !printf '\$1\n'
32 32 > echo2 = !printf '\$2\n'
33 33 > echo13 = !printf '\$1 \$3\n'
34 34 > echotokens = !printf "%s\n" "\$@"
35 35 > count = !hg log -r "\$@" --template=. | wc -c | sed -e 's/ //g'
36 36 > mcount = !hg log \$@ --template=. | wc -c | sed -e 's/ //g'
37 37 > rt = root
38 38 > tglog = log -G --template "{rev}:{node|short}: '{desc}' {branches}\n"
39 39 > idalias = id
40 40 > idaliaslong = id
41 41 > idaliasshell = !echo test
42 42 > parentsshell1 = !echo one
43 43 > parentsshell2 = !echo two
44 44 > escaped1 = !printf 'test\$\$test\n'
45 45 > escaped2 = !sh -c 'echo "HGFOO is \$\$HGFOO"'
46 46 > escaped3 = !sh -c 'echo "\$1 is \$\$\$1"'
47 47 > escaped4 = !printf '\$\$0 \$\$@\n'
48 48 > exit1 = !sh -c 'exit 1'
49 49 >
50 50 > [defaults]
51 51 > mylog = -q
52 52 > lognull = -q
53 53 > log = -v
54 54 > EOF
55 55
56 56
57 57 basic
58 58
59 59 $ hg myinit alias
60 60
61 61
62 62 unknown
63 63
64 64 $ hg unknown
65 65 abort: alias 'unknown' resolves to unknown command 'bargle'
66 66 [255]
67 67 $ hg help unknown
68 68 alias 'unknown' resolves to unknown command 'bargle'
69 69
70 70
71 71 ambiguous
72 72
73 73 $ hg ambiguous
74 74 abort: alias 'ambiguous' resolves to ambiguous command 's'
75 75 [255]
76 76 $ hg help ambiguous
77 77 alias 'ambiguous' resolves to ambiguous command 's'
78 78
79 79
80 80 recursive
81 81
82 82 $ hg recursive
83 83 abort: alias 'recursive' resolves to unknown command 'recursive'
84 84 [255]
85 85 $ hg help recursive
86 86 alias 'recursive' resolves to unknown command 'recursive'
87 87
88 88
89 89 disabled
90 90
91 91 $ hg disabled
92 92 abort: alias 'disabled' resolves to unknown command 'email'
93 93 ('email' is provided by 'patchbomb' extension)
94 94 [255]
95 95 $ hg help disabled
96 96 alias 'disabled' resolves to unknown command 'email'
97 97
98 98 'email' is provided by the following extension:
99 99
100 100 patchbomb command to send changesets as (a series of) patch emails
101 101
102 102 (use "hg help extensions" for information on enabling extensions)
103 103
104 104
105 105 no definition
106 106
107 107 $ hg nodef
108 108 abort: no definition for alias 'nodefinition'
109 109 [255]
110 110 $ hg help nodef
111 111 no definition for alias 'nodefinition'
112 112
113 113
114 114 no closing quotation
115 115
116 116 $ hg noclosing
117 117 abort: error in definition for alias 'noclosingquotation': No closing quotation
118 118 [255]
119 119 $ hg help noclosing
120 120 error in definition for alias 'noclosingquotation': No closing quotation
121 121
122 122
123 123 invalid options
124 124
125 125 $ hg no--cwd
126 126 abort: error in definition for alias 'no--cwd': --cwd may only be given on the command line
127 127 [255]
128 128 $ hg help no--cwd
129 129 error in definition for alias 'no--cwd': --cwd may only be given on the
130 130 command line
131 131 $ hg no-R
132 132 abort: error in definition for alias 'no-R': -R may only be given on the command line
133 133 [255]
134 134 $ hg help no-R
135 135 error in definition for alias 'no-R': -R may only be given on the command line
136 136 $ hg no--repo
137 137 abort: error in definition for alias 'no--repo': --repo may only be given on the command line
138 138 [255]
139 139 $ hg help no--repo
140 140 error in definition for alias 'no--repo': --repo may only be given on the
141 141 command line
142 142 $ hg no--repository
143 143 abort: error in definition for alias 'no--repository': --repository may only be given on the command line
144 144 [255]
145 145 $ hg help no--repository
146 146 error in definition for alias 'no--repository': --repository may only be given
147 147 on the command line
148 148 $ hg no--config
149 149 abort: error in definition for alias 'no--config': --config may only be given on the command line
150 150 [255]
151 151
152 152 optional repository
153 153
154 154 #if no-outer-repo
155 155 $ hg optionalrepo
156 156 init
157 157 #endif
158 158 $ cd alias
159 159 $ cat > .hg/hgrc <<EOF
160 160 > [alias]
161 161 > myinit = init -q
162 162 > EOF
163 163 $ hg optionalrepo
164 164 init -q
165 165
166 166 no usage
167 167
168 168 $ hg nousage
169 169 no rollback information available
170 170 [1]
171 171
172 172 $ echo foo > foo
173 173 $ hg commit -Amfoo
174 174 adding foo
175 175
176 176 infer repository
177 177
178 178 $ cd ..
179 179
180 180 #if no-outer-repo
181 181 $ hg shortlog alias/foo
182 182 0 e63c23eaa88a | 1970-01-01 00:00 +0000
183 183 #endif
184 184
185 185 $ cd alias
186 186
187 187 with opts
188 188
189 189 $ hg cleanst
190 190 C foo
191 191
192 192
193 193 with opts and whitespace
194 194
195 195 $ hg shortlog
196 196 0 e63c23eaa88a | 1970-01-01 00:00 +0000
197 197
198 198 positional arguments
199 199
200 200 $ hg positional
201 201 abort: too few arguments for command alias
202 202 [255]
203 203 $ hg positional a
204 204 abort: too few arguments for command alias
205 205 [255]
206 206 $ hg positional 'node|short' rev
207 207 0 e63c23eaa88a | 1970-01-01 00:00 +0000
208 208
209 209 interaction with defaults
210 210
211 211 $ hg mylog
212 212 0:e63c23eaa88a
213 213 $ hg lognull
214 214 -1:000000000000
215 215
216 216
217 217 properly recursive
218 218
219 219 $ hg dln
220 220 changeset: -1:0000000000000000000000000000000000000000
221 221 phase: public
222 222 parent: -1:0000000000000000000000000000000000000000
223 223 parent: -1:0000000000000000000000000000000000000000
224 224 manifest: -1:0000000000000000000000000000000000000000
225 225 user:
226 226 date: Thu Jan 01 00:00:00 1970 +0000
227 227 extra: branch=default
228 228
229 229
230 230
231 231 path expanding
232 232
233 233 $ FOO=`pwd` hg put
234 234 $ cat 0.diff
235 235 # HG changeset patch
236 236 # User test
237 237 # Date 0 0
238 238 # Thu Jan 01 00:00:00 1970 +0000
239 239 # Node ID e63c23eaa88ae77967edcf4ea194d31167c478b0
240 240 # Parent 0000000000000000000000000000000000000000
241 241 foo
242 242
243 243 diff -r 000000000000 -r e63c23eaa88a foo
244 244 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
245 245 +++ b/foo Thu Jan 01 00:00:00 1970 +0000
246 246 @@ -0,0 +1,1 @@
247 247 +foo
248 248
249 249
250 250 simple shell aliases
251 251
252 252 $ hg blank
253 253
254 254 $ hg blank foo
255 255
256 256 $ hg self
257 257 self
258 258 $ hg echoall
259 259
260 260 $ hg echoall foo
261 261 foo
262 262 $ hg echoall 'test $2' foo
263 263 test $2 foo
264 264 $ hg echoall 'test $@' foo '$@'
265 265 test $@ foo $@
266 266 $ hg echoall 'test "$@"' foo '"$@"'
267 267 test "$@" foo "$@"
268 268 $ hg echo1 foo bar baz
269 269 foo
270 270 $ hg echo2 foo bar baz
271 271 bar
272 272 $ hg echo13 foo bar baz test
273 273 foo baz
274 274 $ hg echo2 foo
275 275
276 276 $ hg echotokens
277 277
278 278 $ hg echotokens foo 'bar $1 baz'
279 279 foo
280 280 bar $1 baz
281 281 $ hg echotokens 'test $2' foo
282 282 test $2
283 283 foo
284 284 $ hg echotokens 'test $@' foo '$@'
285 285 test $@
286 286 foo
287 287 $@
288 288 $ hg echotokens 'test "$@"' foo '"$@"'
289 289 test "$@"
290 290 foo
291 291 "$@"
292 292 $ echo bar > bar
293 293 $ hg commit -qA -m bar
294 294 $ hg count .
295 295 1
296 296 $ hg count 'branch(default)'
297 297 2
298 298 $ hg mcount -r '"branch(default)"'
299 299 2
300 300
301 301 $ hg tglog
302 302 @ 1:042423737847: 'bar'
303 303 |
304 304 o 0:e63c23eaa88a: 'foo'
305 305
306 306
307 307
308 308 shadowing
309 309
310 310 $ hg i
311 311 hg: command 'i' is ambiguous:
312 312 idalias idaliaslong idaliasshell identify import incoming init
313 313 [255]
314 314 $ hg id
315 315 042423737847 tip
316 316 $ hg ida
317 317 hg: command 'ida' is ambiguous:
318 318 idalias idaliaslong idaliasshell
319 319 [255]
320 320 $ hg idalias
321 321 042423737847 tip
322 322 $ hg idaliasl
323 323 042423737847 tip
324 324 $ hg idaliass
325 325 test
326 326 $ hg parentsshell
327 327 hg: command 'parentsshell' is ambiguous:
328 328 parentsshell1 parentsshell2
329 329 [255]
330 330 $ hg parentsshell1
331 331 one
332 332 $ hg parentsshell2
333 333 two
334 334
335 335
336 336 shell aliases with global options
337 337
338 338 $ hg init sub
339 339 $ cd sub
340 340 $ hg count 'branch(default)'
341 341 abort: unknown revision 'default'!
342 342 0
343 343 $ hg -v count 'branch(default)'
344 344 abort: unknown revision 'default'!
345 345 0
346 346 $ hg -R .. count 'branch(default)'
347 347 abort: unknown revision 'default'!
348 348 0
349 349 $ hg --cwd .. count 'branch(default)'
350 350 2
351 351 $ hg echoall --cwd ..
352 352
353 353
354 354
355 355 repo specific shell aliases
356 356
357 357 $ cat >> .hg/hgrc <<EOF
358 358 > [alias]
359 359 > subalias = !echo sub
360 360 > EOF
361 361 $ cat >> ../.hg/hgrc <<EOF
362 362 > [alias]
363 363 > mainalias = !echo main
364 364 > EOF
365 365
366 366
367 367 shell alias defined in current repo
368 368
369 369 $ hg subalias
370 370 sub
371 371 $ hg --cwd .. subalias > /dev/null
372 372 hg: unknown command 'subalias'
373 373 (did you mean idalias?)
374 374 [255]
375 375 $ hg -R .. subalias > /dev/null
376 376 hg: unknown command 'subalias'
377 377 (did you mean idalias?)
378 378 [255]
379 379
380 380
381 381 shell alias defined in other repo
382 382
383 383 $ hg mainalias > /dev/null
384 384 hg: unknown command 'mainalias'
385 385 (did you mean idalias?)
386 386 [255]
387 387 $ hg -R .. mainalias
388 388 main
389 389 $ hg --cwd .. mainalias
390 390 main
391 391
392 392 typos get useful suggestions
393 393 $ hg --cwd .. manalias
394 394 hg: unknown command 'manalias'
395 395 (did you mean one of idalias, mainalias, manifest?)
396 396 [255]
397 397
398 398 shell aliases with escaped $ chars
399 399
400 400 $ hg escaped1
401 401 test$test
402 402 $ hg escaped2
403 403 HGFOO is BAR
404 404 $ hg escaped3 HGFOO
405 405 HGFOO is BAR
406 406 $ hg escaped4 test
407 407 $0 $@
408 408
409 409 abbreviated name, which matches against both shell alias and the
410 410 command provided extension, should be aborted.
411 411
412 412 $ cat >> .hg/hgrc <<EOF
413 413 > [extensions]
414 414 > hgext.rebase =
415 415 > EOF
416 416 #if windows
417 417 $ cat >> .hg/hgrc <<EOF
418 418 > [alias]
419 419 > rebate = !echo this is %HG_ARGS%
420 420 > EOF
421 421 #else
422 422 $ cat >> .hg/hgrc <<EOF
423 423 > [alias]
424 424 > rebate = !echo this is \$HG_ARGS
425 425 > EOF
426 426 #endif
427 427 $ hg reba
428 428 hg: command 'reba' is ambiguous:
429 429 rebase rebate
430 430 [255]
431 431 $ hg rebat
432 432 this is rebate
433 433 $ hg rebat --foo-bar
434 434 this is rebate --foo-bar
435 435
436 436 invalid arguments
437 437
438 438 $ hg rt foo
439 439 hg rt: invalid arguments
440 440 hg rt
441 441
442 442 alias for: hg root
443 443
444 444 (use "hg rt -h" to show more help)
445 445 [255]
446 446
447 447 invalid global arguments for normal commands, aliases, and shell aliases
448 448
449 449 $ hg --invalid root
450 450 hg: option --invalid not recognized
451 451 Mercurial Distributed SCM
452 452
453 453 basic commands:
454 454
455 455 add add the specified files on the next commit
456 456 annotate show changeset information by line for each file
457 457 clone make a copy of an existing repository
458 458 commit commit the specified files or all outstanding changes
459 459 diff diff repository (or selected files)
460 460 export dump the header and diffs for one or more changesets
461 461 forget forget the specified files on the next commit
462 462 init create a new repository in the given directory
463 463 log show revision history of entire repository or files
464 464 merge merge another revision into working directory
465 465 pull pull changes from the specified source
466 466 push push changes to the specified destination
467 467 remove remove the specified files on the next commit
468 468 serve start stand-alone webserver
469 469 status show changed files in the working directory
470 470 summary summarize working directory state
471 471 update update working directory (or switch revisions)
472 472
473 473 (use "hg help" for the full list of commands or "hg -v" for details)
474 474 [255]
475 475 $ hg --invalid mylog
476 476 hg: option --invalid not recognized
477 477 Mercurial Distributed SCM
478 478
479 479 basic commands:
480 480
481 481 add add the specified files on the next commit
482 482 annotate show changeset information by line for each file
483 483 clone make a copy of an existing repository
484 484 commit commit the specified files or all outstanding changes
485 485 diff diff repository (or selected files)
486 486 export dump the header and diffs for one or more changesets
487 487 forget forget the specified files on the next commit
488 488 init create a new repository in the given directory
489 489 log show revision history of entire repository or files
490 490 merge merge another revision into working directory
491 491 pull pull changes from the specified source
492 492 push push changes to the specified destination
493 493 remove remove the specified files on the next commit
494 494 serve start stand-alone webserver
495 495 status show changed files in the working directory
496 496 summary summarize working directory state
497 497 update update working directory (or switch revisions)
498 498
499 499 (use "hg help" for the full list of commands or "hg -v" for details)
500 500 [255]
501 501 $ hg --invalid blank
502 502 hg: option --invalid not recognized
503 503 Mercurial Distributed SCM
504 504
505 505 basic commands:
506 506
507 507 add add the specified files on the next commit
508 508 annotate show changeset information by line for each file
509 509 clone make a copy of an existing repository
510 510 commit commit the specified files or all outstanding changes
511 511 diff diff repository (or selected files)
512 512 export dump the header and diffs for one or more changesets
513 513 forget forget the specified files on the next commit
514 514 init create a new repository in the given directory
515 515 log show revision history of entire repository or files
516 516 merge merge another revision into working directory
517 517 pull pull changes from the specified source
518 518 push push changes to the specified destination
519 519 remove remove the specified files on the next commit
520 520 serve start stand-alone webserver
521 521 status show changed files in the working directory
522 522 summary summarize working directory state
523 523 update update working directory (or switch revisions)
524 524
525 525 (use "hg help" for the full list of commands or "hg -v" for details)
526 526 [255]
527 527
528 environment variable changes in alias commands
529
530 $ cat > $TESTTMP/setcount.py <<EOF
531 > import os
532 > def uisetup(ui):
533 > os.environ['COUNT'] = '2'
534 > EOF
535
536 $ cat >> $HGRCPATH <<'EOF'
537 > [extensions]
538 > setcount = $TESTTMP/setcount.py
539 > [alias]
540 > showcount = log -T "$COUNT\n" -r .
541 > EOF
542
543 $ COUNT=1 hg showcount
544 2
545
528 546 This should show id:
529 547
530 548 $ hg --config alias.log='id' log
531 549 000000000000 tip
532 550
533 551 This shouldn't:
534 552
535 553 $ hg --config alias.log='id' history
536 554
537 555 $ cd ../..
538 556
539 557 return code of command and shell aliases:
540 558
541 559 $ hg mycommit -R alias
542 560 nothing changed
543 561 [1]
544 562 $ hg exit1
545 563 [1]
546 564
547 565 #if no-outer-repo
548 566 $ hg root
549 567 abort: no repository found in '$TESTTMP' (.hg not found)!
550 568 [255]
551 569 $ hg --config alias.hgroot='!hg root' hgroot
552 570 abort: no repository found in '$TESTTMP' (.hg not found)!
553 571 [255]
554 572 #endif
General Comments 0
You need to be logged in to leave comments. Login now