##// END OF EJS Templates
dispatch: offer near-edit-distance suggestions for {file,rev}set functions...
Augie Fackler -
r24221:4e240d6a default
parent child Browse files
Show More
@@ -1,951 +1,968 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 import difflib
10 11 import util, commands, hg, fancyopts, extensions, hook, error
11 12 import cmdutil, encoding
12 13 import ui as uimod
13 14
14 15 class request(object):
15 16 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
16 17 ferr=None):
17 18 self.args = args
18 19 self.ui = ui
19 20 self.repo = repo
20 21
21 22 # input/output/error streams
22 23 self.fin = fin
23 24 self.fout = fout
24 25 self.ferr = ferr
25 26
26 27 def run():
27 28 "run the command in sys.argv"
28 29 sys.exit((dispatch(request(sys.argv[1:])) or 0) & 255)
29 30
31 def _getsimilar(symbols, value):
32 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
33 # The cutoff for similarity here is pretty arbitrary. It should
34 # probably be investigated and tweaked.
35 return [s for s in symbols if sim(s) > 0.6]
36
30 37 def _formatparse(write, inst):
38 similar = []
39 if isinstance(inst, error.UnknownIdentifier):
40 # make sure to check fileset first, as revset can invoke fileset
41 similar = _getsimilar(inst.symbols, inst.function)
31 42 if len(inst.args) > 1:
32 43 write(_("hg: parse error at %s: %s\n") %
33 44 (inst.args[1], inst.args[0]))
34 45 if (inst.args[0][0] == ' '):
35 46 write(_("unexpected leading whitespace\n"))
36 47 else:
37 48 write(_("hg: parse error: %s\n") % inst.args[0])
49 if similar:
50 if len(similar) == 1:
51 write(_("(did you mean %r?)\n") % similar[0])
52 else:
53 ss = ", ".join(sorted(similar))
54 write(_("(did you mean one of %s?)\n") % ss)
38 55
39 56 def dispatch(req):
40 57 "run the command specified in req.args"
41 58 if req.ferr:
42 59 ferr = req.ferr
43 60 elif req.ui:
44 61 ferr = req.ui.ferr
45 62 else:
46 63 ferr = sys.stderr
47 64
48 65 try:
49 66 if not req.ui:
50 67 req.ui = uimod.ui()
51 68 if '--traceback' in req.args:
52 69 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
53 70
54 71 # set ui streams from the request
55 72 if req.fin:
56 73 req.ui.fin = req.fin
57 74 if req.fout:
58 75 req.ui.fout = req.fout
59 76 if req.ferr:
60 77 req.ui.ferr = req.ferr
61 78 except util.Abort, inst:
62 79 ferr.write(_("abort: %s\n") % inst)
63 80 if inst.hint:
64 81 ferr.write(_("(%s)\n") % inst.hint)
65 82 return -1
66 83 except error.ParseError, inst:
67 84 _formatparse(ferr.write, inst)
68 85 return -1
69 86
70 87 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
71 88 starttime = time.time()
72 89 ret = None
73 90 try:
74 91 ret = _runcatch(req)
75 92 return ret
76 93 finally:
77 94 duration = time.time() - starttime
78 95 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
79 96 msg, ret or 0, duration)
80 97
81 98 def _runcatch(req):
82 99 def catchterm(*args):
83 100 raise error.SignalInterrupt
84 101
85 102 ui = req.ui
86 103 try:
87 104 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
88 105 num = getattr(signal, name, None)
89 106 if num:
90 107 signal.signal(num, catchterm)
91 108 except ValueError:
92 109 pass # happens if called in a thread
93 110
94 111 try:
95 112 try:
96 113 debugger = 'pdb'
97 114 debugtrace = {
98 115 'pdb' : pdb.set_trace
99 116 }
100 117 debugmortem = {
101 118 'pdb' : pdb.post_mortem
102 119 }
103 120
104 121 # read --config before doing anything else
105 122 # (e.g. to change trust settings for reading .hg/hgrc)
106 123 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
107 124
108 125 if req.repo:
109 126 # copy configs that were passed on the cmdline (--config) to
110 127 # the repo ui
111 128 for sec, name, val in cfgs:
112 129 req.repo.ui.setconfig(sec, name, val, source='--config')
113 130
114 131 # if we are in HGPLAIN mode, then disable custom debugging
115 132 debugger = ui.config("ui", "debugger")
116 133 debugmod = pdb
117 134 if not debugger or ui.plain():
118 135 debugger = 'pdb'
119 136 elif '--debugger' in req.args:
120 137 # This import can be slow for fancy debuggers, so only
121 138 # do it when absolutely necessary, i.e. when actual
122 139 # debugging has been requested
123 140 try:
124 141 debugmod = __import__(debugger)
125 142 except ImportError:
126 143 pass # Leave debugmod = pdb
127 144
128 145 debugtrace[debugger] = debugmod.set_trace
129 146 debugmortem[debugger] = debugmod.post_mortem
130 147
131 148 # enter the debugger before command execution
132 149 if '--debugger' in req.args:
133 150 ui.warn(_("entering debugger - "
134 151 "type c to continue starting hg or h for help\n"))
135 152
136 153 if (debugger != 'pdb' and
137 154 debugtrace[debugger] == debugtrace['pdb']):
138 155 ui.warn(_("%s debugger specified "
139 156 "but its module was not found\n") % debugger)
140 157
141 158 debugtrace[debugger]()
142 159 try:
143 160 return _dispatch(req)
144 161 finally:
145 162 ui.flush()
146 163 except: # re-raises
147 164 # enter the debugger when we hit an exception
148 165 if '--debugger' in req.args:
149 166 traceback.print_exc()
150 167 debugmortem[debugger](sys.exc_info()[2])
151 168 ui.traceback()
152 169 raise
153 170
154 171 # Global exception handling, alphabetically
155 172 # Mercurial-specific first, followed by built-in and library exceptions
156 173 except error.AmbiguousCommand, inst:
157 174 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
158 175 (inst.args[0], " ".join(inst.args[1])))
159 176 except error.ParseError, inst:
160 177 _formatparse(ui.warn, inst)
161 178 return -1
162 179 except error.LockHeld, inst:
163 180 if inst.errno == errno.ETIMEDOUT:
164 181 reason = _('timed out waiting for lock held by %s') % inst.locker
165 182 else:
166 183 reason = _('lock held by %s') % inst.locker
167 184 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
168 185 except error.LockUnavailable, inst:
169 186 ui.warn(_("abort: could not lock %s: %s\n") %
170 187 (inst.desc or inst.filename, inst.strerror))
171 188 except error.CommandError, inst:
172 189 if inst.args[0]:
173 190 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
174 191 commands.help_(ui, inst.args[0], full=False, command=True)
175 192 else:
176 193 ui.warn(_("hg: %s\n") % inst.args[1])
177 194 commands.help_(ui, 'shortlist')
178 195 except error.OutOfBandError, inst:
179 196 ui.warn(_("abort: remote error:\n"))
180 197 ui.warn(''.join(inst.args))
181 198 except error.RepoError, inst:
182 199 ui.warn(_("abort: %s!\n") % inst)
183 200 if inst.hint:
184 201 ui.warn(_("(%s)\n") % inst.hint)
185 202 except error.ResponseError, inst:
186 203 ui.warn(_("abort: %s") % inst.args[0])
187 204 if not isinstance(inst.args[1], basestring):
188 205 ui.warn(" %r\n" % (inst.args[1],))
189 206 elif not inst.args[1]:
190 207 ui.warn(_(" empty string\n"))
191 208 else:
192 209 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
193 210 except error.CensoredNodeError, inst:
194 211 ui.warn(_("abort: file censored %s!\n") % inst)
195 212 except error.RevlogError, inst:
196 213 ui.warn(_("abort: %s!\n") % inst)
197 214 except error.SignalInterrupt:
198 215 ui.warn(_("killed!\n"))
199 216 except error.UnknownCommand, inst:
200 217 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
201 218 try:
202 219 # check if the command is in a disabled extension
203 220 # (but don't check for extensions themselves)
204 221 commands.help_(ui, inst.args[0], unknowncmd=True)
205 222 except error.UnknownCommand:
206 223 commands.help_(ui, 'shortlist')
207 224 except error.InterventionRequired, inst:
208 225 ui.warn("%s\n" % inst)
209 226 return 1
210 227 except util.Abort, inst:
211 228 ui.warn(_("abort: %s\n") % inst)
212 229 if inst.hint:
213 230 ui.warn(_("(%s)\n") % inst.hint)
214 231 except ImportError, inst:
215 232 ui.warn(_("abort: %s!\n") % inst)
216 233 m = str(inst).split()[-1]
217 234 if m in "mpatch bdiff".split():
218 235 ui.warn(_("(did you forget to compile extensions?)\n"))
219 236 elif m in "zlib".split():
220 237 ui.warn(_("(is your Python install correct?)\n"))
221 238 except IOError, inst:
222 239 if util.safehasattr(inst, "code"):
223 240 ui.warn(_("abort: %s\n") % inst)
224 241 elif util.safehasattr(inst, "reason"):
225 242 try: # usually it is in the form (errno, strerror)
226 243 reason = inst.reason.args[1]
227 244 except (AttributeError, IndexError):
228 245 # it might be anything, for example a string
229 246 reason = inst.reason
230 247 if isinstance(reason, unicode):
231 248 # SSLError of Python 2.7.9 contains a unicode
232 249 reason = reason.encode(encoding.encoding, 'replace')
233 250 ui.warn(_("abort: error: %s\n") % reason)
234 251 elif (util.safehasattr(inst, "args")
235 252 and inst.args and inst.args[0] == errno.EPIPE):
236 253 if ui.debugflag:
237 254 ui.warn(_("broken pipe\n"))
238 255 elif getattr(inst, "strerror", None):
239 256 if getattr(inst, "filename", None):
240 257 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
241 258 else:
242 259 ui.warn(_("abort: %s\n") % inst.strerror)
243 260 else:
244 261 raise
245 262 except OSError, inst:
246 263 if getattr(inst, "filename", None) is not None:
247 264 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
248 265 else:
249 266 ui.warn(_("abort: %s\n") % inst.strerror)
250 267 except KeyboardInterrupt:
251 268 try:
252 269 ui.warn(_("interrupted!\n"))
253 270 except IOError, inst:
254 271 if inst.errno == errno.EPIPE:
255 272 if ui.debugflag:
256 273 ui.warn(_("\nbroken pipe\n"))
257 274 else:
258 275 raise
259 276 except MemoryError:
260 277 ui.warn(_("abort: out of memory\n"))
261 278 except SystemExit, inst:
262 279 # Commands shouldn't sys.exit directly, but give a return code.
263 280 # Just in case catch this and and pass exit code to caller.
264 281 return inst.code
265 282 except socket.error, inst:
266 283 ui.warn(_("abort: %s\n") % inst.args[-1])
267 284 except: # re-raises
268 285 myver = util.version()
269 286 # For compatibility checking, we discard the portion of the hg
270 287 # version after the + on the assumption that if a "normal
271 288 # user" is running a build with a + in it the packager
272 289 # probably built from fairly close to a tag and anyone with a
273 290 # 'make local' copy of hg (where the version number can be out
274 291 # of date) will be clueful enough to notice the implausible
275 292 # version number and try updating.
276 293 compare = myver.split('+')[0]
277 294 ct = tuplever(compare)
278 295 worst = None, ct, ''
279 296 for name, mod in extensions.extensions():
280 297 testedwith = getattr(mod, 'testedwith', '')
281 298 report = getattr(mod, 'buglink', _('the extension author.'))
282 299 if not testedwith.strip():
283 300 # We found an untested extension. It's likely the culprit.
284 301 worst = name, 'unknown', report
285 302 break
286 303
287 304 # Never blame on extensions bundled with Mercurial.
288 305 if testedwith == 'internal':
289 306 continue
290 307
291 308 tested = [tuplever(t) for t in testedwith.split()]
292 309 if ct in tested:
293 310 continue
294 311
295 312 lower = [t for t in tested if t < ct]
296 313 nearest = max(lower or tested)
297 314 if worst[0] is None or nearest < worst[1]:
298 315 worst = name, nearest, report
299 316 if worst[0] is not None:
300 317 name, testedwith, report = worst
301 318 if not isinstance(testedwith, str):
302 319 testedwith = '.'.join([str(c) for c in testedwith])
303 320 warning = (_('** Unknown exception encountered with '
304 321 'possibly-broken third-party extension %s\n'
305 322 '** which supports versions %s of Mercurial.\n'
306 323 '** Please disable %s and try your action again.\n'
307 324 '** If that fixes the bug please report it to %s\n')
308 325 % (name, testedwith, name, report))
309 326 else:
310 327 warning = (_("** unknown exception encountered, "
311 328 "please report by visiting\n") +
312 329 _("** http://mercurial.selenic.com/wiki/BugTracker\n"))
313 330 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
314 331 (_("** Mercurial Distributed SCM (version %s)\n") % myver) +
315 332 (_("** Extensions loaded: %s\n") %
316 333 ", ".join([x[0] for x in extensions.extensions()])))
317 334 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
318 335 ui.warn(warning)
319 336 raise
320 337
321 338 return -1
322 339
323 340 def tuplever(v):
324 341 try:
325 342 # Assertion: tuplever is only used for extension compatibility
326 343 # checking. Otherwise, the discarding of extra version fields is
327 344 # incorrect.
328 345 return tuple([int(i) for i in v.split('.')[0:2]])
329 346 except ValueError:
330 347 return tuple()
331 348
332 349 def aliasargs(fn, givenargs):
333 350 args = getattr(fn, 'args', [])
334 351 if args:
335 352 cmd = ' '.join(map(util.shellquote, args))
336 353
337 354 nums = []
338 355 def replacer(m):
339 356 num = int(m.group(1)) - 1
340 357 nums.append(num)
341 358 if num < len(givenargs):
342 359 return givenargs[num]
343 360 raise util.Abort(_('too few arguments for command alias'))
344 361 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
345 362 givenargs = [x for i, x in enumerate(givenargs)
346 363 if i not in nums]
347 364 args = shlex.split(cmd)
348 365 return args + givenargs
349 366
350 367 def aliasinterpolate(name, args, cmd):
351 368 '''interpolate args into cmd for shell aliases
352 369
353 370 This also handles $0, $@ and "$@".
354 371 '''
355 372 # util.interpolate can't deal with "$@" (with quotes) because it's only
356 373 # built to match prefix + patterns.
357 374 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
358 375 replacemap['$0'] = name
359 376 replacemap['$$'] = '$'
360 377 replacemap['$@'] = ' '.join(args)
361 378 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
362 379 # parameters, separated out into words. Emulate the same behavior here by
363 380 # quoting the arguments individually. POSIX shells will then typically
364 381 # tokenize each argument into exactly one word.
365 382 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
366 383 # escape '\$' for regex
367 384 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
368 385 r = re.compile(regex)
369 386 return r.sub(lambda x: replacemap[x.group()], cmd)
370 387
371 388 class cmdalias(object):
372 389 def __init__(self, name, definition, cmdtable):
373 390 self.name = self.cmd = name
374 391 self.cmdname = ''
375 392 self.definition = definition
376 393 self.fn = None
377 394 self.args = []
378 395 self.opts = []
379 396 self.help = ''
380 397 self.norepo = True
381 398 self.optionalrepo = False
382 399 self.badalias = None
383 400 self.unknowncmd = False
384 401
385 402 try:
386 403 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
387 404 for alias, e in cmdtable.iteritems():
388 405 if e is entry:
389 406 self.cmd = alias
390 407 break
391 408 self.shadows = True
392 409 except error.UnknownCommand:
393 410 self.shadows = False
394 411
395 412 if not self.definition:
396 413 self.badalias = _("no definition for alias '%s'") % self.name
397 414 return
398 415
399 416 if self.definition.startswith('!'):
400 417 self.shell = True
401 418 def fn(ui, *args):
402 419 env = {'HG_ARGS': ' '.join((self.name,) + args)}
403 420 def _checkvar(m):
404 421 if m.groups()[0] == '$':
405 422 return m.group()
406 423 elif int(m.groups()[0]) <= len(args):
407 424 return m.group()
408 425 else:
409 426 ui.debug("No argument found for substitution "
410 427 "of %i variable in alias '%s' definition."
411 428 % (int(m.groups()[0]), self.name))
412 429 return ''
413 430 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
414 431 cmd = aliasinterpolate(self.name, args, cmd)
415 432 return ui.system(cmd, environ=env)
416 433 self.fn = fn
417 434 return
418 435
419 436 try:
420 437 args = shlex.split(self.definition)
421 438 except ValueError, inst:
422 439 self.badalias = (_("error in definition for alias '%s': %s")
423 440 % (self.name, inst))
424 441 return
425 442 self.cmdname = cmd = args.pop(0)
426 443 args = map(util.expandpath, args)
427 444
428 445 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
429 446 if _earlygetopt([invalidarg], args):
430 447 self.badalias = (_("error in definition for alias '%s': %s may "
431 448 "only be given on the command line")
432 449 % (self.name, invalidarg))
433 450 return
434 451
435 452 try:
436 453 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
437 454 if len(tableentry) > 2:
438 455 self.fn, self.opts, self.help = tableentry
439 456 else:
440 457 self.fn, self.opts = tableentry
441 458
442 459 self.args = aliasargs(self.fn, args)
443 460 if cmd not in commands.norepo.split(' '):
444 461 self.norepo = False
445 462 if cmd in commands.optionalrepo.split(' '):
446 463 self.optionalrepo = True
447 464 if self.help.startswith("hg " + cmd):
448 465 # drop prefix in old-style help lines so hg shows the alias
449 466 self.help = self.help[4 + len(cmd):]
450 467 self.__doc__ = self.fn.__doc__
451 468
452 469 except error.UnknownCommand:
453 470 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
454 471 % (self.name, cmd))
455 472 self.unknowncmd = True
456 473 except error.AmbiguousCommand:
457 474 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
458 475 % (self.name, cmd))
459 476
460 477 def __call__(self, ui, *args, **opts):
461 478 if self.badalias:
462 479 hint = None
463 480 if self.unknowncmd:
464 481 try:
465 482 # check if the command is in a disabled extension
466 483 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
467 484 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
468 485 except error.UnknownCommand:
469 486 pass
470 487 raise util.Abort(self.badalias, hint=hint)
471 488 if self.shadows:
472 489 ui.debug("alias '%s' shadows command '%s'\n" %
473 490 (self.name, self.cmdname))
474 491
475 492 if util.safehasattr(self, 'shell'):
476 493 return self.fn(ui, *args, **opts)
477 494 else:
478 495 try:
479 496 return util.checksignature(self.fn)(ui, *args, **opts)
480 497 except error.SignatureError:
481 498 args = ' '.join([self.cmdname] + self.args)
482 499 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
483 500 raise
484 501
485 502 def addaliases(ui, cmdtable):
486 503 # aliases are processed after extensions have been loaded, so they
487 504 # may use extension commands. Aliases can also use other alias definitions,
488 505 # but only if they have been defined prior to the current definition.
489 506 for alias, definition in ui.configitems('alias'):
490 507 aliasdef = cmdalias(alias, definition, cmdtable)
491 508
492 509 try:
493 510 olddef = cmdtable[aliasdef.cmd][0]
494 511 if olddef.definition == aliasdef.definition:
495 512 continue
496 513 except (KeyError, AttributeError):
497 514 # definition might not exist or it might not be a cmdalias
498 515 pass
499 516
500 517 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
501 518 if aliasdef.norepo:
502 519 commands.norepo += ' %s' % alias
503 520 if aliasdef.optionalrepo:
504 521 commands.optionalrepo += ' %s' % alias
505 522
506 523 def _parse(ui, args):
507 524 options = {}
508 525 cmdoptions = {}
509 526
510 527 try:
511 528 args = fancyopts.fancyopts(args, commands.globalopts, options)
512 529 except fancyopts.getopt.GetoptError, inst:
513 530 raise error.CommandError(None, inst)
514 531
515 532 if args:
516 533 cmd, args = args[0], args[1:]
517 534 aliases, entry = cmdutil.findcmd(cmd, commands.table,
518 535 ui.configbool("ui", "strict"))
519 536 cmd = aliases[0]
520 537 args = aliasargs(entry[0], args)
521 538 defaults = ui.config("defaults", cmd)
522 539 if defaults:
523 540 args = map(util.expandpath, shlex.split(defaults)) + args
524 541 c = list(entry[1])
525 542 else:
526 543 cmd = None
527 544 c = []
528 545
529 546 # combine global options into local
530 547 for o in commands.globalopts:
531 548 c.append((o[0], o[1], options[o[1]], o[3]))
532 549
533 550 try:
534 551 args = fancyopts.fancyopts(args, c, cmdoptions, True)
535 552 except fancyopts.getopt.GetoptError, inst:
536 553 raise error.CommandError(cmd, inst)
537 554
538 555 # separate global options back out
539 556 for o in commands.globalopts:
540 557 n = o[1]
541 558 options[n] = cmdoptions[n]
542 559 del cmdoptions[n]
543 560
544 561 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
545 562
546 563 def _parseconfig(ui, config):
547 564 """parse the --config options from the command line"""
548 565 configs = []
549 566
550 567 for cfg in config:
551 568 try:
552 569 name, value = cfg.split('=', 1)
553 570 section, name = name.split('.', 1)
554 571 if not section or not name:
555 572 raise IndexError
556 573 ui.setconfig(section, name, value, '--config')
557 574 configs.append((section, name, value))
558 575 except (IndexError, ValueError):
559 576 raise util.Abort(_('malformed --config option: %r '
560 577 '(use --config section.name=value)') % cfg)
561 578
562 579 return configs
563 580
564 581 def _earlygetopt(aliases, args):
565 582 """Return list of values for an option (or aliases).
566 583
567 584 The values are listed in the order they appear in args.
568 585 The options and values are removed from args.
569 586
570 587 >>> args = ['x', '--cwd', 'foo', 'y']
571 588 >>> _earlygetopt(['--cwd'], args), args
572 589 (['foo'], ['x', 'y'])
573 590
574 591 >>> args = ['x', '--cwd=bar', 'y']
575 592 >>> _earlygetopt(['--cwd'], args), args
576 593 (['bar'], ['x', 'y'])
577 594
578 595 >>> args = ['x', '-R', 'foo', 'y']
579 596 >>> _earlygetopt(['-R'], args), args
580 597 (['foo'], ['x', 'y'])
581 598
582 599 >>> args = ['x', '-Rbar', 'y']
583 600 >>> _earlygetopt(['-R'], args), args
584 601 (['bar'], ['x', 'y'])
585 602 """
586 603 try:
587 604 argcount = args.index("--")
588 605 except ValueError:
589 606 argcount = len(args)
590 607 shortopts = [opt for opt in aliases if len(opt) == 2]
591 608 values = []
592 609 pos = 0
593 610 while pos < argcount:
594 611 fullarg = arg = args[pos]
595 612 equals = arg.find('=')
596 613 if equals > -1:
597 614 arg = arg[:equals]
598 615 if arg in aliases:
599 616 del args[pos]
600 617 if equals > -1:
601 618 values.append(fullarg[equals + 1:])
602 619 argcount -= 1
603 620 else:
604 621 if pos + 1 >= argcount:
605 622 # ignore and let getopt report an error if there is no value
606 623 break
607 624 values.append(args.pop(pos))
608 625 argcount -= 2
609 626 elif arg[:2] in shortopts:
610 627 # short option can have no following space, e.g. hg log -Rfoo
611 628 values.append(args.pop(pos)[2:])
612 629 argcount -= 1
613 630 else:
614 631 pos += 1
615 632 return values
616 633
617 634 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
618 635 # run pre-hook, and abort if it fails
619 636 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
620 637 pats=cmdpats, opts=cmdoptions)
621 638 ret = _runcommand(ui, options, cmd, d)
622 639 # run post-hook, passing command result
623 640 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
624 641 result=ret, pats=cmdpats, opts=cmdoptions)
625 642 return ret
626 643
627 644 def _getlocal(ui, rpath):
628 645 """Return (path, local ui object) for the given target path.
629 646
630 647 Takes paths in [cwd]/.hg/hgrc into account."
631 648 """
632 649 try:
633 650 wd = os.getcwd()
634 651 except OSError, e:
635 652 raise util.Abort(_("error getting current working directory: %s") %
636 653 e.strerror)
637 654 path = cmdutil.findrepo(wd) or ""
638 655 if not path:
639 656 lui = ui
640 657 else:
641 658 lui = ui.copy()
642 659 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
643 660
644 661 if rpath and rpath[-1]:
645 662 path = lui.expandpath(rpath[-1])
646 663 lui = ui.copy()
647 664 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
648 665
649 666 return path, lui
650 667
651 668 def _checkshellalias(lui, ui, args, precheck=True):
652 669 """Return the function to run the shell alias, if it is required
653 670
654 671 'precheck' is whether this function is invoked before adding
655 672 aliases or not.
656 673 """
657 674 options = {}
658 675
659 676 try:
660 677 args = fancyopts.fancyopts(args, commands.globalopts, options)
661 678 except fancyopts.getopt.GetoptError:
662 679 return
663 680
664 681 if not args:
665 682 return
666 683
667 684 if precheck:
668 685 strict = True
669 686 norepo = commands.norepo
670 687 optionalrepo = commands.optionalrepo
671 688 def restorecommands():
672 689 commands.norepo = norepo
673 690 commands.optionalrepo = optionalrepo
674 691 cmdtable = commands.table.copy()
675 692 addaliases(lui, cmdtable)
676 693 else:
677 694 strict = False
678 695 def restorecommands():
679 696 pass
680 697 cmdtable = commands.table
681 698
682 699 cmd = args[0]
683 700 try:
684 701 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
685 702 except (error.AmbiguousCommand, error.UnknownCommand):
686 703 restorecommands()
687 704 return
688 705
689 706 cmd = aliases[0]
690 707 fn = entry[0]
691 708
692 709 if cmd and util.safehasattr(fn, 'shell'):
693 710 d = lambda: fn(ui, *args[1:])
694 711 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
695 712 [], {})
696 713
697 714 restorecommands()
698 715
699 716 _loaded = set()
700 717 def _dispatch(req):
701 718 args = req.args
702 719 ui = req.ui
703 720
704 721 # check for cwd
705 722 cwd = _earlygetopt(['--cwd'], args)
706 723 if cwd:
707 724 os.chdir(cwd[-1])
708 725
709 726 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
710 727 path, lui = _getlocal(ui, rpath)
711 728
712 729 # Now that we're operating in the right directory/repository with
713 730 # the right config settings, check for shell aliases
714 731 shellaliasfn = _checkshellalias(lui, ui, args)
715 732 if shellaliasfn:
716 733 return shellaliasfn()
717 734
718 735 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
719 736 # reposetup. Programs like TortoiseHg will call _dispatch several
720 737 # times so we keep track of configured extensions in _loaded.
721 738 extensions.loadall(lui)
722 739 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
723 740 # Propagate any changes to lui.__class__ by extensions
724 741 ui.__class__ = lui.__class__
725 742
726 743 # (uisetup and extsetup are handled in extensions.loadall)
727 744
728 745 for name, module in exts:
729 746 cmdtable = getattr(module, 'cmdtable', {})
730 747 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
731 748 if overrides:
732 749 ui.warn(_("extension '%s' overrides commands: %s\n")
733 750 % (name, " ".join(overrides)))
734 751 commands.table.update(cmdtable)
735 752 _loaded.add(name)
736 753
737 754 # (reposetup is handled in hg.repository)
738 755
739 756 addaliases(lui, commands.table)
740 757
741 758 if not lui.configbool("ui", "strict"):
742 759 # All aliases and commands are completely defined, now.
743 760 # Check abbreviation/ambiguity of shell alias again, because shell
744 761 # alias may cause failure of "_parse" (see issue4355)
745 762 shellaliasfn = _checkshellalias(lui, ui, args, precheck=False)
746 763 if shellaliasfn:
747 764 return shellaliasfn()
748 765
749 766 # check for fallback encoding
750 767 fallback = lui.config('ui', 'fallbackencoding')
751 768 if fallback:
752 769 encoding.fallbackencoding = fallback
753 770
754 771 fullargs = args
755 772 cmd, func, args, options, cmdoptions = _parse(lui, args)
756 773
757 774 if options["config"]:
758 775 raise util.Abort(_("option --config may not be abbreviated!"))
759 776 if options["cwd"]:
760 777 raise util.Abort(_("option --cwd may not be abbreviated!"))
761 778 if options["repository"]:
762 779 raise util.Abort(_(
763 780 "option -R has to be separated from other options (e.g. not -qR) "
764 781 "and --repository may only be abbreviated as --repo!"))
765 782
766 783 if options["encoding"]:
767 784 encoding.encoding = options["encoding"]
768 785 if options["encodingmode"]:
769 786 encoding.encodingmode = options["encodingmode"]
770 787 if options["time"]:
771 788 def get_times():
772 789 t = os.times()
773 790 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
774 791 t = (t[0], t[1], t[2], t[3], time.clock())
775 792 return t
776 793 s = get_times()
777 794 def print_time():
778 795 t = get_times()
779 796 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
780 797 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
781 798 atexit.register(print_time)
782 799
783 800 uis = set([ui, lui])
784 801
785 802 if req.repo:
786 803 uis.add(req.repo.ui)
787 804
788 805 if options['verbose'] or options['debug'] or options['quiet']:
789 806 for opt in ('verbose', 'debug', 'quiet'):
790 807 val = str(bool(options[opt]))
791 808 for ui_ in uis:
792 809 ui_.setconfig('ui', opt, val, '--' + opt)
793 810
794 811 if options['traceback']:
795 812 for ui_ in uis:
796 813 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
797 814
798 815 if options['noninteractive']:
799 816 for ui_ in uis:
800 817 ui_.setconfig('ui', 'interactive', 'off', '-y')
801 818
802 819 if cmdoptions.get('insecure', False):
803 820 for ui_ in uis:
804 821 ui_.setconfig('web', 'cacerts', '', '--insecure')
805 822
806 823 if options['version']:
807 824 return commands.version_(ui)
808 825 if options['help']:
809 826 return commands.help_(ui, cmd, command=True)
810 827 elif not cmd:
811 828 return commands.help_(ui, 'shortlist')
812 829
813 830 repo = None
814 831 cmdpats = args[:]
815 832 if cmd not in commands.norepo.split():
816 833 # use the repo from the request only if we don't have -R
817 834 if not rpath and not cwd:
818 835 repo = req.repo
819 836
820 837 if repo:
821 838 # set the descriptors of the repo ui to those of ui
822 839 repo.ui.fin = ui.fin
823 840 repo.ui.fout = ui.fout
824 841 repo.ui.ferr = ui.ferr
825 842 else:
826 843 try:
827 844 repo = hg.repository(ui, path=path)
828 845 if not repo.local():
829 846 raise util.Abort(_("repository '%s' is not local") % path)
830 847 repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo')
831 848 except error.RequirementError:
832 849 raise
833 850 except error.RepoError:
834 851 if cmd not in commands.optionalrepo.split():
835 852 if (cmd in commands.inferrepo.split() and
836 853 args and not path): # try to infer -R from command args
837 854 repos = map(cmdutil.findrepo, args)
838 855 guess = repos[0]
839 856 if guess and repos.count(guess) == len(repos):
840 857 req.args = ['--repository', guess] + fullargs
841 858 return _dispatch(req)
842 859 if not path:
843 860 raise error.RepoError(_("no repository found in '%s'"
844 861 " (.hg not found)")
845 862 % os.getcwd())
846 863 raise
847 864 if repo:
848 865 ui = repo.ui
849 866 if options['hidden']:
850 867 repo = repo.unfiltered()
851 868 args.insert(0, repo)
852 869 elif rpath:
853 870 ui.warn(_("warning: --repository ignored\n"))
854 871
855 872 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
856 873 ui.log("command", '%s\n', msg)
857 874 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
858 875 try:
859 876 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
860 877 cmdpats, cmdoptions)
861 878 finally:
862 879 if repo and repo != req.repo:
863 880 repo.close()
864 881
865 882 def lsprofile(ui, func, fp):
866 883 format = ui.config('profiling', 'format', default='text')
867 884 field = ui.config('profiling', 'sort', default='inlinetime')
868 885 limit = ui.configint('profiling', 'limit', default=30)
869 886 climit = ui.configint('profiling', 'nested', default=5)
870 887
871 888 if format not in ['text', 'kcachegrind']:
872 889 ui.warn(_("unrecognized profiling format '%s'"
873 890 " - Ignored\n") % format)
874 891 format = 'text'
875 892
876 893 try:
877 894 from mercurial import lsprof
878 895 except ImportError:
879 896 raise util.Abort(_(
880 897 'lsprof not available - install from '
881 898 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
882 899 p = lsprof.Profiler()
883 900 p.enable(subcalls=True)
884 901 try:
885 902 return func()
886 903 finally:
887 904 p.disable()
888 905
889 906 if format == 'kcachegrind':
890 907 import lsprofcalltree
891 908 calltree = lsprofcalltree.KCacheGrind(p)
892 909 calltree.output(fp)
893 910 else:
894 911 # format == 'text'
895 912 stats = lsprof.Stats(p.getstats())
896 913 stats.sort(field)
897 914 stats.pprint(limit=limit, file=fp, climit=climit)
898 915
899 916 def statprofile(ui, func, fp):
900 917 try:
901 918 import statprof
902 919 except ImportError:
903 920 raise util.Abort(_(
904 921 'statprof not available - install using "easy_install statprof"'))
905 922
906 923 freq = ui.configint('profiling', 'freq', default=1000)
907 924 if freq > 0:
908 925 statprof.reset(freq)
909 926 else:
910 927 ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq)
911 928
912 929 statprof.start()
913 930 try:
914 931 return func()
915 932 finally:
916 933 statprof.stop()
917 934 statprof.display(fp)
918 935
919 936 def _runcommand(ui, options, cmd, cmdfunc):
920 937 def checkargs():
921 938 try:
922 939 return cmdfunc()
923 940 except error.SignatureError:
924 941 raise error.CommandError(cmd, _("invalid arguments"))
925 942
926 943 if options['profile']:
927 944 profiler = os.getenv('HGPROF')
928 945 if profiler is None:
929 946 profiler = ui.config('profiling', 'type', default='ls')
930 947 if profiler not in ('ls', 'stat'):
931 948 ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler)
932 949 profiler = 'ls'
933 950
934 951 output = ui.config('profiling', 'output')
935 952
936 953 if output:
937 954 path = ui.expandpath(output)
938 955 fp = open(path, 'wb')
939 956 else:
940 957 fp = sys.stderr
941 958
942 959 try:
943 960 if profiler == 'ls':
944 961 return lsprofile(ui, checkargs, fp)
945 962 else:
946 963 return statprofile(ui, checkargs, fp)
947 964 finally:
948 965 if output:
949 966 fp.close()
950 967 else:
951 968 return checkargs()
@@ -1,1402 +1,1405 b''
1 1 $ HGENCODING=utf-8
2 2 $ export HGENCODING
3 3
4 4 $ try() {
5 5 > hg debugrevspec --debug "$@"
6 6 > }
7 7
8 8 $ log() {
9 9 > hg log --template '{rev}\n' -r "$1"
10 10 > }
11 11
12 12 $ hg init repo
13 13 $ cd repo
14 14
15 15 $ echo a > a
16 16 $ hg branch a
17 17 marked working directory as branch a
18 18 (branches are permanent and global, did you want a bookmark?)
19 19 $ hg ci -Aqm0
20 20
21 21 $ echo b > b
22 22 $ hg branch b
23 23 marked working directory as branch b
24 24 (branches are permanent and global, did you want a bookmark?)
25 25 $ hg ci -Aqm1
26 26
27 27 $ rm a
28 28 $ hg branch a-b-c-
29 29 marked working directory as branch a-b-c-
30 30 (branches are permanent and global, did you want a bookmark?)
31 31 $ hg ci -Aqm2 -u Bob
32 32
33 33 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
34 34 2
35 35 $ hg log -r "extra('branch')" --template '{rev}\n'
36 36 0
37 37 1
38 38 2
39 39 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
40 40 0 a
41 41 2 a-b-c-
42 42
43 43 $ hg co 1
44 44 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 45 $ hg branch +a+b+c+
46 46 marked working directory as branch +a+b+c+
47 47 (branches are permanent and global, did you want a bookmark?)
48 48 $ hg ci -Aqm3
49 49
50 50 $ hg co 2 # interleave
51 51 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
52 52 $ echo bb > b
53 53 $ hg branch -- -a-b-c-
54 54 marked working directory as branch -a-b-c-
55 55 (branches are permanent and global, did you want a bookmark?)
56 56 $ hg ci -Aqm4 -d "May 12 2005"
57 57
58 58 $ hg co 3
59 59 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 60 $ hg branch !a/b/c/
61 61 marked working directory as branch !a/b/c/
62 62 (branches are permanent and global, did you want a bookmark?)
63 63 $ hg ci -Aqm"5 bug"
64 64
65 65 $ hg merge 4
66 66 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
67 67 (branch merge, don't forget to commit)
68 68 $ hg branch _a_b_c_
69 69 marked working directory as branch _a_b_c_
70 70 (branches are permanent and global, did you want a bookmark?)
71 71 $ hg ci -Aqm"6 issue619"
72 72
73 73 $ hg branch .a.b.c.
74 74 marked working directory as branch .a.b.c.
75 75 (branches are permanent and global, did you want a bookmark?)
76 76 $ hg ci -Aqm7
77 77
78 78 $ hg branch all
79 79 marked working directory as branch all
80 80 (branches are permanent and global, did you want a bookmark?)
81 81
82 82 $ hg co 4
83 83 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 84 $ hg branch Γ©
85 85 marked working directory as branch \xc3\xa9 (esc)
86 86 (branches are permanent and global, did you want a bookmark?)
87 87 $ hg ci -Aqm9
88 88
89 89 $ hg tag -r6 1.0
90 90
91 91 $ hg clone --quiet -U -r 7 . ../remote1
92 92 $ hg clone --quiet -U -r 8 . ../remote2
93 93 $ echo "[paths]" >> .hg/hgrc
94 94 $ echo "default = ../remote1" >> .hg/hgrc
95 95
96 96 names that should work without quoting
97 97
98 98 $ try a
99 99 ('symbol', 'a')
100 100 0
101 101 $ try b-a
102 102 (minus
103 103 ('symbol', 'b')
104 104 ('symbol', 'a'))
105 105 1
106 106 $ try _a_b_c_
107 107 ('symbol', '_a_b_c_')
108 108 6
109 109 $ try _a_b_c_-a
110 110 (minus
111 111 ('symbol', '_a_b_c_')
112 112 ('symbol', 'a'))
113 113 6
114 114 $ try .a.b.c.
115 115 ('symbol', '.a.b.c.')
116 116 7
117 117 $ try .a.b.c.-a
118 118 (minus
119 119 ('symbol', '.a.b.c.')
120 120 ('symbol', 'a'))
121 121 7
122 122 $ try -- '-a-b-c-' # complains
123 123 hg: parse error at 7: not a prefix: end
124 124 [255]
125 125 $ log -a-b-c- # succeeds with fallback
126 126 4
127 127
128 128 $ try -- -a-b-c--a # complains
129 129 (minus
130 130 (minus
131 131 (minus
132 132 (negate
133 133 ('symbol', 'a'))
134 134 ('symbol', 'b'))
135 135 ('symbol', 'c'))
136 136 (negate
137 137 ('symbol', 'a')))
138 138 abort: unknown revision '-a'!
139 139 [255]
140 140 $ try Γ©
141 141 ('symbol', '\xc3\xa9')
142 142 9
143 143
144 144 no quoting needed
145 145
146 146 $ log ::a-b-c-
147 147 0
148 148 1
149 149 2
150 150
151 151 quoting needed
152 152
153 153 $ try '"-a-b-c-"-a'
154 154 (minus
155 155 ('string', '-a-b-c-')
156 156 ('symbol', 'a'))
157 157 4
158 158
159 159 $ log '1 or 2'
160 160 1
161 161 2
162 162 $ log '1|2'
163 163 1
164 164 2
165 165 $ log '1 and 2'
166 166 $ log '1&2'
167 167 $ try '1&2|3' # precedence - and is higher
168 168 (or
169 169 (and
170 170 ('symbol', '1')
171 171 ('symbol', '2'))
172 172 ('symbol', '3'))
173 173 3
174 174 $ try '1|2&3'
175 175 (or
176 176 ('symbol', '1')
177 177 (and
178 178 ('symbol', '2')
179 179 ('symbol', '3')))
180 180 1
181 181 $ try '1&2&3' # associativity
182 182 (and
183 183 (and
184 184 ('symbol', '1')
185 185 ('symbol', '2'))
186 186 ('symbol', '3'))
187 187 $ try '1|(2|3)'
188 188 (or
189 189 ('symbol', '1')
190 190 (group
191 191 (or
192 192 ('symbol', '2')
193 193 ('symbol', '3'))))
194 194 1
195 195 2
196 196 3
197 197 $ log '1.0' # tag
198 198 6
199 199 $ log 'a' # branch
200 200 0
201 201 $ log '2785f51ee'
202 202 0
203 203 $ log 'date(2005)'
204 204 4
205 205 $ log 'date(this is a test)'
206 206 hg: parse error at 10: unexpected token: symbol
207 207 [255]
208 208 $ log 'date()'
209 209 hg: parse error: date requires a string
210 210 [255]
211 211 $ log 'date'
212 212 hg: parse error: can't use date here
213 213 [255]
214 214 $ log 'date('
215 215 hg: parse error at 5: not a prefix: end
216 216 [255]
217 217 $ log 'date(tip)'
218 218 abort: invalid date: 'tip'
219 219 [255]
220 220 $ log '"date"'
221 221 abort: unknown revision 'date'!
222 222 [255]
223 223 $ log 'date(2005) and 1::'
224 224 4
225 225
226 226 ancestor can accept 0 or more arguments
227 227
228 228 $ log 'ancestor()'
229 229 $ log 'ancestor(1)'
230 230 1
231 231 $ log 'ancestor(4,5)'
232 232 1
233 233 $ log 'ancestor(4,5) and 4'
234 234 $ log 'ancestor(0,0,1,3)'
235 235 0
236 236 $ log 'ancestor(3,1,5,3,5,1)'
237 237 1
238 238 $ log 'ancestor(0,1,3,5)'
239 239 0
240 240 $ log 'ancestor(1,2,3,4,5)'
241 241 1
242 242 $ log 'ancestors(5)'
243 243 0
244 244 1
245 245 3
246 246 5
247 247 $ log 'ancestor(ancestors(5))'
248 248 0
249 249 $ log 'author(bob)'
250 250 2
251 251 $ log 'author("re:bob|test")'
252 252 0
253 253 1
254 254 2
255 255 3
256 256 4
257 257 5
258 258 6
259 259 7
260 260 8
261 261 9
262 262 $ log 'branch(Γ©)'
263 263 8
264 264 9
265 265 $ log 'branch(a)'
266 266 0
267 267 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
268 268 0 a
269 269 2 a-b-c-
270 270 3 +a+b+c+
271 271 4 -a-b-c-
272 272 5 !a/b/c/
273 273 6 _a_b_c_
274 274 7 .a.b.c.
275 275 $ log 'children(ancestor(4,5))'
276 276 2
277 277 3
278 278 $ log 'closed()'
279 279 $ log 'contains(a)'
280 280 0
281 281 1
282 282 3
283 283 5
284 284 $ log 'contains("../repo/a")'
285 285 0
286 286 1
287 287 3
288 288 5
289 289 $ log 'desc(B)'
290 290 5
291 291 $ log 'descendants(2 or 3)'
292 292 2
293 293 3
294 294 4
295 295 5
296 296 6
297 297 7
298 298 8
299 299 9
300 300 $ log 'file("b*")'
301 301 1
302 302 4
303 303 $ log 'filelog("b")'
304 304 1
305 305 4
306 306 $ log 'filelog("../repo/b")'
307 307 1
308 308 4
309 309 $ log 'follow()'
310 310 0
311 311 1
312 312 2
313 313 4
314 314 8
315 315 9
316 316 $ log 'grep("issue\d+")'
317 317 6
318 318 $ try 'grep("(")' # invalid regular expression
319 319 (func
320 320 ('symbol', 'grep')
321 321 ('string', '('))
322 322 hg: parse error: invalid match pattern: unbalanced parenthesis
323 323 [255]
324 324 $ try 'grep("\bissue\d+")'
325 325 (func
326 326 ('symbol', 'grep')
327 327 ('string', '\x08issue\\d+'))
328 328 $ try 'grep(r"\bissue\d+")'
329 329 (func
330 330 ('symbol', 'grep')
331 331 ('string', '\\bissue\\d+'))
332 332 6
333 333 $ try 'grep(r"\")'
334 334 hg: parse error at 7: unterminated string
335 335 [255]
336 336 $ log 'head()'
337 337 0
338 338 1
339 339 2
340 340 3
341 341 4
342 342 5
343 343 6
344 344 7
345 345 9
346 346 $ log 'heads(6::)'
347 347 7
348 348 $ log 'keyword(issue)'
349 349 6
350 350 $ log 'keyword("test a")'
351 351 $ log 'limit(head(), 1)'
352 352 0
353 353 $ log 'matching(6)'
354 354 6
355 355 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
356 356 6
357 357 7
358 358
359 359 Testing min and max
360 360
361 361 max: simple
362 362
363 363 $ log 'max(contains(a))'
364 364 5
365 365
366 366 max: simple on unordered set)
367 367
368 368 $ log 'max((4+0+2+5+7) and contains(a))'
369 369 5
370 370
371 371 max: no result
372 372
373 373 $ log 'max(contains(stringthatdoesnotappearanywhere))'
374 374
375 375 max: no result on unordered set
376 376
377 377 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
378 378
379 379 min: simple
380 380
381 381 $ log 'min(contains(a))'
382 382 0
383 383
384 384 min: simple on unordered set
385 385
386 386 $ log 'min((4+0+2+5+7) and contains(a))'
387 387 0
388 388
389 389 min: empty
390 390
391 391 $ log 'min(contains(stringthatdoesnotappearanywhere))'
392 392
393 393 min: empty on unordered set
394 394
395 395 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
396 396
397 397
398 398 $ log 'merge()'
399 399 6
400 400 $ log 'branchpoint()'
401 401 1
402 402 4
403 403 $ log 'modifies(b)'
404 404 4
405 405 $ log 'modifies("path:b")'
406 406 4
407 407 $ log 'modifies("*")'
408 408 4
409 409 6
410 410 $ log 'modifies("set:modified()")'
411 411 4
412 412 $ log 'id(5)'
413 413 2
414 414 $ log 'only(9)'
415 415 8
416 416 9
417 417 $ log 'only(8)'
418 418 8
419 419 $ log 'only(9, 5)'
420 420 2
421 421 4
422 422 8
423 423 9
424 424 $ log 'only(7 + 9, 5 + 2)'
425 425 4
426 426 6
427 427 7
428 428 8
429 429 9
430 430
431 431 Test empty set input
432 432 $ log 'only(p2())'
433 433 $ log 'only(p1(), p2())'
434 434 0
435 435 1
436 436 2
437 437 4
438 438 8
439 439 9
440 440
441 441 Test '%' operator
442 442
443 443 $ log '9%'
444 444 8
445 445 9
446 446 $ log '9%5'
447 447 2
448 448 4
449 449 8
450 450 9
451 451 $ log '(7 + 9)%(5 + 2)'
452 452 4
453 453 6
454 454 7
455 455 8
456 456 9
457 457
458 458 Test the order of operations
459 459
460 460 $ log '7 + 9%5 + 2'
461 461 7
462 462 2
463 463 4
464 464 8
465 465 9
466 466
467 467 Test explicit numeric revision
468 468 $ log 'rev(-2)'
469 469 $ log 'rev(-1)'
470 470 -1
471 471 $ log 'rev(0)'
472 472 0
473 473 $ log 'rev(9)'
474 474 9
475 475 $ log 'rev(10)'
476 476 $ log 'rev(tip)'
477 477 hg: parse error: rev expects a number
478 478 [255]
479 479
480 480 Test null revision
481 481 $ log '(null)'
482 482 -1
483 483 $ log '(null:0)'
484 484 -1
485 485 0
486 486 $ log '(0:null)'
487 487 0
488 488 -1
489 489 $ log 'null::0'
490 490 -1
491 491 0
492 492 $ log 'null:tip - 0:'
493 493 -1
494 494 $ log 'null: and null::' | head -1
495 495 -1
496 496 $ log 'null: or 0:' | head -2
497 497 -1
498 498 0
499 499 $ log 'ancestors(null)'
500 500 -1
501 501 $ log 'reverse(null:)' | tail -2
502 502 0
503 503 -1
504 504 $ log 'first(null:)'
505 505 -1
506 506 $ log 'min(null:)'
507 507 -1
508 508 $ log 'tip:null and all()' | tail -2
509 509 1
510 510 0
511 511
512 512 $ log 'outgoing()'
513 513 8
514 514 9
515 515 $ log 'outgoing("../remote1")'
516 516 8
517 517 9
518 518 $ log 'outgoing("../remote2")'
519 519 3
520 520 5
521 521 6
522 522 7
523 523 9
524 524 $ log 'p1(merge())'
525 525 5
526 526 $ log 'p2(merge())'
527 527 4
528 528 $ log 'parents(merge())'
529 529 4
530 530 5
531 531 $ log 'p1(branchpoint())'
532 532 0
533 533 2
534 534 $ log 'p2(branchpoint())'
535 535 $ log 'parents(branchpoint())'
536 536 0
537 537 2
538 538 $ log 'removes(a)'
539 539 2
540 540 6
541 541 $ log 'roots(all())'
542 542 0
543 543 $ log 'reverse(2 or 3 or 4 or 5)'
544 544 5
545 545 4
546 546 3
547 547 2
548 548 $ log 'reverse(all())'
549 549 9
550 550 8
551 551 7
552 552 6
553 553 5
554 554 4
555 555 3
556 556 2
557 557 1
558 558 0
559 559 $ log 'reverse(all()) & filelog(b)'
560 560 4
561 561 1
562 562 $ log 'rev(5)'
563 563 5
564 564 $ log 'sort(limit(reverse(all()), 3))'
565 565 7
566 566 8
567 567 9
568 568 $ log 'sort(2 or 3 or 4 or 5, date)'
569 569 2
570 570 3
571 571 5
572 572 4
573 573 $ log 'tagged()'
574 574 6
575 575 $ log 'tag()'
576 576 6
577 577 $ log 'tag(1.0)'
578 578 6
579 579 $ log 'tag(tip)'
580 580 9
581 581
582 582 test sort revset
583 583 --------------------------------------------
584 584
585 585 test when adding two unordered revsets
586 586
587 587 $ log 'sort(keyword(issue) or modifies(b))'
588 588 4
589 589 6
590 590
591 591 test when sorting a reversed collection in the same way it is
592 592
593 593 $ log 'sort(reverse(all()), -rev)'
594 594 9
595 595 8
596 596 7
597 597 6
598 598 5
599 599 4
600 600 3
601 601 2
602 602 1
603 603 0
604 604
605 605 test when sorting a reversed collection
606 606
607 607 $ log 'sort(reverse(all()), rev)'
608 608 0
609 609 1
610 610 2
611 611 3
612 612 4
613 613 5
614 614 6
615 615 7
616 616 8
617 617 9
618 618
619 619
620 620 test sorting two sorted collections in different orders
621 621
622 622 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
623 623 2
624 624 6
625 625 8
626 626 9
627 627
628 628 test sorting two sorted collections in different orders backwards
629 629
630 630 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
631 631 9
632 632 8
633 633 6
634 634 2
635 635
636 636 test subtracting something from an addset
637 637
638 638 $ log '(outgoing() or removes(a)) - removes(a)'
639 639 8
640 640 9
641 641
642 642 test intersecting something with an addset
643 643
644 644 $ log 'parents(outgoing() or removes(a))'
645 645 1
646 646 4
647 647 5
648 648 8
649 649
650 650 test that `or` operation combines elements in the right order:
651 651
652 652 $ log '3:4 or 2:5'
653 653 3
654 654 4
655 655 2
656 656 5
657 657 $ log '3:4 or 5:2'
658 658 3
659 659 4
660 660 5
661 661 2
662 662 $ log 'sort(3:4 or 2:5)'
663 663 2
664 664 3
665 665 4
666 666 5
667 667 $ log 'sort(3:4 or 5:2)'
668 668 2
669 669 3
670 670 4
671 671 5
672 672
673 673 check that conversion to only works
674 674 $ try --optimize '::3 - ::1'
675 675 (minus
676 676 (dagrangepre
677 677 ('symbol', '3'))
678 678 (dagrangepre
679 679 ('symbol', '1')))
680 680 * optimized:
681 681 (func
682 682 ('symbol', 'only')
683 683 (list
684 684 ('symbol', '3')
685 685 ('symbol', '1')))
686 686 3
687 687 $ try --optimize 'ancestors(1) - ancestors(3)'
688 688 (minus
689 689 (func
690 690 ('symbol', 'ancestors')
691 691 ('symbol', '1'))
692 692 (func
693 693 ('symbol', 'ancestors')
694 694 ('symbol', '3')))
695 695 * optimized:
696 696 (func
697 697 ('symbol', 'only')
698 698 (list
699 699 ('symbol', '1')
700 700 ('symbol', '3')))
701 701 $ try --optimize 'not ::2 and ::6'
702 702 (and
703 703 (not
704 704 (dagrangepre
705 705 ('symbol', '2')))
706 706 (dagrangepre
707 707 ('symbol', '6')))
708 708 * optimized:
709 709 (func
710 710 ('symbol', 'only')
711 711 (list
712 712 ('symbol', '6')
713 713 ('symbol', '2')))
714 714 3
715 715 4
716 716 5
717 717 6
718 718 $ try --optimize 'ancestors(6) and not ancestors(4)'
719 719 (and
720 720 (func
721 721 ('symbol', 'ancestors')
722 722 ('symbol', '6'))
723 723 (not
724 724 (func
725 725 ('symbol', 'ancestors')
726 726 ('symbol', '4'))))
727 727 * optimized:
728 728 (func
729 729 ('symbol', 'only')
730 730 (list
731 731 ('symbol', '6')
732 732 ('symbol', '4')))
733 733 3
734 734 5
735 735 6
736 736
737 737 we can use patterns when searching for tags
738 738
739 739 $ log 'tag("1..*")'
740 740 abort: tag '1..*' does not exist!
741 741 [255]
742 742 $ log 'tag("re:1..*")'
743 743 6
744 744 $ log 'tag("re:[0-9].[0-9]")'
745 745 6
746 746 $ log 'tag("literal:1.0")'
747 747 6
748 748 $ log 'tag("re:0..*")'
749 749
750 750 $ log 'tag(unknown)'
751 751 abort: tag 'unknown' does not exist!
752 752 [255]
753 753 $ log 'tag("re:unknown")'
754 754 $ log 'present(tag("unknown"))'
755 755 $ log 'present(tag("re:unknown"))'
756 756 $ log 'branch(unknown)'
757 757 abort: unknown revision 'unknown'!
758 758 [255]
759 759 $ log 'branch("re:unknown")'
760 760 $ log 'present(branch("unknown"))'
761 761 $ log 'present(branch("re:unknown"))'
762 762 $ log 'user(bob)'
763 763 2
764 764
765 765 $ log '4::8'
766 766 4
767 767 8
768 768 $ log '4:8'
769 769 4
770 770 5
771 771 6
772 772 7
773 773 8
774 774
775 775 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
776 776 4
777 777 2
778 778 5
779 779
780 780 $ log 'not 0 and 0:2'
781 781 1
782 782 2
783 783 $ log 'not 1 and 0:2'
784 784 0
785 785 2
786 786 $ log 'not 2 and 0:2'
787 787 0
788 788 1
789 789 $ log '(1 and 2)::'
790 790 $ log '(1 and 2):'
791 791 $ log '(1 and 2):3'
792 792 $ log 'sort(head(), -rev)'
793 793 9
794 794 7
795 795 6
796 796 5
797 797 4
798 798 3
799 799 2
800 800 1
801 801 0
802 802 $ log '4::8 - 8'
803 803 4
804 804 $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)'
805 805 2
806 806 3
807 807 1
808 808
809 809 $ log 'named("unknown")'
810 810 abort: namespace 'unknown' does not exist!
811 811 [255]
812 812 $ log 'named("re:unknown")'
813 813 abort: no namespace exists that match 'unknown'!
814 814 [255]
815 815 $ log 'present(named("unknown"))'
816 816 $ log 'present(named("re:unknown"))'
817 817
818 818 $ log 'tag()'
819 819 6
820 820 $ log 'named("tags")'
821 821 6
822 822
823 823 issue2437
824 824
825 825 $ log '3 and p1(5)'
826 826 3
827 827 $ log '4 and p2(6)'
828 828 4
829 829 $ log '1 and parents(:2)'
830 830 1
831 831 $ log '2 and children(1:)'
832 832 2
833 833 $ log 'roots(all()) or roots(all())'
834 834 0
835 835 $ hg debugrevspec 'roots(all()) or roots(all())'
836 836 0
837 837 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
838 838 9
839 839 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
840 840 4
841 841
842 842 issue2654: report a parse error if the revset was not completely parsed
843 843
844 844 $ log '1 OR 2'
845 845 hg: parse error at 2: invalid token
846 846 [255]
847 847
848 848 or operator should preserve ordering:
849 849 $ log 'reverse(2::4) or tip'
850 850 4
851 851 2
852 852 9
853 853
854 854 parentrevspec
855 855
856 856 $ log 'merge()^0'
857 857 6
858 858 $ log 'merge()^'
859 859 5
860 860 $ log 'merge()^1'
861 861 5
862 862 $ log 'merge()^2'
863 863 4
864 864 $ log 'merge()^^'
865 865 3
866 866 $ log 'merge()^1^'
867 867 3
868 868 $ log 'merge()^^^'
869 869 1
870 870
871 871 $ log 'merge()~0'
872 872 6
873 873 $ log 'merge()~1'
874 874 5
875 875 $ log 'merge()~2'
876 876 3
877 877 $ log 'merge()~2^1'
878 878 1
879 879 $ log 'merge()~3'
880 880 1
881 881
882 882 $ log '(-3:tip)^'
883 883 4
884 884 6
885 885 8
886 886
887 887 $ log 'tip^foo'
888 888 hg: parse error: ^ expects a number 0, 1, or 2
889 889 [255]
890 890
891 891 Bogus function gets suggestions
892 892 $ log 'add()'
893 893 hg: parse error: not a function: add
894 (did you mean 'adds'?)
894 895 [255]
895 896 $ log 'added()'
896 897 hg: parse error: not a function: added
898 (did you mean 'adds'?)
897 899 [255]
898 900 $ log 'remo()'
899 901 hg: parse error: not a function: remo
902 (did you mean one of remote, removes?)
900 903 [255]
901 904 $ log 'babar()'
902 905 hg: parse error: not a function: babar
903 906 [255]
904 907
905 908 multiple revspecs
906 909
907 910 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
908 911 8
909 912 9
910 913 4
911 914 5
912 915 6
913 916 7
914 917
915 918 test usage in revpair (with "+")
916 919
917 920 (real pair)
918 921
919 922 $ hg diff -r 'tip^^' -r 'tip'
920 923 diff -r 2326846efdab -r 24286f4ae135 .hgtags
921 924 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
922 925 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
923 926 @@ -0,0 +1,1 @@
924 927 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
925 928 $ hg diff -r 'tip^^::tip'
926 929 diff -r 2326846efdab -r 24286f4ae135 .hgtags
927 930 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
928 931 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
929 932 @@ -0,0 +1,1 @@
930 933 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
931 934
932 935 (single rev)
933 936
934 937 $ hg diff -r 'tip^' -r 'tip^'
935 938 $ hg diff -r 'tip^::tip^ or tip^'
936 939
937 940 (single rev that does not looks like a range)
938 941
939 942 $ hg diff -r 'tip^ or tip^'
940 943 diff -r d5d0dcbdc4d9 .hgtags
941 944 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
942 945 +++ b/.hgtags * (glob)
943 946 @@ -0,0 +1,1 @@
944 947 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
945 948
946 949 (no rev)
947 950
948 951 $ hg diff -r 'author("babar") or author("celeste")'
949 952 abort: empty revision range
950 953 [255]
951 954
952 955 aliases:
953 956
954 957 $ echo '[revsetalias]' >> .hg/hgrc
955 958 $ echo 'm = merge()' >> .hg/hgrc
956 959 $ echo 'sincem = descendants(m)' >> .hg/hgrc
957 960 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
958 961 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
959 962 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
960 963
961 964 $ try m
962 965 ('symbol', 'm')
963 966 (func
964 967 ('symbol', 'merge')
965 968 None)
966 969 6
967 970
968 971 test alias recursion
969 972
970 973 $ try sincem
971 974 ('symbol', 'sincem')
972 975 (func
973 976 ('symbol', 'descendants')
974 977 (func
975 978 ('symbol', 'merge')
976 979 None))
977 980 6
978 981 7
979 982
980 983 test infinite recursion
981 984
982 985 $ echo 'recurse1 = recurse2' >> .hg/hgrc
983 986 $ echo 'recurse2 = recurse1' >> .hg/hgrc
984 987 $ try recurse1
985 988 ('symbol', 'recurse1')
986 989 hg: parse error: infinite expansion of revset alias "recurse1" detected
987 990 [255]
988 991
989 992 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
990 993 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
991 994 $ try "level2(level1(1, 2), 3)"
992 995 (func
993 996 ('symbol', 'level2')
994 997 (list
995 998 (func
996 999 ('symbol', 'level1')
997 1000 (list
998 1001 ('symbol', '1')
999 1002 ('symbol', '2')))
1000 1003 ('symbol', '3')))
1001 1004 (or
1002 1005 ('symbol', '3')
1003 1006 (or
1004 1007 ('symbol', '1')
1005 1008 ('symbol', '2')))
1006 1009 3
1007 1010 1
1008 1011 2
1009 1012
1010 1013 test nesting and variable passing
1011 1014
1012 1015 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
1013 1016 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
1014 1017 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
1015 1018 $ try 'nested(2:5)'
1016 1019 (func
1017 1020 ('symbol', 'nested')
1018 1021 (range
1019 1022 ('symbol', '2')
1020 1023 ('symbol', '5')))
1021 1024 (func
1022 1025 ('symbol', 'max')
1023 1026 (range
1024 1027 ('symbol', '2')
1025 1028 ('symbol', '5')))
1026 1029 5
1027 1030
1028 1031 test variable isolation, variable placeholders are rewritten as string
1029 1032 then parsed and matched again as string. Check they do not leak too
1030 1033 far away.
1031 1034
1032 1035 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
1033 1036 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
1034 1037 $ try 'callinjection(2:5)'
1035 1038 (func
1036 1039 ('symbol', 'callinjection')
1037 1040 (range
1038 1041 ('symbol', '2')
1039 1042 ('symbol', '5')))
1040 1043 (func
1041 1044 ('symbol', 'descendants')
1042 1045 (func
1043 1046 ('symbol', 'max')
1044 1047 ('string', '$1')))
1045 1048 abort: unknown revision '$1'!
1046 1049 [255]
1047 1050
1048 1051 $ echo 'injectparamasstring2 = max(_aliasarg("$1"))' >> .hg/hgrc
1049 1052 $ echo 'callinjection2($1) = descendants(injectparamasstring2)' >> .hg/hgrc
1050 1053 $ try 'callinjection2(2:5)'
1051 1054 (func
1052 1055 ('symbol', 'callinjection2')
1053 1056 (range
1054 1057 ('symbol', '2')
1055 1058 ('symbol', '5')))
1056 1059 abort: failed to parse the definition of revset alias "injectparamasstring2": not a function: _aliasarg
1057 1060 [255]
1058 1061 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1059 1062 ('symbol', 'tip')
1060 1063 warning: failed to parse the definition of revset alias "anotherbadone": at 7: not a prefix: end
1061 1064 warning: failed to parse the definition of revset alias "injectparamasstring2": not a function: _aliasarg
1062 1065 9
1063 1066 >>> data = file('.hg/hgrc', 'rb').read()
1064 1067 >>> file('.hg/hgrc', 'wb').write(data.replace('_aliasarg', ''))
1065 1068
1066 1069 $ try 'tip'
1067 1070 ('symbol', 'tip')
1068 1071 9
1069 1072
1070 1073 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1071 1074 ('symbol', 'tip')
1072 1075 warning: failed to parse the declaration of revset alias "bad name": at 4: invalid token
1073 1076 9
1074 1077 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1075 1078 $ try 'strictreplacing("foo", tip)'
1076 1079 (func
1077 1080 ('symbol', 'strictreplacing')
1078 1081 (list
1079 1082 ('string', 'foo')
1080 1083 ('symbol', 'tip')))
1081 1084 (or
1082 1085 ('symbol', 'tip')
1083 1086 (func
1084 1087 ('symbol', 'desc')
1085 1088 ('string', '$1')))
1086 1089 9
1087 1090
1088 1091 $ try 'd(2:5)'
1089 1092 (func
1090 1093 ('symbol', 'd')
1091 1094 (range
1092 1095 ('symbol', '2')
1093 1096 ('symbol', '5')))
1094 1097 (func
1095 1098 ('symbol', 'reverse')
1096 1099 (func
1097 1100 ('symbol', 'sort')
1098 1101 (list
1099 1102 (range
1100 1103 ('symbol', '2')
1101 1104 ('symbol', '5'))
1102 1105 ('symbol', 'date'))))
1103 1106 4
1104 1107 5
1105 1108 3
1106 1109 2
1107 1110 $ try 'rs(2 or 3, date)'
1108 1111 (func
1109 1112 ('symbol', 'rs')
1110 1113 (list
1111 1114 (or
1112 1115 ('symbol', '2')
1113 1116 ('symbol', '3'))
1114 1117 ('symbol', 'date')))
1115 1118 (func
1116 1119 ('symbol', 'reverse')
1117 1120 (func
1118 1121 ('symbol', 'sort')
1119 1122 (list
1120 1123 (or
1121 1124 ('symbol', '2')
1122 1125 ('symbol', '3'))
1123 1126 ('symbol', 'date'))))
1124 1127 3
1125 1128 2
1126 1129 $ try 'rs()'
1127 1130 (func
1128 1131 ('symbol', 'rs')
1129 1132 None)
1130 1133 hg: parse error: invalid number of arguments: 0
1131 1134 [255]
1132 1135 $ try 'rs(2)'
1133 1136 (func
1134 1137 ('symbol', 'rs')
1135 1138 ('symbol', '2'))
1136 1139 hg: parse error: invalid number of arguments: 1
1137 1140 [255]
1138 1141 $ try 'rs(2, data, 7)'
1139 1142 (func
1140 1143 ('symbol', 'rs')
1141 1144 (list
1142 1145 (list
1143 1146 ('symbol', '2')
1144 1147 ('symbol', 'data'))
1145 1148 ('symbol', '7')))
1146 1149 hg: parse error: invalid number of arguments: 3
1147 1150 [255]
1148 1151 $ try 'rs4(2 or 3, x, x, date)'
1149 1152 (func
1150 1153 ('symbol', 'rs4')
1151 1154 (list
1152 1155 (list
1153 1156 (list
1154 1157 (or
1155 1158 ('symbol', '2')
1156 1159 ('symbol', '3'))
1157 1160 ('symbol', 'x'))
1158 1161 ('symbol', 'x'))
1159 1162 ('symbol', 'date')))
1160 1163 (func
1161 1164 ('symbol', 'reverse')
1162 1165 (func
1163 1166 ('symbol', 'sort')
1164 1167 (list
1165 1168 (or
1166 1169 ('symbol', '2')
1167 1170 ('symbol', '3'))
1168 1171 ('symbol', 'date'))))
1169 1172 3
1170 1173 2
1171 1174
1172 1175 issue4553: check that revset aliases override existing hash prefix
1173 1176
1174 1177 $ hg log -qr e
1175 1178 6:e0cc66ef77e8
1176 1179
1177 1180 $ hg log -qr e --config revsetalias.e="all()"
1178 1181 0:2785f51eece5
1179 1182 1:d75937da8da0
1180 1183 2:5ed5505e9f1c
1181 1184 3:8528aa5637f2
1182 1185 4:2326846efdab
1183 1186 5:904fa392b941
1184 1187 6:e0cc66ef77e8
1185 1188 7:013af1973af4
1186 1189 8:d5d0dcbdc4d9
1187 1190 9:24286f4ae135
1188 1191
1189 1192 $ hg log -qr e: --config revsetalias.e="0"
1190 1193 0:2785f51eece5
1191 1194 1:d75937da8da0
1192 1195 2:5ed5505e9f1c
1193 1196 3:8528aa5637f2
1194 1197 4:2326846efdab
1195 1198 5:904fa392b941
1196 1199 6:e0cc66ef77e8
1197 1200 7:013af1973af4
1198 1201 8:d5d0dcbdc4d9
1199 1202 9:24286f4ae135
1200 1203
1201 1204 $ hg log -qr :e --config revsetalias.e="9"
1202 1205 0:2785f51eece5
1203 1206 1:d75937da8da0
1204 1207 2:5ed5505e9f1c
1205 1208 3:8528aa5637f2
1206 1209 4:2326846efdab
1207 1210 5:904fa392b941
1208 1211 6:e0cc66ef77e8
1209 1212 7:013af1973af4
1210 1213 8:d5d0dcbdc4d9
1211 1214 9:24286f4ae135
1212 1215
1213 1216 $ hg log -qr e:
1214 1217 6:e0cc66ef77e8
1215 1218 7:013af1973af4
1216 1219 8:d5d0dcbdc4d9
1217 1220 9:24286f4ae135
1218 1221
1219 1222 $ hg log -qr :e
1220 1223 0:2785f51eece5
1221 1224 1:d75937da8da0
1222 1225 2:5ed5505e9f1c
1223 1226 3:8528aa5637f2
1224 1227 4:2326846efdab
1225 1228 5:904fa392b941
1226 1229 6:e0cc66ef77e8
1227 1230
1228 1231 issue2549 - correct optimizations
1229 1232
1230 1233 $ log 'limit(1 or 2 or 3, 2) and not 2'
1231 1234 1
1232 1235 $ log 'max(1 or 2) and not 2'
1233 1236 $ log 'min(1 or 2) and not 1'
1234 1237 $ log 'last(1 or 2, 1) and not 2'
1235 1238
1236 1239 issue4289 - ordering of built-ins
1237 1240 $ hg log -M -q -r 3:2
1238 1241 3:8528aa5637f2
1239 1242 2:5ed5505e9f1c
1240 1243
1241 1244 test revsets started with 40-chars hash (issue3669)
1242 1245
1243 1246 $ ISSUE3669_TIP=`hg tip --template '{node}'`
1244 1247 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
1245 1248 9
1246 1249 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
1247 1250 8
1248 1251
1249 1252 test or-ed indirect predicates (issue3775)
1250 1253
1251 1254 $ log '6 or 6^1' | sort
1252 1255 5
1253 1256 6
1254 1257 $ log '6^1 or 6' | sort
1255 1258 5
1256 1259 6
1257 1260 $ log '4 or 4~1' | sort
1258 1261 2
1259 1262 4
1260 1263 $ log '4~1 or 4' | sort
1261 1264 2
1262 1265 4
1263 1266 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
1264 1267 0
1265 1268 1
1266 1269 2
1267 1270 3
1268 1271 4
1269 1272 5
1270 1273 6
1271 1274 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
1272 1275 0
1273 1276 1
1274 1277 2
1275 1278 3
1276 1279 4
1277 1280 5
1278 1281 6
1279 1282
1280 1283 tests for 'remote()' predicate:
1281 1284 #. (csets in remote) (id) (remote)
1282 1285 1. less than local current branch "default"
1283 1286 2. same with local specified "default"
1284 1287 3. more than local specified specified
1285 1288
1286 1289 $ hg clone --quiet -U . ../remote3
1287 1290 $ cd ../remote3
1288 1291 $ hg update -q 7
1289 1292 $ echo r > r
1290 1293 $ hg ci -Aqm 10
1291 1294 $ log 'remote()'
1292 1295 7
1293 1296 $ log 'remote("a-b-c-")'
1294 1297 2
1295 1298 $ cd ../repo
1296 1299 $ log 'remote(".a.b.c.", "../remote3")'
1297 1300
1298 1301 tests for concatenation of strings/symbols by "##"
1299 1302
1300 1303 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
1301 1304 (_concat
1302 1305 (_concat
1303 1306 (_concat
1304 1307 ('symbol', '278')
1305 1308 ('string', '5f5'))
1306 1309 ('symbol', '1ee'))
1307 1310 ('string', 'ce5'))
1308 1311 ('string', '2785f51eece5')
1309 1312 0
1310 1313
1311 1314 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
1312 1315 $ try "cat4(278, '5f5', 1ee, 'ce5')"
1313 1316 (func
1314 1317 ('symbol', 'cat4')
1315 1318 (list
1316 1319 (list
1317 1320 (list
1318 1321 ('symbol', '278')
1319 1322 ('string', '5f5'))
1320 1323 ('symbol', '1ee'))
1321 1324 ('string', 'ce5')))
1322 1325 (_concat
1323 1326 (_concat
1324 1327 (_concat
1325 1328 ('symbol', '278')
1326 1329 ('string', '5f5'))
1327 1330 ('symbol', '1ee'))
1328 1331 ('string', 'ce5'))
1329 1332 ('string', '2785f51eece5')
1330 1333 0
1331 1334
1332 1335 (check concatenation in alias nesting)
1333 1336
1334 1337 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
1335 1338 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
1336 1339 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
1337 1340 0
1338 1341
1339 1342 (check operator priority)
1340 1343
1341 1344 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
1342 1345 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
1343 1346 0
1344 1347 4
1345 1348
1346 1349 $ cd ..
1347 1350
1348 1351 test author/desc/keyword in problematic encoding
1349 1352 # unicode: cp932:
1350 1353 # u30A2 0x83 0x41(= 'A')
1351 1354 # u30C2 0x83 0x61(= 'a')
1352 1355
1353 1356 $ hg init problematicencoding
1354 1357 $ cd problematicencoding
1355 1358
1356 1359 $ python > setup.sh <<EOF
1357 1360 > print u'''
1358 1361 > echo a > text
1359 1362 > hg add text
1360 1363 > hg --encoding utf-8 commit -u '\u30A2' -m none
1361 1364 > echo b > text
1362 1365 > hg --encoding utf-8 commit -u '\u30C2' -m none
1363 1366 > echo c > text
1364 1367 > hg --encoding utf-8 commit -u none -m '\u30A2'
1365 1368 > echo d > text
1366 1369 > hg --encoding utf-8 commit -u none -m '\u30C2'
1367 1370 > '''.encode('utf-8')
1368 1371 > EOF
1369 1372 $ sh < setup.sh
1370 1373
1371 1374 test in problematic encoding
1372 1375 $ python > test.sh <<EOF
1373 1376 > print u'''
1374 1377 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
1375 1378 > echo ====
1376 1379 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
1377 1380 > echo ====
1378 1381 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
1379 1382 > echo ====
1380 1383 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
1381 1384 > echo ====
1382 1385 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
1383 1386 > echo ====
1384 1387 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
1385 1388 > '''.encode('cp932')
1386 1389 > EOF
1387 1390 $ sh < test.sh
1388 1391 0
1389 1392 ====
1390 1393 1
1391 1394 ====
1392 1395 2
1393 1396 ====
1394 1397 3
1395 1398 ====
1396 1399 0
1397 1400 2
1398 1401 ====
1399 1402 1
1400 1403 3
1401 1404
1402 1405 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now