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