##// END OF EJS Templates
revset: add support of keyword arguments to ancestors() and descendants()...
Yuya Nishihara -
r32914:577759ef default
parent child Browse files
Show More
@@ -1,2020 +1,2028
1 1 # revset.py - revision set queries for mercurial
2 2 #
3 3 # Copyright 2010 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
9 9
10 10 import re
11 11
12 12 from .i18n import _
13 13 from . import (
14 14 dagop,
15 15 destutil,
16 16 encoding,
17 17 error,
18 18 hbisect,
19 19 match as matchmod,
20 20 node,
21 21 obsolete as obsmod,
22 22 pathutil,
23 23 phases,
24 24 registrar,
25 25 repoview,
26 26 revsetlang,
27 27 scmutil,
28 28 smartset,
29 29 util,
30 30 )
31 31
32 32 # helpers for processing parsed tree
33 33 getsymbol = revsetlang.getsymbol
34 34 getstring = revsetlang.getstring
35 35 getinteger = revsetlang.getinteger
36 36 getboolean = revsetlang.getboolean
37 37 getlist = revsetlang.getlist
38 38 getrange = revsetlang.getrange
39 39 getargs = revsetlang.getargs
40 40 getargsdict = revsetlang.getargsdict
41 41
42 42 # constants used as an argument of match() and matchany()
43 43 anyorder = revsetlang.anyorder
44 44 defineorder = revsetlang.defineorder
45 45 followorder = revsetlang.followorder
46 46
47 47 baseset = smartset.baseset
48 48 generatorset = smartset.generatorset
49 49 spanset = smartset.spanset
50 50 fullreposet = smartset.fullreposet
51 51
52 52 # helpers
53 53
54 54 def getset(repo, subset, x):
55 55 if not x:
56 56 raise error.ParseError(_("missing argument"))
57 57 return methods[x[0]](repo, subset, *x[1:])
58 58
59 59 def _getrevsource(repo, r):
60 60 extra = repo[r].extra()
61 61 for label in ('source', 'transplant_source', 'rebase_source'):
62 62 if label in extra:
63 63 try:
64 64 return repo[extra[label]].rev()
65 65 except error.RepoLookupError:
66 66 pass
67 67 return None
68 68
69 69 # operator methods
70 70
71 71 def stringset(repo, subset, x):
72 72 x = scmutil.intrev(repo[x])
73 73 if (x in subset
74 74 or x == node.nullrev and isinstance(subset, fullreposet)):
75 75 return baseset([x])
76 76 return baseset()
77 77
78 78 def rangeset(repo, subset, x, y, order):
79 79 m = getset(repo, fullreposet(repo), x)
80 80 n = getset(repo, fullreposet(repo), y)
81 81
82 82 if not m or not n:
83 83 return baseset()
84 84 return _makerangeset(repo, subset, m.first(), n.last(), order)
85 85
86 86 def rangeall(repo, subset, x, order):
87 87 assert x is None
88 88 return _makerangeset(repo, subset, 0, len(repo) - 1, order)
89 89
90 90 def rangepre(repo, subset, y, order):
91 91 # ':y' can't be rewritten to '0:y' since '0' may be hidden
92 92 n = getset(repo, fullreposet(repo), y)
93 93 if not n:
94 94 return baseset()
95 95 return _makerangeset(repo, subset, 0, n.last(), order)
96 96
97 97 def rangepost(repo, subset, x, order):
98 98 m = getset(repo, fullreposet(repo), x)
99 99 if not m:
100 100 return baseset()
101 101 return _makerangeset(repo, subset, m.first(), len(repo) - 1, order)
102 102
103 103 def _makerangeset(repo, subset, m, n, order):
104 104 if m == n:
105 105 r = baseset([m])
106 106 elif n == node.wdirrev:
107 107 r = spanset(repo, m, len(repo)) + baseset([n])
108 108 elif m == node.wdirrev:
109 109 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
110 110 elif m < n:
111 111 r = spanset(repo, m, n + 1)
112 112 else:
113 113 r = spanset(repo, m, n - 1)
114 114
115 115 if order == defineorder:
116 116 return r & subset
117 117 else:
118 118 # carrying the sorting over when possible would be more efficient
119 119 return subset & r
120 120
121 121 def dagrange(repo, subset, x, y, order):
122 122 r = fullreposet(repo)
123 123 xs = dagop.reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
124 124 includepath=True)
125 125 return subset & xs
126 126
127 127 def andset(repo, subset, x, y, order):
128 128 return getset(repo, getset(repo, subset, x), y)
129 129
130 130 def differenceset(repo, subset, x, y, order):
131 131 return getset(repo, subset, x) - getset(repo, subset, y)
132 132
133 133 def _orsetlist(repo, subset, xs):
134 134 assert xs
135 135 if len(xs) == 1:
136 136 return getset(repo, subset, xs[0])
137 137 p = len(xs) // 2
138 138 a = _orsetlist(repo, subset, xs[:p])
139 139 b = _orsetlist(repo, subset, xs[p:])
140 140 return a + b
141 141
142 142 def orset(repo, subset, x, order):
143 143 xs = getlist(x)
144 144 if order == followorder:
145 145 # slow path to take the subset order
146 146 return subset & _orsetlist(repo, fullreposet(repo), xs)
147 147 else:
148 148 return _orsetlist(repo, subset, xs)
149 149
150 150 def notset(repo, subset, x, order):
151 151 return subset - getset(repo, subset, x)
152 152
153 153 def listset(repo, subset, *xs):
154 154 raise error.ParseError(_("can't use a list in this context"),
155 155 hint=_('see hg help "revsets.x or y"'))
156 156
157 157 def keyvaluepair(repo, subset, k, v):
158 158 raise error.ParseError(_("can't use a key-value pair in this context"))
159 159
160 160 def func(repo, subset, a, b, order):
161 161 f = getsymbol(a)
162 162 if f in symbols:
163 163 func = symbols[f]
164 164 if getattr(func, '_takeorder', False):
165 165 return func(repo, subset, b, order)
166 166 return func(repo, subset, b)
167 167
168 168 keep = lambda fn: getattr(fn, '__doc__', None) is not None
169 169
170 170 syms = [s for (s, fn) in symbols.items() if keep(fn)]
171 171 raise error.UnknownIdentifier(f, syms)
172 172
173 173 # functions
174 174
175 175 # symbols are callables like:
176 176 # fn(repo, subset, x)
177 177 # with:
178 178 # repo - current repository instance
179 179 # subset - of revisions to be examined
180 180 # x - argument in tree form
181 181 symbols = {}
182 182
183 183 # symbols which can't be used for a DoS attack for any given input
184 184 # (e.g. those which accept regexes as plain strings shouldn't be included)
185 185 # functions that just return a lot of changesets (like all) don't count here
186 186 safesymbols = set()
187 187
188 188 predicate = registrar.revsetpredicate()
189 189
190 190 @predicate('_destupdate')
191 191 def _destupdate(repo, subset, x):
192 192 # experimental revset for update destination
193 193 args = getargsdict(x, 'limit', 'clean')
194 194 return subset & baseset([destutil.destupdate(repo, **args)[0]])
195 195
196 196 @predicate('_destmerge')
197 197 def _destmerge(repo, subset, x):
198 198 # experimental revset for merge destination
199 199 sourceset = None
200 200 if x is not None:
201 201 sourceset = getset(repo, fullreposet(repo), x)
202 202 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
203 203
204 204 @predicate('adds(pattern)', safe=True)
205 205 def adds(repo, subset, x):
206 206 """Changesets that add a file matching pattern.
207 207
208 208 The pattern without explicit kind like ``glob:`` is expected to be
209 209 relative to the current directory and match against a file or a
210 210 directory.
211 211 """
212 212 # i18n: "adds" is a keyword
213 213 pat = getstring(x, _("adds requires a pattern"))
214 214 return checkstatus(repo, subset, pat, 1)
215 215
216 216 @predicate('ancestor(*changeset)', safe=True)
217 217 def ancestor(repo, subset, x):
218 218 """A greatest common ancestor of the changesets.
219 219
220 220 Accepts 0 or more changesets.
221 221 Will return empty list when passed no args.
222 222 Greatest common ancestor of a single changeset is that changeset.
223 223 """
224 224 # i18n: "ancestor" is a keyword
225 225 l = getlist(x)
226 226 rl = fullreposet(repo)
227 227 anc = None
228 228
229 229 # (getset(repo, rl, i) for i in l) generates a list of lists
230 230 for revs in (getset(repo, rl, i) for i in l):
231 231 for r in revs:
232 232 if anc is None:
233 233 anc = repo[r]
234 234 else:
235 235 anc = anc.ancestor(repo[r])
236 236
237 237 if anc is not None and anc.rev() in subset:
238 238 return baseset([anc.rev()])
239 239 return baseset()
240 240
241 241 def _ancestors(repo, subset, x, followfirst=False):
242 242 heads = getset(repo, fullreposet(repo), x)
243 243 if not heads:
244 244 return baseset()
245 245 s = dagop.revancestors(repo, heads, followfirst)
246 246 return subset & s
247 247
248 248 @predicate('ancestors(set)', safe=True)
249 249 def ancestors(repo, subset, x):
250 250 """Changesets that are ancestors of changesets in set, including the
251 251 given changesets themselves.
252 252 """
253 return _ancestors(repo, subset, x)
253 args = getargsdict(x, 'ancestors', 'set')
254 if 'set' not in args:
255 # i18n: "ancestors" is a keyword
256 raise error.ParseError(_('ancestors takes at least 1 argument'))
257 return _ancestors(repo, subset, args['set'])
254 258
255 259 @predicate('_firstancestors', safe=True)
256 260 def _firstancestors(repo, subset, x):
257 261 # ``_firstancestors(set)``
258 262 # Like ``ancestors(set)`` but follows only the first parents.
259 263 return _ancestors(repo, subset, x, followfirst=True)
260 264
261 265 def _childrenspec(repo, subset, x, n, order):
262 266 """Changesets that are the Nth child of a changeset
263 267 in set.
264 268 """
265 269 cs = set()
266 270 for r in getset(repo, fullreposet(repo), x):
267 271 for i in range(n):
268 272 c = repo[r].children()
269 273 if len(c) == 0:
270 274 break
271 275 if len(c) > 1:
272 276 raise error.RepoLookupError(
273 277 _("revision in set has more than one child"))
274 278 r = c[0].rev()
275 279 else:
276 280 cs.add(r)
277 281 return subset & cs
278 282
279 283 def ancestorspec(repo, subset, x, n, order):
280 284 """``set~n``
281 285 Changesets that are the Nth ancestor (first parents only) of a changeset
282 286 in set.
283 287 """
284 288 n = getinteger(n, _("~ expects a number"))
285 289 if n < 0:
286 290 # children lookup
287 291 return _childrenspec(repo, subset, x, -n, order)
288 292 ps = set()
289 293 cl = repo.changelog
290 294 for r in getset(repo, fullreposet(repo), x):
291 295 for i in range(n):
292 296 try:
293 297 r = cl.parentrevs(r)[0]
294 298 except error.WdirUnsupported:
295 299 r = repo[r].parents()[0].rev()
296 300 ps.add(r)
297 301 return subset & ps
298 302
299 303 @predicate('author(string)', safe=True)
300 304 def author(repo, subset, x):
301 305 """Alias for ``user(string)``.
302 306 """
303 307 # i18n: "author" is a keyword
304 308 n = getstring(x, _("author requires a string"))
305 309 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
306 310 return subset.filter(lambda x: matcher(repo[x].user()),
307 311 condrepr=('<user %r>', n))
308 312
309 313 @predicate('bisect(string)', safe=True)
310 314 def bisect(repo, subset, x):
311 315 """Changesets marked in the specified bisect status:
312 316
313 317 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
314 318 - ``goods``, ``bads`` : csets topologically good/bad
315 319 - ``range`` : csets taking part in the bisection
316 320 - ``pruned`` : csets that are goods, bads or skipped
317 321 - ``untested`` : csets whose fate is yet unknown
318 322 - ``ignored`` : csets ignored due to DAG topology
319 323 - ``current`` : the cset currently being bisected
320 324 """
321 325 # i18n: "bisect" is a keyword
322 326 status = getstring(x, _("bisect requires a string")).lower()
323 327 state = set(hbisect.get(repo, status))
324 328 return subset & state
325 329
326 330 # Backward-compatibility
327 331 # - no help entry so that we do not advertise it any more
328 332 @predicate('bisected', safe=True)
329 333 def bisected(repo, subset, x):
330 334 return bisect(repo, subset, x)
331 335
332 336 @predicate('bookmark([name])', safe=True)
333 337 def bookmark(repo, subset, x):
334 338 """The named bookmark or all bookmarks.
335 339
336 340 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
337 341 """
338 342 # i18n: "bookmark" is a keyword
339 343 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
340 344 if args:
341 345 bm = getstring(args[0],
342 346 # i18n: "bookmark" is a keyword
343 347 _('the argument to bookmark must be a string'))
344 348 kind, pattern, matcher = util.stringmatcher(bm)
345 349 bms = set()
346 350 if kind == 'literal':
347 351 bmrev = repo._bookmarks.get(pattern, None)
348 352 if not bmrev:
349 353 raise error.RepoLookupError(_("bookmark '%s' does not exist")
350 354 % pattern)
351 355 bms.add(repo[bmrev].rev())
352 356 else:
353 357 matchrevs = set()
354 358 for name, bmrev in repo._bookmarks.iteritems():
355 359 if matcher(name):
356 360 matchrevs.add(bmrev)
357 361 if not matchrevs:
358 362 raise error.RepoLookupError(_("no bookmarks exist"
359 363 " that match '%s'") % pattern)
360 364 for bmrev in matchrevs:
361 365 bms.add(repo[bmrev].rev())
362 366 else:
363 367 bms = {repo[r].rev() for r in repo._bookmarks.values()}
364 368 bms -= {node.nullrev}
365 369 return subset & bms
366 370
367 371 @predicate('branch(string or set)', safe=True)
368 372 def branch(repo, subset, x):
369 373 """
370 374 All changesets belonging to the given branch or the branches of the given
371 375 changesets.
372 376
373 377 Pattern matching is supported for `string`. See
374 378 :hg:`help revisions.patterns`.
375 379 """
376 380 getbi = repo.revbranchcache().branchinfo
377 381 def getbranch(r):
378 382 try:
379 383 return getbi(r)[0]
380 384 except error.WdirUnsupported:
381 385 return repo[r].branch()
382 386
383 387 try:
384 388 b = getstring(x, '')
385 389 except error.ParseError:
386 390 # not a string, but another revspec, e.g. tip()
387 391 pass
388 392 else:
389 393 kind, pattern, matcher = util.stringmatcher(b)
390 394 if kind == 'literal':
391 395 # note: falls through to the revspec case if no branch with
392 396 # this name exists and pattern kind is not specified explicitly
393 397 if pattern in repo.branchmap():
394 398 return subset.filter(lambda r: matcher(getbranch(r)),
395 399 condrepr=('<branch %r>', b))
396 400 if b.startswith('literal:'):
397 401 raise error.RepoLookupError(_("branch '%s' does not exist")
398 402 % pattern)
399 403 else:
400 404 return subset.filter(lambda r: matcher(getbranch(r)),
401 405 condrepr=('<branch %r>', b))
402 406
403 407 s = getset(repo, fullreposet(repo), x)
404 408 b = set()
405 409 for r in s:
406 410 b.add(getbranch(r))
407 411 c = s.__contains__
408 412 return subset.filter(lambda r: c(r) or getbranch(r) in b,
409 413 condrepr=lambda: '<branch %r>' % sorted(b))
410 414
411 415 @predicate('bumped()', safe=True)
412 416 def bumped(repo, subset, x):
413 417 """Mutable changesets marked as successors of public changesets.
414 418
415 419 Only non-public and non-obsolete changesets can be `bumped`.
416 420 """
417 421 # i18n: "bumped" is a keyword
418 422 getargs(x, 0, 0, _("bumped takes no arguments"))
419 423 bumped = obsmod.getrevs(repo, 'bumped')
420 424 return subset & bumped
421 425
422 426 @predicate('bundle()', safe=True)
423 427 def bundle(repo, subset, x):
424 428 """Changesets in the bundle.
425 429
426 430 Bundle must be specified by the -R option."""
427 431
428 432 try:
429 433 bundlerevs = repo.changelog.bundlerevs
430 434 except AttributeError:
431 435 raise error.Abort(_("no bundle provided - specify with -R"))
432 436 return subset & bundlerevs
433 437
434 438 def checkstatus(repo, subset, pat, field):
435 439 hasset = matchmod.patkind(pat) == 'set'
436 440
437 441 mcache = [None]
438 442 def matches(x):
439 443 c = repo[x]
440 444 if not mcache[0] or hasset:
441 445 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
442 446 m = mcache[0]
443 447 fname = None
444 448 if not m.anypats() and len(m.files()) == 1:
445 449 fname = m.files()[0]
446 450 if fname is not None:
447 451 if fname not in c.files():
448 452 return False
449 453 else:
450 454 for f in c.files():
451 455 if m(f):
452 456 break
453 457 else:
454 458 return False
455 459 files = repo.status(c.p1().node(), c.node())[field]
456 460 if fname is not None:
457 461 if fname in files:
458 462 return True
459 463 else:
460 464 for f in files:
461 465 if m(f):
462 466 return True
463 467
464 468 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
465 469
466 470 def _children(repo, subset, parentset):
467 471 if not parentset:
468 472 return baseset()
469 473 cs = set()
470 474 pr = repo.changelog.parentrevs
471 475 minrev = parentset.min()
472 476 nullrev = node.nullrev
473 477 for r in subset:
474 478 if r <= minrev:
475 479 continue
476 480 p1, p2 = pr(r)
477 481 if p1 in parentset:
478 482 cs.add(r)
479 483 if p2 != nullrev and p2 in parentset:
480 484 cs.add(r)
481 485 return baseset(cs)
482 486
483 487 @predicate('children(set)', safe=True)
484 488 def children(repo, subset, x):
485 489 """Child changesets of changesets in set.
486 490 """
487 491 s = getset(repo, fullreposet(repo), x)
488 492 cs = _children(repo, subset, s)
489 493 return subset & cs
490 494
491 495 @predicate('closed()', safe=True)
492 496 def closed(repo, subset, x):
493 497 """Changeset is closed.
494 498 """
495 499 # i18n: "closed" is a keyword
496 500 getargs(x, 0, 0, _("closed takes no arguments"))
497 501 return subset.filter(lambda r: repo[r].closesbranch(),
498 502 condrepr='<branch closed>')
499 503
500 504 @predicate('contains(pattern)')
501 505 def contains(repo, subset, x):
502 506 """The revision's manifest contains a file matching pattern (but might not
503 507 modify it). See :hg:`help patterns` for information about file patterns.
504 508
505 509 The pattern without explicit kind like ``glob:`` is expected to be
506 510 relative to the current directory and match against a file exactly
507 511 for efficiency.
508 512 """
509 513 # i18n: "contains" is a keyword
510 514 pat = getstring(x, _("contains requires a pattern"))
511 515
512 516 def matches(x):
513 517 if not matchmod.patkind(pat):
514 518 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
515 519 if pats in repo[x]:
516 520 return True
517 521 else:
518 522 c = repo[x]
519 523 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
520 524 for f in c.manifest():
521 525 if m(f):
522 526 return True
523 527 return False
524 528
525 529 return subset.filter(matches, condrepr=('<contains %r>', pat))
526 530
527 531 @predicate('converted([id])', safe=True)
528 532 def converted(repo, subset, x):
529 533 """Changesets converted from the given identifier in the old repository if
530 534 present, or all converted changesets if no identifier is specified.
531 535 """
532 536
533 537 # There is exactly no chance of resolving the revision, so do a simple
534 538 # string compare and hope for the best
535 539
536 540 rev = None
537 541 # i18n: "converted" is a keyword
538 542 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
539 543 if l:
540 544 # i18n: "converted" is a keyword
541 545 rev = getstring(l[0], _('converted requires a revision'))
542 546
543 547 def _matchvalue(r):
544 548 source = repo[r].extra().get('convert_revision', None)
545 549 return source is not None and (rev is None or source.startswith(rev))
546 550
547 551 return subset.filter(lambda r: _matchvalue(r),
548 552 condrepr=('<converted %r>', rev))
549 553
550 554 @predicate('date(interval)', safe=True)
551 555 def date(repo, subset, x):
552 556 """Changesets within the interval, see :hg:`help dates`.
553 557 """
554 558 # i18n: "date" is a keyword
555 559 ds = getstring(x, _("date requires a string"))
556 560 dm = util.matchdate(ds)
557 561 return subset.filter(lambda x: dm(repo[x].date()[0]),
558 562 condrepr=('<date %r>', ds))
559 563
560 564 @predicate('desc(string)', safe=True)
561 565 def desc(repo, subset, x):
562 566 """Search commit message for string. The match is case-insensitive.
563 567
564 568 Pattern matching is supported for `string`. See
565 569 :hg:`help revisions.patterns`.
566 570 """
567 571 # i18n: "desc" is a keyword
568 572 ds = getstring(x, _("desc requires a string"))
569 573
570 574 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
571 575
572 576 return subset.filter(lambda r: matcher(repo[r].description()),
573 577 condrepr=('<desc %r>', ds))
574 578
575 579 def _descendants(repo, subset, x, followfirst=False):
576 580 roots = getset(repo, fullreposet(repo), x)
577 581 if not roots:
578 582 return baseset()
579 583 s = dagop.revdescendants(repo, roots, followfirst)
580 584
581 585 # Both sets need to be ascending in order to lazily return the union
582 586 # in the correct order.
583 587 base = subset & roots
584 588 desc = subset & s
585 589 result = base + desc
586 590 if subset.isascending():
587 591 result.sort()
588 592 elif subset.isdescending():
589 593 result.sort(reverse=True)
590 594 else:
591 595 result = subset & result
592 596 return result
593 597
594 598 @predicate('descendants(set)', safe=True)
595 599 def descendants(repo, subset, x):
596 600 """Changesets which are descendants of changesets in set, including the
597 601 given changesets themselves.
598 602 """
599 return _descendants(repo, subset, x)
603 args = getargsdict(x, 'descendants', 'set')
604 if 'set' not in args:
605 # i18n: "descendants" is a keyword
606 raise error.ParseError(_('descendants takes at least 1 argument'))
607 return _descendants(repo, subset, args['set'])
600 608
601 609 @predicate('_firstdescendants', safe=True)
602 610 def _firstdescendants(repo, subset, x):
603 611 # ``_firstdescendants(set)``
604 612 # Like ``descendants(set)`` but follows only the first parents.
605 613 return _descendants(repo, subset, x, followfirst=True)
606 614
607 615 @predicate('destination([set])', safe=True)
608 616 def destination(repo, subset, x):
609 617 """Changesets that were created by a graft, transplant or rebase operation,
610 618 with the given revisions specified as the source. Omitting the optional set
611 619 is the same as passing all().
612 620 """
613 621 if x is not None:
614 622 sources = getset(repo, fullreposet(repo), x)
615 623 else:
616 624 sources = fullreposet(repo)
617 625
618 626 dests = set()
619 627
620 628 # subset contains all of the possible destinations that can be returned, so
621 629 # iterate over them and see if their source(s) were provided in the arg set.
622 630 # Even if the immediate src of r is not in the arg set, src's source (or
623 631 # further back) may be. Scanning back further than the immediate src allows
624 632 # transitive transplants and rebases to yield the same results as transitive
625 633 # grafts.
626 634 for r in subset:
627 635 src = _getrevsource(repo, r)
628 636 lineage = None
629 637
630 638 while src is not None:
631 639 if lineage is None:
632 640 lineage = list()
633 641
634 642 lineage.append(r)
635 643
636 644 # The visited lineage is a match if the current source is in the arg
637 645 # set. Since every candidate dest is visited by way of iterating
638 646 # subset, any dests further back in the lineage will be tested by a
639 647 # different iteration over subset. Likewise, if the src was already
640 648 # selected, the current lineage can be selected without going back
641 649 # further.
642 650 if src in sources or src in dests:
643 651 dests.update(lineage)
644 652 break
645 653
646 654 r = src
647 655 src = _getrevsource(repo, r)
648 656
649 657 return subset.filter(dests.__contains__,
650 658 condrepr=lambda: '<destination %r>' % sorted(dests))
651 659
652 660 @predicate('divergent()', safe=True)
653 661 def divergent(repo, subset, x):
654 662 """
655 663 Final successors of changesets with an alternative set of final successors.
656 664 """
657 665 # i18n: "divergent" is a keyword
658 666 getargs(x, 0, 0, _("divergent takes no arguments"))
659 667 divergent = obsmod.getrevs(repo, 'divergent')
660 668 return subset & divergent
661 669
662 670 @predicate('extinct()', safe=True)
663 671 def extinct(repo, subset, x):
664 672 """Obsolete changesets with obsolete descendants only.
665 673 """
666 674 # i18n: "extinct" is a keyword
667 675 getargs(x, 0, 0, _("extinct takes no arguments"))
668 676 extincts = obsmod.getrevs(repo, 'extinct')
669 677 return subset & extincts
670 678
671 679 @predicate('extra(label, [value])', safe=True)
672 680 def extra(repo, subset, x):
673 681 """Changesets with the given label in the extra metadata, with the given
674 682 optional value.
675 683
676 684 Pattern matching is supported for `value`. See
677 685 :hg:`help revisions.patterns`.
678 686 """
679 687 args = getargsdict(x, 'extra', 'label value')
680 688 if 'label' not in args:
681 689 # i18n: "extra" is a keyword
682 690 raise error.ParseError(_('extra takes at least 1 argument'))
683 691 # i18n: "extra" is a keyword
684 692 label = getstring(args['label'], _('first argument to extra must be '
685 693 'a string'))
686 694 value = None
687 695
688 696 if 'value' in args:
689 697 # i18n: "extra" is a keyword
690 698 value = getstring(args['value'], _('second argument to extra must be '
691 699 'a string'))
692 700 kind, value, matcher = util.stringmatcher(value)
693 701
694 702 def _matchvalue(r):
695 703 extra = repo[r].extra()
696 704 return label in extra and (value is None or matcher(extra[label]))
697 705
698 706 return subset.filter(lambda r: _matchvalue(r),
699 707 condrepr=('<extra[%r] %r>', label, value))
700 708
701 709 @predicate('filelog(pattern)', safe=True)
702 710 def filelog(repo, subset, x):
703 711 """Changesets connected to the specified filelog.
704 712
705 713 For performance reasons, visits only revisions mentioned in the file-level
706 714 filelog, rather than filtering through all changesets (much faster, but
707 715 doesn't include deletes or duplicate changes). For a slower, more accurate
708 716 result, use ``file()``.
709 717
710 718 The pattern without explicit kind like ``glob:`` is expected to be
711 719 relative to the current directory and match against a file exactly
712 720 for efficiency.
713 721
714 722 If some linkrev points to revisions filtered by the current repoview, we'll
715 723 work around it to return a non-filtered value.
716 724 """
717 725
718 726 # i18n: "filelog" is a keyword
719 727 pat = getstring(x, _("filelog requires a pattern"))
720 728 s = set()
721 729 cl = repo.changelog
722 730
723 731 if not matchmod.patkind(pat):
724 732 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
725 733 files = [f]
726 734 else:
727 735 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
728 736 files = (f for f in repo[None] if m(f))
729 737
730 738 for f in files:
731 739 fl = repo.file(f)
732 740 known = {}
733 741 scanpos = 0
734 742 for fr in list(fl):
735 743 fn = fl.node(fr)
736 744 if fn in known:
737 745 s.add(known[fn])
738 746 continue
739 747
740 748 lr = fl.linkrev(fr)
741 749 if lr in cl:
742 750 s.add(lr)
743 751 elif scanpos is not None:
744 752 # lowest matching changeset is filtered, scan further
745 753 # ahead in changelog
746 754 start = max(lr, scanpos) + 1
747 755 scanpos = None
748 756 for r in cl.revs(start):
749 757 # minimize parsing of non-matching entries
750 758 if f in cl.revision(r) and f in cl.readfiles(r):
751 759 try:
752 760 # try to use manifest delta fastpath
753 761 n = repo[r].filenode(f)
754 762 if n not in known:
755 763 if n == fn:
756 764 s.add(r)
757 765 scanpos = r
758 766 break
759 767 else:
760 768 known[n] = r
761 769 except error.ManifestLookupError:
762 770 # deletion in changelog
763 771 continue
764 772
765 773 return subset & s
766 774
767 775 @predicate('first(set, [n])', safe=True, takeorder=True)
768 776 def first(repo, subset, x, order):
769 777 """An alias for limit().
770 778 """
771 779 return limit(repo, subset, x, order)
772 780
773 781 def _follow(repo, subset, x, name, followfirst=False):
774 782 l = getargs(x, 0, 2, _("%s takes no arguments or a pattern "
775 783 "and an optional revset") % name)
776 784 c = repo['.']
777 785 if l:
778 786 x = getstring(l[0], _("%s expected a pattern") % name)
779 787 rev = None
780 788 if len(l) >= 2:
781 789 revs = getset(repo, fullreposet(repo), l[1])
782 790 if len(revs) != 1:
783 791 raise error.RepoLookupError(
784 792 _("%s expected one starting revision") % name)
785 793 rev = revs.last()
786 794 c = repo[rev]
787 795 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
788 796 ctx=repo[rev], default='path')
789 797
790 798 files = c.manifest().walk(matcher)
791 799
792 800 s = set()
793 801 for fname in files:
794 802 fctx = c[fname]
795 803 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
796 804 # include the revision responsible for the most recent version
797 805 s.add(fctx.introrev())
798 806 else:
799 807 s = dagop.revancestors(repo, baseset([c.rev()]), followfirst)
800 808
801 809 return subset & s
802 810
803 811 @predicate('follow([pattern[, startrev]])', safe=True)
804 812 def follow(repo, subset, x):
805 813 """
806 814 An alias for ``::.`` (ancestors of the working directory's first parent).
807 815 If pattern is specified, the histories of files matching given
808 816 pattern in the revision given by startrev are followed, including copies.
809 817 """
810 818 return _follow(repo, subset, x, 'follow')
811 819
812 820 @predicate('_followfirst', safe=True)
813 821 def _followfirst(repo, subset, x):
814 822 # ``followfirst([pattern[, startrev]])``
815 823 # Like ``follow([pattern[, startrev]])`` but follows only the first parent
816 824 # of every revisions or files revisions.
817 825 return _follow(repo, subset, x, '_followfirst', followfirst=True)
818 826
819 827 @predicate('followlines(file, fromline:toline[, startrev=., descend=False])',
820 828 safe=True)
821 829 def followlines(repo, subset, x):
822 830 """Changesets modifying `file` in line range ('fromline', 'toline').
823 831
824 832 Line range corresponds to 'file' content at 'startrev' and should hence be
825 833 consistent with file size. If startrev is not specified, working directory's
826 834 parent is used.
827 835
828 836 By default, ancestors of 'startrev' are returned. If 'descend' is True,
829 837 descendants of 'startrev' are returned though renames are (currently) not
830 838 followed in this direction.
831 839 """
832 840 args = getargsdict(x, 'followlines', 'file *lines startrev descend')
833 841 if len(args['lines']) != 1:
834 842 raise error.ParseError(_("followlines requires a line range"))
835 843
836 844 rev = '.'
837 845 if 'startrev' in args:
838 846 revs = getset(repo, fullreposet(repo), args['startrev'])
839 847 if len(revs) != 1:
840 848 raise error.ParseError(
841 849 # i18n: "followlines" is a keyword
842 850 _("followlines expects exactly one revision"))
843 851 rev = revs.last()
844 852
845 853 pat = getstring(args['file'], _("followlines requires a pattern"))
846 854 if not matchmod.patkind(pat):
847 855 fname = pathutil.canonpath(repo.root, repo.getcwd(), pat)
848 856 else:
849 857 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[rev])
850 858 files = [f for f in repo[rev] if m(f)]
851 859 if len(files) != 1:
852 860 # i18n: "followlines" is a keyword
853 861 raise error.ParseError(_("followlines expects exactly one file"))
854 862 fname = files[0]
855 863
856 864 # i18n: "followlines" is a keyword
857 865 lr = getrange(args['lines'][0], _("followlines expects a line range"))
858 866 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
859 867 for a in lr]
860 868 fromline, toline = util.processlinerange(fromline, toline)
861 869
862 870 fctx = repo[rev].filectx(fname)
863 871 descend = False
864 872 if 'descend' in args:
865 873 descend = getboolean(args['descend'],
866 874 # i18n: "descend" is a keyword
867 875 _("descend argument must be a boolean"))
868 876 if descend:
869 877 rs = generatorset(
870 878 (c.rev() for c, _linerange
871 879 in dagop.blockdescendants(fctx, fromline, toline)),
872 880 iterasc=True)
873 881 else:
874 882 rs = generatorset(
875 883 (c.rev() for c, _linerange
876 884 in dagop.blockancestors(fctx, fromline, toline)),
877 885 iterasc=False)
878 886 return subset & rs
879 887
880 888 @predicate('all()', safe=True)
881 889 def getall(repo, subset, x):
882 890 """All changesets, the same as ``0:tip``.
883 891 """
884 892 # i18n: "all" is a keyword
885 893 getargs(x, 0, 0, _("all takes no arguments"))
886 894 return subset & spanset(repo) # drop "null" if any
887 895
888 896 @predicate('grep(regex)')
889 897 def grep(repo, subset, x):
890 898 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
891 899 to ensure special escape characters are handled correctly. Unlike
892 900 ``keyword(string)``, the match is case-sensitive.
893 901 """
894 902 try:
895 903 # i18n: "grep" is a keyword
896 904 gr = re.compile(getstring(x, _("grep requires a string")))
897 905 except re.error as e:
898 906 raise error.ParseError(_('invalid match pattern: %s') % e)
899 907
900 908 def matches(x):
901 909 c = repo[x]
902 910 for e in c.files() + [c.user(), c.description()]:
903 911 if gr.search(e):
904 912 return True
905 913 return False
906 914
907 915 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
908 916
909 917 @predicate('_matchfiles', safe=True)
910 918 def _matchfiles(repo, subset, x):
911 919 # _matchfiles takes a revset list of prefixed arguments:
912 920 #
913 921 # [p:foo, i:bar, x:baz]
914 922 #
915 923 # builds a match object from them and filters subset. Allowed
916 924 # prefixes are 'p:' for regular patterns, 'i:' for include
917 925 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
918 926 # a revision identifier, or the empty string to reference the
919 927 # working directory, from which the match object is
920 928 # initialized. Use 'd:' to set the default matching mode, default
921 929 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
922 930
923 931 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
924 932 pats, inc, exc = [], [], []
925 933 rev, default = None, None
926 934 for arg in l:
927 935 s = getstring(arg, "_matchfiles requires string arguments")
928 936 prefix, value = s[:2], s[2:]
929 937 if prefix == 'p:':
930 938 pats.append(value)
931 939 elif prefix == 'i:':
932 940 inc.append(value)
933 941 elif prefix == 'x:':
934 942 exc.append(value)
935 943 elif prefix == 'r:':
936 944 if rev is not None:
937 945 raise error.ParseError('_matchfiles expected at most one '
938 946 'revision')
939 947 if value != '': # empty means working directory; leave rev as None
940 948 rev = value
941 949 elif prefix == 'd:':
942 950 if default is not None:
943 951 raise error.ParseError('_matchfiles expected at most one '
944 952 'default mode')
945 953 default = value
946 954 else:
947 955 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
948 956 if not default:
949 957 default = 'glob'
950 958
951 959 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
952 960 exclude=exc, ctx=repo[rev], default=default)
953 961
954 962 # This directly read the changelog data as creating changectx for all
955 963 # revisions is quite expensive.
956 964 getfiles = repo.changelog.readfiles
957 965 wdirrev = node.wdirrev
958 966 def matches(x):
959 967 if x == wdirrev:
960 968 files = repo[x].files()
961 969 else:
962 970 files = getfiles(x)
963 971 for f in files:
964 972 if m(f):
965 973 return True
966 974 return False
967 975
968 976 return subset.filter(matches,
969 977 condrepr=('<matchfiles patterns=%r, include=%r '
970 978 'exclude=%r, default=%r, rev=%r>',
971 979 pats, inc, exc, default, rev))
972 980
973 981 @predicate('file(pattern)', safe=True)
974 982 def hasfile(repo, subset, x):
975 983 """Changesets affecting files matched by pattern.
976 984
977 985 For a faster but less accurate result, consider using ``filelog()``
978 986 instead.
979 987
980 988 This predicate uses ``glob:`` as the default kind of pattern.
981 989 """
982 990 # i18n: "file" is a keyword
983 991 pat = getstring(x, _("file requires a pattern"))
984 992 return _matchfiles(repo, subset, ('string', 'p:' + pat))
985 993
986 994 @predicate('head()', safe=True)
987 995 def head(repo, subset, x):
988 996 """Changeset is a named branch head.
989 997 """
990 998 # i18n: "head" is a keyword
991 999 getargs(x, 0, 0, _("head takes no arguments"))
992 1000 hs = set()
993 1001 cl = repo.changelog
994 1002 for ls in repo.branchmap().itervalues():
995 1003 hs.update(cl.rev(h) for h in ls)
996 1004 return subset & baseset(hs)
997 1005
998 1006 @predicate('heads(set)', safe=True)
999 1007 def heads(repo, subset, x):
1000 1008 """Members of set with no children in set.
1001 1009 """
1002 1010 s = getset(repo, subset, x)
1003 1011 ps = parents(repo, subset, x)
1004 1012 return s - ps
1005 1013
1006 1014 @predicate('hidden()', safe=True)
1007 1015 def hidden(repo, subset, x):
1008 1016 """Hidden changesets.
1009 1017 """
1010 1018 # i18n: "hidden" is a keyword
1011 1019 getargs(x, 0, 0, _("hidden takes no arguments"))
1012 1020 hiddenrevs = repoview.filterrevs(repo, 'visible')
1013 1021 return subset & hiddenrevs
1014 1022
1015 1023 @predicate('keyword(string)', safe=True)
1016 1024 def keyword(repo, subset, x):
1017 1025 """Search commit message, user name, and names of changed files for
1018 1026 string. The match is case-insensitive.
1019 1027
1020 1028 For a regular expression or case sensitive search of these fields, use
1021 1029 ``grep(regex)``.
1022 1030 """
1023 1031 # i18n: "keyword" is a keyword
1024 1032 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1025 1033
1026 1034 def matches(r):
1027 1035 c = repo[r]
1028 1036 return any(kw in encoding.lower(t)
1029 1037 for t in c.files() + [c.user(), c.description()])
1030 1038
1031 1039 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1032 1040
1033 1041 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True)
1034 1042 def limit(repo, subset, x, order):
1035 1043 """First n members of set, defaulting to 1, starting from offset.
1036 1044 """
1037 1045 args = getargsdict(x, 'limit', 'set n offset')
1038 1046 if 'set' not in args:
1039 1047 # i18n: "limit" is a keyword
1040 1048 raise error.ParseError(_("limit requires one to three arguments"))
1041 1049 # i18n: "limit" is a keyword
1042 1050 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1043 1051 if lim < 0:
1044 1052 raise error.ParseError(_("negative number to select"))
1045 1053 # i18n: "limit" is a keyword
1046 1054 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1047 1055 if ofs < 0:
1048 1056 raise error.ParseError(_("negative offset"))
1049 1057 os = getset(repo, fullreposet(repo), args['set'])
1050 1058 ls = os.slice(ofs, ofs + lim)
1051 1059 if order == followorder and lim > 1:
1052 1060 return subset & ls
1053 1061 return ls & subset
1054 1062
1055 1063 @predicate('last(set, [n])', safe=True, takeorder=True)
1056 1064 def last(repo, subset, x, order):
1057 1065 """Last n members of set, defaulting to 1.
1058 1066 """
1059 1067 # i18n: "last" is a keyword
1060 1068 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1061 1069 lim = 1
1062 1070 if len(l) == 2:
1063 1071 # i18n: "last" is a keyword
1064 1072 lim = getinteger(l[1], _("last expects a number"))
1065 1073 if lim < 0:
1066 1074 raise error.ParseError(_("negative number to select"))
1067 1075 os = getset(repo, fullreposet(repo), l[0])
1068 1076 os.reverse()
1069 1077 ls = os.slice(0, lim)
1070 1078 if order == followorder and lim > 1:
1071 1079 return subset & ls
1072 1080 ls.reverse()
1073 1081 return ls & subset
1074 1082
1075 1083 @predicate('max(set)', safe=True)
1076 1084 def maxrev(repo, subset, x):
1077 1085 """Changeset with highest revision number in set.
1078 1086 """
1079 1087 os = getset(repo, fullreposet(repo), x)
1080 1088 try:
1081 1089 m = os.max()
1082 1090 if m in subset:
1083 1091 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1084 1092 except ValueError:
1085 1093 # os.max() throws a ValueError when the collection is empty.
1086 1094 # Same as python's max().
1087 1095 pass
1088 1096 return baseset(datarepr=('<max %r, %r>', subset, os))
1089 1097
1090 1098 @predicate('merge()', safe=True)
1091 1099 def merge(repo, subset, x):
1092 1100 """Changeset is a merge changeset.
1093 1101 """
1094 1102 # i18n: "merge" is a keyword
1095 1103 getargs(x, 0, 0, _("merge takes no arguments"))
1096 1104 cl = repo.changelog
1097 1105 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1098 1106 condrepr='<merge>')
1099 1107
1100 1108 @predicate('branchpoint()', safe=True)
1101 1109 def branchpoint(repo, subset, x):
1102 1110 """Changesets with more than one child.
1103 1111 """
1104 1112 # i18n: "branchpoint" is a keyword
1105 1113 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1106 1114 cl = repo.changelog
1107 1115 if not subset:
1108 1116 return baseset()
1109 1117 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1110 1118 # (and if it is not, it should.)
1111 1119 baserev = min(subset)
1112 1120 parentscount = [0]*(len(repo) - baserev)
1113 1121 for r in cl.revs(start=baserev + 1):
1114 1122 for p in cl.parentrevs(r):
1115 1123 if p >= baserev:
1116 1124 parentscount[p - baserev] += 1
1117 1125 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1118 1126 condrepr='<branchpoint>')
1119 1127
1120 1128 @predicate('min(set)', safe=True)
1121 1129 def minrev(repo, subset, x):
1122 1130 """Changeset with lowest revision number in set.
1123 1131 """
1124 1132 os = getset(repo, fullreposet(repo), x)
1125 1133 try:
1126 1134 m = os.min()
1127 1135 if m in subset:
1128 1136 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1129 1137 except ValueError:
1130 1138 # os.min() throws a ValueError when the collection is empty.
1131 1139 # Same as python's min().
1132 1140 pass
1133 1141 return baseset(datarepr=('<min %r, %r>', subset, os))
1134 1142
1135 1143 @predicate('modifies(pattern)', safe=True)
1136 1144 def modifies(repo, subset, x):
1137 1145 """Changesets modifying files matched by pattern.
1138 1146
1139 1147 The pattern without explicit kind like ``glob:`` is expected to be
1140 1148 relative to the current directory and match against a file or a
1141 1149 directory.
1142 1150 """
1143 1151 # i18n: "modifies" is a keyword
1144 1152 pat = getstring(x, _("modifies requires a pattern"))
1145 1153 return checkstatus(repo, subset, pat, 0)
1146 1154
1147 1155 @predicate('named(namespace)')
1148 1156 def named(repo, subset, x):
1149 1157 """The changesets in a given namespace.
1150 1158
1151 1159 Pattern matching is supported for `namespace`. See
1152 1160 :hg:`help revisions.patterns`.
1153 1161 """
1154 1162 # i18n: "named" is a keyword
1155 1163 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1156 1164
1157 1165 ns = getstring(args[0],
1158 1166 # i18n: "named" is a keyword
1159 1167 _('the argument to named must be a string'))
1160 1168 kind, pattern, matcher = util.stringmatcher(ns)
1161 1169 namespaces = set()
1162 1170 if kind == 'literal':
1163 1171 if pattern not in repo.names:
1164 1172 raise error.RepoLookupError(_("namespace '%s' does not exist")
1165 1173 % ns)
1166 1174 namespaces.add(repo.names[pattern])
1167 1175 else:
1168 1176 for name, ns in repo.names.iteritems():
1169 1177 if matcher(name):
1170 1178 namespaces.add(ns)
1171 1179 if not namespaces:
1172 1180 raise error.RepoLookupError(_("no namespace exists"
1173 1181 " that match '%s'") % pattern)
1174 1182
1175 1183 names = set()
1176 1184 for ns in namespaces:
1177 1185 for name in ns.listnames(repo):
1178 1186 if name not in ns.deprecated:
1179 1187 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1180 1188
1181 1189 names -= {node.nullrev}
1182 1190 return subset & names
1183 1191
1184 1192 @predicate('id(string)', safe=True)
1185 1193 def node_(repo, subset, x):
1186 1194 """Revision non-ambiguously specified by the given hex string prefix.
1187 1195 """
1188 1196 # i18n: "id" is a keyword
1189 1197 l = getargs(x, 1, 1, _("id requires one argument"))
1190 1198 # i18n: "id" is a keyword
1191 1199 n = getstring(l[0], _("id requires a string"))
1192 1200 if len(n) == 40:
1193 1201 try:
1194 1202 rn = repo.changelog.rev(node.bin(n))
1195 1203 except error.WdirUnsupported:
1196 1204 rn = node.wdirrev
1197 1205 except (LookupError, TypeError):
1198 1206 rn = None
1199 1207 else:
1200 1208 rn = None
1201 1209 try:
1202 1210 pm = repo.changelog._partialmatch(n)
1203 1211 if pm is not None:
1204 1212 rn = repo.changelog.rev(pm)
1205 1213 except error.WdirUnsupported:
1206 1214 rn = node.wdirrev
1207 1215
1208 1216 if rn is None:
1209 1217 return baseset()
1210 1218 result = baseset([rn])
1211 1219 return result & subset
1212 1220
1213 1221 @predicate('obsolete()', safe=True)
1214 1222 def obsolete(repo, subset, x):
1215 1223 """Mutable changeset with a newer version."""
1216 1224 # i18n: "obsolete" is a keyword
1217 1225 getargs(x, 0, 0, _("obsolete takes no arguments"))
1218 1226 obsoletes = obsmod.getrevs(repo, 'obsolete')
1219 1227 return subset & obsoletes
1220 1228
1221 1229 @predicate('only(set, [set])', safe=True)
1222 1230 def only(repo, subset, x):
1223 1231 """Changesets that are ancestors of the first set that are not ancestors
1224 1232 of any other head in the repo. If a second set is specified, the result
1225 1233 is ancestors of the first set that are not ancestors of the second set
1226 1234 (i.e. ::<set1> - ::<set2>).
1227 1235 """
1228 1236 cl = repo.changelog
1229 1237 # i18n: "only" is a keyword
1230 1238 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1231 1239 include = getset(repo, fullreposet(repo), args[0])
1232 1240 if len(args) == 1:
1233 1241 if not include:
1234 1242 return baseset()
1235 1243
1236 1244 descendants = set(dagop.revdescendants(repo, include, False))
1237 1245 exclude = [rev for rev in cl.headrevs()
1238 1246 if not rev in descendants and not rev in include]
1239 1247 else:
1240 1248 exclude = getset(repo, fullreposet(repo), args[1])
1241 1249
1242 1250 results = set(cl.findmissingrevs(common=exclude, heads=include))
1243 1251 # XXX we should turn this into a baseset instead of a set, smartset may do
1244 1252 # some optimizations from the fact this is a baseset.
1245 1253 return subset & results
1246 1254
1247 1255 @predicate('origin([set])', safe=True)
1248 1256 def origin(repo, subset, x):
1249 1257 """
1250 1258 Changesets that were specified as a source for the grafts, transplants or
1251 1259 rebases that created the given revisions. Omitting the optional set is the
1252 1260 same as passing all(). If a changeset created by these operations is itself
1253 1261 specified as a source for one of these operations, only the source changeset
1254 1262 for the first operation is selected.
1255 1263 """
1256 1264 if x is not None:
1257 1265 dests = getset(repo, fullreposet(repo), x)
1258 1266 else:
1259 1267 dests = fullreposet(repo)
1260 1268
1261 1269 def _firstsrc(rev):
1262 1270 src = _getrevsource(repo, rev)
1263 1271 if src is None:
1264 1272 return None
1265 1273
1266 1274 while True:
1267 1275 prev = _getrevsource(repo, src)
1268 1276
1269 1277 if prev is None:
1270 1278 return src
1271 1279 src = prev
1272 1280
1273 1281 o = {_firstsrc(r) for r in dests}
1274 1282 o -= {None}
1275 1283 # XXX we should turn this into a baseset instead of a set, smartset may do
1276 1284 # some optimizations from the fact this is a baseset.
1277 1285 return subset & o
1278 1286
1279 1287 @predicate('outgoing([path])', safe=False)
1280 1288 def outgoing(repo, subset, x):
1281 1289 """Changesets not found in the specified destination repository, or the
1282 1290 default push location.
1283 1291 """
1284 1292 # Avoid cycles.
1285 1293 from . import (
1286 1294 discovery,
1287 1295 hg,
1288 1296 )
1289 1297 # i18n: "outgoing" is a keyword
1290 1298 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1291 1299 # i18n: "outgoing" is a keyword
1292 1300 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1293 1301 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1294 1302 dest, branches = hg.parseurl(dest)
1295 1303 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1296 1304 if revs:
1297 1305 revs = [repo.lookup(rev) for rev in revs]
1298 1306 other = hg.peer(repo, {}, dest)
1299 1307 repo.ui.pushbuffer()
1300 1308 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1301 1309 repo.ui.popbuffer()
1302 1310 cl = repo.changelog
1303 1311 o = {cl.rev(r) for r in outgoing.missing}
1304 1312 return subset & o
1305 1313
1306 1314 @predicate('p1([set])', safe=True)
1307 1315 def p1(repo, subset, x):
1308 1316 """First parent of changesets in set, or the working directory.
1309 1317 """
1310 1318 if x is None:
1311 1319 p = repo[x].p1().rev()
1312 1320 if p >= 0:
1313 1321 return subset & baseset([p])
1314 1322 return baseset()
1315 1323
1316 1324 ps = set()
1317 1325 cl = repo.changelog
1318 1326 for r in getset(repo, fullreposet(repo), x):
1319 1327 try:
1320 1328 ps.add(cl.parentrevs(r)[0])
1321 1329 except error.WdirUnsupported:
1322 1330 ps.add(repo[r].parents()[0].rev())
1323 1331 ps -= {node.nullrev}
1324 1332 # XXX we should turn this into a baseset instead of a set, smartset may do
1325 1333 # some optimizations from the fact this is a baseset.
1326 1334 return subset & ps
1327 1335
1328 1336 @predicate('p2([set])', safe=True)
1329 1337 def p2(repo, subset, x):
1330 1338 """Second parent of changesets in set, or the working directory.
1331 1339 """
1332 1340 if x is None:
1333 1341 ps = repo[x].parents()
1334 1342 try:
1335 1343 p = ps[1].rev()
1336 1344 if p >= 0:
1337 1345 return subset & baseset([p])
1338 1346 return baseset()
1339 1347 except IndexError:
1340 1348 return baseset()
1341 1349
1342 1350 ps = set()
1343 1351 cl = repo.changelog
1344 1352 for r in getset(repo, fullreposet(repo), x):
1345 1353 try:
1346 1354 ps.add(cl.parentrevs(r)[1])
1347 1355 except error.WdirUnsupported:
1348 1356 parents = repo[r].parents()
1349 1357 if len(parents) == 2:
1350 1358 ps.add(parents[1])
1351 1359 ps -= {node.nullrev}
1352 1360 # XXX we should turn this into a baseset instead of a set, smartset may do
1353 1361 # some optimizations from the fact this is a baseset.
1354 1362 return subset & ps
1355 1363
1356 1364 def parentpost(repo, subset, x, order):
1357 1365 return p1(repo, subset, x)
1358 1366
1359 1367 @predicate('parents([set])', safe=True)
1360 1368 def parents(repo, subset, x):
1361 1369 """
1362 1370 The set of all parents for all changesets in set, or the working directory.
1363 1371 """
1364 1372 if x is None:
1365 1373 ps = set(p.rev() for p in repo[x].parents())
1366 1374 else:
1367 1375 ps = set()
1368 1376 cl = repo.changelog
1369 1377 up = ps.update
1370 1378 parentrevs = cl.parentrevs
1371 1379 for r in getset(repo, fullreposet(repo), x):
1372 1380 try:
1373 1381 up(parentrevs(r))
1374 1382 except error.WdirUnsupported:
1375 1383 up(p.rev() for p in repo[r].parents())
1376 1384 ps -= {node.nullrev}
1377 1385 return subset & ps
1378 1386
1379 1387 def _phase(repo, subset, *targets):
1380 1388 """helper to select all rev in <targets> phases"""
1381 1389 s = repo._phasecache.getrevset(repo, targets)
1382 1390 return subset & s
1383 1391
1384 1392 @predicate('draft()', safe=True)
1385 1393 def draft(repo, subset, x):
1386 1394 """Changeset in draft phase."""
1387 1395 # i18n: "draft" is a keyword
1388 1396 getargs(x, 0, 0, _("draft takes no arguments"))
1389 1397 target = phases.draft
1390 1398 return _phase(repo, subset, target)
1391 1399
1392 1400 @predicate('secret()', safe=True)
1393 1401 def secret(repo, subset, x):
1394 1402 """Changeset in secret phase."""
1395 1403 # i18n: "secret" is a keyword
1396 1404 getargs(x, 0, 0, _("secret takes no arguments"))
1397 1405 target = phases.secret
1398 1406 return _phase(repo, subset, target)
1399 1407
1400 1408 def parentspec(repo, subset, x, n, order):
1401 1409 """``set^0``
1402 1410 The set.
1403 1411 ``set^1`` (or ``set^``), ``set^2``
1404 1412 First or second parent, respectively, of all changesets in set.
1405 1413 """
1406 1414 try:
1407 1415 n = int(n[1])
1408 1416 if n not in (0, 1, 2):
1409 1417 raise ValueError
1410 1418 except (TypeError, ValueError):
1411 1419 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1412 1420 ps = set()
1413 1421 cl = repo.changelog
1414 1422 for r in getset(repo, fullreposet(repo), x):
1415 1423 if n == 0:
1416 1424 ps.add(r)
1417 1425 elif n == 1:
1418 1426 try:
1419 1427 ps.add(cl.parentrevs(r)[0])
1420 1428 except error.WdirUnsupported:
1421 1429 ps.add(repo[r].parents()[0].rev())
1422 1430 else:
1423 1431 try:
1424 1432 parents = cl.parentrevs(r)
1425 1433 if parents[1] != node.nullrev:
1426 1434 ps.add(parents[1])
1427 1435 except error.WdirUnsupported:
1428 1436 parents = repo[r].parents()
1429 1437 if len(parents) == 2:
1430 1438 ps.add(parents[1].rev())
1431 1439 return subset & ps
1432 1440
1433 1441 @predicate('present(set)', safe=True)
1434 1442 def present(repo, subset, x):
1435 1443 """An empty set, if any revision in set isn't found; otherwise,
1436 1444 all revisions in set.
1437 1445
1438 1446 If any of specified revisions is not present in the local repository,
1439 1447 the query is normally aborted. But this predicate allows the query
1440 1448 to continue even in such cases.
1441 1449 """
1442 1450 try:
1443 1451 return getset(repo, subset, x)
1444 1452 except error.RepoLookupError:
1445 1453 return baseset()
1446 1454
1447 1455 # for internal use
1448 1456 @predicate('_notpublic', safe=True)
1449 1457 def _notpublic(repo, subset, x):
1450 1458 getargs(x, 0, 0, "_notpublic takes no arguments")
1451 1459 return _phase(repo, subset, phases.draft, phases.secret)
1452 1460
1453 1461 @predicate('public()', safe=True)
1454 1462 def public(repo, subset, x):
1455 1463 """Changeset in public phase."""
1456 1464 # i18n: "public" is a keyword
1457 1465 getargs(x, 0, 0, _("public takes no arguments"))
1458 1466 phase = repo._phasecache.phase
1459 1467 target = phases.public
1460 1468 condition = lambda r: phase(repo, r) == target
1461 1469 return subset.filter(condition, condrepr=('<phase %r>', target),
1462 1470 cache=False)
1463 1471
1464 1472 @predicate('remote([id [,path]])', safe=False)
1465 1473 def remote(repo, subset, x):
1466 1474 """Local revision that corresponds to the given identifier in a
1467 1475 remote repository, if present. Here, the '.' identifier is a
1468 1476 synonym for the current local branch.
1469 1477 """
1470 1478
1471 1479 from . import hg # avoid start-up nasties
1472 1480 # i18n: "remote" is a keyword
1473 1481 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1474 1482
1475 1483 q = '.'
1476 1484 if len(l) > 0:
1477 1485 # i18n: "remote" is a keyword
1478 1486 q = getstring(l[0], _("remote requires a string id"))
1479 1487 if q == '.':
1480 1488 q = repo['.'].branch()
1481 1489
1482 1490 dest = ''
1483 1491 if len(l) > 1:
1484 1492 # i18n: "remote" is a keyword
1485 1493 dest = getstring(l[1], _("remote requires a repository path"))
1486 1494 dest = repo.ui.expandpath(dest or 'default')
1487 1495 dest, branches = hg.parseurl(dest)
1488 1496 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1489 1497 if revs:
1490 1498 revs = [repo.lookup(rev) for rev in revs]
1491 1499 other = hg.peer(repo, {}, dest)
1492 1500 n = other.lookup(q)
1493 1501 if n in repo:
1494 1502 r = repo[n].rev()
1495 1503 if r in subset:
1496 1504 return baseset([r])
1497 1505 return baseset()
1498 1506
1499 1507 @predicate('removes(pattern)', safe=True)
1500 1508 def removes(repo, subset, x):
1501 1509 """Changesets which remove files matching pattern.
1502 1510
1503 1511 The pattern without explicit kind like ``glob:`` is expected to be
1504 1512 relative to the current directory and match against a file or a
1505 1513 directory.
1506 1514 """
1507 1515 # i18n: "removes" is a keyword
1508 1516 pat = getstring(x, _("removes requires a pattern"))
1509 1517 return checkstatus(repo, subset, pat, 2)
1510 1518
1511 1519 @predicate('rev(number)', safe=True)
1512 1520 def rev(repo, subset, x):
1513 1521 """Revision with the given numeric identifier.
1514 1522 """
1515 1523 # i18n: "rev" is a keyword
1516 1524 l = getargs(x, 1, 1, _("rev requires one argument"))
1517 1525 try:
1518 1526 # i18n: "rev" is a keyword
1519 1527 l = int(getstring(l[0], _("rev requires a number")))
1520 1528 except (TypeError, ValueError):
1521 1529 # i18n: "rev" is a keyword
1522 1530 raise error.ParseError(_("rev expects a number"))
1523 1531 if l not in repo.changelog and l not in (node.nullrev, node.wdirrev):
1524 1532 return baseset()
1525 1533 return subset & baseset([l])
1526 1534
1527 1535 @predicate('matching(revision [, field])', safe=True)
1528 1536 def matching(repo, subset, x):
1529 1537 """Changesets in which a given set of fields match the set of fields in the
1530 1538 selected revision or set.
1531 1539
1532 1540 To match more than one field pass the list of fields to match separated
1533 1541 by spaces (e.g. ``author description``).
1534 1542
1535 1543 Valid fields are most regular revision fields and some special fields.
1536 1544
1537 1545 Regular revision fields are ``description``, ``author``, ``branch``,
1538 1546 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1539 1547 and ``diff``.
1540 1548 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1541 1549 contents of the revision. Two revisions matching their ``diff`` will
1542 1550 also match their ``files``.
1543 1551
1544 1552 Special fields are ``summary`` and ``metadata``:
1545 1553 ``summary`` matches the first line of the description.
1546 1554 ``metadata`` is equivalent to matching ``description user date``
1547 1555 (i.e. it matches the main metadata fields).
1548 1556
1549 1557 ``metadata`` is the default field which is used when no fields are
1550 1558 specified. You can match more than one field at a time.
1551 1559 """
1552 1560 # i18n: "matching" is a keyword
1553 1561 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1554 1562
1555 1563 revs = getset(repo, fullreposet(repo), l[0])
1556 1564
1557 1565 fieldlist = ['metadata']
1558 1566 if len(l) > 1:
1559 1567 fieldlist = getstring(l[1],
1560 1568 # i18n: "matching" is a keyword
1561 1569 _("matching requires a string "
1562 1570 "as its second argument")).split()
1563 1571
1564 1572 # Make sure that there are no repeated fields,
1565 1573 # expand the 'special' 'metadata' field type
1566 1574 # and check the 'files' whenever we check the 'diff'
1567 1575 fields = []
1568 1576 for field in fieldlist:
1569 1577 if field == 'metadata':
1570 1578 fields += ['user', 'description', 'date']
1571 1579 elif field == 'diff':
1572 1580 # a revision matching the diff must also match the files
1573 1581 # since matching the diff is very costly, make sure to
1574 1582 # also match the files first
1575 1583 fields += ['files', 'diff']
1576 1584 else:
1577 1585 if field == 'author':
1578 1586 field = 'user'
1579 1587 fields.append(field)
1580 1588 fields = set(fields)
1581 1589 if 'summary' in fields and 'description' in fields:
1582 1590 # If a revision matches its description it also matches its summary
1583 1591 fields.discard('summary')
1584 1592
1585 1593 # We may want to match more than one field
1586 1594 # Not all fields take the same amount of time to be matched
1587 1595 # Sort the selected fields in order of increasing matching cost
1588 1596 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1589 1597 'files', 'description', 'substate', 'diff']
1590 1598 def fieldkeyfunc(f):
1591 1599 try:
1592 1600 return fieldorder.index(f)
1593 1601 except ValueError:
1594 1602 # assume an unknown field is very costly
1595 1603 return len(fieldorder)
1596 1604 fields = list(fields)
1597 1605 fields.sort(key=fieldkeyfunc)
1598 1606
1599 1607 # Each field will be matched with its own "getfield" function
1600 1608 # which will be added to the getfieldfuncs array of functions
1601 1609 getfieldfuncs = []
1602 1610 _funcs = {
1603 1611 'user': lambda r: repo[r].user(),
1604 1612 'branch': lambda r: repo[r].branch(),
1605 1613 'date': lambda r: repo[r].date(),
1606 1614 'description': lambda r: repo[r].description(),
1607 1615 'files': lambda r: repo[r].files(),
1608 1616 'parents': lambda r: repo[r].parents(),
1609 1617 'phase': lambda r: repo[r].phase(),
1610 1618 'substate': lambda r: repo[r].substate,
1611 1619 'summary': lambda r: repo[r].description().splitlines()[0],
1612 1620 'diff': lambda r: list(repo[r].diff(git=True),)
1613 1621 }
1614 1622 for info in fields:
1615 1623 getfield = _funcs.get(info, None)
1616 1624 if getfield is None:
1617 1625 raise error.ParseError(
1618 1626 # i18n: "matching" is a keyword
1619 1627 _("unexpected field name passed to matching: %s") % info)
1620 1628 getfieldfuncs.append(getfield)
1621 1629 # convert the getfield array of functions into a "getinfo" function
1622 1630 # which returns an array of field values (or a single value if there
1623 1631 # is only one field to match)
1624 1632 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1625 1633
1626 1634 def matches(x):
1627 1635 for rev in revs:
1628 1636 target = getinfo(rev)
1629 1637 match = True
1630 1638 for n, f in enumerate(getfieldfuncs):
1631 1639 if target[n] != f(x):
1632 1640 match = False
1633 1641 if match:
1634 1642 return True
1635 1643 return False
1636 1644
1637 1645 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1638 1646
1639 1647 @predicate('reverse(set)', safe=True, takeorder=True)
1640 1648 def reverse(repo, subset, x, order):
1641 1649 """Reverse order of set.
1642 1650 """
1643 1651 l = getset(repo, subset, x)
1644 1652 if order == defineorder:
1645 1653 l.reverse()
1646 1654 return l
1647 1655
1648 1656 @predicate('roots(set)', safe=True)
1649 1657 def roots(repo, subset, x):
1650 1658 """Changesets in set with no parent changeset in set.
1651 1659 """
1652 1660 s = getset(repo, fullreposet(repo), x)
1653 1661 parents = repo.changelog.parentrevs
1654 1662 def filter(r):
1655 1663 for p in parents(r):
1656 1664 if 0 <= p and p in s:
1657 1665 return False
1658 1666 return True
1659 1667 return subset & s.filter(filter, condrepr='<roots>')
1660 1668
1661 1669 _sortkeyfuncs = {
1662 1670 'rev': lambda c: c.rev(),
1663 1671 'branch': lambda c: c.branch(),
1664 1672 'desc': lambda c: c.description(),
1665 1673 'user': lambda c: c.user(),
1666 1674 'author': lambda c: c.user(),
1667 1675 'date': lambda c: c.date()[0],
1668 1676 }
1669 1677
1670 1678 def _getsortargs(x):
1671 1679 """Parse sort options into (set, [(key, reverse)], opts)"""
1672 1680 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1673 1681 if 'set' not in args:
1674 1682 # i18n: "sort" is a keyword
1675 1683 raise error.ParseError(_('sort requires one or two arguments'))
1676 1684 keys = "rev"
1677 1685 if 'keys' in args:
1678 1686 # i18n: "sort" is a keyword
1679 1687 keys = getstring(args['keys'], _("sort spec must be a string"))
1680 1688
1681 1689 keyflags = []
1682 1690 for k in keys.split():
1683 1691 fk = k
1684 1692 reverse = (k[0] == '-')
1685 1693 if reverse:
1686 1694 k = k[1:]
1687 1695 if k not in _sortkeyfuncs and k != 'topo':
1688 1696 raise error.ParseError(_("unknown sort key %r") % fk)
1689 1697 keyflags.append((k, reverse))
1690 1698
1691 1699 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1692 1700 # i18n: "topo" is a keyword
1693 1701 raise error.ParseError(_('topo sort order cannot be combined '
1694 1702 'with other sort keys'))
1695 1703
1696 1704 opts = {}
1697 1705 if 'topo.firstbranch' in args:
1698 1706 if any(k == 'topo' for k, reverse in keyflags):
1699 1707 opts['topo.firstbranch'] = args['topo.firstbranch']
1700 1708 else:
1701 1709 # i18n: "topo" and "topo.firstbranch" are keywords
1702 1710 raise error.ParseError(_('topo.firstbranch can only be used '
1703 1711 'when using the topo sort key'))
1704 1712
1705 1713 return args['set'], keyflags, opts
1706 1714
1707 1715 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True)
1708 1716 def sort(repo, subset, x, order):
1709 1717 """Sort set by keys. The default sort order is ascending, specify a key
1710 1718 as ``-key`` to sort in descending order.
1711 1719
1712 1720 The keys can be:
1713 1721
1714 1722 - ``rev`` for the revision number,
1715 1723 - ``branch`` for the branch name,
1716 1724 - ``desc`` for the commit message (description),
1717 1725 - ``user`` for user name (``author`` can be used as an alias),
1718 1726 - ``date`` for the commit date
1719 1727 - ``topo`` for a reverse topographical sort
1720 1728
1721 1729 The ``topo`` sort order cannot be combined with other sort keys. This sort
1722 1730 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1723 1731 specifies what topographical branches to prioritize in the sort.
1724 1732
1725 1733 """
1726 1734 s, keyflags, opts = _getsortargs(x)
1727 1735 revs = getset(repo, subset, s)
1728 1736
1729 1737 if not keyflags or order != defineorder:
1730 1738 return revs
1731 1739 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1732 1740 revs.sort(reverse=keyflags[0][1])
1733 1741 return revs
1734 1742 elif keyflags[0][0] == "topo":
1735 1743 firstbranch = ()
1736 1744 if 'topo.firstbranch' in opts:
1737 1745 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1738 1746 revs = baseset(dagop.toposort(revs, repo.changelog.parentrevs,
1739 1747 firstbranch),
1740 1748 istopo=True)
1741 1749 if keyflags[0][1]:
1742 1750 revs.reverse()
1743 1751 return revs
1744 1752
1745 1753 # sort() is guaranteed to be stable
1746 1754 ctxs = [repo[r] for r in revs]
1747 1755 for k, reverse in reversed(keyflags):
1748 1756 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1749 1757 return baseset([c.rev() for c in ctxs])
1750 1758
1751 1759 @predicate('subrepo([pattern])')
1752 1760 def subrepo(repo, subset, x):
1753 1761 """Changesets that add, modify or remove the given subrepo. If no subrepo
1754 1762 pattern is named, any subrepo changes are returned.
1755 1763 """
1756 1764 # i18n: "subrepo" is a keyword
1757 1765 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1758 1766 pat = None
1759 1767 if len(args) != 0:
1760 1768 pat = getstring(args[0], _("subrepo requires a pattern"))
1761 1769
1762 1770 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
1763 1771
1764 1772 def submatches(names):
1765 1773 k, p, m = util.stringmatcher(pat)
1766 1774 for name in names:
1767 1775 if m(name):
1768 1776 yield name
1769 1777
1770 1778 def matches(x):
1771 1779 c = repo[x]
1772 1780 s = repo.status(c.p1().node(), c.node(), match=m)
1773 1781
1774 1782 if pat is None:
1775 1783 return s.added or s.modified or s.removed
1776 1784
1777 1785 if s.added:
1778 1786 return any(submatches(c.substate.keys()))
1779 1787
1780 1788 if s.modified:
1781 1789 subs = set(c.p1().substate.keys())
1782 1790 subs.update(c.substate.keys())
1783 1791
1784 1792 for path in submatches(subs):
1785 1793 if c.p1().substate.get(path) != c.substate.get(path):
1786 1794 return True
1787 1795
1788 1796 if s.removed:
1789 1797 return any(submatches(c.p1().substate.keys()))
1790 1798
1791 1799 return False
1792 1800
1793 1801 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
1794 1802
1795 1803 def _substringmatcher(pattern, casesensitive=True):
1796 1804 kind, pattern, matcher = util.stringmatcher(pattern,
1797 1805 casesensitive=casesensitive)
1798 1806 if kind == 'literal':
1799 1807 if not casesensitive:
1800 1808 pattern = encoding.lower(pattern)
1801 1809 matcher = lambda s: pattern in encoding.lower(s)
1802 1810 else:
1803 1811 matcher = lambda s: pattern in s
1804 1812 return kind, pattern, matcher
1805 1813
1806 1814 @predicate('tag([name])', safe=True)
1807 1815 def tag(repo, subset, x):
1808 1816 """The specified tag by name, or all tagged revisions if no name is given.
1809 1817
1810 1818 Pattern matching is supported for `name`. See
1811 1819 :hg:`help revisions.patterns`.
1812 1820 """
1813 1821 # i18n: "tag" is a keyword
1814 1822 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1815 1823 cl = repo.changelog
1816 1824 if args:
1817 1825 pattern = getstring(args[0],
1818 1826 # i18n: "tag" is a keyword
1819 1827 _('the argument to tag must be a string'))
1820 1828 kind, pattern, matcher = util.stringmatcher(pattern)
1821 1829 if kind == 'literal':
1822 1830 # avoid resolving all tags
1823 1831 tn = repo._tagscache.tags.get(pattern, None)
1824 1832 if tn is None:
1825 1833 raise error.RepoLookupError(_("tag '%s' does not exist")
1826 1834 % pattern)
1827 1835 s = {repo[tn].rev()}
1828 1836 else:
1829 1837 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)}
1830 1838 else:
1831 1839 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'}
1832 1840 return subset & s
1833 1841
1834 1842 @predicate('tagged', safe=True)
1835 1843 def tagged(repo, subset, x):
1836 1844 return tag(repo, subset, x)
1837 1845
1838 1846 @predicate('unstable()', safe=True)
1839 1847 def unstable(repo, subset, x):
1840 1848 """Non-obsolete changesets with obsolete ancestors.
1841 1849 """
1842 1850 # i18n: "unstable" is a keyword
1843 1851 getargs(x, 0, 0, _("unstable takes no arguments"))
1844 1852 unstables = obsmod.getrevs(repo, 'unstable')
1845 1853 return subset & unstables
1846 1854
1847 1855
1848 1856 @predicate('user(string)', safe=True)
1849 1857 def user(repo, subset, x):
1850 1858 """User name contains string. The match is case-insensitive.
1851 1859
1852 1860 Pattern matching is supported for `string`. See
1853 1861 :hg:`help revisions.patterns`.
1854 1862 """
1855 1863 return author(repo, subset, x)
1856 1864
1857 1865 @predicate('wdir()', safe=True)
1858 1866 def wdir(repo, subset, x):
1859 1867 """Working directory. (EXPERIMENTAL)"""
1860 1868 # i18n: "wdir" is a keyword
1861 1869 getargs(x, 0, 0, _("wdir takes no arguments"))
1862 1870 if node.wdirrev in subset or isinstance(subset, fullreposet):
1863 1871 return baseset([node.wdirrev])
1864 1872 return baseset()
1865 1873
1866 1874 def _orderedlist(repo, subset, x):
1867 1875 s = getstring(x, "internal error")
1868 1876 if not s:
1869 1877 return baseset()
1870 1878 # remove duplicates here. it's difficult for caller to deduplicate sets
1871 1879 # because different symbols can point to the same rev.
1872 1880 cl = repo.changelog
1873 1881 ls = []
1874 1882 seen = set()
1875 1883 for t in s.split('\0'):
1876 1884 try:
1877 1885 # fast path for integer revision
1878 1886 r = int(t)
1879 1887 if str(r) != t or r not in cl:
1880 1888 raise ValueError
1881 1889 revs = [r]
1882 1890 except ValueError:
1883 1891 revs = stringset(repo, subset, t)
1884 1892
1885 1893 for r in revs:
1886 1894 if r in seen:
1887 1895 continue
1888 1896 if (r in subset
1889 1897 or r == node.nullrev and isinstance(subset, fullreposet)):
1890 1898 ls.append(r)
1891 1899 seen.add(r)
1892 1900 return baseset(ls)
1893 1901
1894 1902 # for internal use
1895 1903 @predicate('_list', safe=True, takeorder=True)
1896 1904 def _list(repo, subset, x, order):
1897 1905 if order == followorder:
1898 1906 # slow path to take the subset order
1899 1907 return subset & _orderedlist(repo, fullreposet(repo), x)
1900 1908 else:
1901 1909 return _orderedlist(repo, subset, x)
1902 1910
1903 1911 def _orderedintlist(repo, subset, x):
1904 1912 s = getstring(x, "internal error")
1905 1913 if not s:
1906 1914 return baseset()
1907 1915 ls = [int(r) for r in s.split('\0')]
1908 1916 s = subset
1909 1917 return baseset([r for r in ls if r in s])
1910 1918
1911 1919 # for internal use
1912 1920 @predicate('_intlist', safe=True, takeorder=True)
1913 1921 def _intlist(repo, subset, x, order):
1914 1922 if order == followorder:
1915 1923 # slow path to take the subset order
1916 1924 return subset & _orderedintlist(repo, fullreposet(repo), x)
1917 1925 else:
1918 1926 return _orderedintlist(repo, subset, x)
1919 1927
1920 1928 def _orderedhexlist(repo, subset, x):
1921 1929 s = getstring(x, "internal error")
1922 1930 if not s:
1923 1931 return baseset()
1924 1932 cl = repo.changelog
1925 1933 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
1926 1934 s = subset
1927 1935 return baseset([r for r in ls if r in s])
1928 1936
1929 1937 # for internal use
1930 1938 @predicate('_hexlist', safe=True, takeorder=True)
1931 1939 def _hexlist(repo, subset, x, order):
1932 1940 if order == followorder:
1933 1941 # slow path to take the subset order
1934 1942 return subset & _orderedhexlist(repo, fullreposet(repo), x)
1935 1943 else:
1936 1944 return _orderedhexlist(repo, subset, x)
1937 1945
1938 1946 methods = {
1939 1947 "range": rangeset,
1940 1948 "rangeall": rangeall,
1941 1949 "rangepre": rangepre,
1942 1950 "rangepost": rangepost,
1943 1951 "dagrange": dagrange,
1944 1952 "string": stringset,
1945 1953 "symbol": stringset,
1946 1954 "and": andset,
1947 1955 "or": orset,
1948 1956 "not": notset,
1949 1957 "difference": differenceset,
1950 1958 "list": listset,
1951 1959 "keyvalue": keyvaluepair,
1952 1960 "func": func,
1953 1961 "ancestor": ancestorspec,
1954 1962 "parent": parentspec,
1955 1963 "parentpost": parentpost,
1956 1964 }
1957 1965
1958 1966 def posttreebuilthook(tree, repo):
1959 1967 # hook for extensions to execute code on the optimized tree
1960 1968 pass
1961 1969
1962 1970 def match(ui, spec, repo=None, order=defineorder):
1963 1971 """Create a matcher for a single revision spec
1964 1972
1965 1973 If order=followorder, a matcher takes the ordering specified by the input
1966 1974 set.
1967 1975 """
1968 1976 return matchany(ui, [spec], repo=repo, order=order)
1969 1977
1970 1978 def matchany(ui, specs, repo=None, order=defineorder):
1971 1979 """Create a matcher that will include any revisions matching one of the
1972 1980 given specs
1973 1981
1974 1982 If order=followorder, a matcher takes the ordering specified by the input
1975 1983 set.
1976 1984 """
1977 1985 if not specs:
1978 1986 def mfunc(repo, subset=None):
1979 1987 return baseset()
1980 1988 return mfunc
1981 1989 if not all(specs):
1982 1990 raise error.ParseError(_("empty query"))
1983 1991 lookup = None
1984 1992 if repo:
1985 1993 lookup = repo.__contains__
1986 1994 if len(specs) == 1:
1987 1995 tree = revsetlang.parse(specs[0], lookup)
1988 1996 else:
1989 1997 tree = ('or',
1990 1998 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs))
1991 1999
1992 2000 if ui:
1993 2001 tree = revsetlang.expandaliases(ui, tree)
1994 2002 tree = revsetlang.foldconcat(tree)
1995 2003 tree = revsetlang.analyze(tree, order)
1996 2004 tree = revsetlang.optimize(tree)
1997 2005 posttreebuilthook(tree, repo)
1998 2006 return makematcher(tree)
1999 2007
2000 2008 def makematcher(tree):
2001 2009 """Create a matcher from an evaluatable tree"""
2002 2010 def mfunc(repo, subset=None):
2003 2011 if subset is None:
2004 2012 subset = fullreposet(repo)
2005 2013 return getset(repo, subset, tree)
2006 2014 return mfunc
2007 2015
2008 2016 def loadpredicate(ui, extname, registrarobj):
2009 2017 """Load revset predicates from specified registrarobj
2010 2018 """
2011 2019 for name, func in registrarobj._table.iteritems():
2012 2020 symbols[name] = func
2013 2021 if func._safe:
2014 2022 safesymbols.add(name)
2015 2023
2016 2024 # load built-in predicates explicitly to setup safesymbols
2017 2025 loadpredicate(None, None, predicate)
2018 2026
2019 2027 # tell hggettext to extract docstrings from these functions:
2020 2028 i18nfunctions = symbols.values()
@@ -1,4073 +1,4072
1 1 $ HGENCODING=utf-8
2 2 $ export HGENCODING
3 3 $ cat > testrevset.py << EOF
4 4 > import mercurial.revset
5 5 >
6 6 > baseset = mercurial.revset.baseset
7 7 >
8 8 > def r3232(repo, subset, x):
9 9 > """"simple revset that return [3,2,3,2]
10 10 >
11 11 > revisions duplicated on purpose.
12 12 > """
13 13 > if 3 not in subset:
14 14 > if 2 in subset:
15 15 > return baseset([2,2])
16 16 > return baseset()
17 17 > return baseset([3,3,2,2])
18 18 >
19 19 > mercurial.revset.symbols['r3232'] = r3232
20 20 > EOF
21 21 $ cat >> $HGRCPATH << EOF
22 22 > [extensions]
23 23 > testrevset=$TESTTMP/testrevset.py
24 24 > EOF
25 25
26 26 $ try() {
27 27 > hg debugrevspec --debug "$@"
28 28 > }
29 29
30 30 $ log() {
31 31 > hg log --template '{rev}\n' -r "$1"
32 32 > }
33 33
34 34 extension to build '_intlist()' and '_hexlist()', which is necessary because
35 35 these predicates use '\0' as a separator:
36 36
37 37 $ cat <<EOF > debugrevlistspec.py
38 38 > from __future__ import absolute_import
39 39 > from mercurial import (
40 40 > node as nodemod,
41 41 > registrar,
42 42 > revset,
43 43 > revsetlang,
44 44 > smartset,
45 45 > )
46 46 > cmdtable = {}
47 47 > command = registrar.command(cmdtable)
48 48 > @command('debugrevlistspec',
49 49 > [('', 'optimize', None, 'print parsed tree after optimizing'),
50 50 > ('', 'bin', None, 'unhexlify arguments')])
51 51 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
52 52 > if opts['bin']:
53 53 > args = map(nodemod.bin, args)
54 54 > expr = revsetlang.formatspec(fmt, list(args))
55 55 > if ui.verbose:
56 56 > tree = revsetlang.parse(expr, lookup=repo.__contains__)
57 57 > ui.note(revsetlang.prettyformat(tree), "\n")
58 58 > if opts["optimize"]:
59 59 > opttree = revsetlang.optimize(revsetlang.analyze(tree))
60 60 > ui.note("* optimized:\n", revsetlang.prettyformat(opttree),
61 61 > "\n")
62 62 > func = revset.match(ui, expr, repo)
63 63 > revs = func(repo)
64 64 > if ui.verbose:
65 65 > ui.note("* set:\n", smartset.prettyformat(revs), "\n")
66 66 > for c in revs:
67 67 > ui.write("%s\n" % c)
68 68 > EOF
69 69 $ cat <<EOF >> $HGRCPATH
70 70 > [extensions]
71 71 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
72 72 > EOF
73 73 $ trylist() {
74 74 > hg debugrevlistspec --debug "$@"
75 75 > }
76 76
77 77 $ hg init repo
78 78 $ cd repo
79 79
80 80 $ echo a > a
81 81 $ hg branch a
82 82 marked working directory as branch a
83 83 (branches are permanent and global, did you want a bookmark?)
84 84 $ hg ci -Aqm0
85 85
86 86 $ echo b > b
87 87 $ hg branch b
88 88 marked working directory as branch b
89 89 $ hg ci -Aqm1
90 90
91 91 $ rm a
92 92 $ hg branch a-b-c-
93 93 marked working directory as branch a-b-c-
94 94 $ hg ci -Aqm2 -u Bob
95 95
96 96 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
97 97 2
98 98 $ hg log -r "extra('branch')" --template '{rev}\n'
99 99 0
100 100 1
101 101 2
102 102 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
103 103 0 a
104 104 2 a-b-c-
105 105
106 106 $ hg co 1
107 107 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 108 $ hg branch +a+b+c+
109 109 marked working directory as branch +a+b+c+
110 110 $ hg ci -Aqm3
111 111
112 112 $ hg co 2 # interleave
113 113 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
114 114 $ echo bb > b
115 115 $ hg branch -- -a-b-c-
116 116 marked working directory as branch -a-b-c-
117 117 $ hg ci -Aqm4 -d "May 12 2005"
118 118
119 119 $ hg co 3
120 120 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
121 121 $ hg branch !a/b/c/
122 122 marked working directory as branch !a/b/c/
123 123 $ hg ci -Aqm"5 bug"
124 124
125 125 $ hg merge 4
126 126 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
127 127 (branch merge, don't forget to commit)
128 128 $ hg branch _a_b_c_
129 129 marked working directory as branch _a_b_c_
130 130 $ hg ci -Aqm"6 issue619"
131 131
132 132 $ hg branch .a.b.c.
133 133 marked working directory as branch .a.b.c.
134 134 $ hg ci -Aqm7
135 135
136 136 $ hg branch all
137 137 marked working directory as branch all
138 138
139 139 $ hg co 4
140 140 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 141 $ hg branch é
142 142 marked working directory as branch \xc3\xa9 (esc)
143 143 $ hg ci -Aqm9
144 144
145 145 $ hg tag -r6 1.0
146 146 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
147 147
148 148 $ hg clone --quiet -U -r 7 . ../remote1
149 149 $ hg clone --quiet -U -r 8 . ../remote2
150 150 $ echo "[paths]" >> .hg/hgrc
151 151 $ echo "default = ../remote1" >> .hg/hgrc
152 152
153 153 trivial
154 154
155 155 $ try 0:1
156 156 (range
157 157 ('symbol', '0')
158 158 ('symbol', '1'))
159 159 * set:
160 160 <spanset+ 0:2>
161 161 0
162 162 1
163 163 $ try --optimize :
164 164 (rangeall
165 165 None)
166 166 * optimized:
167 167 (rangeall
168 168 None
169 169 define)
170 170 * set:
171 171 <spanset+ 0:10>
172 172 0
173 173 1
174 174 2
175 175 3
176 176 4
177 177 5
178 178 6
179 179 7
180 180 8
181 181 9
182 182 $ try 3::6
183 183 (dagrange
184 184 ('symbol', '3')
185 185 ('symbol', '6'))
186 186 * set:
187 187 <baseset+ [3, 5, 6]>
188 188 3
189 189 5
190 190 6
191 191 $ try '0|1|2'
192 192 (or
193 193 (list
194 194 ('symbol', '0')
195 195 ('symbol', '1')
196 196 ('symbol', '2')))
197 197 * set:
198 198 <baseset [0, 1, 2]>
199 199 0
200 200 1
201 201 2
202 202
203 203 names that should work without quoting
204 204
205 205 $ try a
206 206 ('symbol', 'a')
207 207 * set:
208 208 <baseset [0]>
209 209 0
210 210 $ try b-a
211 211 (minus
212 212 ('symbol', 'b')
213 213 ('symbol', 'a'))
214 214 * set:
215 215 <filteredset
216 216 <baseset [1]>,
217 217 <not
218 218 <baseset [0]>>>
219 219 1
220 220 $ try _a_b_c_
221 221 ('symbol', '_a_b_c_')
222 222 * set:
223 223 <baseset [6]>
224 224 6
225 225 $ try _a_b_c_-a
226 226 (minus
227 227 ('symbol', '_a_b_c_')
228 228 ('symbol', 'a'))
229 229 * set:
230 230 <filteredset
231 231 <baseset [6]>,
232 232 <not
233 233 <baseset [0]>>>
234 234 6
235 235 $ try .a.b.c.
236 236 ('symbol', '.a.b.c.')
237 237 * set:
238 238 <baseset [7]>
239 239 7
240 240 $ try .a.b.c.-a
241 241 (minus
242 242 ('symbol', '.a.b.c.')
243 243 ('symbol', 'a'))
244 244 * set:
245 245 <filteredset
246 246 <baseset [7]>,
247 247 <not
248 248 <baseset [0]>>>
249 249 7
250 250
251 251 names that should be caught by fallback mechanism
252 252
253 253 $ try -- '-a-b-c-'
254 254 ('symbol', '-a-b-c-')
255 255 * set:
256 256 <baseset [4]>
257 257 4
258 258 $ log -a-b-c-
259 259 4
260 260 $ try '+a+b+c+'
261 261 ('symbol', '+a+b+c+')
262 262 * set:
263 263 <baseset [3]>
264 264 3
265 265 $ try '+a+b+c+:'
266 266 (rangepost
267 267 ('symbol', '+a+b+c+'))
268 268 * set:
269 269 <spanset+ 3:10>
270 270 3
271 271 4
272 272 5
273 273 6
274 274 7
275 275 8
276 276 9
277 277 $ try ':+a+b+c+'
278 278 (rangepre
279 279 ('symbol', '+a+b+c+'))
280 280 * set:
281 281 <spanset+ 0:4>
282 282 0
283 283 1
284 284 2
285 285 3
286 286 $ try -- '-a-b-c-:+a+b+c+'
287 287 (range
288 288 ('symbol', '-a-b-c-')
289 289 ('symbol', '+a+b+c+'))
290 290 * set:
291 291 <spanset- 3:5>
292 292 4
293 293 3
294 294 $ log '-a-b-c-:+a+b+c+'
295 295 4
296 296 3
297 297
298 298 $ try -- -a-b-c--a # complains
299 299 (minus
300 300 (minus
301 301 (minus
302 302 (negate
303 303 ('symbol', 'a'))
304 304 ('symbol', 'b'))
305 305 ('symbol', 'c'))
306 306 (negate
307 307 ('symbol', 'a')))
308 308 abort: unknown revision '-a'!
309 309 [255]
310 310 $ try é
311 311 ('symbol', '\xc3\xa9')
312 312 * set:
313 313 <baseset [9]>
314 314 9
315 315
316 316 no quoting needed
317 317
318 318 $ log ::a-b-c-
319 319 0
320 320 1
321 321 2
322 322
323 323 quoting needed
324 324
325 325 $ try '"-a-b-c-"-a'
326 326 (minus
327 327 ('string', '-a-b-c-')
328 328 ('symbol', 'a'))
329 329 * set:
330 330 <filteredset
331 331 <baseset [4]>,
332 332 <not
333 333 <baseset [0]>>>
334 334 4
335 335
336 336 $ log '1 or 2'
337 337 1
338 338 2
339 339 $ log '1|2'
340 340 1
341 341 2
342 342 $ log '1 and 2'
343 343 $ log '1&2'
344 344 $ try '1&2|3' # precedence - and is higher
345 345 (or
346 346 (list
347 347 (and
348 348 ('symbol', '1')
349 349 ('symbol', '2'))
350 350 ('symbol', '3')))
351 351 * set:
352 352 <addset
353 353 <baseset []>,
354 354 <baseset [3]>>
355 355 3
356 356 $ try '1|2&3'
357 357 (or
358 358 (list
359 359 ('symbol', '1')
360 360 (and
361 361 ('symbol', '2')
362 362 ('symbol', '3'))))
363 363 * set:
364 364 <addset
365 365 <baseset [1]>,
366 366 <baseset []>>
367 367 1
368 368 $ try '1&2&3' # associativity
369 369 (and
370 370 (and
371 371 ('symbol', '1')
372 372 ('symbol', '2'))
373 373 ('symbol', '3'))
374 374 * set:
375 375 <baseset []>
376 376 $ try '1|(2|3)'
377 377 (or
378 378 (list
379 379 ('symbol', '1')
380 380 (group
381 381 (or
382 382 (list
383 383 ('symbol', '2')
384 384 ('symbol', '3'))))))
385 385 * set:
386 386 <addset
387 387 <baseset [1]>,
388 388 <baseset [2, 3]>>
389 389 1
390 390 2
391 391 3
392 392 $ log '1.0' # tag
393 393 6
394 394 $ log 'a' # branch
395 395 0
396 396 $ log '2785f51ee'
397 397 0
398 398 $ log 'date(2005)'
399 399 4
400 400 $ log 'date(this is a test)'
401 401 hg: parse error at 10: unexpected token: symbol
402 402 [255]
403 403 $ log 'date()'
404 404 hg: parse error: date requires a string
405 405 [255]
406 406 $ log 'date'
407 407 abort: unknown revision 'date'!
408 408 [255]
409 409 $ log 'date('
410 410 hg: parse error at 5: not a prefix: end
411 411 [255]
412 412 $ log 'date("\xy")'
413 413 hg: parse error: invalid \x escape
414 414 [255]
415 415 $ log 'date(tip)'
416 416 hg: parse error: invalid date: 'tip'
417 417 [255]
418 418 $ log '0:date'
419 419 abort: unknown revision 'date'!
420 420 [255]
421 421 $ log '::"date"'
422 422 abort: unknown revision 'date'!
423 423 [255]
424 424 $ hg book date -r 4
425 425 $ log '0:date'
426 426 0
427 427 1
428 428 2
429 429 3
430 430 4
431 431 $ log '::date'
432 432 0
433 433 1
434 434 2
435 435 4
436 436 $ log '::"date"'
437 437 0
438 438 1
439 439 2
440 440 4
441 441 $ log 'date(2005) and 1::'
442 442 4
443 443 $ hg book -d date
444 444
445 445 function name should be a symbol
446 446
447 447 $ log '"date"(2005)'
448 448 hg: parse error: not a symbol
449 449 [255]
450 450
451 451 keyword arguments
452 452
453 453 $ log 'extra(branch, value=a)'
454 454 0
455 455
456 456 $ log 'extra(branch, a, b)'
457 457 hg: parse error: extra takes at most 2 positional arguments
458 458 [255]
459 459 $ log 'extra(a, label=b)'
460 460 hg: parse error: extra got multiple values for keyword argument 'label'
461 461 [255]
462 462 $ log 'extra(label=branch, default)'
463 463 hg: parse error: extra got an invalid argument
464 464 [255]
465 465 $ log 'extra(branch, foo+bar=baz)'
466 466 hg: parse error: extra got an invalid argument
467 467 [255]
468 468 $ log 'extra(unknown=branch)'
469 469 hg: parse error: extra got an unexpected keyword argument 'unknown'
470 470 [255]
471 471
472 472 $ try 'foo=bar|baz'
473 473 (keyvalue
474 474 ('symbol', 'foo')
475 475 (or
476 476 (list
477 477 ('symbol', 'bar')
478 478 ('symbol', 'baz'))))
479 479 hg: parse error: can't use a key-value pair in this context
480 480 [255]
481 481
482 482 right-hand side should be optimized recursively
483 483
484 484 $ try --optimize 'foo=(not public())'
485 485 (keyvalue
486 486 ('symbol', 'foo')
487 487 (group
488 488 (not
489 489 (func
490 490 ('symbol', 'public')
491 491 None))))
492 492 * optimized:
493 493 (keyvalue
494 494 ('symbol', 'foo')
495 495 (func
496 496 ('symbol', '_notpublic')
497 497 None
498 498 any))
499 499 hg: parse error: can't use a key-value pair in this context
500 500 [255]
501 501
502 502 parsed tree at stages:
503 503
504 504 $ hg debugrevspec -p all '()'
505 505 * parsed:
506 506 (group
507 507 None)
508 508 * expanded:
509 509 (group
510 510 None)
511 511 * concatenated:
512 512 (group
513 513 None)
514 514 * analyzed:
515 515 None
516 516 * optimized:
517 517 None
518 518 hg: parse error: missing argument
519 519 [255]
520 520
521 521 $ hg debugrevspec --no-optimized -p all '()'
522 522 * parsed:
523 523 (group
524 524 None)
525 525 * expanded:
526 526 (group
527 527 None)
528 528 * concatenated:
529 529 (group
530 530 None)
531 531 * analyzed:
532 532 None
533 533 hg: parse error: missing argument
534 534 [255]
535 535
536 536 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
537 537 * parsed:
538 538 (minus
539 539 (group
540 540 (or
541 541 (list
542 542 ('symbol', '0')
543 543 ('symbol', '1'))))
544 544 ('symbol', '1'))
545 545 * analyzed:
546 546 (and
547 547 (or
548 548 (list
549 549 ('symbol', '0')
550 550 ('symbol', '1'))
551 551 define)
552 552 (not
553 553 ('symbol', '1')
554 554 follow)
555 555 define)
556 556 * optimized:
557 557 (difference
558 558 (func
559 559 ('symbol', '_list')
560 560 ('string', '0\x001')
561 561 define)
562 562 ('symbol', '1')
563 563 define)
564 564 0
565 565
566 566 $ hg debugrevspec -p unknown '0'
567 567 abort: invalid stage name: unknown
568 568 [255]
569 569
570 570 $ hg debugrevspec -p all --optimize '0'
571 571 abort: cannot use --optimize with --show-stage
572 572 [255]
573 573
574 574 verify optimized tree:
575 575
576 576 $ hg debugrevspec --verify '0|1'
577 577
578 578 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
579 579 * analyzed:
580 580 (and
581 581 (func
582 582 ('symbol', 'r3232')
583 583 None
584 584 define)
585 585 ('symbol', '2')
586 586 define)
587 587 * optimized:
588 588 (and
589 589 ('symbol', '2')
590 590 (func
591 591 ('symbol', 'r3232')
592 592 None
593 593 define)
594 594 define)
595 595 * analyzed set:
596 596 <baseset [2]>
597 597 * optimized set:
598 598 <baseset [2, 2]>
599 599 --- analyzed
600 600 +++ optimized
601 601 2
602 602 +2
603 603 [1]
604 604
605 605 $ hg debugrevspec --no-optimized --verify-optimized '0'
606 606 abort: cannot use --verify-optimized with --no-optimized
607 607 [255]
608 608
609 609 Test that symbols only get parsed as functions if there's an opening
610 610 parenthesis.
611 611
612 612 $ hg book only -r 9
613 613 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
614 614 8
615 615 9
616 616
617 617 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
618 618 may be hidden (issue5385)
619 619
620 620 $ try -p parsed -p analyzed ':'
621 621 * parsed:
622 622 (rangeall
623 623 None)
624 624 * analyzed:
625 625 (rangeall
626 626 None
627 627 define)
628 628 * set:
629 629 <spanset+ 0:10>
630 630 0
631 631 1
632 632 2
633 633 3
634 634 4
635 635 5
636 636 6
637 637 7
638 638 8
639 639 9
640 640 $ try -p analyzed ':1'
641 641 * analyzed:
642 642 (rangepre
643 643 ('symbol', '1')
644 644 define)
645 645 * set:
646 646 <spanset+ 0:2>
647 647 0
648 648 1
649 649 $ try -p analyzed ':(1|2)'
650 650 * analyzed:
651 651 (rangepre
652 652 (or
653 653 (list
654 654 ('symbol', '1')
655 655 ('symbol', '2'))
656 656 define)
657 657 define)
658 658 * set:
659 659 <spanset+ 0:3>
660 660 0
661 661 1
662 662 2
663 663 $ try -p analyzed ':(1&2)'
664 664 * analyzed:
665 665 (rangepre
666 666 (and
667 667 ('symbol', '1')
668 668 ('symbol', '2')
669 669 define)
670 670 define)
671 671 * set:
672 672 <baseset []>
673 673
674 674 infix/suffix resolution of ^ operator (issue2884):
675 675
676 676 x^:y means (x^):y
677 677
678 678 $ try '1^:2'
679 679 (range
680 680 (parentpost
681 681 ('symbol', '1'))
682 682 ('symbol', '2'))
683 683 * set:
684 684 <spanset+ 0:3>
685 685 0
686 686 1
687 687 2
688 688
689 689 $ try '1^::2'
690 690 (dagrange
691 691 (parentpost
692 692 ('symbol', '1'))
693 693 ('symbol', '2'))
694 694 * set:
695 695 <baseset+ [0, 1, 2]>
696 696 0
697 697 1
698 698 2
699 699
700 700 $ try '9^:'
701 701 (rangepost
702 702 (parentpost
703 703 ('symbol', '9')))
704 704 * set:
705 705 <spanset+ 8:10>
706 706 8
707 707 9
708 708
709 709 x^:y should be resolved before omitting group operators
710 710
711 711 $ try '1^(:2)'
712 712 (parent
713 713 ('symbol', '1')
714 714 (group
715 715 (rangepre
716 716 ('symbol', '2'))))
717 717 hg: parse error: ^ expects a number 0, 1, or 2
718 718 [255]
719 719
720 720 x^:y should be resolved recursively
721 721
722 722 $ try 'sort(1^:2)'
723 723 (func
724 724 ('symbol', 'sort')
725 725 (range
726 726 (parentpost
727 727 ('symbol', '1'))
728 728 ('symbol', '2')))
729 729 * set:
730 730 <spanset+ 0:3>
731 731 0
732 732 1
733 733 2
734 734
735 735 $ try '(3^:4)^:2'
736 736 (range
737 737 (parentpost
738 738 (group
739 739 (range
740 740 (parentpost
741 741 ('symbol', '3'))
742 742 ('symbol', '4'))))
743 743 ('symbol', '2'))
744 744 * set:
745 745 <spanset+ 0:3>
746 746 0
747 747 1
748 748 2
749 749
750 750 $ try '(3^::4)^::2'
751 751 (dagrange
752 752 (parentpost
753 753 (group
754 754 (dagrange
755 755 (parentpost
756 756 ('symbol', '3'))
757 757 ('symbol', '4'))))
758 758 ('symbol', '2'))
759 759 * set:
760 760 <baseset+ [0, 1, 2]>
761 761 0
762 762 1
763 763 2
764 764
765 765 $ try '(9^:)^:'
766 766 (rangepost
767 767 (parentpost
768 768 (group
769 769 (rangepost
770 770 (parentpost
771 771 ('symbol', '9'))))))
772 772 * set:
773 773 <spanset+ 4:10>
774 774 4
775 775 5
776 776 6
777 777 7
778 778 8
779 779 9
780 780
781 781 x^ in alias should also be resolved
782 782
783 783 $ try 'A' --config 'revsetalias.A=1^:2'
784 784 ('symbol', 'A')
785 785 * expanded:
786 786 (range
787 787 (parentpost
788 788 ('symbol', '1'))
789 789 ('symbol', '2'))
790 790 * set:
791 791 <spanset+ 0:3>
792 792 0
793 793 1
794 794 2
795 795
796 796 $ try 'A:2' --config 'revsetalias.A=1^'
797 797 (range
798 798 ('symbol', 'A')
799 799 ('symbol', '2'))
800 800 * expanded:
801 801 (range
802 802 (parentpost
803 803 ('symbol', '1'))
804 804 ('symbol', '2'))
805 805 * set:
806 806 <spanset+ 0:3>
807 807 0
808 808 1
809 809 2
810 810
811 811 but not beyond the boundary of alias expansion, because the resolution should
812 812 be made at the parsing stage
813 813
814 814 $ try '1^A' --config 'revsetalias.A=:2'
815 815 (parent
816 816 ('symbol', '1')
817 817 ('symbol', 'A'))
818 818 * expanded:
819 819 (parent
820 820 ('symbol', '1')
821 821 (rangepre
822 822 ('symbol', '2')))
823 823 hg: parse error: ^ expects a number 0, 1, or 2
824 824 [255]
825 825
826 826 ancestor can accept 0 or more arguments
827 827
828 828 $ log 'ancestor()'
829 829 $ log 'ancestor(1)'
830 830 1
831 831 $ log 'ancestor(4,5)'
832 832 1
833 833 $ log 'ancestor(4,5) and 4'
834 834 $ log 'ancestor(0,0,1,3)'
835 835 0
836 836 $ log 'ancestor(3,1,5,3,5,1)'
837 837 1
838 838 $ log 'ancestor(0,1,3,5)'
839 839 0
840 840 $ log 'ancestor(1,2,3,4,5)'
841 841 1
842 842
843 843 test ancestors
844 844
845 845 $ log 'ancestors(5)'
846 846 0
847 847 1
848 848 3
849 849 5
850 850 $ log 'ancestor(ancestors(5))'
851 851 0
852 852 $ log '::r3232()'
853 853 0
854 854 1
855 855 2
856 856 3
857 857
858 858 $ log 'author(bob)'
859 859 2
860 860 $ log 'author("re:bob|test")'
861 861 0
862 862 1
863 863 2
864 864 3
865 865 4
866 866 5
867 867 6
868 868 7
869 869 8
870 870 9
871 871 $ log 'author(r"re:\S")'
872 872 0
873 873 1
874 874 2
875 875 3
876 876 4
877 877 5
878 878 6
879 879 7
880 880 8
881 881 9
882 882 $ log 'branch(é)'
883 883 8
884 884 9
885 885 $ log 'branch(a)'
886 886 0
887 887 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
888 888 0 a
889 889 2 a-b-c-
890 890 3 +a+b+c+
891 891 4 -a-b-c-
892 892 5 !a/b/c/
893 893 6 _a_b_c_
894 894 7 .a.b.c.
895 895 $ log 'children(ancestor(4,5))'
896 896 2
897 897 3
898 898
899 899 $ log 'children(4)'
900 900 6
901 901 8
902 902 $ log 'children(null)'
903 903 0
904 904
905 905 $ log 'closed()'
906 906 $ log 'contains(a)'
907 907 0
908 908 1
909 909 3
910 910 5
911 911 $ log 'contains("../repo/a")'
912 912 0
913 913 1
914 914 3
915 915 5
916 916 $ log 'desc(B)'
917 917 5
918 918 $ hg log -r 'desc(r"re:S?u")' --template "{rev} {desc|firstline}\n"
919 919 5 5 bug
920 920 6 6 issue619
921 921 $ log 'descendants(2 or 3)'
922 922 2
923 923 3
924 924 4
925 925 5
926 926 6
927 927 7
928 928 8
929 929 9
930 930 $ log 'file("b*")'
931 931 1
932 932 4
933 933 $ log 'filelog("b")'
934 934 1
935 935 4
936 936 $ log 'filelog("../repo/b")'
937 937 1
938 938 4
939 939 $ log 'follow()'
940 940 0
941 941 1
942 942 2
943 943 4
944 944 8
945 945 9
946 946 $ log 'grep("issue\d+")'
947 947 6
948 948 $ try 'grep("(")' # invalid regular expression
949 949 (func
950 950 ('symbol', 'grep')
951 951 ('string', '('))
952 952 hg: parse error: invalid match pattern: unbalanced parenthesis
953 953 [255]
954 954 $ try 'grep("\bissue\d+")'
955 955 (func
956 956 ('symbol', 'grep')
957 957 ('string', '\x08issue\\d+'))
958 958 * set:
959 959 <filteredset
960 960 <fullreposet+ 0:10>,
961 961 <grep '\x08issue\\d+'>>
962 962 $ try 'grep(r"\bissue\d+")'
963 963 (func
964 964 ('symbol', 'grep')
965 965 ('string', '\\bissue\\d+'))
966 966 * set:
967 967 <filteredset
968 968 <fullreposet+ 0:10>,
969 969 <grep '\\bissue\\d+'>>
970 970 6
971 971 $ try 'grep(r"\")'
972 972 hg: parse error at 7: unterminated string
973 973 [255]
974 974 $ log 'head()'
975 975 0
976 976 1
977 977 2
978 978 3
979 979 4
980 980 5
981 981 6
982 982 7
983 983 9
984 984 $ log 'heads(6::)'
985 985 7
986 986 $ log 'keyword(issue)'
987 987 6
988 988 $ log 'keyword("test a")'
989 989
990 990 Test first (=limit) and last
991 991
992 992 $ log 'limit(head(), 1)'
993 993 0
994 994 $ log 'limit(author("re:bob|test"), 3, 5)'
995 995 5
996 996 6
997 997 7
998 998 $ log 'limit(author("re:bob|test"), offset=6)'
999 999 6
1000 1000 $ log 'limit(author("re:bob|test"), offset=10)'
1001 1001 $ log 'limit(all(), 1, -1)'
1002 1002 hg: parse error: negative offset
1003 1003 [255]
1004 1004 $ log 'limit(all(), -1)'
1005 1005 hg: parse error: negative number to select
1006 1006 [255]
1007 1007 $ log 'limit(all(), 0)'
1008 1008
1009 1009 $ log 'last(all(), -1)'
1010 1010 hg: parse error: negative number to select
1011 1011 [255]
1012 1012 $ log 'last(all(), 0)'
1013 1013 $ log 'last(all(), 1)'
1014 1014 9
1015 1015 $ log 'last(all(), 2)'
1016 1016 8
1017 1017 9
1018 1018
1019 1019 Test smartset.slice() by first/last()
1020 1020
1021 1021 (using unoptimized set, filteredset as example)
1022 1022
1023 1023 $ hg debugrevspec --no-show-revs -s '0:7 & branch("re:")'
1024 1024 * set:
1025 1025 <filteredset
1026 1026 <spanset+ 0:8>,
1027 1027 <branch 're:'>>
1028 1028 $ log 'limit(0:7 & branch("re:"), 3, 4)'
1029 1029 4
1030 1030 5
1031 1031 6
1032 1032 $ log 'limit(7:0 & branch("re:"), 3, 4)'
1033 1033 3
1034 1034 2
1035 1035 1
1036 1036 $ log 'last(0:7 & branch("re:"), 2)'
1037 1037 6
1038 1038 7
1039 1039
1040 1040 (using baseset)
1041 1041
1042 1042 $ hg debugrevspec --no-show-revs -s 0+1+2+3+4+5+6+7
1043 1043 * set:
1044 1044 <baseset [0, 1, 2, 3, 4, 5, 6, 7]>
1045 1045 $ hg debugrevspec --no-show-revs -s 0::7
1046 1046 * set:
1047 1047 <baseset+ [0, 1, 2, 3, 4, 5, 6, 7]>
1048 1048 $ log 'limit(0+1+2+3+4+5+6+7, 3, 4)'
1049 1049 4
1050 1050 5
1051 1051 6
1052 1052 $ log 'limit(sort(0::7, rev), 3, 4)'
1053 1053 4
1054 1054 5
1055 1055 6
1056 1056 $ log 'limit(sort(0::7, -rev), 3, 4)'
1057 1057 3
1058 1058 2
1059 1059 1
1060 1060 $ log 'last(sort(0::7, rev), 2)'
1061 1061 6
1062 1062 7
1063 1063 $ hg debugrevspec -s 'limit(sort(0::7, rev), 3, 6)'
1064 1064 * set:
1065 1065 <baseset+ [6, 7]>
1066 1066 6
1067 1067 7
1068 1068 $ hg debugrevspec -s 'limit(sort(0::7, rev), 3, 9)'
1069 1069 * set:
1070 1070 <baseset+ []>
1071 1071 $ hg debugrevspec -s 'limit(sort(0::7, -rev), 3, 6)'
1072 1072 * set:
1073 1073 <baseset- [0, 1]>
1074 1074 1
1075 1075 0
1076 1076 $ hg debugrevspec -s 'limit(sort(0::7, -rev), 3, 9)'
1077 1077 * set:
1078 1078 <baseset- []>
1079 1079 $ hg debugrevspec -s 'limit(0::7, 0)'
1080 1080 * set:
1081 1081 <baseset+ []>
1082 1082
1083 1083 (using spanset)
1084 1084
1085 1085 $ hg debugrevspec --no-show-revs -s 0:7
1086 1086 * set:
1087 1087 <spanset+ 0:8>
1088 1088 $ log 'limit(0:7, 3, 4)'
1089 1089 4
1090 1090 5
1091 1091 6
1092 1092 $ log 'limit(7:0, 3, 4)'
1093 1093 3
1094 1094 2
1095 1095 1
1096 1096 $ log 'limit(0:7, 3, 6)'
1097 1097 6
1098 1098 7
1099 1099 $ log 'limit(7:0, 3, 6)'
1100 1100 1
1101 1101 0
1102 1102 $ log 'last(0:7, 2)'
1103 1103 6
1104 1104 7
1105 1105 $ hg debugrevspec -s 'limit(0:7, 3, 6)'
1106 1106 * set:
1107 1107 <spanset+ 6:8>
1108 1108 6
1109 1109 7
1110 1110 $ hg debugrevspec -s 'limit(0:7, 3, 9)'
1111 1111 * set:
1112 1112 <spanset+ 8:8>
1113 1113 $ hg debugrevspec -s 'limit(7:0, 3, 6)'
1114 1114 * set:
1115 1115 <spanset- 0:2>
1116 1116 1
1117 1117 0
1118 1118 $ hg debugrevspec -s 'limit(7:0, 3, 9)'
1119 1119 * set:
1120 1120 <spanset- 0:0>
1121 1121 $ hg debugrevspec -s 'limit(0:7, 0)'
1122 1122 * set:
1123 1123 <spanset+ 0:0>
1124 1124
1125 1125 Test order of first/last revisions
1126 1126
1127 1127 $ hg debugrevspec -s 'first(4:0, 3) & 3:'
1128 1128 * set:
1129 1129 <filteredset
1130 1130 <spanset- 2:5>,
1131 1131 <spanset+ 3:10>>
1132 1132 4
1133 1133 3
1134 1134
1135 1135 $ hg debugrevspec -s '3: & first(4:0, 3)'
1136 1136 * set:
1137 1137 <filteredset
1138 1138 <spanset+ 3:10>,
1139 1139 <spanset- 2:5>>
1140 1140 3
1141 1141 4
1142 1142
1143 1143 $ hg debugrevspec -s 'last(4:0, 3) & :1'
1144 1144 * set:
1145 1145 <filteredset
1146 1146 <spanset- 0:3>,
1147 1147 <spanset+ 0:2>>
1148 1148 1
1149 1149 0
1150 1150
1151 1151 $ hg debugrevspec -s ':1 & last(4:0, 3)'
1152 1152 * set:
1153 1153 <filteredset
1154 1154 <spanset+ 0:2>,
1155 1155 <spanset+ 0:3>>
1156 1156 0
1157 1157 1
1158 1158
1159 1159 Test matching
1160 1160
1161 1161 $ log 'matching(6)'
1162 1162 6
1163 1163 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
1164 1164 6
1165 1165 7
1166 1166
1167 1167 Testing min and max
1168 1168
1169 1169 max: simple
1170 1170
1171 1171 $ log 'max(contains(a))'
1172 1172 5
1173 1173
1174 1174 max: simple on unordered set)
1175 1175
1176 1176 $ log 'max((4+0+2+5+7) and contains(a))'
1177 1177 5
1178 1178
1179 1179 max: no result
1180 1180
1181 1181 $ log 'max(contains(stringthatdoesnotappearanywhere))'
1182 1182
1183 1183 max: no result on unordered set
1184 1184
1185 1185 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1186 1186
1187 1187 min: simple
1188 1188
1189 1189 $ log 'min(contains(a))'
1190 1190 0
1191 1191
1192 1192 min: simple on unordered set
1193 1193
1194 1194 $ log 'min((4+0+2+5+7) and contains(a))'
1195 1195 0
1196 1196
1197 1197 min: empty
1198 1198
1199 1199 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1200 1200
1201 1201 min: empty on unordered set
1202 1202
1203 1203 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1204 1204
1205 1205
1206 1206 $ log 'merge()'
1207 1207 6
1208 1208 $ log 'branchpoint()'
1209 1209 1
1210 1210 4
1211 1211 $ log 'modifies(b)'
1212 1212 4
1213 1213 $ log 'modifies("path:b")'
1214 1214 4
1215 1215 $ log 'modifies("*")'
1216 1216 4
1217 1217 6
1218 1218 $ log 'modifies("set:modified()")'
1219 1219 4
1220 1220 $ log 'id(5)'
1221 1221 2
1222 1222 $ log 'only(9)'
1223 1223 8
1224 1224 9
1225 1225 $ log 'only(8)'
1226 1226 8
1227 1227 $ log 'only(9, 5)'
1228 1228 2
1229 1229 4
1230 1230 8
1231 1231 9
1232 1232 $ log 'only(7 + 9, 5 + 2)'
1233 1233 4
1234 1234 6
1235 1235 7
1236 1236 8
1237 1237 9
1238 1238
1239 1239 Test empty set input
1240 1240 $ log 'only(p2())'
1241 1241 $ log 'only(p1(), p2())'
1242 1242 0
1243 1243 1
1244 1244 2
1245 1245 4
1246 1246 8
1247 1247 9
1248 1248
1249 1249 Test '%' operator
1250 1250
1251 1251 $ log '9%'
1252 1252 8
1253 1253 9
1254 1254 $ log '9%5'
1255 1255 2
1256 1256 4
1257 1257 8
1258 1258 9
1259 1259 $ log '(7 + 9)%(5 + 2)'
1260 1260 4
1261 1261 6
1262 1262 7
1263 1263 8
1264 1264 9
1265 1265
1266 1266 Test operand of '%' is optimized recursively (issue4670)
1267 1267
1268 1268 $ try --optimize '8:9-8%'
1269 1269 (onlypost
1270 1270 (minus
1271 1271 (range
1272 1272 ('symbol', '8')
1273 1273 ('symbol', '9'))
1274 1274 ('symbol', '8')))
1275 1275 * optimized:
1276 1276 (func
1277 1277 ('symbol', 'only')
1278 1278 (difference
1279 1279 (range
1280 1280 ('symbol', '8')
1281 1281 ('symbol', '9')
1282 1282 define)
1283 1283 ('symbol', '8')
1284 1284 define)
1285 1285 define)
1286 1286 * set:
1287 1287 <baseset+ [8, 9]>
1288 1288 8
1289 1289 9
1290 1290 $ try --optimize '(9)%(5)'
1291 1291 (only
1292 1292 (group
1293 1293 ('symbol', '9'))
1294 1294 (group
1295 1295 ('symbol', '5')))
1296 1296 * optimized:
1297 1297 (func
1298 1298 ('symbol', 'only')
1299 1299 (list
1300 1300 ('symbol', '9')
1301 1301 ('symbol', '5'))
1302 1302 define)
1303 1303 * set:
1304 1304 <baseset+ [2, 4, 8, 9]>
1305 1305 2
1306 1306 4
1307 1307 8
1308 1308 9
1309 1309
1310 1310 Test the order of operations
1311 1311
1312 1312 $ log '7 + 9%5 + 2'
1313 1313 7
1314 1314 2
1315 1315 4
1316 1316 8
1317 1317 9
1318 1318
1319 1319 Test explicit numeric revision
1320 1320 $ log 'rev(-2)'
1321 1321 $ log 'rev(-1)'
1322 1322 -1
1323 1323 $ log 'rev(0)'
1324 1324 0
1325 1325 $ log 'rev(9)'
1326 1326 9
1327 1327 $ log 'rev(10)'
1328 1328 $ log 'rev(tip)'
1329 1329 hg: parse error: rev expects a number
1330 1330 [255]
1331 1331
1332 1332 Test hexadecimal revision
1333 1333 $ log 'id(2)'
1334 1334 abort: 00changelog.i@2: ambiguous identifier!
1335 1335 [255]
1336 1336 $ log 'id(23268)'
1337 1337 4
1338 1338 $ log 'id(2785f51eece)'
1339 1339 0
1340 1340 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1341 1341 8
1342 1342 $ log 'id(d5d0dcbdc4a)'
1343 1343 $ log 'id(d5d0dcbdc4w)'
1344 1344 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1345 1345 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1346 1346 $ log 'id(1.0)'
1347 1347 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1348 1348
1349 1349 Test null revision
1350 1350 $ log '(null)'
1351 1351 -1
1352 1352 $ log '(null:0)'
1353 1353 -1
1354 1354 0
1355 1355 $ log '(0:null)'
1356 1356 0
1357 1357 -1
1358 1358 $ log 'null::0'
1359 1359 -1
1360 1360 0
1361 1361 $ log 'null:tip - 0:'
1362 1362 -1
1363 1363 $ log 'null: and null::' | head -1
1364 1364 -1
1365 1365 $ log 'null: or 0:' | head -2
1366 1366 -1
1367 1367 0
1368 1368 $ log 'ancestors(null)'
1369 1369 -1
1370 1370 $ log 'reverse(null:)' | tail -2
1371 1371 0
1372 1372 -1
1373 1373 $ log 'first(null:)'
1374 1374 -1
1375 1375 $ log 'min(null:)'
1376 1376 BROKEN: should be '-1'
1377 1377 $ log 'tip:null and all()' | tail -2
1378 1378 1
1379 1379 0
1380 1380
1381 1381 Test working-directory revision
1382 1382 $ hg debugrevspec 'wdir()'
1383 1383 2147483647
1384 1384 $ hg debugrevspec 'wdir()^'
1385 1385 9
1386 1386 $ hg up 7
1387 1387 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1388 1388 $ hg debugrevspec 'wdir()^'
1389 1389 7
1390 1390 $ hg debugrevspec 'wdir()^0'
1391 1391 2147483647
1392 1392 $ hg debugrevspec 'wdir()~3'
1393 1393 5
1394 1394 $ hg debugrevspec 'ancestors(wdir())'
1395 1395 0
1396 1396 1
1397 1397 2
1398 1398 3
1399 1399 4
1400 1400 5
1401 1401 6
1402 1402 7
1403 1403 2147483647
1404 1404 $ hg debugrevspec 'wdir()~0'
1405 1405 2147483647
1406 1406 $ hg debugrevspec 'p1(wdir())'
1407 1407 7
1408 1408 $ hg debugrevspec 'p2(wdir())'
1409 1409 $ hg debugrevspec 'parents(wdir())'
1410 1410 7
1411 1411 $ hg debugrevspec 'wdir()^1'
1412 1412 7
1413 1413 $ hg debugrevspec 'wdir()^2'
1414 1414 $ hg debugrevspec 'wdir()^3'
1415 1415 hg: parse error: ^ expects a number 0, 1, or 2
1416 1416 [255]
1417 1417 For tests consistency
1418 1418 $ hg up 9
1419 1419 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1420 1420 $ hg debugrevspec 'tip or wdir()'
1421 1421 9
1422 1422 2147483647
1423 1423 $ hg debugrevspec '0:tip and wdir()'
1424 1424 $ log '0:wdir()' | tail -3
1425 1425 8
1426 1426 9
1427 1427 2147483647
1428 1428 $ log 'wdir():0' | head -3
1429 1429 2147483647
1430 1430 9
1431 1431 8
1432 1432 $ log 'wdir():wdir()'
1433 1433 2147483647
1434 1434 $ log '(all() + wdir()) & min(. + wdir())'
1435 1435 9
1436 1436 $ log '(all() + wdir()) & max(. + wdir())'
1437 1437 2147483647
1438 1438 $ log 'first(wdir() + .)'
1439 1439 2147483647
1440 1440 $ log 'last(. + wdir())'
1441 1441 2147483647
1442 1442
1443 1443 Test working-directory integer revision and node id
1444 1444 (BUG: '0:wdir()' is still needed to populate wdir revision)
1445 1445
1446 1446 $ hg debugrevspec '0:wdir() & 2147483647'
1447 1447 2147483647
1448 1448 $ hg debugrevspec '0:wdir() & rev(2147483647)'
1449 1449 2147483647
1450 1450 $ hg debugrevspec '0:wdir() & ffffffffffffffffffffffffffffffffffffffff'
1451 1451 2147483647
1452 1452 $ hg debugrevspec '0:wdir() & ffffffffffff'
1453 1453 2147483647
1454 1454 $ hg debugrevspec '0:wdir() & id(ffffffffffffffffffffffffffffffffffffffff)'
1455 1455 2147483647
1456 1456 $ hg debugrevspec '0:wdir() & id(ffffffffffff)'
1457 1457 2147483647
1458 1458
1459 1459 $ cd ..
1460 1460
1461 1461 Test short 'ff...' hash collision
1462 1462 (BUG: '0:wdir()' is still needed to populate wdir revision)
1463 1463
1464 1464 $ hg init wdir-hashcollision
1465 1465 $ cd wdir-hashcollision
1466 1466 $ cat <<EOF >> .hg/hgrc
1467 1467 > [experimental]
1468 1468 > evolution = createmarkers
1469 1469 > EOF
1470 1470 $ echo 0 > a
1471 1471 $ hg ci -qAm 0
1472 1472 $ for i in 2463 2961 6726 78127; do
1473 1473 > hg up -q 0
1474 1474 > echo $i > a
1475 1475 > hg ci -qm $i
1476 1476 > done
1477 1477 $ hg up -q null
1478 1478 $ hg log -r '0:wdir()' -T '{rev}:{node} {shortest(node, 3)}\n'
1479 1479 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a b4e
1480 1480 1:fffbae3886c8fbb2114296380d276fd37715d571 fffba
1481 1481 2:fffb6093b00943f91034b9bdad069402c834e572 fffb6
1482 1482 3:fff48a9b9de34a4d64120c29548214c67980ade3 fff4
1483 1483 4:ffff85cff0ff78504fcdc3c0bc10de0c65379249 ffff8
1484 1484 2147483647:ffffffffffffffffffffffffffffffffffffffff fffff
1485 1485 $ hg debugobsolete fffbae3886c8fbb2114296380d276fd37715d571
1486 1486
1487 1487 $ hg debugrevspec '0:wdir() & fff'
1488 1488 abort: 00changelog.i@fff: ambiguous identifier!
1489 1489 [255]
1490 1490 $ hg debugrevspec '0:wdir() & ffff'
1491 1491 abort: 00changelog.i@ffff: ambiguous identifier!
1492 1492 [255]
1493 1493 $ hg debugrevspec '0:wdir() & fffb'
1494 1494 abort: 00changelog.i@fffb: ambiguous identifier!
1495 1495 [255]
1496 1496 BROKEN should be '2' (node lookup uses unfiltered repo since dc25ed84bee8)
1497 1497 $ hg debugrevspec '0:wdir() & id(fffb)'
1498 1498 2
1499 1499 $ hg debugrevspec '0:wdir() & ffff8'
1500 1500 4
1501 1501 $ hg debugrevspec '0:wdir() & fffff'
1502 1502 2147483647
1503 1503
1504 1504 $ cd ..
1505 1505
1506 1506 Test branch() with wdir()
1507 1507
1508 1508 $ cd repo
1509 1509
1510 1510 $ log '0:wdir() & branch("literal")'
1511 1511 8
1512 1512 9
1513 1513 2147483647
1514 1514 $ log '0:wdir() & branch("re")'
1515 1515 8
1516 1516 9
1517 1517 2147483647
1518 1518 $ log '0:wdir() & branch("re:^a")'
1519 1519 0
1520 1520 2
1521 1521 $ log '0:wdir() & branch(8)'
1522 1522 8
1523 1523 9
1524 1524 2147483647
1525 1525
1526 1526 branch(wdir()) returns all revisions belonging to the working branch. The wdir
1527 1527 itself isn't returned unless it is explicitly populated.
1528 1528
1529 1529 $ log 'branch(wdir())'
1530 1530 8
1531 1531 9
1532 1532 $ log '0:wdir() & branch(wdir())'
1533 1533 8
1534 1534 9
1535 1535 2147483647
1536 1536
1537 1537 $ log 'outgoing()'
1538 1538 8
1539 1539 9
1540 1540 $ log 'outgoing("../remote1")'
1541 1541 8
1542 1542 9
1543 1543 $ log 'outgoing("../remote2")'
1544 1544 3
1545 1545 5
1546 1546 6
1547 1547 7
1548 1548 9
1549 1549 $ log 'p1(merge())'
1550 1550 5
1551 1551 $ log 'p2(merge())'
1552 1552 4
1553 1553 $ log 'parents(merge())'
1554 1554 4
1555 1555 5
1556 1556 $ log 'p1(branchpoint())'
1557 1557 0
1558 1558 2
1559 1559 $ log 'p2(branchpoint())'
1560 1560 $ log 'parents(branchpoint())'
1561 1561 0
1562 1562 2
1563 1563 $ log 'removes(a)'
1564 1564 2
1565 1565 6
1566 1566 $ log 'roots(all())'
1567 1567 0
1568 1568 $ log 'reverse(2 or 3 or 4 or 5)'
1569 1569 5
1570 1570 4
1571 1571 3
1572 1572 2
1573 1573 $ log 'reverse(all())'
1574 1574 9
1575 1575 8
1576 1576 7
1577 1577 6
1578 1578 5
1579 1579 4
1580 1580 3
1581 1581 2
1582 1582 1
1583 1583 0
1584 1584 $ log 'reverse(all()) & filelog(b)'
1585 1585 4
1586 1586 1
1587 1587 $ log 'rev(5)'
1588 1588 5
1589 1589 $ log 'sort(limit(reverse(all()), 3))'
1590 1590 7
1591 1591 8
1592 1592 9
1593 1593 $ log 'sort(2 or 3 or 4 or 5, date)'
1594 1594 2
1595 1595 3
1596 1596 5
1597 1597 4
1598 1598 $ log 'tagged()'
1599 1599 6
1600 1600 $ log 'tag()'
1601 1601 6
1602 1602 $ log 'tag(1.0)'
1603 1603 6
1604 1604 $ log 'tag(tip)'
1605 1605 9
1606 1606
1607 1607 Test order of revisions in compound expression
1608 1608 ----------------------------------------------
1609 1609
1610 1610 The general rule is that only the outermost (= leftmost) predicate can
1611 1611 enforce its ordering requirement. The other predicates should take the
1612 1612 ordering defined by it.
1613 1613
1614 1614 'A & B' should follow the order of 'A':
1615 1615
1616 1616 $ log '2:0 & 0::2'
1617 1617 2
1618 1618 1
1619 1619 0
1620 1620
1621 1621 'head()' combines sets in right order:
1622 1622
1623 1623 $ log '2:0 & head()'
1624 1624 2
1625 1625 1
1626 1626 0
1627 1627
1628 1628 'x:y' takes ordering parameter into account:
1629 1629
1630 1630 $ try -p optimized '3:0 & 0:3 & not 2:1'
1631 1631 * optimized:
1632 1632 (difference
1633 1633 (and
1634 1634 (range
1635 1635 ('symbol', '3')
1636 1636 ('symbol', '0')
1637 1637 define)
1638 1638 (range
1639 1639 ('symbol', '0')
1640 1640 ('symbol', '3')
1641 1641 follow)
1642 1642 define)
1643 1643 (range
1644 1644 ('symbol', '2')
1645 1645 ('symbol', '1')
1646 1646 any)
1647 1647 define)
1648 1648 * set:
1649 1649 <filteredset
1650 1650 <filteredset
1651 1651 <spanset- 0:4>,
1652 1652 <spanset+ 0:4>>,
1653 1653 <not
1654 1654 <spanset+ 1:3>>>
1655 1655 3
1656 1656 0
1657 1657
1658 1658 'a + b', which is optimized to '_list(a b)', should take the ordering of
1659 1659 the left expression:
1660 1660
1661 1661 $ try --optimize '2:0 & (0 + 1 + 2)'
1662 1662 (and
1663 1663 (range
1664 1664 ('symbol', '2')
1665 1665 ('symbol', '0'))
1666 1666 (group
1667 1667 (or
1668 1668 (list
1669 1669 ('symbol', '0')
1670 1670 ('symbol', '1')
1671 1671 ('symbol', '2')))))
1672 1672 * optimized:
1673 1673 (and
1674 1674 (range
1675 1675 ('symbol', '2')
1676 1676 ('symbol', '0')
1677 1677 define)
1678 1678 (func
1679 1679 ('symbol', '_list')
1680 1680 ('string', '0\x001\x002')
1681 1681 follow)
1682 1682 define)
1683 1683 * set:
1684 1684 <filteredset
1685 1685 <spanset- 0:3>,
1686 1686 <baseset [0, 1, 2]>>
1687 1687 2
1688 1688 1
1689 1689 0
1690 1690
1691 1691 'A + B' should take the ordering of the left expression:
1692 1692
1693 1693 $ try --optimize '2:0 & (0:1 + 2)'
1694 1694 (and
1695 1695 (range
1696 1696 ('symbol', '2')
1697 1697 ('symbol', '0'))
1698 1698 (group
1699 1699 (or
1700 1700 (list
1701 1701 (range
1702 1702 ('symbol', '0')
1703 1703 ('symbol', '1'))
1704 1704 ('symbol', '2')))))
1705 1705 * optimized:
1706 1706 (and
1707 1707 (range
1708 1708 ('symbol', '2')
1709 1709 ('symbol', '0')
1710 1710 define)
1711 1711 (or
1712 1712 (list
1713 1713 ('symbol', '2')
1714 1714 (range
1715 1715 ('symbol', '0')
1716 1716 ('symbol', '1')
1717 1717 follow))
1718 1718 follow)
1719 1719 define)
1720 1720 * set:
1721 1721 <filteredset
1722 1722 <spanset- 0:3>,
1723 1723 <addset
1724 1724 <baseset [2]>,
1725 1725 <spanset+ 0:2>>>
1726 1726 2
1727 1727 1
1728 1728 0
1729 1729
1730 1730 '_intlist(a b)' should behave like 'a + b':
1731 1731
1732 1732 $ trylist --optimize '2:0 & %ld' 0 1 2
1733 1733 (and
1734 1734 (range
1735 1735 ('symbol', '2')
1736 1736 ('symbol', '0'))
1737 1737 (func
1738 1738 ('symbol', '_intlist')
1739 1739 ('string', '0\x001\x002')))
1740 1740 * optimized:
1741 1741 (and
1742 1742 (func
1743 1743 ('symbol', '_intlist')
1744 1744 ('string', '0\x001\x002')
1745 1745 follow)
1746 1746 (range
1747 1747 ('symbol', '2')
1748 1748 ('symbol', '0')
1749 1749 define)
1750 1750 define)
1751 1751 * set:
1752 1752 <filteredset
1753 1753 <spanset- 0:3>,
1754 1754 <baseset+ [0, 1, 2]>>
1755 1755 2
1756 1756 1
1757 1757 0
1758 1758
1759 1759 $ trylist --optimize '%ld & 2:0' 0 2 1
1760 1760 (and
1761 1761 (func
1762 1762 ('symbol', '_intlist')
1763 1763 ('string', '0\x002\x001'))
1764 1764 (range
1765 1765 ('symbol', '2')
1766 1766 ('symbol', '0')))
1767 1767 * optimized:
1768 1768 (and
1769 1769 (func
1770 1770 ('symbol', '_intlist')
1771 1771 ('string', '0\x002\x001')
1772 1772 define)
1773 1773 (range
1774 1774 ('symbol', '2')
1775 1775 ('symbol', '0')
1776 1776 follow)
1777 1777 define)
1778 1778 * set:
1779 1779 <filteredset
1780 1780 <baseset [0, 2, 1]>,
1781 1781 <spanset- 0:3>>
1782 1782 0
1783 1783 2
1784 1784 1
1785 1785
1786 1786 '_hexlist(a b)' should behave like 'a + b':
1787 1787
1788 1788 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
1789 1789 (and
1790 1790 (range
1791 1791 ('symbol', '2')
1792 1792 ('symbol', '0'))
1793 1793 (func
1794 1794 ('symbol', '_hexlist')
1795 1795 ('string', '*'))) (glob)
1796 1796 * optimized:
1797 1797 (and
1798 1798 (range
1799 1799 ('symbol', '2')
1800 1800 ('symbol', '0')
1801 1801 define)
1802 1802 (func
1803 1803 ('symbol', '_hexlist')
1804 1804 ('string', '*') (glob)
1805 1805 follow)
1806 1806 define)
1807 1807 * set:
1808 1808 <filteredset
1809 1809 <spanset- 0:3>,
1810 1810 <baseset [0, 1, 2]>>
1811 1811 2
1812 1812 1
1813 1813 0
1814 1814
1815 1815 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
1816 1816 (and
1817 1817 (func
1818 1818 ('symbol', '_hexlist')
1819 1819 ('string', '*')) (glob)
1820 1820 (range
1821 1821 ('symbol', '2')
1822 1822 ('symbol', '0')))
1823 1823 * optimized:
1824 1824 (and
1825 1825 (range
1826 1826 ('symbol', '2')
1827 1827 ('symbol', '0')
1828 1828 follow)
1829 1829 (func
1830 1830 ('symbol', '_hexlist')
1831 1831 ('string', '*') (glob)
1832 1832 define)
1833 1833 define)
1834 1834 * set:
1835 1835 <baseset [0, 2, 1]>
1836 1836 0
1837 1837 2
1838 1838 1
1839 1839
1840 1840 '_list' should not go through the slow follow-order path if order doesn't
1841 1841 matter:
1842 1842
1843 1843 $ try -p optimized '2:0 & not (0 + 1)'
1844 1844 * optimized:
1845 1845 (difference
1846 1846 (range
1847 1847 ('symbol', '2')
1848 1848 ('symbol', '0')
1849 1849 define)
1850 1850 (func
1851 1851 ('symbol', '_list')
1852 1852 ('string', '0\x001')
1853 1853 any)
1854 1854 define)
1855 1855 * set:
1856 1856 <filteredset
1857 1857 <spanset- 0:3>,
1858 1858 <not
1859 1859 <baseset [0, 1]>>>
1860 1860 2
1861 1861
1862 1862 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
1863 1863 * optimized:
1864 1864 (difference
1865 1865 (range
1866 1866 ('symbol', '2')
1867 1867 ('symbol', '0')
1868 1868 define)
1869 1869 (and
1870 1870 (range
1871 1871 ('symbol', '0')
1872 1872 ('symbol', '2')
1873 1873 any)
1874 1874 (func
1875 1875 ('symbol', '_list')
1876 1876 ('string', '0\x001')
1877 1877 any)
1878 1878 any)
1879 1879 define)
1880 1880 * set:
1881 1881 <filteredset
1882 1882 <spanset- 0:3>,
1883 1883 <not
1884 1884 <baseset [0, 1]>>>
1885 1885 2
1886 1886
1887 1887 because 'present()' does nothing other than suppressing an error, the
1888 1888 ordering requirement should be forwarded to the nested expression
1889 1889
1890 1890 $ try -p optimized 'present(2 + 0 + 1)'
1891 1891 * optimized:
1892 1892 (func
1893 1893 ('symbol', 'present')
1894 1894 (func
1895 1895 ('symbol', '_list')
1896 1896 ('string', '2\x000\x001')
1897 1897 define)
1898 1898 define)
1899 1899 * set:
1900 1900 <baseset [2, 0, 1]>
1901 1901 2
1902 1902 0
1903 1903 1
1904 1904
1905 1905 $ try --optimize '2:0 & present(0 + 1 + 2)'
1906 1906 (and
1907 1907 (range
1908 1908 ('symbol', '2')
1909 1909 ('symbol', '0'))
1910 1910 (func
1911 1911 ('symbol', 'present')
1912 1912 (or
1913 1913 (list
1914 1914 ('symbol', '0')
1915 1915 ('symbol', '1')
1916 1916 ('symbol', '2')))))
1917 1917 * optimized:
1918 1918 (and
1919 1919 (range
1920 1920 ('symbol', '2')
1921 1921 ('symbol', '0')
1922 1922 define)
1923 1923 (func
1924 1924 ('symbol', 'present')
1925 1925 (func
1926 1926 ('symbol', '_list')
1927 1927 ('string', '0\x001\x002')
1928 1928 follow)
1929 1929 follow)
1930 1930 define)
1931 1931 * set:
1932 1932 <filteredset
1933 1933 <spanset- 0:3>,
1934 1934 <baseset [0, 1, 2]>>
1935 1935 2
1936 1936 1
1937 1937 0
1938 1938
1939 1939 'reverse()' should take effect only if it is the outermost expression:
1940 1940
1941 1941 $ try --optimize '0:2 & reverse(all())'
1942 1942 (and
1943 1943 (range
1944 1944 ('symbol', '0')
1945 1945 ('symbol', '2'))
1946 1946 (func
1947 1947 ('symbol', 'reverse')
1948 1948 (func
1949 1949 ('symbol', 'all')
1950 1950 None)))
1951 1951 * optimized:
1952 1952 (and
1953 1953 (range
1954 1954 ('symbol', '0')
1955 1955 ('symbol', '2')
1956 1956 define)
1957 1957 (func
1958 1958 ('symbol', 'reverse')
1959 1959 (func
1960 1960 ('symbol', 'all')
1961 1961 None
1962 1962 define)
1963 1963 follow)
1964 1964 define)
1965 1965 * set:
1966 1966 <filteredset
1967 1967 <spanset+ 0:3>,
1968 1968 <spanset+ 0:10>>
1969 1969 0
1970 1970 1
1971 1971 2
1972 1972
1973 1973 'sort()' should take effect only if it is the outermost expression:
1974 1974
1975 1975 $ try --optimize '0:2 & sort(all(), -rev)'
1976 1976 (and
1977 1977 (range
1978 1978 ('symbol', '0')
1979 1979 ('symbol', '2'))
1980 1980 (func
1981 1981 ('symbol', 'sort')
1982 1982 (list
1983 1983 (func
1984 1984 ('symbol', 'all')
1985 1985 None)
1986 1986 (negate
1987 1987 ('symbol', 'rev')))))
1988 1988 * optimized:
1989 1989 (and
1990 1990 (range
1991 1991 ('symbol', '0')
1992 1992 ('symbol', '2')
1993 1993 define)
1994 1994 (func
1995 1995 ('symbol', 'sort')
1996 1996 (list
1997 1997 (func
1998 1998 ('symbol', 'all')
1999 1999 None
2000 2000 define)
2001 2001 ('string', '-rev'))
2002 2002 follow)
2003 2003 define)
2004 2004 * set:
2005 2005 <filteredset
2006 2006 <spanset+ 0:3>,
2007 2007 <spanset+ 0:10>>
2008 2008 0
2009 2009 1
2010 2010 2
2011 2011
2012 2012 invalid argument passed to noop sort():
2013 2013
2014 2014 $ log '0:2 & sort()'
2015 2015 hg: parse error: sort requires one or two arguments
2016 2016 [255]
2017 2017 $ log '0:2 & sort(all(), -invalid)'
2018 2018 hg: parse error: unknown sort key '-invalid'
2019 2019 [255]
2020 2020
2021 2021 for 'A & f(B)', 'B' should not be affected by the order of 'A':
2022 2022
2023 2023 $ try --optimize '2:0 & first(1 + 0 + 2)'
2024 2024 (and
2025 2025 (range
2026 2026 ('symbol', '2')
2027 2027 ('symbol', '0'))
2028 2028 (func
2029 2029 ('symbol', 'first')
2030 2030 (or
2031 2031 (list
2032 2032 ('symbol', '1')
2033 2033 ('symbol', '0')
2034 2034 ('symbol', '2')))))
2035 2035 * optimized:
2036 2036 (and
2037 2037 (range
2038 2038 ('symbol', '2')
2039 2039 ('symbol', '0')
2040 2040 define)
2041 2041 (func
2042 2042 ('symbol', 'first')
2043 2043 (func
2044 2044 ('symbol', '_list')
2045 2045 ('string', '1\x000\x002')
2046 2046 define)
2047 2047 follow)
2048 2048 define)
2049 2049 * set:
2050 2050 <filteredset
2051 2051 <baseset [1]>,
2052 2052 <spanset- 0:3>>
2053 2053 1
2054 2054
2055 2055 $ try --optimize '2:0 & not last(0 + 2 + 1)'
2056 2056 (and
2057 2057 (range
2058 2058 ('symbol', '2')
2059 2059 ('symbol', '0'))
2060 2060 (not
2061 2061 (func
2062 2062 ('symbol', 'last')
2063 2063 (or
2064 2064 (list
2065 2065 ('symbol', '0')
2066 2066 ('symbol', '2')
2067 2067 ('symbol', '1'))))))
2068 2068 * optimized:
2069 2069 (difference
2070 2070 (range
2071 2071 ('symbol', '2')
2072 2072 ('symbol', '0')
2073 2073 define)
2074 2074 (func
2075 2075 ('symbol', 'last')
2076 2076 (func
2077 2077 ('symbol', '_list')
2078 2078 ('string', '0\x002\x001')
2079 2079 define)
2080 2080 any)
2081 2081 define)
2082 2082 * set:
2083 2083 <filteredset
2084 2084 <spanset- 0:3>,
2085 2085 <not
2086 2086 <baseset [1]>>>
2087 2087 2
2088 2088 0
2089 2089
2090 2090 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
2091 2091
2092 2092 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
2093 2093 (and
2094 2094 (range
2095 2095 ('symbol', '2')
2096 2096 ('symbol', '0'))
2097 2097 (range
2098 2098 (group
2099 2099 (or
2100 2100 (list
2101 2101 ('symbol', '1')
2102 2102 ('symbol', '0')
2103 2103 ('symbol', '2'))))
2104 2104 (group
2105 2105 (or
2106 2106 (list
2107 2107 ('symbol', '0')
2108 2108 ('symbol', '2')
2109 2109 ('symbol', '1'))))))
2110 2110 * optimized:
2111 2111 (and
2112 2112 (range
2113 2113 ('symbol', '2')
2114 2114 ('symbol', '0')
2115 2115 define)
2116 2116 (range
2117 2117 (func
2118 2118 ('symbol', '_list')
2119 2119 ('string', '1\x000\x002')
2120 2120 define)
2121 2121 (func
2122 2122 ('symbol', '_list')
2123 2123 ('string', '0\x002\x001')
2124 2124 define)
2125 2125 follow)
2126 2126 define)
2127 2127 * set:
2128 2128 <filteredset
2129 2129 <spanset- 0:3>,
2130 2130 <baseset [1]>>
2131 2131 1
2132 2132
2133 2133 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as
2134 2134 the ordering rule is determined before the rewrite; in this example,
2135 2135 'B' follows the order of the initial set, which is the same order as 'A'
2136 2136 since 'A' also follows the order:
2137 2137
2138 2138 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
2139 2139 (and
2140 2140 (func
2141 2141 ('symbol', 'contains')
2142 2142 ('string', 'glob:*'))
2143 2143 (group
2144 2144 (or
2145 2145 (list
2146 2146 ('symbol', '2')
2147 2147 ('symbol', '0')
2148 2148 ('symbol', '1')))))
2149 2149 * optimized:
2150 2150 (and
2151 2151 (func
2152 2152 ('symbol', '_list')
2153 2153 ('string', '2\x000\x001')
2154 2154 follow)
2155 2155 (func
2156 2156 ('symbol', 'contains')
2157 2157 ('string', 'glob:*')
2158 2158 define)
2159 2159 define)
2160 2160 * set:
2161 2161 <filteredset
2162 2162 <baseset+ [0, 1, 2]>,
2163 2163 <contains 'glob:*'>>
2164 2164 0
2165 2165 1
2166 2166 2
2167 2167
2168 2168 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
2169 2169 the order appropriately:
2170 2170
2171 2171 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
2172 2172 (and
2173 2173 (func
2174 2174 ('symbol', 'reverse')
2175 2175 (func
2176 2176 ('symbol', 'contains')
2177 2177 ('string', 'glob:*')))
2178 2178 (group
2179 2179 (or
2180 2180 (list
2181 2181 ('symbol', '0')
2182 2182 ('symbol', '2')
2183 2183 ('symbol', '1')))))
2184 2184 * optimized:
2185 2185 (and
2186 2186 (func
2187 2187 ('symbol', '_list')
2188 2188 ('string', '0\x002\x001')
2189 2189 follow)
2190 2190 (func
2191 2191 ('symbol', 'reverse')
2192 2192 (func
2193 2193 ('symbol', 'contains')
2194 2194 ('string', 'glob:*')
2195 2195 define)
2196 2196 define)
2197 2197 define)
2198 2198 * set:
2199 2199 <filteredset
2200 2200 <baseset- [0, 1, 2]>,
2201 2201 <contains 'glob:*'>>
2202 2202 2
2203 2203 1
2204 2204 0
2205 2205
2206 2206 'A + B' can be rewritten to 'B + A' by weight only when the order doesn't
2207 2207 matter (e.g. 'X & (A + B)' can be 'X & (B + A)', but '(A + B) & X' can't):
2208 2208
2209 2209 $ try -p optimized '0:2 & (reverse(contains("a")) + 2)'
2210 2210 * optimized:
2211 2211 (and
2212 2212 (range
2213 2213 ('symbol', '0')
2214 2214 ('symbol', '2')
2215 2215 define)
2216 2216 (or
2217 2217 (list
2218 2218 ('symbol', '2')
2219 2219 (func
2220 2220 ('symbol', 'reverse')
2221 2221 (func
2222 2222 ('symbol', 'contains')
2223 2223 ('string', 'a')
2224 2224 define)
2225 2225 follow))
2226 2226 follow)
2227 2227 define)
2228 2228 * set:
2229 2229 <filteredset
2230 2230 <spanset+ 0:3>,
2231 2231 <addset
2232 2232 <baseset [2]>,
2233 2233 <filteredset
2234 2234 <fullreposet+ 0:10>,
2235 2235 <contains 'a'>>>>
2236 2236 0
2237 2237 1
2238 2238 2
2239 2239
2240 2240 $ try -p optimized '(reverse(contains("a")) + 2) & 0:2'
2241 2241 * optimized:
2242 2242 (and
2243 2243 (range
2244 2244 ('symbol', '0')
2245 2245 ('symbol', '2')
2246 2246 follow)
2247 2247 (or
2248 2248 (list
2249 2249 (func
2250 2250 ('symbol', 'reverse')
2251 2251 (func
2252 2252 ('symbol', 'contains')
2253 2253 ('string', 'a')
2254 2254 define)
2255 2255 define)
2256 2256 ('symbol', '2'))
2257 2257 define)
2258 2258 define)
2259 2259 * set:
2260 2260 <addset
2261 2261 <filteredset
2262 2262 <spanset- 0:3>,
2263 2263 <contains 'a'>>,
2264 2264 <baseset [2]>>
2265 2265 1
2266 2266 0
2267 2267 2
2268 2268
2269 2269 test sort revset
2270 2270 --------------------------------------------
2271 2271
2272 2272 test when adding two unordered revsets
2273 2273
2274 2274 $ log 'sort(keyword(issue) or modifies(b))'
2275 2275 4
2276 2276 6
2277 2277
2278 2278 test when sorting a reversed collection in the same way it is
2279 2279
2280 2280 $ log 'sort(reverse(all()), -rev)'
2281 2281 9
2282 2282 8
2283 2283 7
2284 2284 6
2285 2285 5
2286 2286 4
2287 2287 3
2288 2288 2
2289 2289 1
2290 2290 0
2291 2291
2292 2292 test when sorting a reversed collection
2293 2293
2294 2294 $ log 'sort(reverse(all()), rev)'
2295 2295 0
2296 2296 1
2297 2297 2
2298 2298 3
2299 2299 4
2300 2300 5
2301 2301 6
2302 2302 7
2303 2303 8
2304 2304 9
2305 2305
2306 2306
2307 2307 test sorting two sorted collections in different orders
2308 2308
2309 2309 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
2310 2310 2
2311 2311 6
2312 2312 8
2313 2313 9
2314 2314
2315 2315 test sorting two sorted collections in different orders backwards
2316 2316
2317 2317 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
2318 2318 9
2319 2319 8
2320 2320 6
2321 2321 2
2322 2322
2323 2323 test empty sort key which is noop
2324 2324
2325 2325 $ log 'sort(0 + 2 + 1, "")'
2326 2326 0
2327 2327 2
2328 2328 1
2329 2329
2330 2330 test invalid sort keys
2331 2331
2332 2332 $ log 'sort(all(), -invalid)'
2333 2333 hg: parse error: unknown sort key '-invalid'
2334 2334 [255]
2335 2335
2336 2336 $ cd ..
2337 2337
2338 2338 test sorting by multiple keys including variable-length strings
2339 2339
2340 2340 $ hg init sorting
2341 2341 $ cd sorting
2342 2342 $ cat <<EOF >> .hg/hgrc
2343 2343 > [ui]
2344 2344 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
2345 2345 > [templatealias]
2346 2346 > p5(s) = pad(s, 5)
2347 2347 > EOF
2348 2348 $ hg branch -qf b12
2349 2349 $ hg ci -m m111 -u u112 -d '111 10800'
2350 2350 $ hg branch -qf b11
2351 2351 $ hg ci -m m12 -u u111 -d '112 7200'
2352 2352 $ hg branch -qf b111
2353 2353 $ hg ci -m m11 -u u12 -d '111 3600'
2354 2354 $ hg branch -qf b112
2355 2355 $ hg ci -m m111 -u u11 -d '120 0'
2356 2356 $ hg branch -qf b111
2357 2357 $ hg ci -m m112 -u u111 -d '110 14400'
2358 2358 created new head
2359 2359
2360 2360 compare revisions (has fast path):
2361 2361
2362 2362 $ hg log -r 'sort(all(), rev)'
2363 2363 0 b12 m111 u112 111 10800
2364 2364 1 b11 m12 u111 112 7200
2365 2365 2 b111 m11 u12 111 3600
2366 2366 3 b112 m111 u11 120 0
2367 2367 4 b111 m112 u111 110 14400
2368 2368
2369 2369 $ hg log -r 'sort(all(), -rev)'
2370 2370 4 b111 m112 u111 110 14400
2371 2371 3 b112 m111 u11 120 0
2372 2372 2 b111 m11 u12 111 3600
2373 2373 1 b11 m12 u111 112 7200
2374 2374 0 b12 m111 u112 111 10800
2375 2375
2376 2376 compare variable-length strings (issue5218):
2377 2377
2378 2378 $ hg log -r 'sort(all(), branch)'
2379 2379 1 b11 m12 u111 112 7200
2380 2380 2 b111 m11 u12 111 3600
2381 2381 4 b111 m112 u111 110 14400
2382 2382 3 b112 m111 u11 120 0
2383 2383 0 b12 m111 u112 111 10800
2384 2384
2385 2385 $ hg log -r 'sort(all(), -branch)'
2386 2386 0 b12 m111 u112 111 10800
2387 2387 3 b112 m111 u11 120 0
2388 2388 2 b111 m11 u12 111 3600
2389 2389 4 b111 m112 u111 110 14400
2390 2390 1 b11 m12 u111 112 7200
2391 2391
2392 2392 $ hg log -r 'sort(all(), desc)'
2393 2393 2 b111 m11 u12 111 3600
2394 2394 0 b12 m111 u112 111 10800
2395 2395 3 b112 m111 u11 120 0
2396 2396 4 b111 m112 u111 110 14400
2397 2397 1 b11 m12 u111 112 7200
2398 2398
2399 2399 $ hg log -r 'sort(all(), -desc)'
2400 2400 1 b11 m12 u111 112 7200
2401 2401 4 b111 m112 u111 110 14400
2402 2402 0 b12 m111 u112 111 10800
2403 2403 3 b112 m111 u11 120 0
2404 2404 2 b111 m11 u12 111 3600
2405 2405
2406 2406 $ hg log -r 'sort(all(), user)'
2407 2407 3 b112 m111 u11 120 0
2408 2408 1 b11 m12 u111 112 7200
2409 2409 4 b111 m112 u111 110 14400
2410 2410 0 b12 m111 u112 111 10800
2411 2411 2 b111 m11 u12 111 3600
2412 2412
2413 2413 $ hg log -r 'sort(all(), -user)'
2414 2414 2 b111 m11 u12 111 3600
2415 2415 0 b12 m111 u112 111 10800
2416 2416 1 b11 m12 u111 112 7200
2417 2417 4 b111 m112 u111 110 14400
2418 2418 3 b112 m111 u11 120 0
2419 2419
2420 2420 compare dates (tz offset should have no effect):
2421 2421
2422 2422 $ hg log -r 'sort(all(), date)'
2423 2423 4 b111 m112 u111 110 14400
2424 2424 0 b12 m111 u112 111 10800
2425 2425 2 b111 m11 u12 111 3600
2426 2426 1 b11 m12 u111 112 7200
2427 2427 3 b112 m111 u11 120 0
2428 2428
2429 2429 $ hg log -r 'sort(all(), -date)'
2430 2430 3 b112 m111 u11 120 0
2431 2431 1 b11 m12 u111 112 7200
2432 2432 0 b12 m111 u112 111 10800
2433 2433 2 b111 m11 u12 111 3600
2434 2434 4 b111 m112 u111 110 14400
2435 2435
2436 2436 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2437 2437 because '-k' reverses the comparison, not the list itself:
2438 2438
2439 2439 $ hg log -r 'sort(0 + 2, date)'
2440 2440 0 b12 m111 u112 111 10800
2441 2441 2 b111 m11 u12 111 3600
2442 2442
2443 2443 $ hg log -r 'sort(0 + 2, -date)'
2444 2444 0 b12 m111 u112 111 10800
2445 2445 2 b111 m11 u12 111 3600
2446 2446
2447 2447 $ hg log -r 'reverse(sort(0 + 2, date))'
2448 2448 2 b111 m11 u12 111 3600
2449 2449 0 b12 m111 u112 111 10800
2450 2450
2451 2451 sort by multiple keys:
2452 2452
2453 2453 $ hg log -r 'sort(all(), "branch -rev")'
2454 2454 1 b11 m12 u111 112 7200
2455 2455 4 b111 m112 u111 110 14400
2456 2456 2 b111 m11 u12 111 3600
2457 2457 3 b112 m111 u11 120 0
2458 2458 0 b12 m111 u112 111 10800
2459 2459
2460 2460 $ hg log -r 'sort(all(), "-desc -date")'
2461 2461 1 b11 m12 u111 112 7200
2462 2462 4 b111 m112 u111 110 14400
2463 2463 3 b112 m111 u11 120 0
2464 2464 0 b12 m111 u112 111 10800
2465 2465 2 b111 m11 u12 111 3600
2466 2466
2467 2467 $ hg log -r 'sort(all(), "user -branch date rev")'
2468 2468 3 b112 m111 u11 120 0
2469 2469 4 b111 m112 u111 110 14400
2470 2470 1 b11 m12 u111 112 7200
2471 2471 0 b12 m111 u112 111 10800
2472 2472 2 b111 m11 u12 111 3600
2473 2473
2474 2474 toposort prioritises graph branches
2475 2475
2476 2476 $ hg up 2
2477 2477 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2478 2478 $ touch a
2479 2479 $ hg addremove
2480 2480 adding a
2481 2481 $ hg ci -m 't1' -u 'tu' -d '130 0'
2482 2482 created new head
2483 2483 $ echo 'a' >> a
2484 2484 $ hg ci -m 't2' -u 'tu' -d '130 0'
2485 2485 $ hg book book1
2486 2486 $ hg up 4
2487 2487 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2488 2488 (leaving bookmark book1)
2489 2489 $ touch a
2490 2490 $ hg addremove
2491 2491 adding a
2492 2492 $ hg ci -m 't3' -u 'tu' -d '130 0'
2493 2493
2494 2494 $ hg log -r 'sort(all(), topo)'
2495 2495 7 b111 t3 tu 130 0
2496 2496 4 b111 m112 u111 110 14400
2497 2497 3 b112 m111 u11 120 0
2498 2498 6 b111 t2 tu 130 0
2499 2499 5 b111 t1 tu 130 0
2500 2500 2 b111 m11 u12 111 3600
2501 2501 1 b11 m12 u111 112 7200
2502 2502 0 b12 m111 u112 111 10800
2503 2503
2504 2504 $ hg log -r 'sort(all(), -topo)'
2505 2505 0 b12 m111 u112 111 10800
2506 2506 1 b11 m12 u111 112 7200
2507 2507 2 b111 m11 u12 111 3600
2508 2508 5 b111 t1 tu 130 0
2509 2509 6 b111 t2 tu 130 0
2510 2510 3 b112 m111 u11 120 0
2511 2511 4 b111 m112 u111 110 14400
2512 2512 7 b111 t3 tu 130 0
2513 2513
2514 2514 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2515 2515 6 b111 t2 tu 130 0
2516 2516 5 b111 t1 tu 130 0
2517 2517 7 b111 t3 tu 130 0
2518 2518 4 b111 m112 u111 110 14400
2519 2519 3 b112 m111 u11 120 0
2520 2520 2 b111 m11 u12 111 3600
2521 2521 1 b11 m12 u111 112 7200
2522 2522 0 b12 m111 u112 111 10800
2523 2523
2524 2524 topographical sorting can't be combined with other sort keys, and you can't
2525 2525 use the topo.firstbranch option when topo sort is not active:
2526 2526
2527 2527 $ hg log -r 'sort(all(), "topo user")'
2528 2528 hg: parse error: topo sort order cannot be combined with other sort keys
2529 2529 [255]
2530 2530
2531 2531 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2532 2532 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2533 2533 [255]
2534 2534
2535 2535 topo.firstbranch should accept any kind of expressions:
2536 2536
2537 2537 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2538 2538 0 b12 m111 u112 111 10800
2539 2539
2540 2540 $ cd ..
2541 2541 $ cd repo
2542 2542
2543 2543 test subtracting something from an addset
2544 2544
2545 2545 $ log '(outgoing() or removes(a)) - removes(a)'
2546 2546 8
2547 2547 9
2548 2548
2549 2549 test intersecting something with an addset
2550 2550
2551 2551 $ log 'parents(outgoing() or removes(a))'
2552 2552 1
2553 2553 4
2554 2554 5
2555 2555 8
2556 2556
2557 2557 test that `or` operation combines elements in the right order:
2558 2558
2559 2559 $ log '3:4 or 2:5'
2560 2560 3
2561 2561 4
2562 2562 2
2563 2563 5
2564 2564 $ log '3:4 or 5:2'
2565 2565 3
2566 2566 4
2567 2567 5
2568 2568 2
2569 2569 $ log 'sort(3:4 or 2:5)'
2570 2570 2
2571 2571 3
2572 2572 4
2573 2573 5
2574 2574 $ log 'sort(3:4 or 5:2)'
2575 2575 2
2576 2576 3
2577 2577 4
2578 2578 5
2579 2579
2580 2580 test that more than one `-r`s are combined in the right order and deduplicated:
2581 2581
2582 2582 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
2583 2583 3
2584 2584 4
2585 2585 5
2586 2586 2
2587 2587 0
2588 2588 1
2589 2589
2590 2590 test that `or` operation skips duplicated revisions from right-hand side
2591 2591
2592 2592 $ try 'reverse(1::5) or ancestors(4)'
2593 2593 (or
2594 2594 (list
2595 2595 (func
2596 2596 ('symbol', 'reverse')
2597 2597 (dagrange
2598 2598 ('symbol', '1')
2599 2599 ('symbol', '5')))
2600 2600 (func
2601 2601 ('symbol', 'ancestors')
2602 2602 ('symbol', '4'))))
2603 2603 * set:
2604 2604 <addset
2605 2605 <baseset- [1, 3, 5]>,
2606 2606 <generatorset+>>
2607 2607 5
2608 2608 3
2609 2609 1
2610 2610 0
2611 2611 2
2612 2612 4
2613 2613 $ try 'sort(ancestors(4) or reverse(1::5))'
2614 2614 (func
2615 2615 ('symbol', 'sort')
2616 2616 (or
2617 2617 (list
2618 2618 (func
2619 2619 ('symbol', 'ancestors')
2620 2620 ('symbol', '4'))
2621 2621 (func
2622 2622 ('symbol', 'reverse')
2623 2623 (dagrange
2624 2624 ('symbol', '1')
2625 2625 ('symbol', '5'))))))
2626 2626 * set:
2627 2627 <addset+
2628 2628 <generatorset+>,
2629 2629 <baseset- [1, 3, 5]>>
2630 2630 0
2631 2631 1
2632 2632 2
2633 2633 3
2634 2634 4
2635 2635 5
2636 2636
2637 2637 test optimization of trivial `or` operation
2638 2638
2639 2639 $ try --optimize '0|(1)|"2"|-2|tip|null'
2640 2640 (or
2641 2641 (list
2642 2642 ('symbol', '0')
2643 2643 (group
2644 2644 ('symbol', '1'))
2645 2645 ('string', '2')
2646 2646 (negate
2647 2647 ('symbol', '2'))
2648 2648 ('symbol', 'tip')
2649 2649 ('symbol', 'null')))
2650 2650 * optimized:
2651 2651 (func
2652 2652 ('symbol', '_list')
2653 2653 ('string', '0\x001\x002\x00-2\x00tip\x00null')
2654 2654 define)
2655 2655 * set:
2656 2656 <baseset [0, 1, 2, 8, 9, -1]>
2657 2657 0
2658 2658 1
2659 2659 2
2660 2660 8
2661 2661 9
2662 2662 -1
2663 2663
2664 2664 $ try --optimize '0|1|2:3'
2665 2665 (or
2666 2666 (list
2667 2667 ('symbol', '0')
2668 2668 ('symbol', '1')
2669 2669 (range
2670 2670 ('symbol', '2')
2671 2671 ('symbol', '3'))))
2672 2672 * optimized:
2673 2673 (or
2674 2674 (list
2675 2675 (func
2676 2676 ('symbol', '_list')
2677 2677 ('string', '0\x001')
2678 2678 define)
2679 2679 (range
2680 2680 ('symbol', '2')
2681 2681 ('symbol', '3')
2682 2682 define))
2683 2683 define)
2684 2684 * set:
2685 2685 <addset
2686 2686 <baseset [0, 1]>,
2687 2687 <spanset+ 2:4>>
2688 2688 0
2689 2689 1
2690 2690 2
2691 2691 3
2692 2692
2693 2693 $ try --optimize '0:1|2|3:4|5|6'
2694 2694 (or
2695 2695 (list
2696 2696 (range
2697 2697 ('symbol', '0')
2698 2698 ('symbol', '1'))
2699 2699 ('symbol', '2')
2700 2700 (range
2701 2701 ('symbol', '3')
2702 2702 ('symbol', '4'))
2703 2703 ('symbol', '5')
2704 2704 ('symbol', '6')))
2705 2705 * optimized:
2706 2706 (or
2707 2707 (list
2708 2708 (range
2709 2709 ('symbol', '0')
2710 2710 ('symbol', '1')
2711 2711 define)
2712 2712 ('symbol', '2')
2713 2713 (range
2714 2714 ('symbol', '3')
2715 2715 ('symbol', '4')
2716 2716 define)
2717 2717 (func
2718 2718 ('symbol', '_list')
2719 2719 ('string', '5\x006')
2720 2720 define))
2721 2721 define)
2722 2722 * set:
2723 2723 <addset
2724 2724 <addset
2725 2725 <spanset+ 0:2>,
2726 2726 <baseset [2]>>,
2727 2727 <addset
2728 2728 <spanset+ 3:5>,
2729 2729 <baseset [5, 6]>>>
2730 2730 0
2731 2731 1
2732 2732 2
2733 2733 3
2734 2734 4
2735 2735 5
2736 2736 6
2737 2737
2738 2738 unoptimized `or` looks like this
2739 2739
2740 2740 $ try --no-optimized -p analyzed '0|1|2|3|4'
2741 2741 * analyzed:
2742 2742 (or
2743 2743 (list
2744 2744 ('symbol', '0')
2745 2745 ('symbol', '1')
2746 2746 ('symbol', '2')
2747 2747 ('symbol', '3')
2748 2748 ('symbol', '4'))
2749 2749 define)
2750 2750 * set:
2751 2751 <addset
2752 2752 <addset
2753 2753 <baseset [0]>,
2754 2754 <baseset [1]>>,
2755 2755 <addset
2756 2756 <baseset [2]>,
2757 2757 <addset
2758 2758 <baseset [3]>,
2759 2759 <baseset [4]>>>>
2760 2760 0
2761 2761 1
2762 2762 2
2763 2763 3
2764 2764 4
2765 2765
2766 2766 test that `_list` should be narrowed by provided `subset`
2767 2767
2768 2768 $ log '0:2 and (null|1|2|3)'
2769 2769 1
2770 2770 2
2771 2771
2772 2772 test that `_list` should remove duplicates
2773 2773
2774 2774 $ log '0|1|2|1|2|-1|tip'
2775 2775 0
2776 2776 1
2777 2777 2
2778 2778 9
2779 2779
2780 2780 test unknown revision in `_list`
2781 2781
2782 2782 $ log '0|unknown'
2783 2783 abort: unknown revision 'unknown'!
2784 2784 [255]
2785 2785
2786 2786 test integer range in `_list`
2787 2787
2788 2788 $ log '-1|-10'
2789 2789 9
2790 2790 0
2791 2791
2792 2792 $ log '-10|-11'
2793 2793 abort: unknown revision '-11'!
2794 2794 [255]
2795 2795
2796 2796 $ log '9|10'
2797 2797 abort: unknown revision '10'!
2798 2798 [255]
2799 2799
2800 2800 test '0000' != '0' in `_list`
2801 2801
2802 2802 $ log '0|0000'
2803 2803 0
2804 2804 -1
2805 2805
2806 2806 test ',' in `_list`
2807 2807 $ log '0,1'
2808 2808 hg: parse error: can't use a list in this context
2809 2809 (see hg help "revsets.x or y")
2810 2810 [255]
2811 2811 $ try '0,1,2'
2812 2812 (list
2813 2813 ('symbol', '0')
2814 2814 ('symbol', '1')
2815 2815 ('symbol', '2'))
2816 2816 hg: parse error: can't use a list in this context
2817 2817 (see hg help "revsets.x or y")
2818 2818 [255]
2819 2819
2820 2820 test that chained `or` operations make balanced addsets
2821 2821
2822 2822 $ try '0:1|1:2|2:3|3:4|4:5'
2823 2823 (or
2824 2824 (list
2825 2825 (range
2826 2826 ('symbol', '0')
2827 2827 ('symbol', '1'))
2828 2828 (range
2829 2829 ('symbol', '1')
2830 2830 ('symbol', '2'))
2831 2831 (range
2832 2832 ('symbol', '2')
2833 2833 ('symbol', '3'))
2834 2834 (range
2835 2835 ('symbol', '3')
2836 2836 ('symbol', '4'))
2837 2837 (range
2838 2838 ('symbol', '4')
2839 2839 ('symbol', '5'))))
2840 2840 * set:
2841 2841 <addset
2842 2842 <addset
2843 2843 <spanset+ 0:2>,
2844 2844 <spanset+ 1:3>>,
2845 2845 <addset
2846 2846 <spanset+ 2:4>,
2847 2847 <addset
2848 2848 <spanset+ 3:5>,
2849 2849 <spanset+ 4:6>>>>
2850 2850 0
2851 2851 1
2852 2852 2
2853 2853 3
2854 2854 4
2855 2855 5
2856 2856
2857 2857 no crash by empty group "()" while optimizing `or` operations
2858 2858
2859 2859 $ try --optimize '0|()'
2860 2860 (or
2861 2861 (list
2862 2862 ('symbol', '0')
2863 2863 (group
2864 2864 None)))
2865 2865 * optimized:
2866 2866 (or
2867 2867 (list
2868 2868 ('symbol', '0')
2869 2869 None)
2870 2870 define)
2871 2871 hg: parse error: missing argument
2872 2872 [255]
2873 2873
2874 2874 test that chained `or` operations never eat up stack (issue4624)
2875 2875 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
2876 2876
2877 2877 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
2878 2878 0
2879 2879 1
2880 2880
2881 2881 test that repeated `-r` options never eat up stack (issue4565)
2882 2882 (uses `-r 0::1` to avoid possible optimization at old-style parser)
2883 2883
2884 2884 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
2885 2885 0
2886 2886 1
2887 2887
2888 2888 check that conversion to only works
2889 2889 $ try --optimize '::3 - ::1'
2890 2890 (minus
2891 2891 (dagrangepre
2892 2892 ('symbol', '3'))
2893 2893 (dagrangepre
2894 2894 ('symbol', '1')))
2895 2895 * optimized:
2896 2896 (func
2897 2897 ('symbol', 'only')
2898 2898 (list
2899 2899 ('symbol', '3')
2900 2900 ('symbol', '1'))
2901 2901 define)
2902 2902 * set:
2903 2903 <baseset+ [3]>
2904 2904 3
2905 2905 $ try --optimize 'ancestors(1) - ancestors(3)'
2906 2906 (minus
2907 2907 (func
2908 2908 ('symbol', 'ancestors')
2909 2909 ('symbol', '1'))
2910 2910 (func
2911 2911 ('symbol', 'ancestors')
2912 2912 ('symbol', '3')))
2913 2913 * optimized:
2914 2914 (func
2915 2915 ('symbol', 'only')
2916 2916 (list
2917 2917 ('symbol', '1')
2918 2918 ('symbol', '3'))
2919 2919 define)
2920 2920 * set:
2921 2921 <baseset+ []>
2922 2922 $ try --optimize 'not ::2 and ::6'
2923 2923 (and
2924 2924 (not
2925 2925 (dagrangepre
2926 2926 ('symbol', '2')))
2927 2927 (dagrangepre
2928 2928 ('symbol', '6')))
2929 2929 * optimized:
2930 2930 (func
2931 2931 ('symbol', 'only')
2932 2932 (list
2933 2933 ('symbol', '6')
2934 2934 ('symbol', '2'))
2935 2935 define)
2936 2936 * set:
2937 2937 <baseset+ [3, 4, 5, 6]>
2938 2938 3
2939 2939 4
2940 2940 5
2941 2941 6
2942 2942 $ try --optimize 'ancestors(6) and not ancestors(4)'
2943 2943 (and
2944 2944 (func
2945 2945 ('symbol', 'ancestors')
2946 2946 ('symbol', '6'))
2947 2947 (not
2948 2948 (func
2949 2949 ('symbol', 'ancestors')
2950 2950 ('symbol', '4'))))
2951 2951 * optimized:
2952 2952 (func
2953 2953 ('symbol', 'only')
2954 2954 (list
2955 2955 ('symbol', '6')
2956 2956 ('symbol', '4'))
2957 2957 define)
2958 2958 * set:
2959 2959 <baseset+ [3, 5, 6]>
2960 2960 3
2961 2961 5
2962 2962 6
2963 2963
2964 2964 no crash by empty group "()" while optimizing to "only()"
2965 2965
2966 2966 $ try --optimize '::1 and ()'
2967 2967 (and
2968 2968 (dagrangepre
2969 2969 ('symbol', '1'))
2970 2970 (group
2971 2971 None))
2972 2972 * optimized:
2973 2973 (and
2974 2974 None
2975 2975 (func
2976 2976 ('symbol', 'ancestors')
2977 2977 ('symbol', '1')
2978 2978 define)
2979 2979 define)
2980 2980 hg: parse error: missing argument
2981 2981 [255]
2982 2982
2983 2983 optimization to only() works only if ancestors() takes only one argument
2984 2984
2985 2985 $ hg debugrevspec -p optimized 'ancestors(6) - ancestors(4, 1)'
2986 2986 * optimized:
2987 2987 (difference
2988 2988 (func
2989 2989 ('symbol', 'ancestors')
2990 2990 ('symbol', '6')
2991 2991 define)
2992 2992 (func
2993 2993 ('symbol', 'ancestors')
2994 2994 (list
2995 2995 ('symbol', '4')
2996 2996 ('symbol', '1'))
2997 2997 any)
2998 2998 define)
2999 hg: parse error: can't use a list in this context
3000 (see hg help "revsets.x or y")
2999 hg: parse error: ancestors takes at most 1 positional arguments
3001 3000 [255]
3002 3001 $ hg debugrevspec -p optimized 'ancestors(6, 1) - ancestors(4)'
3003 3002 * optimized:
3004 3003 (difference
3005 3004 (func
3006 3005 ('symbol', 'ancestors')
3007 3006 (list
3008 3007 ('symbol', '6')
3009 3008 ('symbol', '1'))
3010 3009 define)
3011 3010 (func
3012 3011 ('symbol', 'ancestors')
3013 3012 ('symbol', '4')
3014 3013 any)
3015 3014 define)
3016 hg: parse error: can't use a list in this context
3017 (see hg help "revsets.x or y")
3015 hg: parse error: ancestors takes at most 1 positional arguments
3018 3016 [255]
3019 3017
3020 3018 optimization disabled if keyword arguments passed (because we're too lazy
3021 3019 to support it)
3022 3020
3023 3021 $ hg debugrevspec -p optimized 'ancestors(set=6) - ancestors(set=4)'
3024 3022 * optimized:
3025 3023 (difference
3026 3024 (func
3027 3025 ('symbol', 'ancestors')
3028 3026 (keyvalue
3029 3027 ('symbol', 'set')
3030 3028 ('symbol', '6'))
3031 3029 define)
3032 3030 (func
3033 3031 ('symbol', 'ancestors')
3034 3032 (keyvalue
3035 3033 ('symbol', 'set')
3036 3034 ('symbol', '4'))
3037 3035 any)
3038 3036 define)
3039 hg: parse error: can't use a key-value pair in this context
3040 [255]
3037 3
3038 5
3039 6
3041 3040
3042 3041 invalid function call should not be optimized to only()
3043 3042
3044 3043 $ log '"ancestors"(6) and not ancestors(4)'
3045 3044 hg: parse error: not a symbol
3046 3045 [255]
3047 3046
3048 3047 $ log 'ancestors(6) and not "ancestors"(4)'
3049 3048 hg: parse error: not a symbol
3050 3049 [255]
3051 3050
3052 3051 we can use patterns when searching for tags
3053 3052
3054 3053 $ log 'tag("1..*")'
3055 3054 abort: tag '1..*' does not exist!
3056 3055 [255]
3057 3056 $ log 'tag("re:1..*")'
3058 3057 6
3059 3058 $ log 'tag("re:[0-9].[0-9]")'
3060 3059 6
3061 3060 $ log 'tag("literal:1.0")'
3062 3061 6
3063 3062 $ log 'tag("re:0..*")'
3064 3063
3065 3064 $ log 'tag(unknown)'
3066 3065 abort: tag 'unknown' does not exist!
3067 3066 [255]
3068 3067 $ log 'tag("re:unknown")'
3069 3068 $ log 'present(tag("unknown"))'
3070 3069 $ log 'present(tag("re:unknown"))'
3071 3070 $ log 'branch(unknown)'
3072 3071 abort: unknown revision 'unknown'!
3073 3072 [255]
3074 3073 $ log 'branch("literal:unknown")'
3075 3074 abort: branch 'unknown' does not exist!
3076 3075 [255]
3077 3076 $ log 'branch("re:unknown")'
3078 3077 $ log 'present(branch("unknown"))'
3079 3078 $ log 'present(branch("re:unknown"))'
3080 3079 $ log 'user(bob)'
3081 3080 2
3082 3081
3083 3082 $ log '4::8'
3084 3083 4
3085 3084 8
3086 3085 $ log '4:8'
3087 3086 4
3088 3087 5
3089 3088 6
3090 3089 7
3091 3090 8
3092 3091
3093 3092 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
3094 3093 4
3095 3094 2
3096 3095 5
3097 3096
3098 3097 $ log 'not 0 and 0:2'
3099 3098 1
3100 3099 2
3101 3100 $ log 'not 1 and 0:2'
3102 3101 0
3103 3102 2
3104 3103 $ log 'not 2 and 0:2'
3105 3104 0
3106 3105 1
3107 3106 $ log '(1 and 2)::'
3108 3107 $ log '(1 and 2):'
3109 3108 $ log '(1 and 2):3'
3110 3109 $ log 'sort(head(), -rev)'
3111 3110 9
3112 3111 7
3113 3112 6
3114 3113 5
3115 3114 4
3116 3115 3
3117 3116 2
3118 3117 1
3119 3118 0
3120 3119 $ log '4::8 - 8'
3121 3120 4
3122 3121
3123 3122 matching() should preserve the order of the input set:
3124 3123
3125 3124 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
3126 3125 2
3127 3126 3
3128 3127 1
3129 3128
3130 3129 $ log 'named("unknown")'
3131 3130 abort: namespace 'unknown' does not exist!
3132 3131 [255]
3133 3132 $ log 'named("re:unknown")'
3134 3133 abort: no namespace exists that match 'unknown'!
3135 3134 [255]
3136 3135 $ log 'present(named("unknown"))'
3137 3136 $ log 'present(named("re:unknown"))'
3138 3137
3139 3138 $ log 'tag()'
3140 3139 6
3141 3140 $ log 'named("tags")'
3142 3141 6
3143 3142
3144 3143 issue2437
3145 3144
3146 3145 $ log '3 and p1(5)'
3147 3146 3
3148 3147 $ log '4 and p2(6)'
3149 3148 4
3150 3149 $ log '1 and parents(:2)'
3151 3150 1
3152 3151 $ log '2 and children(1:)'
3153 3152 2
3154 3153 $ log 'roots(all()) or roots(all())'
3155 3154 0
3156 3155 $ hg debugrevspec 'roots(all()) or roots(all())'
3157 3156 0
3158 3157 $ log 'heads(branch(é)) or heads(branch(é))'
3159 3158 9
3160 3159 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(é)))'
3161 3160 4
3162 3161
3163 3162 issue2654: report a parse error if the revset was not completely parsed
3164 3163
3165 3164 $ log '1 OR 2'
3166 3165 hg: parse error at 2: invalid token
3167 3166 [255]
3168 3167
3169 3168 or operator should preserve ordering:
3170 3169 $ log 'reverse(2::4) or tip'
3171 3170 4
3172 3171 2
3173 3172 9
3174 3173
3175 3174 parentrevspec
3176 3175
3177 3176 $ log 'merge()^0'
3178 3177 6
3179 3178 $ log 'merge()^'
3180 3179 5
3181 3180 $ log 'merge()^1'
3182 3181 5
3183 3182 $ log 'merge()^2'
3184 3183 4
3185 3184 $ log '(not merge())^2'
3186 3185 $ log 'merge()^^'
3187 3186 3
3188 3187 $ log 'merge()^1^'
3189 3188 3
3190 3189 $ log 'merge()^^^'
3191 3190 1
3192 3191
3193 3192 $ hg debugrevspec -s '(merge() | 0)~-1'
3194 3193 * set:
3195 3194 <baseset+ [1, 7]>
3196 3195 1
3197 3196 7
3198 3197 $ log 'merge()~-1'
3199 3198 7
3200 3199 $ log 'tip~-1'
3201 3200 $ log '(tip | merge())~-1'
3202 3201 7
3203 3202 $ log 'merge()~0'
3204 3203 6
3205 3204 $ log 'merge()~1'
3206 3205 5
3207 3206 $ log 'merge()~2'
3208 3207 3
3209 3208 $ log 'merge()~2^1'
3210 3209 1
3211 3210 $ log 'merge()~3'
3212 3211 1
3213 3212
3214 3213 $ log '(-3:tip)^'
3215 3214 4
3216 3215 6
3217 3216 8
3218 3217
3219 3218 $ log 'tip^foo'
3220 3219 hg: parse error: ^ expects a number 0, 1, or 2
3221 3220 [255]
3222 3221
3223 3222 $ log 'branchpoint()~-1'
3224 3223 abort: revision in set has more than one child!
3225 3224 [255]
3226 3225
3227 3226 Bogus function gets suggestions
3228 3227 $ log 'add()'
3229 3228 hg: parse error: unknown identifier: add
3230 3229 (did you mean adds?)
3231 3230 [255]
3232 3231 $ log 'added()'
3233 3232 hg: parse error: unknown identifier: added
3234 3233 (did you mean adds?)
3235 3234 [255]
3236 3235 $ log 'remo()'
3237 3236 hg: parse error: unknown identifier: remo
3238 3237 (did you mean one of remote, removes?)
3239 3238 [255]
3240 3239 $ log 'babar()'
3241 3240 hg: parse error: unknown identifier: babar
3242 3241 [255]
3243 3242
3244 3243 Bogus function with a similar internal name doesn't suggest the internal name
3245 3244 $ log 'matches()'
3246 3245 hg: parse error: unknown identifier: matches
3247 3246 (did you mean matching?)
3248 3247 [255]
3249 3248
3250 3249 Undocumented functions aren't suggested as similar either
3251 3250 $ log 'tagged2()'
3252 3251 hg: parse error: unknown identifier: tagged2
3253 3252 [255]
3254 3253
3255 3254 multiple revspecs
3256 3255
3257 3256 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
3258 3257 8
3259 3258 9
3260 3259 4
3261 3260 5
3262 3261 6
3263 3262 7
3264 3263
3265 3264 test usage in revpair (with "+")
3266 3265
3267 3266 (real pair)
3268 3267
3269 3268 $ hg diff -r 'tip^^' -r 'tip'
3270 3269 diff -r 2326846efdab -r 24286f4ae135 .hgtags
3271 3270 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3272 3271 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
3273 3272 @@ -0,0 +1,1 @@
3274 3273 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3275 3274 $ hg diff -r 'tip^^::tip'
3276 3275 diff -r 2326846efdab -r 24286f4ae135 .hgtags
3277 3276 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3278 3277 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
3279 3278 @@ -0,0 +1,1 @@
3280 3279 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3281 3280
3282 3281 (single rev)
3283 3282
3284 3283 $ hg diff -r 'tip^' -r 'tip^'
3285 3284 $ hg diff -r 'tip^:tip^'
3286 3285
3287 3286 (single rev that does not looks like a range)
3288 3287
3289 3288 $ hg diff -r 'tip^::tip^ or tip^'
3290 3289 diff -r d5d0dcbdc4d9 .hgtags
3291 3290 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3292 3291 +++ b/.hgtags * (glob)
3293 3292 @@ -0,0 +1,1 @@
3294 3293 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3295 3294 $ hg diff -r 'tip^ or tip^'
3296 3295 diff -r d5d0dcbdc4d9 .hgtags
3297 3296 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3298 3297 +++ b/.hgtags * (glob)
3299 3298 @@ -0,0 +1,1 @@
3300 3299 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
3301 3300
3302 3301 (no rev)
3303 3302
3304 3303 $ hg diff -r 'author("babar") or author("celeste")'
3305 3304 abort: empty revision range
3306 3305 [255]
3307 3306
3308 3307 aliases:
3309 3308
3310 3309 $ echo '[revsetalias]' >> .hg/hgrc
3311 3310 $ echo 'm = merge()' >> .hg/hgrc
3312 3311 (revset aliases can override builtin revsets)
3313 3312 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
3314 3313 $ echo 'sincem = descendants(m)' >> .hg/hgrc
3315 3314 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
3316 3315 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
3317 3316 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
3318 3317
3319 3318 $ try m
3320 3319 ('symbol', 'm')
3321 3320 * expanded:
3322 3321 (func
3323 3322 ('symbol', 'merge')
3324 3323 None)
3325 3324 * set:
3326 3325 <filteredset
3327 3326 <fullreposet+ 0:10>,
3328 3327 <merge>>
3329 3328 6
3330 3329
3331 3330 $ HGPLAIN=1
3332 3331 $ export HGPLAIN
3333 3332 $ try m
3334 3333 ('symbol', 'm')
3335 3334 abort: unknown revision 'm'!
3336 3335 [255]
3337 3336
3338 3337 $ HGPLAINEXCEPT=revsetalias
3339 3338 $ export HGPLAINEXCEPT
3340 3339 $ try m
3341 3340 ('symbol', 'm')
3342 3341 * expanded:
3343 3342 (func
3344 3343 ('symbol', 'merge')
3345 3344 None)
3346 3345 * set:
3347 3346 <filteredset
3348 3347 <fullreposet+ 0:10>,
3349 3348 <merge>>
3350 3349 6
3351 3350
3352 3351 $ unset HGPLAIN
3353 3352 $ unset HGPLAINEXCEPT
3354 3353
3355 3354 $ try 'p2(.)'
3356 3355 (func
3357 3356 ('symbol', 'p2')
3358 3357 ('symbol', '.'))
3359 3358 * expanded:
3360 3359 (func
3361 3360 ('symbol', 'p1')
3362 3361 ('symbol', '.'))
3363 3362 * set:
3364 3363 <baseset+ [8]>
3365 3364 8
3366 3365
3367 3366 $ HGPLAIN=1
3368 3367 $ export HGPLAIN
3369 3368 $ try 'p2(.)'
3370 3369 (func
3371 3370 ('symbol', 'p2')
3372 3371 ('symbol', '.'))
3373 3372 * set:
3374 3373 <baseset+ []>
3375 3374
3376 3375 $ HGPLAINEXCEPT=revsetalias
3377 3376 $ export HGPLAINEXCEPT
3378 3377 $ try 'p2(.)'
3379 3378 (func
3380 3379 ('symbol', 'p2')
3381 3380 ('symbol', '.'))
3382 3381 * expanded:
3383 3382 (func
3384 3383 ('symbol', 'p1')
3385 3384 ('symbol', '.'))
3386 3385 * set:
3387 3386 <baseset+ [8]>
3388 3387 8
3389 3388
3390 3389 $ unset HGPLAIN
3391 3390 $ unset HGPLAINEXCEPT
3392 3391
3393 3392 test alias recursion
3394 3393
3395 3394 $ try sincem
3396 3395 ('symbol', 'sincem')
3397 3396 * expanded:
3398 3397 (func
3399 3398 ('symbol', 'descendants')
3400 3399 (func
3401 3400 ('symbol', 'merge')
3402 3401 None))
3403 3402 * set:
3404 3403 <addset+
3405 3404 <filteredset
3406 3405 <fullreposet+ 0:10>,
3407 3406 <merge>>,
3408 3407 <generatorset+>>
3409 3408 6
3410 3409 7
3411 3410
3412 3411 test infinite recursion
3413 3412
3414 3413 $ echo 'recurse1 = recurse2' >> .hg/hgrc
3415 3414 $ echo 'recurse2 = recurse1' >> .hg/hgrc
3416 3415 $ try recurse1
3417 3416 ('symbol', 'recurse1')
3418 3417 hg: parse error: infinite expansion of revset alias "recurse1" detected
3419 3418 [255]
3420 3419
3421 3420 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
3422 3421 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
3423 3422 $ try "level2(level1(1, 2), 3)"
3424 3423 (func
3425 3424 ('symbol', 'level2')
3426 3425 (list
3427 3426 (func
3428 3427 ('symbol', 'level1')
3429 3428 (list
3430 3429 ('symbol', '1')
3431 3430 ('symbol', '2')))
3432 3431 ('symbol', '3')))
3433 3432 * expanded:
3434 3433 (or
3435 3434 (list
3436 3435 ('symbol', '3')
3437 3436 (or
3438 3437 (list
3439 3438 ('symbol', '1')
3440 3439 ('symbol', '2')))))
3441 3440 * set:
3442 3441 <addset
3443 3442 <baseset [3]>,
3444 3443 <baseset [1, 2]>>
3445 3444 3
3446 3445 1
3447 3446 2
3448 3447
3449 3448 test nesting and variable passing
3450 3449
3451 3450 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
3452 3451 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
3453 3452 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
3454 3453 $ try 'nested(2:5)'
3455 3454 (func
3456 3455 ('symbol', 'nested')
3457 3456 (range
3458 3457 ('symbol', '2')
3459 3458 ('symbol', '5')))
3460 3459 * expanded:
3461 3460 (func
3462 3461 ('symbol', 'max')
3463 3462 (range
3464 3463 ('symbol', '2')
3465 3464 ('symbol', '5')))
3466 3465 * set:
3467 3466 <baseset
3468 3467 <max
3469 3468 <fullreposet+ 0:10>,
3470 3469 <spanset+ 2:6>>>
3471 3470 5
3472 3471
3473 3472 test chained `or` operations are flattened at parsing phase
3474 3473
3475 3474 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
3476 3475 $ try 'chainedorops(0:1, 1:2, 2:3)'
3477 3476 (func
3478 3477 ('symbol', 'chainedorops')
3479 3478 (list
3480 3479 (range
3481 3480 ('symbol', '0')
3482 3481 ('symbol', '1'))
3483 3482 (range
3484 3483 ('symbol', '1')
3485 3484 ('symbol', '2'))
3486 3485 (range
3487 3486 ('symbol', '2')
3488 3487 ('symbol', '3'))))
3489 3488 * expanded:
3490 3489 (or
3491 3490 (list
3492 3491 (range
3493 3492 ('symbol', '0')
3494 3493 ('symbol', '1'))
3495 3494 (range
3496 3495 ('symbol', '1')
3497 3496 ('symbol', '2'))
3498 3497 (range
3499 3498 ('symbol', '2')
3500 3499 ('symbol', '3'))))
3501 3500 * set:
3502 3501 <addset
3503 3502 <spanset+ 0:2>,
3504 3503 <addset
3505 3504 <spanset+ 1:3>,
3506 3505 <spanset+ 2:4>>>
3507 3506 0
3508 3507 1
3509 3508 2
3510 3509 3
3511 3510
3512 3511 test variable isolation, variable placeholders are rewritten as string
3513 3512 then parsed and matched again as string. Check they do not leak too
3514 3513 far away.
3515 3514
3516 3515 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
3517 3516 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
3518 3517 $ try 'callinjection(2:5)'
3519 3518 (func
3520 3519 ('symbol', 'callinjection')
3521 3520 (range
3522 3521 ('symbol', '2')
3523 3522 ('symbol', '5')))
3524 3523 * expanded:
3525 3524 (func
3526 3525 ('symbol', 'descendants')
3527 3526 (func
3528 3527 ('symbol', 'max')
3529 3528 ('string', '$1')))
3530 3529 abort: unknown revision '$1'!
3531 3530 [255]
3532 3531
3533 3532 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
3534 3533 but 'all()' should never be substituted to '0()'.
3535 3534
3536 3535 $ echo 'universe = all()' >> .hg/hgrc
3537 3536 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
3538 3537 $ try 'shadowall(0)'
3539 3538 (func
3540 3539 ('symbol', 'shadowall')
3541 3540 ('symbol', '0'))
3542 3541 * expanded:
3543 3542 (and
3544 3543 ('symbol', '0')
3545 3544 (func
3546 3545 ('symbol', 'all')
3547 3546 None))
3548 3547 * set:
3549 3548 <filteredset
3550 3549 <baseset [0]>,
3551 3550 <spanset+ 0:10>>
3552 3551 0
3553 3552
3554 3553 test unknown reference:
3555 3554
3556 3555 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
3557 3556 (func
3558 3557 ('symbol', 'unknownref')
3559 3558 ('symbol', '0'))
3560 3559 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
3561 3560 [255]
3562 3561
3563 3562 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
3564 3563 ('symbol', 'tip')
3565 3564 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
3566 3565 * set:
3567 3566 <baseset [9]>
3568 3567 9
3569 3568
3570 3569 $ try 'tip'
3571 3570 ('symbol', 'tip')
3572 3571 * set:
3573 3572 <baseset [9]>
3574 3573 9
3575 3574
3576 3575 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
3577 3576 ('symbol', 'tip')
3578 3577 warning: bad declaration of revset alias "bad name": at 4: invalid token
3579 3578 * set:
3580 3579 <baseset [9]>
3581 3580 9
3582 3581 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
3583 3582 $ try 'strictreplacing("foo", tip)'
3584 3583 (func
3585 3584 ('symbol', 'strictreplacing')
3586 3585 (list
3587 3586 ('string', 'foo')
3588 3587 ('symbol', 'tip')))
3589 3588 * expanded:
3590 3589 (or
3591 3590 (list
3592 3591 ('symbol', 'tip')
3593 3592 (func
3594 3593 ('symbol', 'desc')
3595 3594 ('string', '$1'))))
3596 3595 * set:
3597 3596 <addset
3598 3597 <baseset [9]>,
3599 3598 <filteredset
3600 3599 <fullreposet+ 0:10>,
3601 3600 <desc '$1'>>>
3602 3601 9
3603 3602
3604 3603 $ try 'd(2:5)'
3605 3604 (func
3606 3605 ('symbol', 'd')
3607 3606 (range
3608 3607 ('symbol', '2')
3609 3608 ('symbol', '5')))
3610 3609 * expanded:
3611 3610 (func
3612 3611 ('symbol', 'reverse')
3613 3612 (func
3614 3613 ('symbol', 'sort')
3615 3614 (list
3616 3615 (range
3617 3616 ('symbol', '2')
3618 3617 ('symbol', '5'))
3619 3618 ('symbol', 'date'))))
3620 3619 * set:
3621 3620 <baseset [4, 5, 3, 2]>
3622 3621 4
3623 3622 5
3624 3623 3
3625 3624 2
3626 3625 $ try 'rs(2 or 3, date)'
3627 3626 (func
3628 3627 ('symbol', 'rs')
3629 3628 (list
3630 3629 (or
3631 3630 (list
3632 3631 ('symbol', '2')
3633 3632 ('symbol', '3')))
3634 3633 ('symbol', 'date')))
3635 3634 * expanded:
3636 3635 (func
3637 3636 ('symbol', 'reverse')
3638 3637 (func
3639 3638 ('symbol', 'sort')
3640 3639 (list
3641 3640 (or
3642 3641 (list
3643 3642 ('symbol', '2')
3644 3643 ('symbol', '3')))
3645 3644 ('symbol', 'date'))))
3646 3645 * set:
3647 3646 <baseset [3, 2]>
3648 3647 3
3649 3648 2
3650 3649 $ try 'rs()'
3651 3650 (func
3652 3651 ('symbol', 'rs')
3653 3652 None)
3654 3653 hg: parse error: invalid number of arguments: 0
3655 3654 [255]
3656 3655 $ try 'rs(2)'
3657 3656 (func
3658 3657 ('symbol', 'rs')
3659 3658 ('symbol', '2'))
3660 3659 hg: parse error: invalid number of arguments: 1
3661 3660 [255]
3662 3661 $ try 'rs(2, data, 7)'
3663 3662 (func
3664 3663 ('symbol', 'rs')
3665 3664 (list
3666 3665 ('symbol', '2')
3667 3666 ('symbol', 'data')
3668 3667 ('symbol', '7')))
3669 3668 hg: parse error: invalid number of arguments: 3
3670 3669 [255]
3671 3670 $ try 'rs4(2 or 3, x, x, date)'
3672 3671 (func
3673 3672 ('symbol', 'rs4')
3674 3673 (list
3675 3674 (or
3676 3675 (list
3677 3676 ('symbol', '2')
3678 3677 ('symbol', '3')))
3679 3678 ('symbol', 'x')
3680 3679 ('symbol', 'x')
3681 3680 ('symbol', 'date')))
3682 3681 * expanded:
3683 3682 (func
3684 3683 ('symbol', 'reverse')
3685 3684 (func
3686 3685 ('symbol', 'sort')
3687 3686 (list
3688 3687 (or
3689 3688 (list
3690 3689 ('symbol', '2')
3691 3690 ('symbol', '3')))
3692 3691 ('symbol', 'date'))))
3693 3692 * set:
3694 3693 <baseset [3, 2]>
3695 3694 3
3696 3695 2
3697 3696
3698 3697 issue4553: check that revset aliases override existing hash prefix
3699 3698
3700 3699 $ hg log -qr e
3701 3700 6:e0cc66ef77e8
3702 3701
3703 3702 $ hg log -qr e --config revsetalias.e="all()"
3704 3703 0:2785f51eece5
3705 3704 1:d75937da8da0
3706 3705 2:5ed5505e9f1c
3707 3706 3:8528aa5637f2
3708 3707 4:2326846efdab
3709 3708 5:904fa392b941
3710 3709 6:e0cc66ef77e8
3711 3710 7:013af1973af4
3712 3711 8:d5d0dcbdc4d9
3713 3712 9:24286f4ae135
3714 3713
3715 3714 $ hg log -qr e: --config revsetalias.e="0"
3716 3715 0:2785f51eece5
3717 3716 1:d75937da8da0
3718 3717 2:5ed5505e9f1c
3719 3718 3:8528aa5637f2
3720 3719 4:2326846efdab
3721 3720 5:904fa392b941
3722 3721 6:e0cc66ef77e8
3723 3722 7:013af1973af4
3724 3723 8:d5d0dcbdc4d9
3725 3724 9:24286f4ae135
3726 3725
3727 3726 $ hg log -qr :e --config revsetalias.e="9"
3728 3727 0:2785f51eece5
3729 3728 1:d75937da8da0
3730 3729 2:5ed5505e9f1c
3731 3730 3:8528aa5637f2
3732 3731 4:2326846efdab
3733 3732 5:904fa392b941
3734 3733 6:e0cc66ef77e8
3735 3734 7:013af1973af4
3736 3735 8:d5d0dcbdc4d9
3737 3736 9:24286f4ae135
3738 3737
3739 3738 $ hg log -qr e:
3740 3739 6:e0cc66ef77e8
3741 3740 7:013af1973af4
3742 3741 8:d5d0dcbdc4d9
3743 3742 9:24286f4ae135
3744 3743
3745 3744 $ hg log -qr :e
3746 3745 0:2785f51eece5
3747 3746 1:d75937da8da0
3748 3747 2:5ed5505e9f1c
3749 3748 3:8528aa5637f2
3750 3749 4:2326846efdab
3751 3750 5:904fa392b941
3752 3751 6:e0cc66ef77e8
3753 3752
3754 3753 issue2549 - correct optimizations
3755 3754
3756 3755 $ try 'limit(1 or 2 or 3, 2) and not 2'
3757 3756 (and
3758 3757 (func
3759 3758 ('symbol', 'limit')
3760 3759 (list
3761 3760 (or
3762 3761 (list
3763 3762 ('symbol', '1')
3764 3763 ('symbol', '2')
3765 3764 ('symbol', '3')))
3766 3765 ('symbol', '2')))
3767 3766 (not
3768 3767 ('symbol', '2')))
3769 3768 * set:
3770 3769 <filteredset
3771 3770 <baseset [1, 2]>,
3772 3771 <not
3773 3772 <baseset [2]>>>
3774 3773 1
3775 3774 $ try 'max(1 or 2) and not 2'
3776 3775 (and
3777 3776 (func
3778 3777 ('symbol', 'max')
3779 3778 (or
3780 3779 (list
3781 3780 ('symbol', '1')
3782 3781 ('symbol', '2'))))
3783 3782 (not
3784 3783 ('symbol', '2')))
3785 3784 * set:
3786 3785 <filteredset
3787 3786 <baseset
3788 3787 <max
3789 3788 <fullreposet+ 0:10>,
3790 3789 <baseset [1, 2]>>>,
3791 3790 <not
3792 3791 <baseset [2]>>>
3793 3792 $ try 'min(1 or 2) and not 1'
3794 3793 (and
3795 3794 (func
3796 3795 ('symbol', 'min')
3797 3796 (or
3798 3797 (list
3799 3798 ('symbol', '1')
3800 3799 ('symbol', '2'))))
3801 3800 (not
3802 3801 ('symbol', '1')))
3803 3802 * set:
3804 3803 <filteredset
3805 3804 <baseset
3806 3805 <min
3807 3806 <fullreposet+ 0:10>,
3808 3807 <baseset [1, 2]>>>,
3809 3808 <not
3810 3809 <baseset [1]>>>
3811 3810 $ try 'last(1 or 2, 1) and not 2'
3812 3811 (and
3813 3812 (func
3814 3813 ('symbol', 'last')
3815 3814 (list
3816 3815 (or
3817 3816 (list
3818 3817 ('symbol', '1')
3819 3818 ('symbol', '2')))
3820 3819 ('symbol', '1')))
3821 3820 (not
3822 3821 ('symbol', '2')))
3823 3822 * set:
3824 3823 <filteredset
3825 3824 <baseset [2]>,
3826 3825 <not
3827 3826 <baseset [2]>>>
3828 3827
3829 3828 issue4289 - ordering of built-ins
3830 3829 $ hg log -M -q -r 3:2
3831 3830 3:8528aa5637f2
3832 3831 2:5ed5505e9f1c
3833 3832
3834 3833 test revsets started with 40-chars hash (issue3669)
3835 3834
3836 3835 $ ISSUE3669_TIP=`hg tip --template '{node}'`
3837 3836 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
3838 3837 9
3839 3838 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
3840 3839 8
3841 3840
3842 3841 test or-ed indirect predicates (issue3775)
3843 3842
3844 3843 $ log '6 or 6^1' | sort
3845 3844 5
3846 3845 6
3847 3846 $ log '6^1 or 6' | sort
3848 3847 5
3849 3848 6
3850 3849 $ log '4 or 4~1' | sort
3851 3850 2
3852 3851 4
3853 3852 $ log '4~1 or 4' | sort
3854 3853 2
3855 3854 4
3856 3855 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
3857 3856 0
3858 3857 1
3859 3858 2
3860 3859 3
3861 3860 4
3862 3861 5
3863 3862 6
3864 3863 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
3865 3864 0
3866 3865 1
3867 3866 2
3868 3867 3
3869 3868 4
3870 3869 5
3871 3870 6
3872 3871
3873 3872 tests for 'remote()' predicate:
3874 3873 #. (csets in remote) (id) (remote)
3875 3874 1. less than local current branch "default"
3876 3875 2. same with local specified "default"
3877 3876 3. more than local specified specified
3878 3877
3879 3878 $ hg clone --quiet -U . ../remote3
3880 3879 $ cd ../remote3
3881 3880 $ hg update -q 7
3882 3881 $ echo r > r
3883 3882 $ hg ci -Aqm 10
3884 3883 $ log 'remote()'
3885 3884 7
3886 3885 $ log 'remote("a-b-c-")'
3887 3886 2
3888 3887 $ cd ../repo
3889 3888 $ log 'remote(".a.b.c.", "../remote3")'
3890 3889
3891 3890 tests for concatenation of strings/symbols by "##"
3892 3891
3893 3892 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
3894 3893 (_concat
3895 3894 (_concat
3896 3895 (_concat
3897 3896 ('symbol', '278')
3898 3897 ('string', '5f5'))
3899 3898 ('symbol', '1ee'))
3900 3899 ('string', 'ce5'))
3901 3900 * concatenated:
3902 3901 ('string', '2785f51eece5')
3903 3902 * set:
3904 3903 <baseset [0]>
3905 3904 0
3906 3905
3907 3906 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
3908 3907 $ try "cat4(278, '5f5', 1ee, 'ce5')"
3909 3908 (func
3910 3909 ('symbol', 'cat4')
3911 3910 (list
3912 3911 ('symbol', '278')
3913 3912 ('string', '5f5')
3914 3913 ('symbol', '1ee')
3915 3914 ('string', 'ce5')))
3916 3915 * expanded:
3917 3916 (_concat
3918 3917 (_concat
3919 3918 (_concat
3920 3919 ('symbol', '278')
3921 3920 ('string', '5f5'))
3922 3921 ('symbol', '1ee'))
3923 3922 ('string', 'ce5'))
3924 3923 * concatenated:
3925 3924 ('string', '2785f51eece5')
3926 3925 * set:
3927 3926 <baseset [0]>
3928 3927 0
3929 3928
3930 3929 (check concatenation in alias nesting)
3931 3930
3932 3931 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
3933 3932 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
3934 3933 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
3935 3934 0
3936 3935
3937 3936 (check operator priority)
3938 3937
3939 3938 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
3940 3939 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
3941 3940 0
3942 3941 4
3943 3942
3944 3943 $ cd ..
3945 3944
3946 3945 prepare repository that has "default" branches of multiple roots
3947 3946
3948 3947 $ hg init namedbranch
3949 3948 $ cd namedbranch
3950 3949
3951 3950 $ echo default0 >> a
3952 3951 $ hg ci -Aqm0
3953 3952 $ echo default1 >> a
3954 3953 $ hg ci -m1
3955 3954
3956 3955 $ hg branch -q stable
3957 3956 $ echo stable2 >> a
3958 3957 $ hg ci -m2
3959 3958 $ echo stable3 >> a
3960 3959 $ hg ci -m3
3961 3960
3962 3961 $ hg update -q null
3963 3962 $ echo default4 >> a
3964 3963 $ hg ci -Aqm4
3965 3964 $ echo default5 >> a
3966 3965 $ hg ci -m5
3967 3966
3968 3967 "null" revision belongs to "default" branch (issue4683)
3969 3968
3970 3969 $ log 'branch(null)'
3971 3970 0
3972 3971 1
3973 3972 4
3974 3973 5
3975 3974
3976 3975 "null" revision belongs to "default" branch, but it shouldn't appear in set
3977 3976 unless explicitly specified (issue4682)
3978 3977
3979 3978 $ log 'children(branch(default))'
3980 3979 1
3981 3980 2
3982 3981 5
3983 3982
3984 3983 $ cd ..
3985 3984
3986 3985 test author/desc/keyword in problematic encoding
3987 3986 # unicode: cp932:
3988 3987 # u30A2 0x83 0x41(= 'A')
3989 3988 # u30C2 0x83 0x61(= 'a')
3990 3989
3991 3990 $ hg init problematicencoding
3992 3991 $ cd problematicencoding
3993 3992
3994 3993 $ python > setup.sh <<EOF
3995 3994 > print u'''
3996 3995 > echo a > text
3997 3996 > hg add text
3998 3997 > hg --encoding utf-8 commit -u '\u30A2' -m none
3999 3998 > echo b > text
4000 3999 > hg --encoding utf-8 commit -u '\u30C2' -m none
4001 4000 > echo c > text
4002 4001 > hg --encoding utf-8 commit -u none -m '\u30A2'
4003 4002 > echo d > text
4004 4003 > hg --encoding utf-8 commit -u none -m '\u30C2'
4005 4004 > '''.encode('utf-8')
4006 4005 > EOF
4007 4006 $ sh < setup.sh
4008 4007
4009 4008 test in problematic encoding
4010 4009 $ python > test.sh <<EOF
4011 4010 > print u'''
4012 4011 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
4013 4012 > echo ====
4014 4013 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
4015 4014 > echo ====
4016 4015 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
4017 4016 > echo ====
4018 4017 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
4019 4018 > echo ====
4020 4019 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
4021 4020 > echo ====
4022 4021 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
4023 4022 > '''.encode('cp932')
4024 4023 > EOF
4025 4024 $ sh < test.sh
4026 4025 0
4027 4026 ====
4028 4027 1
4029 4028 ====
4030 4029 2
4031 4030 ====
4032 4031 3
4033 4032 ====
4034 4033 0
4035 4034 2
4036 4035 ====
4037 4036 1
4038 4037 3
4039 4038
4040 4039 test error message of bad revset
4041 4040 $ hg log -r 'foo\\'
4042 4041 hg: parse error at 3: syntax error in revset 'foo\\'
4043 4042 [255]
4044 4043
4045 4044 $ cd ..
4046 4045
4047 4046 Test that revset predicate of extension isn't loaded at failure of
4048 4047 loading it
4049 4048
4050 4049 $ cd repo
4051 4050
4052 4051 $ cat <<EOF > $TESTTMP/custompredicate.py
4053 4052 > from mercurial import error, registrar, revset
4054 4053 >
4055 4054 > revsetpredicate = registrar.revsetpredicate()
4056 4055 >
4057 4056 > @revsetpredicate('custom1()')
4058 4057 > def custom1(repo, subset, x):
4059 4058 > return revset.baseset([1])
4060 4059 >
4061 4060 > raise error.Abort('intentional failure of loading extension')
4062 4061 > EOF
4063 4062 $ cat <<EOF > .hg/hgrc
4064 4063 > [extensions]
4065 4064 > custompredicate = $TESTTMP/custompredicate.py
4066 4065 > EOF
4067 4066
4068 4067 $ hg debugrevspec "custom1()"
4069 4068 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
4070 4069 hg: parse error: unknown identifier: custom1
4071 4070 [255]
4072 4071
4073 4072 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now