##// END OF EJS Templates
merge with stable
Yuya Nishihara -
r39864:85a474ad merge default
parent child Browse files
Show More
@@ -1,2290 +1,2282 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 diffutil,
17 17 encoding,
18 18 error,
19 19 hbisect,
20 20 match as matchmod,
21 21 node,
22 22 obsolete as obsmod,
23 23 obsutil,
24 24 pathutil,
25 25 phases,
26 26 pycompat,
27 27 registrar,
28 28 repoview,
29 29 revsetlang,
30 30 scmutil,
31 31 smartset,
32 32 stack as stackmod,
33 33 util,
34 34 )
35 35 from .utils import (
36 36 dateutil,
37 37 stringutil,
38 38 )
39 39
40 40 # helpers for processing parsed tree
41 41 getsymbol = revsetlang.getsymbol
42 42 getstring = revsetlang.getstring
43 43 getinteger = revsetlang.getinteger
44 44 getboolean = revsetlang.getboolean
45 45 getlist = revsetlang.getlist
46 46 getrange = revsetlang.getrange
47 47 getargs = revsetlang.getargs
48 48 getargsdict = revsetlang.getargsdict
49 49
50 50 baseset = smartset.baseset
51 51 generatorset = smartset.generatorset
52 52 spanset = smartset.spanset
53 53 fullreposet = smartset.fullreposet
54 54
55 55 # Constants for ordering requirement, used in getset():
56 56 #
57 57 # If 'define', any nested functions and operations MAY change the ordering of
58 58 # the entries in the set (but if changes the ordering, it MUST ALWAYS change
59 59 # it). If 'follow', any nested functions and operations MUST take the ordering
60 60 # specified by the first operand to the '&' operator.
61 61 #
62 62 # For instance,
63 63 #
64 64 # X & (Y | Z)
65 65 # ^ ^^^^^^^
66 66 # | follow
67 67 # define
68 68 #
69 69 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
70 70 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
71 71 #
72 72 # 'any' means the order doesn't matter. For instance,
73 73 #
74 74 # (X & !Y) | ancestors(Z)
75 75 # ^ ^
76 76 # any any
77 77 #
78 78 # For 'X & !Y', 'X' decides the order and 'Y' is subtracted from 'X', so the
79 79 # order of 'Y' does not matter. For 'ancestors(Z)', Z's order does not matter
80 80 # since 'ancestors' does not care about the order of its argument.
81 81 #
82 82 # Currently, most revsets do not care about the order, so 'define' is
83 83 # equivalent to 'follow' for them, and the resulting order is based on the
84 84 # 'subset' parameter passed down to them:
85 85 #
86 86 # m = revset.match(...)
87 87 # m(repo, subset, order=defineorder)
88 88 # ^^^^^^
89 89 # For most revsets, 'define' means using the order this subset provides
90 90 #
91 91 # There are a few revsets that always redefine the order if 'define' is
92 92 # specified: 'sort(X)', 'reverse(X)', 'x:y'.
93 93 anyorder = 'any' # don't care the order, could be even random-shuffled
94 94 defineorder = 'define' # ALWAYS redefine, or ALWAYS follow the current order
95 95 followorder = 'follow' # MUST follow the current order
96 96
97 97 # helpers
98 98
99 99 def getset(repo, subset, x, order=defineorder):
100 100 if not x:
101 101 raise error.ParseError(_("missing argument"))
102 102 return methods[x[0]](repo, subset, *x[1:], order=order)
103 103
104 104 def _getrevsource(repo, r):
105 105 extra = repo[r].extra()
106 106 for label in ('source', 'transplant_source', 'rebase_source'):
107 107 if label in extra:
108 108 try:
109 109 return repo[extra[label]].rev()
110 110 except error.RepoLookupError:
111 111 pass
112 112 return None
113 113
114 114 def _sortedb(xs):
115 115 return sorted(pycompat.rapply(pycompat.maybebytestr, xs))
116 116
117 117 # operator methods
118 118
119 119 def stringset(repo, subset, x, order):
120 120 if not x:
121 121 raise error.ParseError(_("empty string is not a valid revision"))
122 122 x = scmutil.intrev(scmutil.revsymbol(repo, x))
123 123 if (x in subset
124 124 or x == node.nullrev and isinstance(subset, fullreposet)):
125 125 return baseset([x])
126 126 return baseset()
127 127
128 128 def rangeset(repo, subset, x, y, order):
129 129 m = getset(repo, fullreposet(repo), x)
130 130 n = getset(repo, fullreposet(repo), y)
131 131
132 132 if not m or not n:
133 133 return baseset()
134 134 return _makerangeset(repo, subset, m.first(), n.last(), order)
135 135
136 136 def rangeall(repo, subset, x, order):
137 137 assert x is None
138 138 return _makerangeset(repo, subset, 0, repo.changelog.tiprev(), order)
139 139
140 140 def rangepre(repo, subset, y, order):
141 141 # ':y' can't be rewritten to '0:y' since '0' may be hidden
142 142 n = getset(repo, fullreposet(repo), y)
143 143 if not n:
144 144 return baseset()
145 145 return _makerangeset(repo, subset, 0, n.last(), order)
146 146
147 147 def rangepost(repo, subset, x, order):
148 148 m = getset(repo, fullreposet(repo), x)
149 149 if not m:
150 150 return baseset()
151 151 return _makerangeset(repo, subset, m.first(), repo.changelog.tiprev(),
152 152 order)
153 153
154 154 def _makerangeset(repo, subset, m, n, order):
155 155 if m == n:
156 156 r = baseset([m])
157 157 elif n == node.wdirrev:
158 158 r = spanset(repo, m, len(repo)) + baseset([n])
159 159 elif m == node.wdirrev:
160 160 r = baseset([m]) + spanset(repo, repo.changelog.tiprev(), n - 1)
161 161 elif m < n:
162 162 r = spanset(repo, m, n + 1)
163 163 else:
164 164 r = spanset(repo, m, n - 1)
165 165
166 166 if order == defineorder:
167 167 return r & subset
168 168 else:
169 169 # carrying the sorting over when possible would be more efficient
170 170 return subset & r
171 171
172 172 def dagrange(repo, subset, x, y, order):
173 173 r = fullreposet(repo)
174 174 xs = dagop.reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
175 175 includepath=True)
176 176 return subset & xs
177 177
178 178 def andset(repo, subset, x, y, order):
179 179 if order == anyorder:
180 180 yorder = anyorder
181 181 else:
182 182 yorder = followorder
183 183 return getset(repo, getset(repo, subset, x, order), y, yorder)
184 184
185 185 def andsmallyset(repo, subset, x, y, order):
186 186 # 'andsmally(x, y)' is equivalent to 'and(x, y)', but faster when y is small
187 187 if order == anyorder:
188 188 yorder = anyorder
189 189 else:
190 190 yorder = followorder
191 191 return getset(repo, getset(repo, subset, y, yorder), x, order)
192 192
193 193 def differenceset(repo, subset, x, y, order):
194 194 return getset(repo, subset, x, order) - getset(repo, subset, y, anyorder)
195 195
196 196 def _orsetlist(repo, subset, xs, order):
197 197 assert xs
198 198 if len(xs) == 1:
199 199 return getset(repo, subset, xs[0], order)
200 200 p = len(xs) // 2
201 201 a = _orsetlist(repo, subset, xs[:p], order)
202 202 b = _orsetlist(repo, subset, xs[p:], order)
203 203 return a + b
204 204
205 205 def orset(repo, subset, x, order):
206 206 xs = getlist(x)
207 207 if not xs:
208 208 return baseset()
209 209 if order == followorder:
210 210 # slow path to take the subset order
211 211 return subset & _orsetlist(repo, fullreposet(repo), xs, anyorder)
212 212 else:
213 213 return _orsetlist(repo, subset, xs, order)
214 214
215 215 def notset(repo, subset, x, order):
216 216 return subset - getset(repo, subset, x, anyorder)
217 217
218 218 def relationset(repo, subset, x, y, order):
219 219 raise error.ParseError(_("can't use a relation in this context"))
220 220
221 221 def relsubscriptset(repo, subset, x, y, z, order):
222 222 # this is pretty basic implementation of 'x#y[z]' operator, still
223 223 # experimental so undocumented. see the wiki for further ideas.
224 224 # https://www.mercurial-scm.org/wiki/RevsetOperatorPlan
225 225 rel = getsymbol(y)
226 226 n = getinteger(z, _("relation subscript must be an integer"))
227 227
228 228 # TODO: perhaps this should be a table of relation functions
229 229 if rel in ('g', 'generations'):
230 230 # TODO: support range, rewrite tests, and drop startdepth argument
231 231 # from ancestors() and descendants() predicates
232 232 if n <= 0:
233 233 n = -n
234 234 return _ancestors(repo, subset, x, startdepth=n, stopdepth=n + 1)
235 235 else:
236 236 return _descendants(repo, subset, x, startdepth=n, stopdepth=n + 1)
237 237
238 238 raise error.UnknownIdentifier(rel, ['generations'])
239 239
240 240 def subscriptset(repo, subset, x, y, order):
241 241 raise error.ParseError(_("can't use a subscript in this context"))
242 242
243 243 def listset(repo, subset, *xs, **opts):
244 244 raise error.ParseError(_("can't use a list in this context"),
245 245 hint=_('see \'hg help "revsets.x or y"\''))
246 246
247 247 def keyvaluepair(repo, subset, k, v, order):
248 248 raise error.ParseError(_("can't use a key-value pair in this context"))
249 249
250 250 def func(repo, subset, a, b, order):
251 251 f = getsymbol(a)
252 252 if f in symbols:
253 253 func = symbols[f]
254 254 if getattr(func, '_takeorder', False):
255 255 return func(repo, subset, b, order)
256 256 return func(repo, subset, b)
257 257
258 258 keep = lambda fn: getattr(fn, '__doc__', None) is not None
259 259
260 260 syms = [s for (s, fn) in symbols.items() if keep(fn)]
261 261 raise error.UnknownIdentifier(f, syms)
262 262
263 263 # functions
264 264
265 265 # symbols are callables like:
266 266 # fn(repo, subset, x)
267 267 # with:
268 268 # repo - current repository instance
269 269 # subset - of revisions to be examined
270 270 # x - argument in tree form
271 271 symbols = revsetlang.symbols
272 272
273 273 # symbols which can't be used for a DoS attack for any given input
274 274 # (e.g. those which accept regexes as plain strings shouldn't be included)
275 275 # functions that just return a lot of changesets (like all) don't count here
276 276 safesymbols = set()
277 277
278 278 predicate = registrar.revsetpredicate()
279 279
280 280 @predicate('_destupdate')
281 281 def _destupdate(repo, subset, x):
282 282 # experimental revset for update destination
283 283 args = getargsdict(x, 'limit', 'clean')
284 284 return subset & baseset([destutil.destupdate(repo,
285 285 **pycompat.strkwargs(args))[0]])
286 286
287 287 @predicate('_destmerge')
288 288 def _destmerge(repo, subset, x):
289 289 # experimental revset for merge destination
290 290 sourceset = None
291 291 if x is not None:
292 292 sourceset = getset(repo, fullreposet(repo), x)
293 293 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
294 294
295 295 @predicate('adds(pattern)', safe=True, weight=30)
296 296 def adds(repo, subset, x):
297 297 """Changesets that add a file matching pattern.
298 298
299 299 The pattern without explicit kind like ``glob:`` is expected to be
300 300 relative to the current directory and match against a file or a
301 301 directory.
302 302 """
303 303 # i18n: "adds" is a keyword
304 304 pat = getstring(x, _("adds requires a pattern"))
305 305 return checkstatus(repo, subset, pat, 1)
306 306
307 307 @predicate('ancestor(*changeset)', safe=True, weight=0.5)
308 308 def ancestor(repo, subset, x):
309 309 """A greatest common ancestor of the changesets.
310 310
311 311 Accepts 0 or more changesets.
312 312 Will return empty list when passed no args.
313 313 Greatest common ancestor of a single changeset is that changeset.
314 314 """
315 315 reviter = iter(orset(repo, fullreposet(repo), x, order=anyorder))
316 316 try:
317 317 anc = repo[next(reviter)]
318 318 except StopIteration:
319 319 return baseset()
320 320 for r in reviter:
321 321 anc = anc.ancestor(repo[r])
322 322
323 323 r = scmutil.intrev(anc)
324 324 if r in subset:
325 325 return baseset([r])
326 326 return baseset()
327 327
328 328 def _ancestors(repo, subset, x, followfirst=False, startdepth=None,
329 329 stopdepth=None):
330 330 heads = getset(repo, fullreposet(repo), x)
331 331 if not heads:
332 332 return baseset()
333 333 s = dagop.revancestors(repo, heads, followfirst, startdepth, stopdepth)
334 334 return subset & s
335 335
336 336 @predicate('ancestors(set[, depth])', safe=True)
337 337 def ancestors(repo, subset, x):
338 338 """Changesets that are ancestors of changesets in set, including the
339 339 given changesets themselves.
340 340
341 341 If depth is specified, the result only includes changesets up to
342 342 the specified generation.
343 343 """
344 344 # startdepth is for internal use only until we can decide the UI
345 345 args = getargsdict(x, 'ancestors', 'set depth startdepth')
346 346 if 'set' not in args:
347 347 # i18n: "ancestors" is a keyword
348 348 raise error.ParseError(_('ancestors takes at least 1 argument'))
349 349 startdepth = stopdepth = None
350 350 if 'startdepth' in args:
351 351 n = getinteger(args['startdepth'],
352 352 "ancestors expects an integer startdepth")
353 353 if n < 0:
354 354 raise error.ParseError("negative startdepth")
355 355 startdepth = n
356 356 if 'depth' in args:
357 357 # i18n: "ancestors" is a keyword
358 358 n = getinteger(args['depth'], _("ancestors expects an integer depth"))
359 359 if n < 0:
360 360 raise error.ParseError(_("negative depth"))
361 361 stopdepth = n + 1
362 362 return _ancestors(repo, subset, args['set'],
363 363 startdepth=startdepth, stopdepth=stopdepth)
364 364
365 365 @predicate('_firstancestors', safe=True)
366 366 def _firstancestors(repo, subset, x):
367 367 # ``_firstancestors(set)``
368 368 # Like ``ancestors(set)`` but follows only the first parents.
369 369 return _ancestors(repo, subset, x, followfirst=True)
370 370
371 371 def _childrenspec(repo, subset, x, n, order):
372 372 """Changesets that are the Nth child of a changeset
373 373 in set.
374 374 """
375 375 cs = set()
376 376 for r in getset(repo, fullreposet(repo), x):
377 377 for i in range(n):
378 378 c = repo[r].children()
379 379 if len(c) == 0:
380 380 break
381 381 if len(c) > 1:
382 382 raise error.RepoLookupError(
383 383 _("revision in set has more than one child"))
384 384 r = c[0].rev()
385 385 else:
386 386 cs.add(r)
387 387 return subset & cs
388 388
389 389 def ancestorspec(repo, subset, x, n, order):
390 390 """``set~n``
391 391 Changesets that are the Nth ancestor (first parents only) of a changeset
392 392 in set.
393 393 """
394 394 n = getinteger(n, _("~ expects a number"))
395 395 if n < 0:
396 396 # children lookup
397 397 return _childrenspec(repo, subset, x, -n, order)
398 398 ps = set()
399 399 cl = repo.changelog
400 400 for r in getset(repo, fullreposet(repo), x):
401 401 for i in range(n):
402 402 try:
403 403 r = cl.parentrevs(r)[0]
404 404 except error.WdirUnsupported:
405 405 r = repo[r].parents()[0].rev()
406 406 ps.add(r)
407 407 return subset & ps
408 408
409 409 @predicate('author(string)', safe=True, weight=10)
410 410 def author(repo, subset, x):
411 411 """Alias for ``user(string)``.
412 412 """
413 413 # i18n: "author" is a keyword
414 414 n = getstring(x, _("author requires a string"))
415 415 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
416 416 return subset.filter(lambda x: matcher(repo[x].user()),
417 417 condrepr=('<user %r>', n))
418 418
419 419 @predicate('bisect(string)', safe=True)
420 420 def bisect(repo, subset, x):
421 421 """Changesets marked in the specified bisect status:
422 422
423 423 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
424 424 - ``goods``, ``bads`` : csets topologically good/bad
425 425 - ``range`` : csets taking part in the bisection
426 426 - ``pruned`` : csets that are goods, bads or skipped
427 427 - ``untested`` : csets whose fate is yet unknown
428 428 - ``ignored`` : csets ignored due to DAG topology
429 429 - ``current`` : the cset currently being bisected
430 430 """
431 431 # i18n: "bisect" is a keyword
432 432 status = getstring(x, _("bisect requires a string")).lower()
433 433 state = set(hbisect.get(repo, status))
434 434 return subset & state
435 435
436 436 # Backward-compatibility
437 437 # - no help entry so that we do not advertise it any more
438 438 @predicate('bisected', safe=True)
439 439 def bisected(repo, subset, x):
440 440 return bisect(repo, subset, x)
441 441
442 442 @predicate('bookmark([name])', safe=True)
443 443 def bookmark(repo, subset, x):
444 444 """The named bookmark or all bookmarks.
445 445
446 446 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
447 447 """
448 448 # i18n: "bookmark" is a keyword
449 449 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
450 450 if args:
451 451 bm = getstring(args[0],
452 452 # i18n: "bookmark" is a keyword
453 453 _('the argument to bookmark must be a string'))
454 454 kind, pattern, matcher = stringutil.stringmatcher(bm)
455 455 bms = set()
456 456 if kind == 'literal':
457 457 if bm == pattern:
458 458 pattern = repo._bookmarks.expandname(pattern)
459 459 bmrev = repo._bookmarks.get(pattern, None)
460 460 if not bmrev:
461 461 raise error.RepoLookupError(_("bookmark '%s' does not exist")
462 462 % pattern)
463 463 bms.add(repo[bmrev].rev())
464 464 else:
465 465 matchrevs = set()
466 466 for name, bmrev in repo._bookmarks.iteritems():
467 467 if matcher(name):
468 468 matchrevs.add(bmrev)
469 469 if not matchrevs:
470 470 raise error.RepoLookupError(_("no bookmarks exist"
471 471 " that match '%s'") % pattern)
472 472 for bmrev in matchrevs:
473 473 bms.add(repo[bmrev].rev())
474 474 else:
475 475 bms = {repo[r].rev() for r in repo._bookmarks.values()}
476 476 bms -= {node.nullrev}
477 477 return subset & bms
478 478
479 479 @predicate('branch(string or set)', safe=True, weight=10)
480 480 def branch(repo, subset, x):
481 481 """
482 482 All changesets belonging to the given branch or the branches of the given
483 483 changesets.
484 484
485 485 Pattern matching is supported for `string`. See
486 486 :hg:`help revisions.patterns`.
487 487 """
488 488 getbi = repo.revbranchcache().branchinfo
489 489 def getbranch(r):
490 490 try:
491 491 return getbi(r)[0]
492 492 except error.WdirUnsupported:
493 493 return repo[r].branch()
494 494
495 495 try:
496 496 b = getstring(x, '')
497 497 except error.ParseError:
498 498 # not a string, but another revspec, e.g. tip()
499 499 pass
500 500 else:
501 501 kind, pattern, matcher = stringutil.stringmatcher(b)
502 502 if kind == 'literal':
503 503 # note: falls through to the revspec case if no branch with
504 504 # this name exists and pattern kind is not specified explicitly
505 505 if pattern in repo.branchmap():
506 506 return subset.filter(lambda r: matcher(getbranch(r)),
507 507 condrepr=('<branch %r>', b))
508 508 if b.startswith('literal:'):
509 509 raise error.RepoLookupError(_("branch '%s' does not exist")
510 510 % pattern)
511 511 else:
512 512 return subset.filter(lambda r: matcher(getbranch(r)),
513 513 condrepr=('<branch %r>', b))
514 514
515 515 s = getset(repo, fullreposet(repo), x)
516 516 b = set()
517 517 for r in s:
518 518 b.add(getbranch(r))
519 519 c = s.__contains__
520 520 return subset.filter(lambda r: c(r) or getbranch(r) in b,
521 521 condrepr=lambda: '<branch %r>' % _sortedb(b))
522 522
523 523 @predicate('phasedivergent()', safe=True)
524 524 def phasedivergent(repo, subset, x):
525 525 """Mutable changesets marked as successors of public changesets.
526 526
527 527 Only non-public and non-obsolete changesets can be `phasedivergent`.
528 528 (EXPERIMENTAL)
529 529 """
530 530 # i18n: "phasedivergent" is a keyword
531 531 getargs(x, 0, 0, _("phasedivergent takes no arguments"))
532 532 phasedivergent = obsmod.getrevs(repo, 'phasedivergent')
533 533 return subset & phasedivergent
534 534
535 535 @predicate('bundle()', safe=True)
536 536 def bundle(repo, subset, x):
537 537 """Changesets in the bundle.
538 538
539 539 Bundle must be specified by the -R option."""
540 540
541 541 try:
542 542 bundlerevs = repo.changelog.bundlerevs
543 543 except AttributeError:
544 544 raise error.Abort(_("no bundle provided - specify with -R"))
545 545 return subset & bundlerevs
546 546
547 547 def checkstatus(repo, subset, pat, field):
548 548 hasset = matchmod.patkind(pat) == 'set'
549 549
550 550 mcache = [None]
551 551 def matches(x):
552 552 c = repo[x]
553 553 if not mcache[0] or hasset:
554 554 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
555 555 m = mcache[0]
556 556 fname = None
557 557 if not m.anypats() and len(m.files()) == 1:
558 558 fname = m.files()[0]
559 559 if fname is not None:
560 560 if fname not in c.files():
561 561 return False
562 562 else:
563 563 for f in c.files():
564 564 if m(f):
565 565 break
566 566 else:
567 567 return False
568 568 files = repo.status(c.p1().node(), c.node())[field]
569 569 if fname is not None:
570 570 if fname in files:
571 571 return True
572 572 else:
573 573 for f in files:
574 574 if m(f):
575 575 return True
576 576
577 577 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
578 578
579 579 def _children(repo, subset, parentset):
580 580 if not parentset:
581 581 return baseset()
582 582 cs = set()
583 583 pr = repo.changelog.parentrevs
584 584 minrev = parentset.min()
585 585 nullrev = node.nullrev
586 586 for r in subset:
587 587 if r <= minrev:
588 588 continue
589 589 p1, p2 = pr(r)
590 590 if p1 in parentset:
591 591 cs.add(r)
592 592 if p2 != nullrev and p2 in parentset:
593 593 cs.add(r)
594 594 return baseset(cs)
595 595
596 596 @predicate('children(set)', safe=True)
597 597 def children(repo, subset, x):
598 598 """Child changesets of changesets in set.
599 599 """
600 600 s = getset(repo, fullreposet(repo), x)
601 601 cs = _children(repo, subset, s)
602 602 return subset & cs
603 603
604 604 @predicate('closed()', safe=True, weight=10)
605 605 def closed(repo, subset, x):
606 606 """Changeset is closed.
607 607 """
608 608 # i18n: "closed" is a keyword
609 609 getargs(x, 0, 0, _("closed takes no arguments"))
610 610 return subset.filter(lambda r: repo[r].closesbranch(),
611 611 condrepr='<branch closed>')
612 612
613 613 # for internal use
614 614 @predicate('_commonancestorheads(set)', safe=True)
615 615 def _commonancestorheads(repo, subset, x):
616 616 # This is an internal method is for quickly calculating "heads(::x and
617 617 # ::y)"
618 618
619 # These greatest common ancestors are the same ones that the consesus bid
619 # These greatest common ancestors are the same ones that the consensus bid
620 620 # merge will find.
621 h = heads(repo, fullreposet(repo), x, anyorder)
621 startrevs = getset(repo, fullreposet(repo), x, order=anyorder)
622 622
623 ancs = repo.changelog._commonancestorsheads(*list(h))
623 ancs = repo.changelog._commonancestorsheads(*list(startrevs))
624 624 return subset & baseset(ancs)
625 625
626 626 @predicate('commonancestors(set)', safe=True)
627 627 def commonancestors(repo, subset, x):
628 """Returns all common ancestors of the set.
629
630 This method is for calculating "::x and ::y" (i.e. all the ancestors that
631 are common to both x and y) in an easy and optimized way. We can't quite
632 use "::head()" because that revset returns "::x + ::y + ..." for each head
633 in the repo (whereas we want "::x *and* ::y").
634
628 """Changesets that are ancestors of every changeset in set.
635 629 """
636 # only wants the heads of the set passed in
637 h = heads(repo, fullreposet(repo), x, anyorder)
638 if not h:
630 startrevs = getset(repo, fullreposet(repo), x, order=anyorder)
631 if not startrevs:
639 632 return baseset()
640 for r in h:
633 for r in startrevs:
641 634 subset &= dagop.revancestors(repo, baseset([r]))
642
643 635 return subset
644 636
645 637 @predicate('contains(pattern)', weight=100)
646 638 def contains(repo, subset, x):
647 639 """The revision's manifest contains a file matching pattern (but might not
648 640 modify it). See :hg:`help patterns` for information about file patterns.
649 641
650 642 The pattern without explicit kind like ``glob:`` is expected to be
651 643 relative to the current directory and match against a file exactly
652 644 for efficiency.
653 645 """
654 646 # i18n: "contains" is a keyword
655 647 pat = getstring(x, _("contains requires a pattern"))
656 648
657 649 def matches(x):
658 650 if not matchmod.patkind(pat):
659 651 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
660 652 if pats in repo[x]:
661 653 return True
662 654 else:
663 655 c = repo[x]
664 656 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
665 657 for f in c.manifest():
666 658 if m(f):
667 659 return True
668 660 return False
669 661
670 662 return subset.filter(matches, condrepr=('<contains %r>', pat))
671 663
672 664 @predicate('converted([id])', safe=True)
673 665 def converted(repo, subset, x):
674 666 """Changesets converted from the given identifier in the old repository if
675 667 present, or all converted changesets if no identifier is specified.
676 668 """
677 669
678 670 # There is exactly no chance of resolving the revision, so do a simple
679 671 # string compare and hope for the best
680 672
681 673 rev = None
682 674 # i18n: "converted" is a keyword
683 675 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
684 676 if l:
685 677 # i18n: "converted" is a keyword
686 678 rev = getstring(l[0], _('converted requires a revision'))
687 679
688 680 def _matchvalue(r):
689 681 source = repo[r].extra().get('convert_revision', None)
690 682 return source is not None and (rev is None or source.startswith(rev))
691 683
692 684 return subset.filter(lambda r: _matchvalue(r),
693 685 condrepr=('<converted %r>', rev))
694 686
695 687 @predicate('date(interval)', safe=True, weight=10)
696 688 def date(repo, subset, x):
697 689 """Changesets within the interval, see :hg:`help dates`.
698 690 """
699 691 # i18n: "date" is a keyword
700 692 ds = getstring(x, _("date requires a string"))
701 693 dm = dateutil.matchdate(ds)
702 694 return subset.filter(lambda x: dm(repo[x].date()[0]),
703 695 condrepr=('<date %r>', ds))
704 696
705 697 @predicate('desc(string)', safe=True, weight=10)
706 698 def desc(repo, subset, x):
707 699 """Search commit message for string. The match is case-insensitive.
708 700
709 701 Pattern matching is supported for `string`. See
710 702 :hg:`help revisions.patterns`.
711 703 """
712 704 # i18n: "desc" is a keyword
713 705 ds = getstring(x, _("desc requires a string"))
714 706
715 707 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
716 708
717 709 return subset.filter(lambda r: matcher(repo[r].description()),
718 710 condrepr=('<desc %r>', ds))
719 711
720 712 def _descendants(repo, subset, x, followfirst=False, startdepth=None,
721 713 stopdepth=None):
722 714 roots = getset(repo, fullreposet(repo), x)
723 715 if not roots:
724 716 return baseset()
725 717 s = dagop.revdescendants(repo, roots, followfirst, startdepth, stopdepth)
726 718 return subset & s
727 719
728 720 @predicate('descendants(set[, depth])', safe=True)
729 721 def descendants(repo, subset, x):
730 722 """Changesets which are descendants of changesets in set, including the
731 723 given changesets themselves.
732 724
733 725 If depth is specified, the result only includes changesets up to
734 726 the specified generation.
735 727 """
736 728 # startdepth is for internal use only until we can decide the UI
737 729 args = getargsdict(x, 'descendants', 'set depth startdepth')
738 730 if 'set' not in args:
739 731 # i18n: "descendants" is a keyword
740 732 raise error.ParseError(_('descendants takes at least 1 argument'))
741 733 startdepth = stopdepth = None
742 734 if 'startdepth' in args:
743 735 n = getinteger(args['startdepth'],
744 736 "descendants expects an integer startdepth")
745 737 if n < 0:
746 738 raise error.ParseError("negative startdepth")
747 739 startdepth = n
748 740 if 'depth' in args:
749 741 # i18n: "descendants" is a keyword
750 742 n = getinteger(args['depth'], _("descendants expects an integer depth"))
751 743 if n < 0:
752 744 raise error.ParseError(_("negative depth"))
753 745 stopdepth = n + 1
754 746 return _descendants(repo, subset, args['set'],
755 747 startdepth=startdepth, stopdepth=stopdepth)
756 748
757 749 @predicate('_firstdescendants', safe=True)
758 750 def _firstdescendants(repo, subset, x):
759 751 # ``_firstdescendants(set)``
760 752 # Like ``descendants(set)`` but follows only the first parents.
761 753 return _descendants(repo, subset, x, followfirst=True)
762 754
763 755 @predicate('destination([set])', safe=True, weight=10)
764 756 def destination(repo, subset, x):
765 757 """Changesets that were created by a graft, transplant or rebase operation,
766 758 with the given revisions specified as the source. Omitting the optional set
767 759 is the same as passing all().
768 760 """
769 761 if x is not None:
770 762 sources = getset(repo, fullreposet(repo), x)
771 763 else:
772 764 sources = fullreposet(repo)
773 765
774 766 dests = set()
775 767
776 768 # subset contains all of the possible destinations that can be returned, so
777 769 # iterate over them and see if their source(s) were provided in the arg set.
778 770 # Even if the immediate src of r is not in the arg set, src's source (or
779 771 # further back) may be. Scanning back further than the immediate src allows
780 772 # transitive transplants and rebases to yield the same results as transitive
781 773 # grafts.
782 774 for r in subset:
783 775 src = _getrevsource(repo, r)
784 776 lineage = None
785 777
786 778 while src is not None:
787 779 if lineage is None:
788 780 lineage = list()
789 781
790 782 lineage.append(r)
791 783
792 784 # The visited lineage is a match if the current source is in the arg
793 785 # set. Since every candidate dest is visited by way of iterating
794 786 # subset, any dests further back in the lineage will be tested by a
795 787 # different iteration over subset. Likewise, if the src was already
796 788 # selected, the current lineage can be selected without going back
797 789 # further.
798 790 if src in sources or src in dests:
799 791 dests.update(lineage)
800 792 break
801 793
802 794 r = src
803 795 src = _getrevsource(repo, r)
804 796
805 797 return subset.filter(dests.__contains__,
806 798 condrepr=lambda: '<destination %r>' % _sortedb(dests))
807 799
808 800 @predicate('contentdivergent()', safe=True)
809 801 def contentdivergent(repo, subset, x):
810 802 """
811 803 Final successors of changesets with an alternative set of final
812 804 successors. (EXPERIMENTAL)
813 805 """
814 806 # i18n: "contentdivergent" is a keyword
815 807 getargs(x, 0, 0, _("contentdivergent takes no arguments"))
816 808 contentdivergent = obsmod.getrevs(repo, 'contentdivergent')
817 809 return subset & contentdivergent
818 810
819 811 @predicate('extdata(source)', safe=False, weight=100)
820 812 def extdata(repo, subset, x):
821 813 """Changesets in the specified extdata source. (EXPERIMENTAL)"""
822 814 # i18n: "extdata" is a keyword
823 815 args = getargsdict(x, 'extdata', 'source')
824 816 source = getstring(args.get('source'),
825 817 # i18n: "extdata" is a keyword
826 818 _('extdata takes at least 1 string argument'))
827 819 data = scmutil.extdatasource(repo, source)
828 820 return subset & baseset(data)
829 821
830 822 @predicate('extinct()', safe=True)
831 823 def extinct(repo, subset, x):
832 824 """Obsolete changesets with obsolete descendants only.
833 825 """
834 826 # i18n: "extinct" is a keyword
835 827 getargs(x, 0, 0, _("extinct takes no arguments"))
836 828 extincts = obsmod.getrevs(repo, 'extinct')
837 829 return subset & extincts
838 830
839 831 @predicate('extra(label, [value])', safe=True)
840 832 def extra(repo, subset, x):
841 833 """Changesets with the given label in the extra metadata, with the given
842 834 optional value.
843 835
844 836 Pattern matching is supported for `value`. See
845 837 :hg:`help revisions.patterns`.
846 838 """
847 839 args = getargsdict(x, 'extra', 'label value')
848 840 if 'label' not in args:
849 841 # i18n: "extra" is a keyword
850 842 raise error.ParseError(_('extra takes at least 1 argument'))
851 843 # i18n: "extra" is a keyword
852 844 label = getstring(args['label'], _('first argument to extra must be '
853 845 'a string'))
854 846 value = None
855 847
856 848 if 'value' in args:
857 849 # i18n: "extra" is a keyword
858 850 value = getstring(args['value'], _('second argument to extra must be '
859 851 'a string'))
860 852 kind, value, matcher = stringutil.stringmatcher(value)
861 853
862 854 def _matchvalue(r):
863 855 extra = repo[r].extra()
864 856 return label in extra and (value is None or matcher(extra[label]))
865 857
866 858 return subset.filter(lambda r: _matchvalue(r),
867 859 condrepr=('<extra[%r] %r>', label, value))
868 860
869 861 @predicate('filelog(pattern)', safe=True)
870 862 def filelog(repo, subset, x):
871 863 """Changesets connected to the specified filelog.
872 864
873 865 For performance reasons, visits only revisions mentioned in the file-level
874 866 filelog, rather than filtering through all changesets (much faster, but
875 867 doesn't include deletes or duplicate changes). For a slower, more accurate
876 868 result, use ``file()``.
877 869
878 870 The pattern without explicit kind like ``glob:`` is expected to be
879 871 relative to the current directory and match against a file exactly
880 872 for efficiency.
881 873
882 874 If some linkrev points to revisions filtered by the current repoview, we'll
883 875 work around it to return a non-filtered value.
884 876 """
885 877
886 878 # i18n: "filelog" is a keyword
887 879 pat = getstring(x, _("filelog requires a pattern"))
888 880 s = set()
889 881 cl = repo.changelog
890 882
891 883 if not matchmod.patkind(pat):
892 884 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
893 885 files = [f]
894 886 else:
895 887 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
896 888 files = (f for f in repo[None] if m(f))
897 889
898 890 for f in files:
899 891 fl = repo.file(f)
900 892 known = {}
901 893 scanpos = 0
902 894 for fr in list(fl):
903 895 fn = fl.node(fr)
904 896 if fn in known:
905 897 s.add(known[fn])
906 898 continue
907 899
908 900 lr = fl.linkrev(fr)
909 901 if lr in cl:
910 902 s.add(lr)
911 903 elif scanpos is not None:
912 904 # lowest matching changeset is filtered, scan further
913 905 # ahead in changelog
914 906 start = max(lr, scanpos) + 1
915 907 scanpos = None
916 908 for r in cl.revs(start):
917 909 # minimize parsing of non-matching entries
918 910 if f in cl.revision(r) and f in cl.readfiles(r):
919 911 try:
920 912 # try to use manifest delta fastpath
921 913 n = repo[r].filenode(f)
922 914 if n not in known:
923 915 if n == fn:
924 916 s.add(r)
925 917 scanpos = r
926 918 break
927 919 else:
928 920 known[n] = r
929 921 except error.ManifestLookupError:
930 922 # deletion in changelog
931 923 continue
932 924
933 925 return subset & s
934 926
935 927 @predicate('first(set, [n])', safe=True, takeorder=True, weight=0)
936 928 def first(repo, subset, x, order):
937 929 """An alias for limit().
938 930 """
939 931 return limit(repo, subset, x, order)
940 932
941 933 def _follow(repo, subset, x, name, followfirst=False):
942 934 args = getargsdict(x, name, 'file startrev')
943 935 revs = None
944 936 if 'startrev' in args:
945 937 revs = getset(repo, fullreposet(repo), args['startrev'])
946 938 if 'file' in args:
947 939 x = getstring(args['file'], _("%s expected a pattern") % name)
948 940 if revs is None:
949 941 revs = [None]
950 942 fctxs = []
951 943 for r in revs:
952 944 ctx = mctx = repo[r]
953 945 if r is None:
954 946 ctx = repo['.']
955 947 m = matchmod.match(repo.root, repo.getcwd(), [x],
956 948 ctx=mctx, default='path')
957 949 fctxs.extend(ctx[f].introfilectx() for f in ctx.manifest().walk(m))
958 950 s = dagop.filerevancestors(fctxs, followfirst)
959 951 else:
960 952 if revs is None:
961 953 revs = baseset([repo['.'].rev()])
962 954 s = dagop.revancestors(repo, revs, followfirst)
963 955
964 956 return subset & s
965 957
966 958 @predicate('follow([file[, startrev]])', safe=True)
967 959 def follow(repo, subset, x):
968 960 """
969 961 An alias for ``::.`` (ancestors of the working directory's first parent).
970 962 If file pattern is specified, the histories of files matching given
971 963 pattern in the revision given by startrev are followed, including copies.
972 964 """
973 965 return _follow(repo, subset, x, 'follow')
974 966
975 967 @predicate('_followfirst', safe=True)
976 968 def _followfirst(repo, subset, x):
977 969 # ``followfirst([file[, startrev]])``
978 970 # Like ``follow([file[, startrev]])`` but follows only the first parent
979 971 # of every revisions or files revisions.
980 972 return _follow(repo, subset, x, '_followfirst', followfirst=True)
981 973
982 974 @predicate('followlines(file, fromline:toline[, startrev=., descend=False])',
983 975 safe=True)
984 976 def followlines(repo, subset, x):
985 977 """Changesets modifying `file` in line range ('fromline', 'toline').
986 978
987 979 Line range corresponds to 'file' content at 'startrev' and should hence be
988 980 consistent with file size. If startrev is not specified, working directory's
989 981 parent is used.
990 982
991 983 By default, ancestors of 'startrev' are returned. If 'descend' is True,
992 984 descendants of 'startrev' are returned though renames are (currently) not
993 985 followed in this direction.
994 986 """
995 987 args = getargsdict(x, 'followlines', 'file *lines startrev descend')
996 988 if len(args['lines']) != 1:
997 989 raise error.ParseError(_("followlines requires a line range"))
998 990
999 991 rev = '.'
1000 992 if 'startrev' in args:
1001 993 revs = getset(repo, fullreposet(repo), args['startrev'])
1002 994 if len(revs) != 1:
1003 995 raise error.ParseError(
1004 996 # i18n: "followlines" is a keyword
1005 997 _("followlines expects exactly one revision"))
1006 998 rev = revs.last()
1007 999
1008 1000 pat = getstring(args['file'], _("followlines requires a pattern"))
1009 1001 # i18n: "followlines" is a keyword
1010 1002 msg = _("followlines expects exactly one file")
1011 1003 fname = scmutil.parsefollowlinespattern(repo, rev, pat, msg)
1012 1004 # i18n: "followlines" is a keyword
1013 1005 lr = getrange(args['lines'][0], _("followlines expects a line range"))
1014 1006 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
1015 1007 for a in lr]
1016 1008 fromline, toline = util.processlinerange(fromline, toline)
1017 1009
1018 1010 fctx = repo[rev].filectx(fname)
1019 1011 descend = False
1020 1012 if 'descend' in args:
1021 1013 descend = getboolean(args['descend'],
1022 1014 # i18n: "descend" is a keyword
1023 1015 _("descend argument must be a boolean"))
1024 1016 if descend:
1025 1017 rs = generatorset(
1026 1018 (c.rev() for c, _linerange
1027 1019 in dagop.blockdescendants(fctx, fromline, toline)),
1028 1020 iterasc=True)
1029 1021 else:
1030 1022 rs = generatorset(
1031 1023 (c.rev() for c, _linerange
1032 1024 in dagop.blockancestors(fctx, fromline, toline)),
1033 1025 iterasc=False)
1034 1026 return subset & rs
1035 1027
1036 1028 @predicate('all()', safe=True)
1037 1029 def getall(repo, subset, x):
1038 1030 """All changesets, the same as ``0:tip``.
1039 1031 """
1040 1032 # i18n: "all" is a keyword
1041 1033 getargs(x, 0, 0, _("all takes no arguments"))
1042 1034 return subset & spanset(repo) # drop "null" if any
1043 1035
1044 1036 @predicate('grep(regex)', weight=10)
1045 1037 def grep(repo, subset, x):
1046 1038 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1047 1039 to ensure special escape characters are handled correctly. Unlike
1048 1040 ``keyword(string)``, the match is case-sensitive.
1049 1041 """
1050 1042 try:
1051 1043 # i18n: "grep" is a keyword
1052 1044 gr = re.compile(getstring(x, _("grep requires a string")))
1053 1045 except re.error as e:
1054 1046 raise error.ParseError(
1055 1047 _('invalid match pattern: %s') % stringutil.forcebytestr(e))
1056 1048
1057 1049 def matches(x):
1058 1050 c = repo[x]
1059 1051 for e in c.files() + [c.user(), c.description()]:
1060 1052 if gr.search(e):
1061 1053 return True
1062 1054 return False
1063 1055
1064 1056 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1065 1057
1066 1058 @predicate('_matchfiles', safe=True)
1067 1059 def _matchfiles(repo, subset, x):
1068 1060 # _matchfiles takes a revset list of prefixed arguments:
1069 1061 #
1070 1062 # [p:foo, i:bar, x:baz]
1071 1063 #
1072 1064 # builds a match object from them and filters subset. Allowed
1073 1065 # prefixes are 'p:' for regular patterns, 'i:' for include
1074 1066 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1075 1067 # a revision identifier, or the empty string to reference the
1076 1068 # working directory, from which the match object is
1077 1069 # initialized. Use 'd:' to set the default matching mode, default
1078 1070 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1079 1071
1080 1072 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1081 1073 pats, inc, exc = [], [], []
1082 1074 rev, default = None, None
1083 1075 for arg in l:
1084 1076 s = getstring(arg, "_matchfiles requires string arguments")
1085 1077 prefix, value = s[:2], s[2:]
1086 1078 if prefix == 'p:':
1087 1079 pats.append(value)
1088 1080 elif prefix == 'i:':
1089 1081 inc.append(value)
1090 1082 elif prefix == 'x:':
1091 1083 exc.append(value)
1092 1084 elif prefix == 'r:':
1093 1085 if rev is not None:
1094 1086 raise error.ParseError('_matchfiles expected at most one '
1095 1087 'revision')
1096 1088 if value == '': # empty means working directory
1097 1089 rev = node.wdirrev
1098 1090 else:
1099 1091 rev = value
1100 1092 elif prefix == 'd:':
1101 1093 if default is not None:
1102 1094 raise error.ParseError('_matchfiles expected at most one '
1103 1095 'default mode')
1104 1096 default = value
1105 1097 else:
1106 1098 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1107 1099 if not default:
1108 1100 default = 'glob'
1109 1101 hasset = any(matchmod.patkind(p) == 'set' for p in pats + inc + exc)
1110 1102
1111 1103 mcache = [None]
1112 1104
1113 1105 # This directly read the changelog data as creating changectx for all
1114 1106 # revisions is quite expensive.
1115 1107 getfiles = repo.changelog.readfiles
1116 1108 wdirrev = node.wdirrev
1117 1109 def matches(x):
1118 1110 if x == wdirrev:
1119 1111 files = repo[x].files()
1120 1112 else:
1121 1113 files = getfiles(x)
1122 1114
1123 1115 if not mcache[0] or (hasset and rev is None):
1124 1116 r = x if rev is None else rev
1125 1117 mcache[0] = matchmod.match(repo.root, repo.getcwd(), pats,
1126 1118 include=inc, exclude=exc, ctx=repo[r],
1127 1119 default=default)
1128 1120 m = mcache[0]
1129 1121
1130 1122 for f in files:
1131 1123 if m(f):
1132 1124 return True
1133 1125 return False
1134 1126
1135 1127 return subset.filter(matches,
1136 1128 condrepr=('<matchfiles patterns=%r, include=%r '
1137 1129 'exclude=%r, default=%r, rev=%r>',
1138 1130 pats, inc, exc, default, rev))
1139 1131
1140 1132 @predicate('file(pattern)', safe=True, weight=10)
1141 1133 def hasfile(repo, subset, x):
1142 1134 """Changesets affecting files matched by pattern.
1143 1135
1144 1136 For a faster but less accurate result, consider using ``filelog()``
1145 1137 instead.
1146 1138
1147 1139 This predicate uses ``glob:`` as the default kind of pattern.
1148 1140 """
1149 1141 # i18n: "file" is a keyword
1150 1142 pat = getstring(x, _("file requires a pattern"))
1151 1143 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1152 1144
1153 1145 @predicate('head()', safe=True)
1154 1146 def head(repo, subset, x):
1155 1147 """Changeset is a named branch head.
1156 1148 """
1157 1149 # i18n: "head" is a keyword
1158 1150 getargs(x, 0, 0, _("head takes no arguments"))
1159 1151 hs = set()
1160 1152 cl = repo.changelog
1161 1153 for ls in repo.branchmap().itervalues():
1162 1154 hs.update(cl.rev(h) for h in ls)
1163 1155 return subset & baseset(hs)
1164 1156
1165 1157 @predicate('heads(set)', safe=True, takeorder=True)
1166 1158 def heads(repo, subset, x, order):
1167 1159 """Members of set with no children in set.
1168 1160 """
1169 1161 # argument set should never define order
1170 1162 if order == defineorder:
1171 1163 order = followorder
1172 1164 s = getset(repo, subset, x, order=order)
1173 1165 ps = parents(repo, subset, x)
1174 1166 return s - ps
1175 1167
1176 1168 @predicate('hidden()', safe=True)
1177 1169 def hidden(repo, subset, x):
1178 1170 """Hidden changesets.
1179 1171 """
1180 1172 # i18n: "hidden" is a keyword
1181 1173 getargs(x, 0, 0, _("hidden takes no arguments"))
1182 1174 hiddenrevs = repoview.filterrevs(repo, 'visible')
1183 1175 return subset & hiddenrevs
1184 1176
1185 1177 @predicate('keyword(string)', safe=True, weight=10)
1186 1178 def keyword(repo, subset, x):
1187 1179 """Search commit message, user name, and names of changed files for
1188 1180 string. The match is case-insensitive.
1189 1181
1190 1182 For a regular expression or case sensitive search of these fields, use
1191 1183 ``grep(regex)``.
1192 1184 """
1193 1185 # i18n: "keyword" is a keyword
1194 1186 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1195 1187
1196 1188 def matches(r):
1197 1189 c = repo[r]
1198 1190 return any(kw in encoding.lower(t)
1199 1191 for t in c.files() + [c.user(), c.description()])
1200 1192
1201 1193 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1202 1194
1203 1195 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True, weight=0)
1204 1196 def limit(repo, subset, x, order):
1205 1197 """First n members of set, defaulting to 1, starting from offset.
1206 1198 """
1207 1199 args = getargsdict(x, 'limit', 'set n offset')
1208 1200 if 'set' not in args:
1209 1201 # i18n: "limit" is a keyword
1210 1202 raise error.ParseError(_("limit requires one to three arguments"))
1211 1203 # i18n: "limit" is a keyword
1212 1204 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1213 1205 if lim < 0:
1214 1206 raise error.ParseError(_("negative number to select"))
1215 1207 # i18n: "limit" is a keyword
1216 1208 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1217 1209 if ofs < 0:
1218 1210 raise error.ParseError(_("negative offset"))
1219 1211 os = getset(repo, fullreposet(repo), args['set'])
1220 1212 ls = os.slice(ofs, ofs + lim)
1221 1213 if order == followorder and lim > 1:
1222 1214 return subset & ls
1223 1215 return ls & subset
1224 1216
1225 1217 @predicate('last(set, [n])', safe=True, takeorder=True)
1226 1218 def last(repo, subset, x, order):
1227 1219 """Last n members of set, defaulting to 1.
1228 1220 """
1229 1221 # i18n: "last" is a keyword
1230 1222 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1231 1223 lim = 1
1232 1224 if len(l) == 2:
1233 1225 # i18n: "last" is a keyword
1234 1226 lim = getinteger(l[1], _("last expects a number"))
1235 1227 if lim < 0:
1236 1228 raise error.ParseError(_("negative number to select"))
1237 1229 os = getset(repo, fullreposet(repo), l[0])
1238 1230 os.reverse()
1239 1231 ls = os.slice(0, lim)
1240 1232 if order == followorder and lim > 1:
1241 1233 return subset & ls
1242 1234 ls.reverse()
1243 1235 return ls & subset
1244 1236
1245 1237 @predicate('max(set)', safe=True)
1246 1238 def maxrev(repo, subset, x):
1247 1239 """Changeset with highest revision number in set.
1248 1240 """
1249 1241 os = getset(repo, fullreposet(repo), x)
1250 1242 try:
1251 1243 m = os.max()
1252 1244 if m in subset:
1253 1245 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1254 1246 except ValueError:
1255 1247 # os.max() throws a ValueError when the collection is empty.
1256 1248 # Same as python's max().
1257 1249 pass
1258 1250 return baseset(datarepr=('<max %r, %r>', subset, os))
1259 1251
1260 1252 @predicate('merge()', safe=True)
1261 1253 def merge(repo, subset, x):
1262 1254 """Changeset is a merge changeset.
1263 1255 """
1264 1256 # i18n: "merge" is a keyword
1265 1257 getargs(x, 0, 0, _("merge takes no arguments"))
1266 1258 cl = repo.changelog
1267 1259 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1268 1260 condrepr='<merge>')
1269 1261
1270 1262 @predicate('branchpoint()', safe=True)
1271 1263 def branchpoint(repo, subset, x):
1272 1264 """Changesets with more than one child.
1273 1265 """
1274 1266 # i18n: "branchpoint" is a keyword
1275 1267 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1276 1268 cl = repo.changelog
1277 1269 if not subset:
1278 1270 return baseset()
1279 1271 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1280 1272 # (and if it is not, it should.)
1281 1273 baserev = min(subset)
1282 1274 parentscount = [0]*(len(repo) - baserev)
1283 1275 for r in cl.revs(start=baserev + 1):
1284 1276 for p in cl.parentrevs(r):
1285 1277 if p >= baserev:
1286 1278 parentscount[p - baserev] += 1
1287 1279 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1288 1280 condrepr='<branchpoint>')
1289 1281
1290 1282 @predicate('min(set)', safe=True)
1291 1283 def minrev(repo, subset, x):
1292 1284 """Changeset with lowest revision number in set.
1293 1285 """
1294 1286 os = getset(repo, fullreposet(repo), x)
1295 1287 try:
1296 1288 m = os.min()
1297 1289 if m in subset:
1298 1290 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1299 1291 except ValueError:
1300 1292 # os.min() throws a ValueError when the collection is empty.
1301 1293 # Same as python's min().
1302 1294 pass
1303 1295 return baseset(datarepr=('<min %r, %r>', subset, os))
1304 1296
1305 1297 @predicate('modifies(pattern)', safe=True, weight=30)
1306 1298 def modifies(repo, subset, x):
1307 1299 """Changesets modifying files matched by pattern.
1308 1300
1309 1301 The pattern without explicit kind like ``glob:`` is expected to be
1310 1302 relative to the current directory and match against a file or a
1311 1303 directory.
1312 1304 """
1313 1305 # i18n: "modifies" is a keyword
1314 1306 pat = getstring(x, _("modifies requires a pattern"))
1315 1307 return checkstatus(repo, subset, pat, 0)
1316 1308
1317 1309 @predicate('named(namespace)')
1318 1310 def named(repo, subset, x):
1319 1311 """The changesets in a given namespace.
1320 1312
1321 1313 Pattern matching is supported for `namespace`. See
1322 1314 :hg:`help revisions.patterns`.
1323 1315 """
1324 1316 # i18n: "named" is a keyword
1325 1317 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1326 1318
1327 1319 ns = getstring(args[0],
1328 1320 # i18n: "named" is a keyword
1329 1321 _('the argument to named must be a string'))
1330 1322 kind, pattern, matcher = stringutil.stringmatcher(ns)
1331 1323 namespaces = set()
1332 1324 if kind == 'literal':
1333 1325 if pattern not in repo.names:
1334 1326 raise error.RepoLookupError(_("namespace '%s' does not exist")
1335 1327 % ns)
1336 1328 namespaces.add(repo.names[pattern])
1337 1329 else:
1338 1330 for name, ns in repo.names.iteritems():
1339 1331 if matcher(name):
1340 1332 namespaces.add(ns)
1341 1333 if not namespaces:
1342 1334 raise error.RepoLookupError(_("no namespace exists"
1343 1335 " that match '%s'") % pattern)
1344 1336
1345 1337 names = set()
1346 1338 for ns in namespaces:
1347 1339 for name in ns.listnames(repo):
1348 1340 if name not in ns.deprecated:
1349 1341 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1350 1342
1351 1343 names -= {node.nullrev}
1352 1344 return subset & names
1353 1345
1354 1346 @predicate('id(string)', safe=True)
1355 1347 def node_(repo, subset, x):
1356 1348 """Revision non-ambiguously specified by the given hex string prefix.
1357 1349 """
1358 1350 # i18n: "id" is a keyword
1359 1351 l = getargs(x, 1, 1, _("id requires one argument"))
1360 1352 # i18n: "id" is a keyword
1361 1353 n = getstring(l[0], _("id requires a string"))
1362 1354 if len(n) == 40:
1363 1355 try:
1364 1356 rn = repo.changelog.rev(node.bin(n))
1365 1357 except error.WdirUnsupported:
1366 1358 rn = node.wdirrev
1367 1359 except (LookupError, TypeError):
1368 1360 rn = None
1369 1361 else:
1370 1362 rn = None
1371 1363 try:
1372 1364 pm = scmutil.resolvehexnodeidprefix(repo, n)
1373 1365 if pm is not None:
1374 1366 rn = repo.changelog.rev(pm)
1375 1367 except LookupError:
1376 1368 pass
1377 1369 except error.WdirUnsupported:
1378 1370 rn = node.wdirrev
1379 1371
1380 1372 if rn is None:
1381 1373 return baseset()
1382 1374 result = baseset([rn])
1383 1375 return result & subset
1384 1376
1385 1377 @predicate('none()', safe=True)
1386 1378 def none(repo, subset, x):
1387 1379 """No changesets.
1388 1380 """
1389 1381 # i18n: "none" is a keyword
1390 1382 getargs(x, 0, 0, _("none takes no arguments"))
1391 1383 return baseset()
1392 1384
1393 1385 @predicate('obsolete()', safe=True)
1394 1386 def obsolete(repo, subset, x):
1395 1387 """Mutable changeset with a newer version."""
1396 1388 # i18n: "obsolete" is a keyword
1397 1389 getargs(x, 0, 0, _("obsolete takes no arguments"))
1398 1390 obsoletes = obsmod.getrevs(repo, 'obsolete')
1399 1391 return subset & obsoletes
1400 1392
1401 1393 @predicate('only(set, [set])', safe=True)
1402 1394 def only(repo, subset, x):
1403 1395 """Changesets that are ancestors of the first set that are not ancestors
1404 1396 of any other head in the repo. If a second set is specified, the result
1405 1397 is ancestors of the first set that are not ancestors of the second set
1406 1398 (i.e. ::<set1> - ::<set2>).
1407 1399 """
1408 1400 cl = repo.changelog
1409 1401 # i18n: "only" is a keyword
1410 1402 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1411 1403 include = getset(repo, fullreposet(repo), args[0])
1412 1404 if len(args) == 1:
1413 1405 if not include:
1414 1406 return baseset()
1415 1407
1416 1408 descendants = set(dagop.revdescendants(repo, include, False))
1417 1409 exclude = [rev for rev in cl.headrevs()
1418 1410 if not rev in descendants and not rev in include]
1419 1411 else:
1420 1412 exclude = getset(repo, fullreposet(repo), args[1])
1421 1413
1422 1414 results = set(cl.findmissingrevs(common=exclude, heads=include))
1423 1415 # XXX we should turn this into a baseset instead of a set, smartset may do
1424 1416 # some optimizations from the fact this is a baseset.
1425 1417 return subset & results
1426 1418
1427 1419 @predicate('origin([set])', safe=True)
1428 1420 def origin(repo, subset, x):
1429 1421 """
1430 1422 Changesets that were specified as a source for the grafts, transplants or
1431 1423 rebases that created the given revisions. Omitting the optional set is the
1432 1424 same as passing all(). If a changeset created by these operations is itself
1433 1425 specified as a source for one of these operations, only the source changeset
1434 1426 for the first operation is selected.
1435 1427 """
1436 1428 if x is not None:
1437 1429 dests = getset(repo, fullreposet(repo), x)
1438 1430 else:
1439 1431 dests = fullreposet(repo)
1440 1432
1441 1433 def _firstsrc(rev):
1442 1434 src = _getrevsource(repo, rev)
1443 1435 if src is None:
1444 1436 return None
1445 1437
1446 1438 while True:
1447 1439 prev = _getrevsource(repo, src)
1448 1440
1449 1441 if prev is None:
1450 1442 return src
1451 1443 src = prev
1452 1444
1453 1445 o = {_firstsrc(r) for r in dests}
1454 1446 o -= {None}
1455 1447 # XXX we should turn this into a baseset instead of a set, smartset may do
1456 1448 # some optimizations from the fact this is a baseset.
1457 1449 return subset & o
1458 1450
1459 1451 @predicate('outgoing([path])', safe=False, weight=10)
1460 1452 def outgoing(repo, subset, x):
1461 1453 """Changesets not found in the specified destination repository, or the
1462 1454 default push location.
1463 1455 """
1464 1456 # Avoid cycles.
1465 1457 from . import (
1466 1458 discovery,
1467 1459 hg,
1468 1460 )
1469 1461 # i18n: "outgoing" is a keyword
1470 1462 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1471 1463 # i18n: "outgoing" is a keyword
1472 1464 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1473 1465 if not dest:
1474 1466 # ui.paths.getpath() explicitly tests for None, not just a boolean
1475 1467 dest = None
1476 1468 path = repo.ui.paths.getpath(dest, default=('default-push', 'default'))
1477 1469 if not path:
1478 1470 raise error.Abort(_('default repository not configured!'),
1479 1471 hint=_("see 'hg help config.paths'"))
1480 1472 dest = path.pushloc or path.loc
1481 1473 branches = path.branch, []
1482 1474
1483 1475 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1484 1476 if revs:
1485 1477 revs = [repo.lookup(rev) for rev in revs]
1486 1478 other = hg.peer(repo, {}, dest)
1487 1479 repo.ui.pushbuffer()
1488 1480 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1489 1481 repo.ui.popbuffer()
1490 1482 cl = repo.changelog
1491 1483 o = {cl.rev(r) for r in outgoing.missing}
1492 1484 return subset & o
1493 1485
1494 1486 @predicate('p1([set])', safe=True)
1495 1487 def p1(repo, subset, x):
1496 1488 """First parent of changesets in set, or the working directory.
1497 1489 """
1498 1490 if x is None:
1499 1491 p = repo[x].p1().rev()
1500 1492 if p >= 0:
1501 1493 return subset & baseset([p])
1502 1494 return baseset()
1503 1495
1504 1496 ps = set()
1505 1497 cl = repo.changelog
1506 1498 for r in getset(repo, fullreposet(repo), x):
1507 1499 try:
1508 1500 ps.add(cl.parentrevs(r)[0])
1509 1501 except error.WdirUnsupported:
1510 1502 ps.add(repo[r].parents()[0].rev())
1511 1503 ps -= {node.nullrev}
1512 1504 # XXX we should turn this into a baseset instead of a set, smartset may do
1513 1505 # some optimizations from the fact this is a baseset.
1514 1506 return subset & ps
1515 1507
1516 1508 @predicate('p2([set])', safe=True)
1517 1509 def p2(repo, subset, x):
1518 1510 """Second parent of changesets in set, or the working directory.
1519 1511 """
1520 1512 if x is None:
1521 1513 ps = repo[x].parents()
1522 1514 try:
1523 1515 p = ps[1].rev()
1524 1516 if p >= 0:
1525 1517 return subset & baseset([p])
1526 1518 return baseset()
1527 1519 except IndexError:
1528 1520 return baseset()
1529 1521
1530 1522 ps = set()
1531 1523 cl = repo.changelog
1532 1524 for r in getset(repo, fullreposet(repo), x):
1533 1525 try:
1534 1526 ps.add(cl.parentrevs(r)[1])
1535 1527 except error.WdirUnsupported:
1536 1528 parents = repo[r].parents()
1537 1529 if len(parents) == 2:
1538 1530 ps.add(parents[1])
1539 1531 ps -= {node.nullrev}
1540 1532 # XXX we should turn this into a baseset instead of a set, smartset may do
1541 1533 # some optimizations from the fact this is a baseset.
1542 1534 return subset & ps
1543 1535
1544 1536 def parentpost(repo, subset, x, order):
1545 1537 return p1(repo, subset, x)
1546 1538
1547 1539 @predicate('parents([set])', safe=True)
1548 1540 def parents(repo, subset, x):
1549 1541 """
1550 1542 The set of all parents for all changesets in set, or the working directory.
1551 1543 """
1552 1544 if x is None:
1553 1545 ps = set(p.rev() for p in repo[x].parents())
1554 1546 else:
1555 1547 ps = set()
1556 1548 cl = repo.changelog
1557 1549 up = ps.update
1558 1550 parentrevs = cl.parentrevs
1559 1551 for r in getset(repo, fullreposet(repo), x):
1560 1552 try:
1561 1553 up(parentrevs(r))
1562 1554 except error.WdirUnsupported:
1563 1555 up(p.rev() for p in repo[r].parents())
1564 1556 ps -= {node.nullrev}
1565 1557 return subset & ps
1566 1558
1567 1559 def _phase(repo, subset, *targets):
1568 1560 """helper to select all rev in <targets> phases"""
1569 1561 return repo._phasecache.getrevset(repo, targets, subset)
1570 1562
1571 1563 @predicate('_phase(idx)', safe=True)
1572 1564 def phase(repo, subset, x):
1573 1565 l = getargs(x, 1, 1, ("_phase requires one argument"))
1574 1566 target = getinteger(l[0], ("_phase expects a number"))
1575 1567 return _phase(repo, subset, target)
1576 1568
1577 1569 @predicate('draft()', safe=True)
1578 1570 def draft(repo, subset, x):
1579 1571 """Changeset in draft phase."""
1580 1572 # i18n: "draft" is a keyword
1581 1573 getargs(x, 0, 0, _("draft takes no arguments"))
1582 1574 target = phases.draft
1583 1575 return _phase(repo, subset, target)
1584 1576
1585 1577 @predicate('secret()', safe=True)
1586 1578 def secret(repo, subset, x):
1587 1579 """Changeset in secret phase."""
1588 1580 # i18n: "secret" is a keyword
1589 1581 getargs(x, 0, 0, _("secret takes no arguments"))
1590 1582 target = phases.secret
1591 1583 return _phase(repo, subset, target)
1592 1584
1593 1585 @predicate('stack([revs])', safe=True)
1594 1586 def stack(repo, subset, x):
1595 1587 """Experimental revset for the stack of changesets or working directory
1596 1588 parent. (EXPERIMENTAL)
1597 1589 """
1598 1590 if x is None:
1599 1591 stacks = stackmod.getstack(repo, x)
1600 1592 else:
1601 1593 stacks = smartset.baseset([])
1602 1594 for revision in getset(repo, fullreposet(repo), x):
1603 1595 currentstack = stackmod.getstack(repo, revision)
1604 1596 stacks = stacks + currentstack
1605 1597
1606 1598 return subset & stacks
1607 1599
1608 1600 def parentspec(repo, subset, x, n, order):
1609 1601 """``set^0``
1610 1602 The set.
1611 1603 ``set^1`` (or ``set^``), ``set^2``
1612 1604 First or second parent, respectively, of all changesets in set.
1613 1605 """
1614 1606 try:
1615 1607 n = int(n[1])
1616 1608 if n not in (0, 1, 2):
1617 1609 raise ValueError
1618 1610 except (TypeError, ValueError):
1619 1611 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1620 1612 ps = set()
1621 1613 cl = repo.changelog
1622 1614 for r in getset(repo, fullreposet(repo), x):
1623 1615 if n == 0:
1624 1616 ps.add(r)
1625 1617 elif n == 1:
1626 1618 try:
1627 1619 ps.add(cl.parentrevs(r)[0])
1628 1620 except error.WdirUnsupported:
1629 1621 ps.add(repo[r].parents()[0].rev())
1630 1622 else:
1631 1623 try:
1632 1624 parents = cl.parentrevs(r)
1633 1625 if parents[1] != node.nullrev:
1634 1626 ps.add(parents[1])
1635 1627 except error.WdirUnsupported:
1636 1628 parents = repo[r].parents()
1637 1629 if len(parents) == 2:
1638 1630 ps.add(parents[1].rev())
1639 1631 return subset & ps
1640 1632
1641 1633 @predicate('present(set)', safe=True, takeorder=True)
1642 1634 def present(repo, subset, x, order):
1643 1635 """An empty set, if any revision in set isn't found; otherwise,
1644 1636 all revisions in set.
1645 1637
1646 1638 If any of specified revisions is not present in the local repository,
1647 1639 the query is normally aborted. But this predicate allows the query
1648 1640 to continue even in such cases.
1649 1641 """
1650 1642 try:
1651 1643 return getset(repo, subset, x, order)
1652 1644 except error.RepoLookupError:
1653 1645 return baseset()
1654 1646
1655 1647 # for internal use
1656 1648 @predicate('_notpublic', safe=True)
1657 1649 def _notpublic(repo, subset, x):
1658 1650 getargs(x, 0, 0, "_notpublic takes no arguments")
1659 1651 return _phase(repo, subset, phases.draft, phases.secret)
1660 1652
1661 1653 # for internal use
1662 1654 @predicate('_phaseandancestors(phasename, set)', safe=True)
1663 1655 def _phaseandancestors(repo, subset, x):
1664 1656 # equivalent to (phasename() & ancestors(set)) but more efficient
1665 1657 # phasename could be one of 'draft', 'secret', or '_notpublic'
1666 1658 args = getargs(x, 2, 2, "_phaseandancestors requires two arguments")
1667 1659 phasename = getsymbol(args[0])
1668 1660 s = getset(repo, fullreposet(repo), args[1])
1669 1661
1670 1662 draft = phases.draft
1671 1663 secret = phases.secret
1672 1664 phasenamemap = {
1673 1665 '_notpublic': draft,
1674 1666 'draft': draft, # follow secret's ancestors
1675 1667 'secret': secret,
1676 1668 }
1677 1669 if phasename not in phasenamemap:
1678 1670 raise error.ParseError('%r is not a valid phasename' % phasename)
1679 1671
1680 1672 minimalphase = phasenamemap[phasename]
1681 1673 getphase = repo._phasecache.phase
1682 1674
1683 1675 def cutfunc(rev):
1684 1676 return getphase(repo, rev) < minimalphase
1685 1677
1686 1678 revs = dagop.revancestors(repo, s, cutfunc=cutfunc)
1687 1679
1688 1680 if phasename == 'draft': # need to remove secret changesets
1689 1681 revs = revs.filter(lambda r: getphase(repo, r) == draft)
1690 1682 return subset & revs
1691 1683
1692 1684 @predicate('public()', safe=True)
1693 1685 def public(repo, subset, x):
1694 1686 """Changeset in public phase."""
1695 1687 # i18n: "public" is a keyword
1696 1688 getargs(x, 0, 0, _("public takes no arguments"))
1697 1689 return _phase(repo, subset, phases.public)
1698 1690
1699 1691 @predicate('remote([id [,path]])', safe=False)
1700 1692 def remote(repo, subset, x):
1701 1693 """Local revision that corresponds to the given identifier in a
1702 1694 remote repository, if present. Here, the '.' identifier is a
1703 1695 synonym for the current local branch.
1704 1696 """
1705 1697
1706 1698 from . import hg # avoid start-up nasties
1707 1699 # i18n: "remote" is a keyword
1708 1700 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1709 1701
1710 1702 q = '.'
1711 1703 if len(l) > 0:
1712 1704 # i18n: "remote" is a keyword
1713 1705 q = getstring(l[0], _("remote requires a string id"))
1714 1706 if q == '.':
1715 1707 q = repo['.'].branch()
1716 1708
1717 1709 dest = ''
1718 1710 if len(l) > 1:
1719 1711 # i18n: "remote" is a keyword
1720 1712 dest = getstring(l[1], _("remote requires a repository path"))
1721 1713 dest = repo.ui.expandpath(dest or 'default')
1722 1714 dest, branches = hg.parseurl(dest)
1723 1715 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1724 1716 if revs:
1725 1717 revs = [repo.lookup(rev) for rev in revs]
1726 1718 other = hg.peer(repo, {}, dest)
1727 1719 n = other.lookup(q)
1728 1720 if n in repo:
1729 1721 r = repo[n].rev()
1730 1722 if r in subset:
1731 1723 return baseset([r])
1732 1724 return baseset()
1733 1725
1734 1726 @predicate('removes(pattern)', safe=True, weight=30)
1735 1727 def removes(repo, subset, x):
1736 1728 """Changesets which remove files matching pattern.
1737 1729
1738 1730 The pattern without explicit kind like ``glob:`` is expected to be
1739 1731 relative to the current directory and match against a file or a
1740 1732 directory.
1741 1733 """
1742 1734 # i18n: "removes" is a keyword
1743 1735 pat = getstring(x, _("removes requires a pattern"))
1744 1736 return checkstatus(repo, subset, pat, 2)
1745 1737
1746 1738 @predicate('rev(number)', safe=True)
1747 1739 def rev(repo, subset, x):
1748 1740 """Revision with the given numeric identifier.
1749 1741 """
1750 1742 # i18n: "rev" is a keyword
1751 1743 l = getargs(x, 1, 1, _("rev requires one argument"))
1752 1744 try:
1753 1745 # i18n: "rev" is a keyword
1754 1746 l = int(getstring(l[0], _("rev requires a number")))
1755 1747 except (TypeError, ValueError):
1756 1748 # i18n: "rev" is a keyword
1757 1749 raise error.ParseError(_("rev expects a number"))
1758 1750 if l not in repo.changelog and l not in (node.nullrev, node.wdirrev):
1759 1751 return baseset()
1760 1752 return subset & baseset([l])
1761 1753
1762 1754 @predicate('matching(revision [, field])', safe=True)
1763 1755 def matching(repo, subset, x):
1764 1756 """Changesets in which a given set of fields match the set of fields in the
1765 1757 selected revision or set.
1766 1758
1767 1759 To match more than one field pass the list of fields to match separated
1768 1760 by spaces (e.g. ``author description``).
1769 1761
1770 1762 Valid fields are most regular revision fields and some special fields.
1771 1763
1772 1764 Regular revision fields are ``description``, ``author``, ``branch``,
1773 1765 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1774 1766 and ``diff``.
1775 1767 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1776 1768 contents of the revision. Two revisions matching their ``diff`` will
1777 1769 also match their ``files``.
1778 1770
1779 1771 Special fields are ``summary`` and ``metadata``:
1780 1772 ``summary`` matches the first line of the description.
1781 1773 ``metadata`` is equivalent to matching ``description user date``
1782 1774 (i.e. it matches the main metadata fields).
1783 1775
1784 1776 ``metadata`` is the default field which is used when no fields are
1785 1777 specified. You can match more than one field at a time.
1786 1778 """
1787 1779 # i18n: "matching" is a keyword
1788 1780 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1789 1781
1790 1782 revs = getset(repo, fullreposet(repo), l[0])
1791 1783
1792 1784 fieldlist = ['metadata']
1793 1785 if len(l) > 1:
1794 1786 fieldlist = getstring(l[1],
1795 1787 # i18n: "matching" is a keyword
1796 1788 _("matching requires a string "
1797 1789 "as its second argument")).split()
1798 1790
1799 1791 # Make sure that there are no repeated fields,
1800 1792 # expand the 'special' 'metadata' field type
1801 1793 # and check the 'files' whenever we check the 'diff'
1802 1794 fields = []
1803 1795 for field in fieldlist:
1804 1796 if field == 'metadata':
1805 1797 fields += ['user', 'description', 'date']
1806 1798 elif field == 'diff':
1807 1799 # a revision matching the diff must also match the files
1808 1800 # since matching the diff is very costly, make sure to
1809 1801 # also match the files first
1810 1802 fields += ['files', 'diff']
1811 1803 else:
1812 1804 if field == 'author':
1813 1805 field = 'user'
1814 1806 fields.append(field)
1815 1807 fields = set(fields)
1816 1808 if 'summary' in fields and 'description' in fields:
1817 1809 # If a revision matches its description it also matches its summary
1818 1810 fields.discard('summary')
1819 1811
1820 1812 # We may want to match more than one field
1821 1813 # Not all fields take the same amount of time to be matched
1822 1814 # Sort the selected fields in order of increasing matching cost
1823 1815 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1824 1816 'files', 'description', 'substate', 'diff']
1825 1817 def fieldkeyfunc(f):
1826 1818 try:
1827 1819 return fieldorder.index(f)
1828 1820 except ValueError:
1829 1821 # assume an unknown field is very costly
1830 1822 return len(fieldorder)
1831 1823 fields = list(fields)
1832 1824 fields.sort(key=fieldkeyfunc)
1833 1825
1834 1826 # Each field will be matched with its own "getfield" function
1835 1827 # which will be added to the getfieldfuncs array of functions
1836 1828 getfieldfuncs = []
1837 1829 _funcs = {
1838 1830 'user': lambda r: repo[r].user(),
1839 1831 'branch': lambda r: repo[r].branch(),
1840 1832 'date': lambda r: repo[r].date(),
1841 1833 'description': lambda r: repo[r].description(),
1842 1834 'files': lambda r: repo[r].files(),
1843 1835 'parents': lambda r: repo[r].parents(),
1844 1836 'phase': lambda r: repo[r].phase(),
1845 1837 'substate': lambda r: repo[r].substate,
1846 1838 'summary': lambda r: repo[r].description().splitlines()[0],
1847 1839 'diff': lambda r: list(repo[r].diff(
1848 1840 opts=diffutil.diffallopts(repo.ui, {'git': True}))),
1849 1841 }
1850 1842 for info in fields:
1851 1843 getfield = _funcs.get(info, None)
1852 1844 if getfield is None:
1853 1845 raise error.ParseError(
1854 1846 # i18n: "matching" is a keyword
1855 1847 _("unexpected field name passed to matching: %s") % info)
1856 1848 getfieldfuncs.append(getfield)
1857 1849 # convert the getfield array of functions into a "getinfo" function
1858 1850 # which returns an array of field values (or a single value if there
1859 1851 # is only one field to match)
1860 1852 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1861 1853
1862 1854 def matches(x):
1863 1855 for rev in revs:
1864 1856 target = getinfo(rev)
1865 1857 match = True
1866 1858 for n, f in enumerate(getfieldfuncs):
1867 1859 if target[n] != f(x):
1868 1860 match = False
1869 1861 if match:
1870 1862 return True
1871 1863 return False
1872 1864
1873 1865 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1874 1866
1875 1867 @predicate('reverse(set)', safe=True, takeorder=True, weight=0)
1876 1868 def reverse(repo, subset, x, order):
1877 1869 """Reverse order of set.
1878 1870 """
1879 1871 l = getset(repo, subset, x, order)
1880 1872 if order == defineorder:
1881 1873 l.reverse()
1882 1874 return l
1883 1875
1884 1876 @predicate('roots(set)', safe=True)
1885 1877 def roots(repo, subset, x):
1886 1878 """Changesets in set with no parent changeset in set.
1887 1879 """
1888 1880 s = getset(repo, fullreposet(repo), x)
1889 1881 parents = repo.changelog.parentrevs
1890 1882 def filter(r):
1891 1883 for p in parents(r):
1892 1884 if 0 <= p and p in s:
1893 1885 return False
1894 1886 return True
1895 1887 return subset & s.filter(filter, condrepr='<roots>')
1896 1888
1897 1889 _sortkeyfuncs = {
1898 1890 'rev': lambda c: c.rev(),
1899 1891 'branch': lambda c: c.branch(),
1900 1892 'desc': lambda c: c.description(),
1901 1893 'user': lambda c: c.user(),
1902 1894 'author': lambda c: c.user(),
1903 1895 'date': lambda c: c.date()[0],
1904 1896 }
1905 1897
1906 1898 def _getsortargs(x):
1907 1899 """Parse sort options into (set, [(key, reverse)], opts)"""
1908 1900 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1909 1901 if 'set' not in args:
1910 1902 # i18n: "sort" is a keyword
1911 1903 raise error.ParseError(_('sort requires one or two arguments'))
1912 1904 keys = "rev"
1913 1905 if 'keys' in args:
1914 1906 # i18n: "sort" is a keyword
1915 1907 keys = getstring(args['keys'], _("sort spec must be a string"))
1916 1908
1917 1909 keyflags = []
1918 1910 for k in keys.split():
1919 1911 fk = k
1920 1912 reverse = (k.startswith('-'))
1921 1913 if reverse:
1922 1914 k = k[1:]
1923 1915 if k not in _sortkeyfuncs and k != 'topo':
1924 1916 raise error.ParseError(
1925 1917 _("unknown sort key %r") % pycompat.bytestr(fk))
1926 1918 keyflags.append((k, reverse))
1927 1919
1928 1920 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1929 1921 # i18n: "topo" is a keyword
1930 1922 raise error.ParseError(_('topo sort order cannot be combined '
1931 1923 'with other sort keys'))
1932 1924
1933 1925 opts = {}
1934 1926 if 'topo.firstbranch' in args:
1935 1927 if any(k == 'topo' for k, reverse in keyflags):
1936 1928 opts['topo.firstbranch'] = args['topo.firstbranch']
1937 1929 else:
1938 1930 # i18n: "topo" and "topo.firstbranch" are keywords
1939 1931 raise error.ParseError(_('topo.firstbranch can only be used '
1940 1932 'when using the topo sort key'))
1941 1933
1942 1934 return args['set'], keyflags, opts
1943 1935
1944 1936 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True,
1945 1937 weight=10)
1946 1938 def sort(repo, subset, x, order):
1947 1939 """Sort set by keys. The default sort order is ascending, specify a key
1948 1940 as ``-key`` to sort in descending order.
1949 1941
1950 1942 The keys can be:
1951 1943
1952 1944 - ``rev`` for the revision number,
1953 1945 - ``branch`` for the branch name,
1954 1946 - ``desc`` for the commit message (description),
1955 1947 - ``user`` for user name (``author`` can be used as an alias),
1956 1948 - ``date`` for the commit date
1957 1949 - ``topo`` for a reverse topographical sort
1958 1950
1959 1951 The ``topo`` sort order cannot be combined with other sort keys. This sort
1960 1952 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1961 1953 specifies what topographical branches to prioritize in the sort.
1962 1954
1963 1955 """
1964 1956 s, keyflags, opts = _getsortargs(x)
1965 1957 revs = getset(repo, subset, s, order)
1966 1958
1967 1959 if not keyflags or order != defineorder:
1968 1960 return revs
1969 1961 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1970 1962 revs.sort(reverse=keyflags[0][1])
1971 1963 return revs
1972 1964 elif keyflags[0][0] == "topo":
1973 1965 firstbranch = ()
1974 1966 if 'topo.firstbranch' in opts:
1975 1967 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1976 1968 revs = baseset(dagop.toposort(revs, repo.changelog.parentrevs,
1977 1969 firstbranch),
1978 1970 istopo=True)
1979 1971 if keyflags[0][1]:
1980 1972 revs.reverse()
1981 1973 return revs
1982 1974
1983 1975 # sort() is guaranteed to be stable
1984 1976 ctxs = [repo[r] for r in revs]
1985 1977 for k, reverse in reversed(keyflags):
1986 1978 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1987 1979 return baseset([c.rev() for c in ctxs])
1988 1980
1989 1981 @predicate('subrepo([pattern])')
1990 1982 def subrepo(repo, subset, x):
1991 1983 """Changesets that add, modify or remove the given subrepo. If no subrepo
1992 1984 pattern is named, any subrepo changes are returned.
1993 1985 """
1994 1986 # i18n: "subrepo" is a keyword
1995 1987 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1996 1988 pat = None
1997 1989 if len(args) != 0:
1998 1990 pat = getstring(args[0], _("subrepo requires a pattern"))
1999 1991
2000 1992 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2001 1993
2002 1994 def submatches(names):
2003 1995 k, p, m = stringutil.stringmatcher(pat)
2004 1996 for name in names:
2005 1997 if m(name):
2006 1998 yield name
2007 1999
2008 2000 def matches(x):
2009 2001 c = repo[x]
2010 2002 s = repo.status(c.p1().node(), c.node(), match=m)
2011 2003
2012 2004 if pat is None:
2013 2005 return s.added or s.modified or s.removed
2014 2006
2015 2007 if s.added:
2016 2008 return any(submatches(c.substate.keys()))
2017 2009
2018 2010 if s.modified:
2019 2011 subs = set(c.p1().substate.keys())
2020 2012 subs.update(c.substate.keys())
2021 2013
2022 2014 for path in submatches(subs):
2023 2015 if c.p1().substate.get(path) != c.substate.get(path):
2024 2016 return True
2025 2017
2026 2018 if s.removed:
2027 2019 return any(submatches(c.p1().substate.keys()))
2028 2020
2029 2021 return False
2030 2022
2031 2023 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2032 2024
2033 2025 def _mapbynodefunc(repo, s, f):
2034 2026 """(repo, smartset, [node] -> [node]) -> smartset
2035 2027
2036 2028 Helper method to map a smartset to another smartset given a function only
2037 2029 talking about nodes. Handles converting between rev numbers and nodes, and
2038 2030 filtering.
2039 2031 """
2040 2032 cl = repo.unfiltered().changelog
2041 2033 torev = cl.rev
2042 2034 tonode = cl.node
2043 2035 nodemap = cl.nodemap
2044 2036 result = set(torev(n) for n in f(tonode(r) for r in s) if n in nodemap)
2045 2037 return smartset.baseset(result - repo.changelog.filteredrevs)
2046 2038
2047 2039 @predicate('successors(set)', safe=True)
2048 2040 def successors(repo, subset, x):
2049 2041 """All successors for set, including the given set themselves"""
2050 2042 s = getset(repo, fullreposet(repo), x)
2051 2043 f = lambda nodes: obsutil.allsuccessors(repo.obsstore, nodes)
2052 2044 d = _mapbynodefunc(repo, s, f)
2053 2045 return subset & d
2054 2046
2055 2047 def _substringmatcher(pattern, casesensitive=True):
2056 2048 kind, pattern, matcher = stringutil.stringmatcher(
2057 2049 pattern, casesensitive=casesensitive)
2058 2050 if kind == 'literal':
2059 2051 if not casesensitive:
2060 2052 pattern = encoding.lower(pattern)
2061 2053 matcher = lambda s: pattern in encoding.lower(s)
2062 2054 else:
2063 2055 matcher = lambda s: pattern in s
2064 2056 return kind, pattern, matcher
2065 2057
2066 2058 @predicate('tag([name])', safe=True)
2067 2059 def tag(repo, subset, x):
2068 2060 """The specified tag by name, or all tagged revisions if no name is given.
2069 2061
2070 2062 Pattern matching is supported for `name`. See
2071 2063 :hg:`help revisions.patterns`.
2072 2064 """
2073 2065 # i18n: "tag" is a keyword
2074 2066 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2075 2067 cl = repo.changelog
2076 2068 if args:
2077 2069 pattern = getstring(args[0],
2078 2070 # i18n: "tag" is a keyword
2079 2071 _('the argument to tag must be a string'))
2080 2072 kind, pattern, matcher = stringutil.stringmatcher(pattern)
2081 2073 if kind == 'literal':
2082 2074 # avoid resolving all tags
2083 2075 tn = repo._tagscache.tags.get(pattern, None)
2084 2076 if tn is None:
2085 2077 raise error.RepoLookupError(_("tag '%s' does not exist")
2086 2078 % pattern)
2087 2079 s = {repo[tn].rev()}
2088 2080 else:
2089 2081 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)}
2090 2082 else:
2091 2083 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'}
2092 2084 return subset & s
2093 2085
2094 2086 @predicate('tagged', safe=True)
2095 2087 def tagged(repo, subset, x):
2096 2088 return tag(repo, subset, x)
2097 2089
2098 2090 @predicate('orphan()', safe=True)
2099 2091 def orphan(repo, subset, x):
2100 2092 """Non-obsolete changesets with obsolete ancestors. (EXPERIMENTAL)
2101 2093 """
2102 2094 # i18n: "orphan" is a keyword
2103 2095 getargs(x, 0, 0, _("orphan takes no arguments"))
2104 2096 orphan = obsmod.getrevs(repo, 'orphan')
2105 2097 return subset & orphan
2106 2098
2107 2099
2108 2100 @predicate('user(string)', safe=True, weight=10)
2109 2101 def user(repo, subset, x):
2110 2102 """User name contains string. The match is case-insensitive.
2111 2103
2112 2104 Pattern matching is supported for `string`. See
2113 2105 :hg:`help revisions.patterns`.
2114 2106 """
2115 2107 return author(repo, subset, x)
2116 2108
2117 2109 @predicate('wdir()', safe=True, weight=0)
2118 2110 def wdir(repo, subset, x):
2119 2111 """Working directory. (EXPERIMENTAL)"""
2120 2112 # i18n: "wdir" is a keyword
2121 2113 getargs(x, 0, 0, _("wdir takes no arguments"))
2122 2114 if node.wdirrev in subset or isinstance(subset, fullreposet):
2123 2115 return baseset([node.wdirrev])
2124 2116 return baseset()
2125 2117
2126 2118 def _orderedlist(repo, subset, x):
2127 2119 s = getstring(x, "internal error")
2128 2120 if not s:
2129 2121 return baseset()
2130 2122 # remove duplicates here. it's difficult for caller to deduplicate sets
2131 2123 # because different symbols can point to the same rev.
2132 2124 cl = repo.changelog
2133 2125 ls = []
2134 2126 seen = set()
2135 2127 for t in s.split('\0'):
2136 2128 try:
2137 2129 # fast path for integer revision
2138 2130 r = int(t)
2139 2131 if ('%d' % r) != t or r not in cl:
2140 2132 raise ValueError
2141 2133 revs = [r]
2142 2134 except ValueError:
2143 2135 revs = stringset(repo, subset, t, defineorder)
2144 2136
2145 2137 for r in revs:
2146 2138 if r in seen:
2147 2139 continue
2148 2140 if (r in subset
2149 2141 or r == node.nullrev and isinstance(subset, fullreposet)):
2150 2142 ls.append(r)
2151 2143 seen.add(r)
2152 2144 return baseset(ls)
2153 2145
2154 2146 # for internal use
2155 2147 @predicate('_list', safe=True, takeorder=True)
2156 2148 def _list(repo, subset, x, order):
2157 2149 if order == followorder:
2158 2150 # slow path to take the subset order
2159 2151 return subset & _orderedlist(repo, fullreposet(repo), x)
2160 2152 else:
2161 2153 return _orderedlist(repo, subset, x)
2162 2154
2163 2155 def _orderedintlist(repo, subset, x):
2164 2156 s = getstring(x, "internal error")
2165 2157 if not s:
2166 2158 return baseset()
2167 2159 ls = [int(r) for r in s.split('\0')]
2168 2160 s = subset
2169 2161 return baseset([r for r in ls if r in s])
2170 2162
2171 2163 # for internal use
2172 2164 @predicate('_intlist', safe=True, takeorder=True, weight=0)
2173 2165 def _intlist(repo, subset, x, order):
2174 2166 if order == followorder:
2175 2167 # slow path to take the subset order
2176 2168 return subset & _orderedintlist(repo, fullreposet(repo), x)
2177 2169 else:
2178 2170 return _orderedintlist(repo, subset, x)
2179 2171
2180 2172 def _orderedhexlist(repo, subset, x):
2181 2173 s = getstring(x, "internal error")
2182 2174 if not s:
2183 2175 return baseset()
2184 2176 cl = repo.changelog
2185 2177 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2186 2178 s = subset
2187 2179 return baseset([r for r in ls if r in s])
2188 2180
2189 2181 # for internal use
2190 2182 @predicate('_hexlist', safe=True, takeorder=True)
2191 2183 def _hexlist(repo, subset, x, order):
2192 2184 if order == followorder:
2193 2185 # slow path to take the subset order
2194 2186 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2195 2187 else:
2196 2188 return _orderedhexlist(repo, subset, x)
2197 2189
2198 2190 methods = {
2199 2191 "range": rangeset,
2200 2192 "rangeall": rangeall,
2201 2193 "rangepre": rangepre,
2202 2194 "rangepost": rangepost,
2203 2195 "dagrange": dagrange,
2204 2196 "string": stringset,
2205 2197 "symbol": stringset,
2206 2198 "and": andset,
2207 2199 "andsmally": andsmallyset,
2208 2200 "or": orset,
2209 2201 "not": notset,
2210 2202 "difference": differenceset,
2211 2203 "relation": relationset,
2212 2204 "relsubscript": relsubscriptset,
2213 2205 "subscript": subscriptset,
2214 2206 "list": listset,
2215 2207 "keyvalue": keyvaluepair,
2216 2208 "func": func,
2217 2209 "ancestor": ancestorspec,
2218 2210 "parent": parentspec,
2219 2211 "parentpost": parentpost,
2220 2212 }
2221 2213
2222 2214 def lookupfn(repo):
2223 2215 return lambda symbol: scmutil.isrevsymbol(repo, symbol)
2224 2216
2225 2217 def match(ui, spec, lookup=None):
2226 2218 """Create a matcher for a single revision spec"""
2227 2219 return matchany(ui, [spec], lookup=lookup)
2228 2220
2229 2221 def matchany(ui, specs, lookup=None, localalias=None):
2230 2222 """Create a matcher that will include any revisions matching one of the
2231 2223 given specs
2232 2224
2233 2225 If lookup function is not None, the parser will first attempt to handle
2234 2226 old-style ranges, which may contain operator characters.
2235 2227
2236 2228 If localalias is not None, it is a dict {name: definitionstring}. It takes
2237 2229 precedence over [revsetalias] config section.
2238 2230 """
2239 2231 if not specs:
2240 2232 def mfunc(repo, subset=None):
2241 2233 return baseset()
2242 2234 return mfunc
2243 2235 if not all(specs):
2244 2236 raise error.ParseError(_("empty query"))
2245 2237 if len(specs) == 1:
2246 2238 tree = revsetlang.parse(specs[0], lookup)
2247 2239 else:
2248 2240 tree = ('or',
2249 2241 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs))
2250 2242
2251 2243 aliases = []
2252 2244 warn = None
2253 2245 if ui:
2254 2246 aliases.extend(ui.configitems('revsetalias'))
2255 2247 warn = ui.warn
2256 2248 if localalias:
2257 2249 aliases.extend(localalias.items())
2258 2250 if aliases:
2259 2251 tree = revsetlang.expandaliases(tree, aliases, warn=warn)
2260 2252 tree = revsetlang.foldconcat(tree)
2261 2253 tree = revsetlang.analyze(tree)
2262 2254 tree = revsetlang.optimize(tree)
2263 2255 return makematcher(tree)
2264 2256
2265 2257 def makematcher(tree):
2266 2258 """Create a matcher from an evaluatable tree"""
2267 2259 def mfunc(repo, subset=None, order=None):
2268 2260 if order is None:
2269 2261 if subset is None:
2270 2262 order = defineorder # 'x'
2271 2263 else:
2272 2264 order = followorder # 'subset & x'
2273 2265 if subset is None:
2274 2266 subset = fullreposet(repo)
2275 2267 return getset(repo, subset, tree, order)
2276 2268 return mfunc
2277 2269
2278 2270 def loadpredicate(ui, extname, registrarobj):
2279 2271 """Load revset predicates from specified registrarobj
2280 2272 """
2281 2273 for name, func in registrarobj._table.iteritems():
2282 2274 symbols[name] = func
2283 2275 if func._safe:
2284 2276 safesymbols.add(name)
2285 2277
2286 2278 # load built-in predicates explicitly to setup safesymbols
2287 2279 loadpredicate(None, None, predicate)
2288 2280
2289 2281 # tell hggettext to extract docstrings from these functions:
2290 2282 i18nfunctions = symbols.values()
@@ -1,2921 +1,2941 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[b'r3232'] = r3232
20 20 > EOF
21 21 $ cat >> $HGRCPATH << EOF
22 22 > [extensions]
23 23 > drawdag=$TESTDIR/drawdag.py
24 24 > testrevset=$TESTTMP/testrevset.py
25 25 > EOF
26 26
27 27 $ try() {
28 28 > hg debugrevspec --debug "$@"
29 29 > }
30 30
31 31 $ log() {
32 32 > hg log --template '{rev}\n' -r "$1"
33 33 > }
34 34
35 35 extension to build '_intlist()' and '_hexlist()', which is necessary because
36 36 these predicates use '\0' as a separator:
37 37
38 38 $ cat <<EOF > debugrevlistspec.py
39 39 > from __future__ import absolute_import
40 40 > from mercurial import (
41 41 > node as nodemod,
42 42 > registrar,
43 43 > revset,
44 44 > revsetlang,
45 45 > )
46 46 > from mercurial.utils import stringutil
47 47 > cmdtable = {}
48 48 > command = registrar.command(cmdtable)
49 49 > @command(b'debugrevlistspec',
50 50 > [(b'', b'optimize', None, b'print parsed tree after optimizing'),
51 51 > (b'', b'bin', None, b'unhexlify arguments')])
52 52 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
53 53 > if opts['bin']:
54 54 > args = map(nodemod.bin, args)
55 55 > expr = revsetlang.formatspec(fmt, list(args))
56 56 > if ui.verbose:
57 57 > tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
58 58 > ui.note(revsetlang.prettyformat(tree), b"\n")
59 59 > if opts["optimize"]:
60 60 > opttree = revsetlang.optimize(revsetlang.analyze(tree))
61 61 > ui.note(b"* optimized:\n", revsetlang.prettyformat(opttree),
62 62 > b"\n")
63 63 > func = revset.match(ui, expr, lookup=revset.lookupfn(repo))
64 64 > revs = func(repo)
65 65 > if ui.verbose:
66 66 > ui.note(b"* set:\n", stringutil.prettyrepr(revs), b"\n")
67 67 > for c in revs:
68 68 > ui.write(b"%d\n" % c)
69 69 > EOF
70 70 $ cat <<EOF >> $HGRCPATH
71 71 > [extensions]
72 72 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
73 73 > EOF
74 74 $ trylist() {
75 75 > hg debugrevlistspec --debug "$@"
76 76 > }
77 77
78 78 $ hg init repo
79 79 $ cd repo
80 80
81 81 $ echo a > a
82 82 $ hg branch a
83 83 marked working directory as branch a
84 84 (branches are permanent and global, did you want a bookmark?)
85 85 $ hg ci -Aqm0
86 86
87 87 $ echo b > b
88 88 $ hg branch b
89 89 marked working directory as branch b
90 90 $ hg ci -Aqm1
91 91
92 92 $ rm a
93 93 $ hg branch a-b-c-
94 94 marked working directory as branch a-b-c-
95 95 $ hg ci -Aqm2 -u Bob
96 96
97 97 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
98 98 2
99 99 $ hg log -r "extra('branch')" --template '{rev}\n'
100 100 0
101 101 1
102 102 2
103 103 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
104 104 0 a
105 105 2 a-b-c-
106 106
107 107 $ hg co 1
108 108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 109 $ hg branch +a+b+c+
110 110 marked working directory as branch +a+b+c+
111 111 $ hg ci -Aqm3
112 112
113 113 $ hg co 2 # interleave
114 114 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
115 115 $ echo bb > b
116 116 $ hg branch -- -a-b-c-
117 117 marked working directory as branch -a-b-c-
118 118 $ hg ci -Aqm4 -d "May 12 2005"
119 119
120 120 $ hg co 3
121 121 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 122 $ hg branch !a/b/c/
123 123 marked working directory as branch !a/b/c/
124 124 $ hg ci -Aqm"5 bug"
125 125
126 126 $ hg merge 4
127 127 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
128 128 (branch merge, don't forget to commit)
129 129 $ hg branch _a_b_c_
130 130 marked working directory as branch _a_b_c_
131 131 $ hg ci -Aqm"6 issue619"
132 132
133 133 $ hg branch .a.b.c.
134 134 marked working directory as branch .a.b.c.
135 135 $ hg ci -Aqm7
136 136
137 137 $ hg branch all
138 138 marked working directory as branch all
139 139
140 140 $ hg co 4
141 141 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
142 142 $ hg branch Γ©
143 143 marked working directory as branch \xc3\xa9 (esc)
144 144 $ hg ci -Aqm9
145 145
146 146 $ hg tag -r6 1.0
147 147 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
148 148
149 149 $ hg clone --quiet -U -r 7 . ../remote1
150 150 $ hg clone --quiet -U -r 8 . ../remote2
151 151 $ echo "[paths]" >> .hg/hgrc
152 152 $ echo "default = ../remote1" >> .hg/hgrc
153 153
154 154 trivial
155 155
156 156 $ try 0:1
157 157 (range
158 158 (symbol '0')
159 159 (symbol '1'))
160 160 * set:
161 161 <spanset+ 0:2>
162 162 0
163 163 1
164 164 $ try --optimize :
165 165 (rangeall
166 166 None)
167 167 * optimized:
168 168 (rangeall
169 169 None)
170 170 * set:
171 171 <spanset+ 0:10>
172 172 0
173 173 1
174 174 2
175 175 3
176 176 4
177 177 5
178 178 6
179 179 7
180 180 8
181 181 9
182 182 $ try 3::6
183 183 (dagrange
184 184 (symbol '3')
185 185 (symbol '6'))
186 186 * set:
187 187 <baseset+ [3, 5, 6]>
188 188 3
189 189 5
190 190 6
191 191 $ try '0|1|2'
192 192 (or
193 193 (list
194 194 (symbol '0')
195 195 (symbol '1')
196 196 (symbol '2')))
197 197 * set:
198 198 <baseset [0, 1, 2]>
199 199 0
200 200 1
201 201 2
202 202
203 203 names that should work without quoting
204 204
205 205 $ try a
206 206 (symbol 'a')
207 207 * set:
208 208 <baseset [0]>
209 209 0
210 210 $ try b-a
211 211 (minus
212 212 (symbol 'b')
213 213 (symbol 'a'))
214 214 * set:
215 215 <filteredset
216 216 <baseset [1]>,
217 217 <not
218 218 <baseset [0]>>>
219 219 1
220 220 $ try _a_b_c_
221 221 (symbol '_a_b_c_')
222 222 * set:
223 223 <baseset [6]>
224 224 6
225 225 $ try _a_b_c_-a
226 226 (minus
227 227 (symbol '_a_b_c_')
228 228 (symbol 'a'))
229 229 * set:
230 230 <filteredset
231 231 <baseset [6]>,
232 232 <not
233 233 <baseset [0]>>>
234 234 6
235 235 $ try .a.b.c.
236 236 (symbol '.a.b.c.')
237 237 * set:
238 238 <baseset [7]>
239 239 7
240 240 $ try .a.b.c.-a
241 241 (minus
242 242 (symbol '.a.b.c.')
243 243 (symbol 'a'))
244 244 * set:
245 245 <filteredset
246 246 <baseset [7]>,
247 247 <not
248 248 <baseset [0]>>>
249 249 7
250 250
251 251 names that should be caught by fallback mechanism
252 252
253 253 $ try -- '-a-b-c-'
254 254 (symbol '-a-b-c-')
255 255 * set:
256 256 <baseset [4]>
257 257 4
258 258 $ log -a-b-c-
259 259 4
260 260 $ try '+a+b+c+'
261 261 (symbol '+a+b+c+')
262 262 * set:
263 263 <baseset [3]>
264 264 3
265 265 $ try '+a+b+c+:'
266 266 (rangepost
267 267 (symbol '+a+b+c+'))
268 268 * set:
269 269 <spanset+ 3:10>
270 270 3
271 271 4
272 272 5
273 273 6
274 274 7
275 275 8
276 276 9
277 277 $ try ':+a+b+c+'
278 278 (rangepre
279 279 (symbol '+a+b+c+'))
280 280 * set:
281 281 <spanset+ 0:4>
282 282 0
283 283 1
284 284 2
285 285 3
286 286 $ try -- '-a-b-c-:+a+b+c+'
287 287 (range
288 288 (symbol '-a-b-c-')
289 289 (symbol '+a+b+c+'))
290 290 * set:
291 291 <spanset- 3:5>
292 292 4
293 293 3
294 294 $ log '-a-b-c-:+a+b+c+'
295 295 4
296 296 3
297 297
298 298 $ try -- -a-b-c--a # complains
299 299 (minus
300 300 (minus
301 301 (minus
302 302 (negate
303 303 (symbol 'a'))
304 304 (symbol 'b'))
305 305 (symbol 'c'))
306 306 (negate
307 307 (symbol 'a')))
308 308 abort: unknown revision '-a'!
309 309 [255]
310 310 $ try Γ©
311 311 (symbol '\xc3\xa9')
312 312 * set:
313 313 <baseset [9]>
314 314 9
315 315
316 316 no quoting needed
317 317
318 318 $ log ::a-b-c-
319 319 0
320 320 1
321 321 2
322 322
323 323 quoting needed
324 324
325 325 $ try '"-a-b-c-"-a'
326 326 (minus
327 327 (string '-a-b-c-')
328 328 (symbol 'a'))
329 329 * set:
330 330 <filteredset
331 331 <baseset [4]>,
332 332 <not
333 333 <baseset [0]>>>
334 334 4
335 335
336 336 $ log '1 or 2'
337 337 1
338 338 2
339 339 $ log '1|2'
340 340 1
341 341 2
342 342 $ log '1 and 2'
343 343 $ log '1&2'
344 344 $ try '1&2|3' # precedence - and is higher
345 345 (or
346 346 (list
347 347 (and
348 348 (symbol '1')
349 349 (symbol '2'))
350 350 (symbol '3')))
351 351 * set:
352 352 <addset
353 353 <baseset []>,
354 354 <baseset [3]>>
355 355 3
356 356 $ try '1|2&3'
357 357 (or
358 358 (list
359 359 (symbol '1')
360 360 (and
361 361 (symbol '2')
362 362 (symbol '3'))))
363 363 * set:
364 364 <addset
365 365 <baseset [1]>,
366 366 <baseset []>>
367 367 1
368 368 $ try '1&2&3' # associativity
369 369 (and
370 370 (and
371 371 (symbol '1')
372 372 (symbol '2'))
373 373 (symbol '3'))
374 374 * set:
375 375 <baseset []>
376 376 $ try '1|(2|3)'
377 377 (or
378 378 (list
379 379 (symbol '1')
380 380 (group
381 381 (or
382 382 (list
383 383 (symbol '2')
384 384 (symbol '3'))))))
385 385 * set:
386 386 <addset
387 387 <baseset [1]>,
388 388 <baseset [2, 3]>>
389 389 1
390 390 2
391 391 3
392 392 $ log '1.0' # tag
393 393 6
394 394 $ log 'a' # branch
395 395 0
396 396 $ log '2785f51ee'
397 397 0
398 398 $ log 'date(2005)'
399 399 4
400 400 $ log 'date(this is a test)'
401 401 hg: parse error at 10: unexpected token: symbol
402 402 (date(this is a test)
403 403 ^ here)
404 404 [255]
405 405 $ log 'date()'
406 406 hg: parse error: date requires a string
407 407 [255]
408 408 $ log 'date'
409 409 abort: unknown revision 'date'!
410 410 [255]
411 411 $ log 'date('
412 412 hg: parse error at 5: not a prefix: end
413 413 (date(
414 414 ^ here)
415 415 [255]
416 416 $ log 'date("\xy")'
417 417 hg: parse error: invalid \x escape* (glob)
418 418 [255]
419 419 $ log 'date(tip)'
420 420 hg: parse error: invalid date: 'tip'
421 421 [255]
422 422 $ log '0:date'
423 423 abort: unknown revision 'date'!
424 424 [255]
425 425 $ log '::"date"'
426 426 abort: unknown revision 'date'!
427 427 [255]
428 428 $ hg book date -r 4
429 429 $ log '0:date'
430 430 0
431 431 1
432 432 2
433 433 3
434 434 4
435 435 $ log '::date'
436 436 0
437 437 1
438 438 2
439 439 4
440 440 $ log '::"date"'
441 441 0
442 442 1
443 443 2
444 444 4
445 445 $ log 'date(2005) and 1::'
446 446 4
447 447 $ hg book -d date
448 448
449 449 function name should be a symbol
450 450
451 451 $ log '"date"(2005)'
452 452 hg: parse error: not a symbol
453 453 [255]
454 454
455 455 keyword arguments
456 456
457 457 $ log 'extra(branch, value=a)'
458 458 0
459 459
460 460 $ log 'extra(branch, a, b)'
461 461 hg: parse error: extra takes at most 2 positional arguments
462 462 [255]
463 463 $ log 'extra(a, label=b)'
464 464 hg: parse error: extra got multiple values for keyword argument 'label'
465 465 [255]
466 466 $ log 'extra(label=branch, default)'
467 467 hg: parse error: extra got an invalid argument
468 468 [255]
469 469 $ log 'extra(branch, foo+bar=baz)'
470 470 hg: parse error: extra got an invalid argument
471 471 [255]
472 472 $ log 'extra(unknown=branch)'
473 473 hg: parse error: extra got an unexpected keyword argument 'unknown'
474 474 [255]
475 475
476 476 $ try 'foo=bar|baz'
477 477 (keyvalue
478 478 (symbol 'foo')
479 479 (or
480 480 (list
481 481 (symbol 'bar')
482 482 (symbol 'baz'))))
483 483 hg: parse error: can't use a key-value pair in this context
484 484 [255]
485 485
486 486 right-hand side should be optimized recursively
487 487
488 488 $ try --optimize 'foo=(not public())'
489 489 (keyvalue
490 490 (symbol 'foo')
491 491 (group
492 492 (not
493 493 (func
494 494 (symbol 'public')
495 495 None))))
496 496 * optimized:
497 497 (keyvalue
498 498 (symbol 'foo')
499 499 (func
500 500 (symbol '_notpublic')
501 501 None))
502 502 hg: parse error: can't use a key-value pair in this context
503 503 [255]
504 504
505 505 relation-subscript operator has the highest binding strength (as function call):
506 506
507 507 $ hg debugrevspec -p parsed 'tip:tip^#generations[-1]'
508 508 * parsed:
509 509 (range
510 510 (symbol 'tip')
511 511 (relsubscript
512 512 (parentpost
513 513 (symbol 'tip'))
514 514 (symbol 'generations')
515 515 (negate
516 516 (symbol '1'))))
517 517 9
518 518 8
519 519 7
520 520 6
521 521 5
522 522 4
523 523
524 524 $ hg debugrevspec -p parsed --no-show-revs 'not public()#generations[0]'
525 525 * parsed:
526 526 (not
527 527 (relsubscript
528 528 (func
529 529 (symbol 'public')
530 530 None)
531 531 (symbol 'generations')
532 532 (symbol '0')))
533 533
534 534 left-hand side of relation-subscript operator should be optimized recursively:
535 535
536 536 $ hg debugrevspec -p analyzed -p optimized --no-show-revs \
537 537 > '(not public())#generations[0]'
538 538 * analyzed:
539 539 (relsubscript
540 540 (not
541 541 (func
542 542 (symbol 'public')
543 543 None))
544 544 (symbol 'generations')
545 545 (symbol '0'))
546 546 * optimized:
547 547 (relsubscript
548 548 (func
549 549 (symbol '_notpublic')
550 550 None)
551 551 (symbol 'generations')
552 552 (symbol '0'))
553 553
554 554 resolution of subscript and relation-subscript ternary operators:
555 555
556 556 $ hg debugrevspec -p analyzed 'tip[0]'
557 557 * analyzed:
558 558 (subscript
559 559 (symbol 'tip')
560 560 (symbol '0'))
561 561 hg: parse error: can't use a subscript in this context
562 562 [255]
563 563
564 564 $ hg debugrevspec -p analyzed 'tip#rel[0]'
565 565 * analyzed:
566 566 (relsubscript
567 567 (symbol 'tip')
568 568 (symbol 'rel')
569 569 (symbol '0'))
570 570 hg: parse error: unknown identifier: rel
571 571 [255]
572 572
573 573 $ hg debugrevspec -p analyzed '(tip#rel)[0]'
574 574 * analyzed:
575 575 (subscript
576 576 (relation
577 577 (symbol 'tip')
578 578 (symbol 'rel'))
579 579 (symbol '0'))
580 580 hg: parse error: can't use a subscript in this context
581 581 [255]
582 582
583 583 $ hg debugrevspec -p analyzed 'tip#rel[0][1]'
584 584 * analyzed:
585 585 (subscript
586 586 (relsubscript
587 587 (symbol 'tip')
588 588 (symbol 'rel')
589 589 (symbol '0'))
590 590 (symbol '1'))
591 591 hg: parse error: can't use a subscript in this context
592 592 [255]
593 593
594 594 $ hg debugrevspec -p analyzed 'tip#rel0#rel1[1]'
595 595 * analyzed:
596 596 (relsubscript
597 597 (relation
598 598 (symbol 'tip')
599 599 (symbol 'rel0'))
600 600 (symbol 'rel1')
601 601 (symbol '1'))
602 602 hg: parse error: unknown identifier: rel1
603 603 [255]
604 604
605 605 $ hg debugrevspec -p analyzed 'tip#rel0[0]#rel1[1]'
606 606 * analyzed:
607 607 (relsubscript
608 608 (relsubscript
609 609 (symbol 'tip')
610 610 (symbol 'rel0')
611 611 (symbol '0'))
612 612 (symbol 'rel1')
613 613 (symbol '1'))
614 614 hg: parse error: unknown identifier: rel1
615 615 [255]
616 616
617 617 parse errors of relation, subscript and relation-subscript operators:
618 618
619 619 $ hg debugrevspec '[0]'
620 620 hg: parse error at 0: not a prefix: [
621 621 ([0]
622 622 ^ here)
623 623 [255]
624 624 $ hg debugrevspec '.#'
625 625 hg: parse error at 2: not a prefix: end
626 626 (.#
627 627 ^ here)
628 628 [255]
629 629 $ hg debugrevspec '#rel'
630 630 hg: parse error at 0: not a prefix: #
631 631 (#rel
632 632 ^ here)
633 633 [255]
634 634 $ hg debugrevspec '.#rel[0'
635 635 hg: parse error at 7: unexpected token: end
636 636 (.#rel[0
637 637 ^ here)
638 638 [255]
639 639 $ hg debugrevspec '.]'
640 640 hg: parse error at 1: invalid token
641 641 (.]
642 642 ^ here)
643 643 [255]
644 644
645 645 $ hg debugrevspec '.#generations[a]'
646 646 hg: parse error: relation subscript must be an integer
647 647 [255]
648 648 $ hg debugrevspec '.#generations[1-2]'
649 649 hg: parse error: relation subscript must be an integer
650 650 [255]
651 651
652 652 parsed tree at stages:
653 653
654 654 $ hg debugrevspec -p all '()'
655 655 * parsed:
656 656 (group
657 657 None)
658 658 * expanded:
659 659 (group
660 660 None)
661 661 * concatenated:
662 662 (group
663 663 None)
664 664 * analyzed:
665 665 None
666 666 * optimized:
667 667 None
668 668 hg: parse error: missing argument
669 669 [255]
670 670
671 671 $ hg debugrevspec --no-optimized -p all '()'
672 672 * parsed:
673 673 (group
674 674 None)
675 675 * expanded:
676 676 (group
677 677 None)
678 678 * concatenated:
679 679 (group
680 680 None)
681 681 * analyzed:
682 682 None
683 683 hg: parse error: missing argument
684 684 [255]
685 685
686 686 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
687 687 * parsed:
688 688 (minus
689 689 (group
690 690 (or
691 691 (list
692 692 (symbol '0')
693 693 (symbol '1'))))
694 694 (symbol '1'))
695 695 * analyzed:
696 696 (and
697 697 (or
698 698 (list
699 699 (symbol '0')
700 700 (symbol '1')))
701 701 (not
702 702 (symbol '1')))
703 703 * optimized:
704 704 (difference
705 705 (func
706 706 (symbol '_list')
707 707 (string '0\x001'))
708 708 (symbol '1'))
709 709 0
710 710
711 711 $ hg debugrevspec -p unknown '0'
712 712 abort: invalid stage name: unknown
713 713 [255]
714 714
715 715 $ hg debugrevspec -p all --optimize '0'
716 716 abort: cannot use --optimize with --show-stage
717 717 [255]
718 718
719 719 verify optimized tree:
720 720
721 721 $ hg debugrevspec --verify '0|1'
722 722
723 723 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
724 724 * analyzed:
725 725 (and
726 726 (func
727 727 (symbol 'r3232')
728 728 None)
729 729 (symbol '2'))
730 730 * optimized:
731 731 (andsmally
732 732 (func
733 733 (symbol 'r3232')
734 734 None)
735 735 (symbol '2'))
736 736 * analyzed set:
737 737 <baseset [2]>
738 738 * optimized set:
739 739 <baseset [2, 2]>
740 740 --- analyzed
741 741 +++ optimized
742 742 2
743 743 +2
744 744 [1]
745 745
746 746 $ hg debugrevspec --no-optimized --verify-optimized '0'
747 747 abort: cannot use --verify-optimized with --no-optimized
748 748 [255]
749 749
750 750 Test that symbols only get parsed as functions if there's an opening
751 751 parenthesis.
752 752
753 753 $ hg book only -r 9
754 754 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
755 755 8
756 756 9
757 757
758 758 ':y' behaves like '0:y', but can't be rewritten as such since the revision '0'
759 759 may be hidden (issue5385)
760 760
761 761 $ try -p parsed -p analyzed ':'
762 762 * parsed:
763 763 (rangeall
764 764 None)
765 765 * analyzed:
766 766 (rangeall
767 767 None)
768 768 * set:
769 769 <spanset+ 0:10>
770 770 0
771 771 1
772 772 2
773 773 3
774 774 4
775 775 5
776 776 6
777 777 7
778 778 8
779 779 9
780 780 $ try -p analyzed ':1'
781 781 * analyzed:
782 782 (rangepre
783 783 (symbol '1'))
784 784 * set:
785 785 <spanset+ 0:2>
786 786 0
787 787 1
788 788 $ try -p analyzed ':(1|2)'
789 789 * analyzed:
790 790 (rangepre
791 791 (or
792 792 (list
793 793 (symbol '1')
794 794 (symbol '2'))))
795 795 * set:
796 796 <spanset+ 0:3>
797 797 0
798 798 1
799 799 2
800 800 $ try -p analyzed ':(1&2)'
801 801 * analyzed:
802 802 (rangepre
803 803 (and
804 804 (symbol '1')
805 805 (symbol '2')))
806 806 * set:
807 807 <baseset []>
808 808
809 809 infix/suffix resolution of ^ operator (issue2884, issue5764):
810 810
811 811 x^:y means (x^):y
812 812
813 813 $ try '1^:2'
814 814 (range
815 815 (parentpost
816 816 (symbol '1'))
817 817 (symbol '2'))
818 818 * set:
819 819 <spanset+ 0:3>
820 820 0
821 821 1
822 822 2
823 823
824 824 $ try '1^::2'
825 825 (dagrange
826 826 (parentpost
827 827 (symbol '1'))
828 828 (symbol '2'))
829 829 * set:
830 830 <baseset+ [0, 1, 2]>
831 831 0
832 832 1
833 833 2
834 834
835 835 $ try '1^..2'
836 836 (dagrange
837 837 (parentpost
838 838 (symbol '1'))
839 839 (symbol '2'))
840 840 * set:
841 841 <baseset+ [0, 1, 2]>
842 842 0
843 843 1
844 844 2
845 845
846 846 $ try '9^:'
847 847 (rangepost
848 848 (parentpost
849 849 (symbol '9')))
850 850 * set:
851 851 <spanset+ 8:10>
852 852 8
853 853 9
854 854
855 855 $ try '9^::'
856 856 (dagrangepost
857 857 (parentpost
858 858 (symbol '9')))
859 859 * set:
860 860 <generatorsetasc+>
861 861 8
862 862 9
863 863
864 864 $ try '9^..'
865 865 (dagrangepost
866 866 (parentpost
867 867 (symbol '9')))
868 868 * set:
869 869 <generatorsetasc+>
870 870 8
871 871 9
872 872
873 873 x^:y should be resolved before omitting group operators
874 874
875 875 $ try '1^(:2)'
876 876 (parent
877 877 (symbol '1')
878 878 (group
879 879 (rangepre
880 880 (symbol '2'))))
881 881 hg: parse error: ^ expects a number 0, 1, or 2
882 882 [255]
883 883
884 884 x^:y should be resolved recursively
885 885
886 886 $ try 'sort(1^:2)'
887 887 (func
888 888 (symbol 'sort')
889 889 (range
890 890 (parentpost
891 891 (symbol '1'))
892 892 (symbol '2')))
893 893 * set:
894 894 <spanset+ 0:3>
895 895 0
896 896 1
897 897 2
898 898
899 899 $ try '(3^:4)^:2'
900 900 (range
901 901 (parentpost
902 902 (group
903 903 (range
904 904 (parentpost
905 905 (symbol '3'))
906 906 (symbol '4'))))
907 907 (symbol '2'))
908 908 * set:
909 909 <spanset+ 0:3>
910 910 0
911 911 1
912 912 2
913 913
914 914 $ try '(3^::4)^::2'
915 915 (dagrange
916 916 (parentpost
917 917 (group
918 918 (dagrange
919 919 (parentpost
920 920 (symbol '3'))
921 921 (symbol '4'))))
922 922 (symbol '2'))
923 923 * set:
924 924 <baseset+ [0, 1, 2]>
925 925 0
926 926 1
927 927 2
928 928
929 929 $ try '(9^:)^:'
930 930 (rangepost
931 931 (parentpost
932 932 (group
933 933 (rangepost
934 934 (parentpost
935 935 (symbol '9'))))))
936 936 * set:
937 937 <spanset+ 4:10>
938 938 4
939 939 5
940 940 6
941 941 7
942 942 8
943 943 9
944 944
945 945 x^ in alias should also be resolved
946 946
947 947 $ try 'A' --config 'revsetalias.A=1^:2'
948 948 (symbol 'A')
949 949 * expanded:
950 950 (range
951 951 (parentpost
952 952 (symbol '1'))
953 953 (symbol '2'))
954 954 * set:
955 955 <spanset+ 0:3>
956 956 0
957 957 1
958 958 2
959 959
960 960 $ try 'A:2' --config 'revsetalias.A=1^'
961 961 (range
962 962 (symbol 'A')
963 963 (symbol '2'))
964 964 * expanded:
965 965 (range
966 966 (parentpost
967 967 (symbol '1'))
968 968 (symbol '2'))
969 969 * set:
970 970 <spanset+ 0:3>
971 971 0
972 972 1
973 973 2
974 974
975 975 but not beyond the boundary of alias expansion, because the resolution should
976 976 be made at the parsing stage
977 977
978 978 $ try '1^A' --config 'revsetalias.A=:2'
979 979 (parent
980 980 (symbol '1')
981 981 (symbol 'A'))
982 982 * expanded:
983 983 (parent
984 984 (symbol '1')
985 985 (rangepre
986 986 (symbol '2')))
987 987 hg: parse error: ^ expects a number 0, 1, or 2
988 988 [255]
989 989
990 990 '::' itself isn't a valid expression
991 991
992 992 $ try '::'
993 993 (dagrangeall
994 994 None)
995 995 hg: parse error: can't use '::' in this context
996 996 [255]
997 997
998 998 ancestor can accept 0 or more arguments
999 999
1000 1000 $ log 'ancestor()'
1001 1001 $ log 'ancestor(1)'
1002 1002 1
1003 1003 $ log 'ancestor(4,5)'
1004 1004 1
1005 1005 $ log 'ancestor(4,5) and 4'
1006 1006 $ log 'ancestor(0,0,1,3)'
1007 1007 0
1008 1008 $ log 'ancestor(3,1,5,3,5,1)'
1009 1009 1
1010 1010 $ log 'ancestor(0,1,3,5)'
1011 1011 0
1012 1012 $ log 'ancestor(1,2,3,4,5)'
1013 1013 1
1014 1014
1015 1015 test ancestors
1016 1016
1017 1017 $ hg log -G -T '{rev}\n' --config experimental.graphshorten=True
1018 1018 @ 9
1019 1019 o 8
1020 1020 | o 7
1021 1021 | o 6
1022 1022 |/|
1023 1023 | o 5
1024 1024 o | 4
1025 1025 | o 3
1026 1026 o | 2
1027 1027 |/
1028 1028 o 1
1029 1029 o 0
1030 1030
1031 1031 $ log 'ancestors(5)'
1032 1032 0
1033 1033 1
1034 1034 3
1035 1035 5
1036 1036 $ log 'ancestor(ancestors(5))'
1037 1037 0
1038 1038 $ log '::r3232()'
1039 1039 0
1040 1040 1
1041 1041 2
1042 1042 3
1043 1043
1044 1044 test common ancestors
1045 1045
1046 1046 $ hg log -T '{rev}\n' -r 'commonancestors(7 + 9)'
1047 1047 0
1048 1048 1
1049 1049 2
1050 1050 4
1051 1051
1052 $ hg log -T '{rev}\n' -r 'commonancestors(head())'
1052 $ hg log -T '{rev}\n' -r 'commonancestors(heads(all()))'
1053 1053 0
1054 1054 1
1055 1055 2
1056 1056 4
1057 1057
1058 1058 $ hg log -T '{rev}\n' -r 'commonancestors(9)'
1059 1059 0
1060 1060 1
1061 1061 2
1062 1062 4
1063 1063 8
1064 1064 9
1065 1065
1066 $ hg log -T '{rev}\n' -r 'commonancestors(8 + 9)'
1067 0
1068 1
1069 2
1070 4
1071 8
1072
1073 test the specialized implementation of heads(commonancestors(..))
1074 (2 gcas is tested in test-merge-criss-cross.t)
1075
1076 $ hg log -T '{rev}\n' -r 'heads(commonancestors(7 + 9))'
1077 4
1078 $ hg log -T '{rev}\n' -r 'heads(commonancestors(heads(all())))'
1079 4
1080 $ hg log -T '{rev}\n' -r 'heads(commonancestors(9))'
1081 9
1082 $ hg log -T '{rev}\n' -r 'heads(commonancestors(8 + 9))'
1083 8
1084
1066 1085 test ancestor variants of empty revision
1067 1086
1068 1087 $ log 'ancestor(none())'
1069 1088 $ log 'ancestors(none())'
1070 1089 $ log 'commonancestors(none())'
1090 $ log 'heads(commonancestors(none()))'
1071 1091
1072 1092 test ancestors with depth limit
1073 1093
1074 1094 (depth=0 selects the node itself)
1075 1095
1076 1096 $ log 'reverse(ancestors(9, depth=0))'
1077 1097 9
1078 1098
1079 1099 (interleaved: '4' would be missing if heap queue were higher depth first)
1080 1100
1081 1101 $ log 'reverse(ancestors(8:9, depth=1))'
1082 1102 9
1083 1103 8
1084 1104 4
1085 1105
1086 1106 (interleaved: '2' would be missing if heap queue were higher depth first)
1087 1107
1088 1108 $ log 'reverse(ancestors(7+8, depth=2))'
1089 1109 8
1090 1110 7
1091 1111 6
1092 1112 5
1093 1113 4
1094 1114 2
1095 1115
1096 1116 (walk example above by separate queries)
1097 1117
1098 1118 $ log 'reverse(ancestors(8, depth=2)) + reverse(ancestors(7, depth=2))'
1099 1119 8
1100 1120 4
1101 1121 2
1102 1122 7
1103 1123 6
1104 1124 5
1105 1125
1106 1126 (walk 2nd and 3rd ancestors)
1107 1127
1108 1128 $ log 'reverse(ancestors(7, depth=3, startdepth=2))'
1109 1129 5
1110 1130 4
1111 1131 3
1112 1132 2
1113 1133
1114 1134 (interleaved: '4' would be missing if higher-depth ancestors weren't scanned)
1115 1135
1116 1136 $ log 'reverse(ancestors(7+8, depth=2, startdepth=2))'
1117 1137 5
1118 1138 4
1119 1139 2
1120 1140
1121 1141 (note that 'ancestors(x, depth=y, startdepth=z)' does not identical to
1122 1142 'ancestors(x, depth=y) - ancestors(x, depth=z-1)' because a node may have
1123 1143 multiple depths)
1124 1144
1125 1145 $ log 'reverse(ancestors(7+8, depth=2) - ancestors(7+8, depth=1))'
1126 1146 5
1127 1147 2
1128 1148
1129 1149 test bad arguments passed to ancestors()
1130 1150
1131 1151 $ log 'ancestors(., depth=-1)'
1132 1152 hg: parse error: negative depth
1133 1153 [255]
1134 1154 $ log 'ancestors(., depth=foo)'
1135 1155 hg: parse error: ancestors expects an integer depth
1136 1156 [255]
1137 1157
1138 1158 test descendants
1139 1159
1140 1160 $ hg log -G -T '{rev}\n' --config experimental.graphshorten=True
1141 1161 @ 9
1142 1162 o 8
1143 1163 | o 7
1144 1164 | o 6
1145 1165 |/|
1146 1166 | o 5
1147 1167 o | 4
1148 1168 | o 3
1149 1169 o | 2
1150 1170 |/
1151 1171 o 1
1152 1172 o 0
1153 1173
1154 1174 (null is ultimate root and has optimized path)
1155 1175
1156 1176 $ log 'null:4 & descendants(null)'
1157 1177 -1
1158 1178 0
1159 1179 1
1160 1180 2
1161 1181 3
1162 1182 4
1163 1183
1164 1184 (including merge)
1165 1185
1166 1186 $ log ':8 & descendants(2)'
1167 1187 2
1168 1188 4
1169 1189 6
1170 1190 7
1171 1191 8
1172 1192
1173 1193 (multiple roots)
1174 1194
1175 1195 $ log ':8 & descendants(2+5)'
1176 1196 2
1177 1197 4
1178 1198 5
1179 1199 6
1180 1200 7
1181 1201 8
1182 1202
1183 1203 test descendants with depth limit
1184 1204
1185 1205 (depth=0 selects the node itself)
1186 1206
1187 1207 $ log 'descendants(0, depth=0)'
1188 1208 0
1189 1209 $ log 'null: & descendants(null, depth=0)'
1190 1210 -1
1191 1211
1192 1212 (p2 = null should be ignored)
1193 1213
1194 1214 $ log 'null: & descendants(null, depth=2)'
1195 1215 -1
1196 1216 0
1197 1217 1
1198 1218
1199 1219 (multiple paths: depth(6) = (2, 3))
1200 1220
1201 1221 $ log 'descendants(1+3, depth=2)'
1202 1222 1
1203 1223 2
1204 1224 3
1205 1225 4
1206 1226 5
1207 1227 6
1208 1228
1209 1229 (multiple paths: depth(5) = (1, 2), depth(6) = (2, 3))
1210 1230
1211 1231 $ log 'descendants(3+1, depth=2, startdepth=2)'
1212 1232 4
1213 1233 5
1214 1234 6
1215 1235
1216 1236 (multiple depths: depth(6) = (0, 2, 4), search for depth=2)
1217 1237
1218 1238 $ log 'descendants(0+3+6, depth=3, startdepth=1)'
1219 1239 1
1220 1240 2
1221 1241 3
1222 1242 4
1223 1243 5
1224 1244 6
1225 1245 7
1226 1246
1227 1247 (multiple depths: depth(6) = (0, 4), no match)
1228 1248
1229 1249 $ log 'descendants(0+6, depth=3, startdepth=1)'
1230 1250 1
1231 1251 2
1232 1252 3
1233 1253 4
1234 1254 5
1235 1255 7
1236 1256
1237 1257 test ancestors/descendants relation subscript:
1238 1258
1239 1259 $ log 'tip#generations[0]'
1240 1260 9
1241 1261 $ log '.#generations[-1]'
1242 1262 8
1243 1263 $ log '.#g[(-1)]'
1244 1264 8
1245 1265
1246 1266 $ hg debugrevspec -p parsed 'roots(:)#g[2]'
1247 1267 * parsed:
1248 1268 (relsubscript
1249 1269 (func
1250 1270 (symbol 'roots')
1251 1271 (rangeall
1252 1272 None))
1253 1273 (symbol 'g')
1254 1274 (symbol '2'))
1255 1275 2
1256 1276 3
1257 1277
1258 1278 test author
1259 1279
1260 1280 $ log 'author(bob)'
1261 1281 2
1262 1282 $ log 'author("re:bob|test")'
1263 1283 0
1264 1284 1
1265 1285 2
1266 1286 3
1267 1287 4
1268 1288 5
1269 1289 6
1270 1290 7
1271 1291 8
1272 1292 9
1273 1293 $ log 'author(r"re:\S")'
1274 1294 0
1275 1295 1
1276 1296 2
1277 1297 3
1278 1298 4
1279 1299 5
1280 1300 6
1281 1301 7
1282 1302 8
1283 1303 9
1284 1304 $ log 'branch(Γ©)'
1285 1305 8
1286 1306 9
1287 1307 $ log 'branch(a)'
1288 1308 0
1289 1309 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
1290 1310 0 a
1291 1311 2 a-b-c-
1292 1312 3 +a+b+c+
1293 1313 4 -a-b-c-
1294 1314 5 !a/b/c/
1295 1315 6 _a_b_c_
1296 1316 7 .a.b.c.
1297 1317 $ log 'children(ancestor(4,5))'
1298 1318 2
1299 1319 3
1300 1320
1301 1321 $ log 'children(4)'
1302 1322 6
1303 1323 8
1304 1324 $ log 'children(null)'
1305 1325 0
1306 1326
1307 1327 $ log 'closed()'
1308 1328 $ log 'contains(a)'
1309 1329 0
1310 1330 1
1311 1331 3
1312 1332 5
1313 1333 $ log 'contains("../repo/a")'
1314 1334 0
1315 1335 1
1316 1336 3
1317 1337 5
1318 1338 $ log 'desc(B)'
1319 1339 5
1320 1340 $ hg log -r 'desc(r"re:S?u")' --template "{rev} {desc|firstline}\n"
1321 1341 5 5 bug
1322 1342 6 6 issue619
1323 1343 $ log 'descendants(2 or 3)'
1324 1344 2
1325 1345 3
1326 1346 4
1327 1347 5
1328 1348 6
1329 1349 7
1330 1350 8
1331 1351 9
1332 1352 $ log 'file("b*")'
1333 1353 1
1334 1354 4
1335 1355 $ log 'filelog("b")'
1336 1356 1
1337 1357 4
1338 1358 $ log 'filelog("../repo/b")'
1339 1359 1
1340 1360 4
1341 1361 $ log 'follow()'
1342 1362 0
1343 1363 1
1344 1364 2
1345 1365 4
1346 1366 8
1347 1367 9
1348 1368 $ log 'grep("issue\d+")'
1349 1369 6
1350 1370 $ try 'grep("(")' # invalid regular expression
1351 1371 (func
1352 1372 (symbol 'grep')
1353 1373 (string '('))
1354 1374 hg: parse error: invalid match pattern: (unbalanced parenthesis|missing \),.*) (re)
1355 1375 [255]
1356 1376 $ try 'grep("\bissue\d+")'
1357 1377 (func
1358 1378 (symbol 'grep')
1359 1379 (string '\x08issue\\d+'))
1360 1380 * set:
1361 1381 <filteredset
1362 1382 <fullreposet+ 0:10>,
1363 1383 <grep '\x08issue\\d+'>>
1364 1384 $ try 'grep(r"\bissue\d+")'
1365 1385 (func
1366 1386 (symbol 'grep')
1367 1387 (string '\\bissue\\d+'))
1368 1388 * set:
1369 1389 <filteredset
1370 1390 <fullreposet+ 0:10>,
1371 1391 <grep '\\bissue\\d+'>>
1372 1392 6
1373 1393 $ try 'grep(r"\")'
1374 1394 hg: parse error at 7: unterminated string
1375 1395 (grep(r"\")
1376 1396 ^ here)
1377 1397 [255]
1378 1398 $ log 'head()'
1379 1399 0
1380 1400 1
1381 1401 2
1382 1402 3
1383 1403 4
1384 1404 5
1385 1405 6
1386 1406 7
1387 1407 9
1388 1408
1389 1409 Test heads
1390 1410
1391 1411 $ log 'heads(6::)'
1392 1412 7
1393 1413
1394 1414 heads() can be computed in subset '9:'
1395 1415
1396 1416 $ hg debugrevspec -s '9: & heads(all())'
1397 1417 * set:
1398 1418 <filteredset
1399 1419 <filteredset
1400 1420 <baseset [9]>,
1401 1421 <spanset+ 0:10>>,
1402 1422 <not
1403 1423 <filteredset
1404 1424 <baseset [9]>, set([0, 1, 2, 3, 4, 5, 6, 8])>>>
1405 1425 9
1406 1426
1407 1427 but should follow the order of the subset
1408 1428
1409 1429 $ log 'heads(all())'
1410 1430 7
1411 1431 9
1412 1432 $ log 'heads(tip:0)'
1413 1433 7
1414 1434 9
1415 1435 $ log 'tip:0 & heads(all())'
1416 1436 9
1417 1437 7
1418 1438 $ log 'tip:0 & heads(0:tip)'
1419 1439 9
1420 1440 7
1421 1441
1422 1442 $ log 'keyword(issue)'
1423 1443 6
1424 1444 $ log 'keyword("test a")'
1425 1445
1426 1446 Test first (=limit) and last
1427 1447
1428 1448 $ log 'limit(head(), 1)'
1429 1449 0
1430 1450 $ log 'limit(author("re:bob|test"), 3, 5)'
1431 1451 5
1432 1452 6
1433 1453 7
1434 1454 $ log 'limit(author("re:bob|test"), offset=6)'
1435 1455 6
1436 1456 $ log 'limit(author("re:bob|test"), offset=10)'
1437 1457 $ log 'limit(all(), 1, -1)'
1438 1458 hg: parse error: negative offset
1439 1459 [255]
1440 1460 $ log 'limit(all(), -1)'
1441 1461 hg: parse error: negative number to select
1442 1462 [255]
1443 1463 $ log 'limit(all(), 0)'
1444 1464
1445 1465 $ log 'last(all(), -1)'
1446 1466 hg: parse error: negative number to select
1447 1467 [255]
1448 1468 $ log 'last(all(), 0)'
1449 1469 $ log 'last(all(), 1)'
1450 1470 9
1451 1471 $ log 'last(all(), 2)'
1452 1472 8
1453 1473 9
1454 1474
1455 1475 Test smartset.slice() by first/last()
1456 1476
1457 1477 (using unoptimized set, filteredset as example)
1458 1478
1459 1479 $ hg debugrevspec --no-show-revs -s '0:7 & branch("re:")'
1460 1480 * set:
1461 1481 <filteredset
1462 1482 <spanset+ 0:8>,
1463 1483 <branch 're:'>>
1464 1484 $ log 'limit(0:7 & branch("re:"), 3, 4)'
1465 1485 4
1466 1486 5
1467 1487 6
1468 1488 $ log 'limit(7:0 & branch("re:"), 3, 4)'
1469 1489 3
1470 1490 2
1471 1491 1
1472 1492 $ log 'last(0:7 & branch("re:"), 2)'
1473 1493 6
1474 1494 7
1475 1495
1476 1496 (using baseset)
1477 1497
1478 1498 $ hg debugrevspec --no-show-revs -s 0+1+2+3+4+5+6+7
1479 1499 * set:
1480 1500 <baseset [0, 1, 2, 3, 4, 5, 6, 7]>
1481 1501 $ hg debugrevspec --no-show-revs -s 0::7
1482 1502 * set:
1483 1503 <baseset+ [0, 1, 2, 3, 4, 5, 6, 7]>
1484 1504 $ log 'limit(0+1+2+3+4+5+6+7, 3, 4)'
1485 1505 4
1486 1506 5
1487 1507 6
1488 1508 $ log 'limit(sort(0::7, rev), 3, 4)'
1489 1509 4
1490 1510 5
1491 1511 6
1492 1512 $ log 'limit(sort(0::7, -rev), 3, 4)'
1493 1513 3
1494 1514 2
1495 1515 1
1496 1516 $ log 'last(sort(0::7, rev), 2)'
1497 1517 6
1498 1518 7
1499 1519 $ hg debugrevspec -s 'limit(sort(0::7, rev), 3, 6)'
1500 1520 * set:
1501 1521 <baseset+ [6, 7]>
1502 1522 6
1503 1523 7
1504 1524 $ hg debugrevspec -s 'limit(sort(0::7, rev), 3, 9)'
1505 1525 * set:
1506 1526 <baseset+ []>
1507 1527 $ hg debugrevspec -s 'limit(sort(0::7, -rev), 3, 6)'
1508 1528 * set:
1509 1529 <baseset- [0, 1]>
1510 1530 1
1511 1531 0
1512 1532 $ hg debugrevspec -s 'limit(sort(0::7, -rev), 3, 9)'
1513 1533 * set:
1514 1534 <baseset- []>
1515 1535 $ hg debugrevspec -s 'limit(0::7, 0)'
1516 1536 * set:
1517 1537 <baseset+ []>
1518 1538
1519 1539 (using spanset)
1520 1540
1521 1541 $ hg debugrevspec --no-show-revs -s 0:7
1522 1542 * set:
1523 1543 <spanset+ 0:8>
1524 1544 $ log 'limit(0:7, 3, 4)'
1525 1545 4
1526 1546 5
1527 1547 6
1528 1548 $ log 'limit(7:0, 3, 4)'
1529 1549 3
1530 1550 2
1531 1551 1
1532 1552 $ log 'limit(0:7, 3, 6)'
1533 1553 6
1534 1554 7
1535 1555 $ log 'limit(7:0, 3, 6)'
1536 1556 1
1537 1557 0
1538 1558 $ log 'last(0:7, 2)'
1539 1559 6
1540 1560 7
1541 1561 $ hg debugrevspec -s 'limit(0:7, 3, 6)'
1542 1562 * set:
1543 1563 <spanset+ 6:8>
1544 1564 6
1545 1565 7
1546 1566 $ hg debugrevspec -s 'limit(0:7, 3, 9)'
1547 1567 * set:
1548 1568 <spanset+ 8:8>
1549 1569 $ hg debugrevspec -s 'limit(7:0, 3, 6)'
1550 1570 * set:
1551 1571 <spanset- 0:2>
1552 1572 1
1553 1573 0
1554 1574 $ hg debugrevspec -s 'limit(7:0, 3, 9)'
1555 1575 * set:
1556 1576 <spanset- 0:0>
1557 1577 $ hg debugrevspec -s 'limit(0:7, 0)'
1558 1578 * set:
1559 1579 <spanset+ 0:0>
1560 1580
1561 1581 Test order of first/last revisions
1562 1582
1563 1583 $ hg debugrevspec -s 'first(4:0, 3) & 3:'
1564 1584 * set:
1565 1585 <filteredset
1566 1586 <spanset- 2:5>,
1567 1587 <spanset+ 3:10>>
1568 1588 4
1569 1589 3
1570 1590
1571 1591 $ hg debugrevspec -s '3: & first(4:0, 3)'
1572 1592 * set:
1573 1593 <filteredset
1574 1594 <spanset+ 3:10>,
1575 1595 <spanset- 2:5>>
1576 1596 3
1577 1597 4
1578 1598
1579 1599 $ hg debugrevspec -s 'last(4:0, 3) & :1'
1580 1600 * set:
1581 1601 <filteredset
1582 1602 <spanset- 0:3>,
1583 1603 <spanset+ 0:2>>
1584 1604 1
1585 1605 0
1586 1606
1587 1607 $ hg debugrevspec -s ':1 & last(4:0, 3)'
1588 1608 * set:
1589 1609 <filteredset
1590 1610 <spanset+ 0:2>,
1591 1611 <spanset+ 0:3>>
1592 1612 0
1593 1613 1
1594 1614
1595 1615 Test scmutil.revsingle() should return the last revision
1596 1616
1597 1617 $ hg debugrevspec -s 'last(0::)'
1598 1618 * set:
1599 1619 <baseset slice=0:1
1600 1620 <generatorsetasc->>
1601 1621 9
1602 1622 $ hg identify -r '0::' --num
1603 1623 9
1604 1624
1605 1625 Test matching
1606 1626
1607 1627 $ log 'matching(6)'
1608 1628 6
1609 1629 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
1610 1630 6
1611 1631 7
1612 1632
1613 1633 Testing min and max
1614 1634
1615 1635 max: simple
1616 1636
1617 1637 $ log 'max(contains(a))'
1618 1638 5
1619 1639
1620 1640 max: simple on unordered set)
1621 1641
1622 1642 $ log 'max((4+0+2+5+7) and contains(a))'
1623 1643 5
1624 1644
1625 1645 max: no result
1626 1646
1627 1647 $ log 'max(contains(stringthatdoesnotappearanywhere))'
1628 1648
1629 1649 max: no result on unordered set
1630 1650
1631 1651 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1632 1652
1633 1653 min: simple
1634 1654
1635 1655 $ log 'min(contains(a))'
1636 1656 0
1637 1657
1638 1658 min: simple on unordered set
1639 1659
1640 1660 $ log 'min((4+0+2+5+7) and contains(a))'
1641 1661 0
1642 1662
1643 1663 min: empty
1644 1664
1645 1665 $ log 'min(contains(stringthatdoesnotappearanywhere))'
1646 1666
1647 1667 min: empty on unordered set
1648 1668
1649 1669 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
1650 1670
1651 1671
1652 1672 $ log 'merge()'
1653 1673 6
1654 1674 $ log 'branchpoint()'
1655 1675 1
1656 1676 4
1657 1677 $ log 'modifies(b)'
1658 1678 4
1659 1679 $ log 'modifies("path:b")'
1660 1680 4
1661 1681 $ log 'modifies("*")'
1662 1682 4
1663 1683 6
1664 1684 $ log 'modifies("set:modified()")'
1665 1685 4
1666 1686 $ log 'id(5)'
1667 1687 2
1668 1688 $ log 'only(9)'
1669 1689 8
1670 1690 9
1671 1691 $ log 'only(8)'
1672 1692 8
1673 1693 $ log 'only(9, 5)'
1674 1694 2
1675 1695 4
1676 1696 8
1677 1697 9
1678 1698 $ log 'only(7 + 9, 5 + 2)'
1679 1699 4
1680 1700 6
1681 1701 7
1682 1702 8
1683 1703 9
1684 1704
1685 1705 Test empty set input
1686 1706 $ log 'only(p2())'
1687 1707 $ log 'only(p1(), p2())'
1688 1708 0
1689 1709 1
1690 1710 2
1691 1711 4
1692 1712 8
1693 1713 9
1694 1714
1695 1715 Test '%' operator
1696 1716
1697 1717 $ log '9%'
1698 1718 8
1699 1719 9
1700 1720 $ log '9%5'
1701 1721 2
1702 1722 4
1703 1723 8
1704 1724 9
1705 1725 $ log '(7 + 9)%(5 + 2)'
1706 1726 4
1707 1727 6
1708 1728 7
1709 1729 8
1710 1730 9
1711 1731
1712 1732 Test operand of '%' is optimized recursively (issue4670)
1713 1733
1714 1734 $ try --optimize '8:9-8%'
1715 1735 (onlypost
1716 1736 (minus
1717 1737 (range
1718 1738 (symbol '8')
1719 1739 (symbol '9'))
1720 1740 (symbol '8')))
1721 1741 * optimized:
1722 1742 (func
1723 1743 (symbol 'only')
1724 1744 (difference
1725 1745 (range
1726 1746 (symbol '8')
1727 1747 (symbol '9'))
1728 1748 (symbol '8')))
1729 1749 * set:
1730 1750 <baseset+ [8, 9]>
1731 1751 8
1732 1752 9
1733 1753 $ try --optimize '(9)%(5)'
1734 1754 (only
1735 1755 (group
1736 1756 (symbol '9'))
1737 1757 (group
1738 1758 (symbol '5')))
1739 1759 * optimized:
1740 1760 (func
1741 1761 (symbol 'only')
1742 1762 (list
1743 1763 (symbol '9')
1744 1764 (symbol '5')))
1745 1765 * set:
1746 1766 <baseset+ [2, 4, 8, 9]>
1747 1767 2
1748 1768 4
1749 1769 8
1750 1770 9
1751 1771
1752 1772 Test the order of operations
1753 1773
1754 1774 $ log '7 + 9%5 + 2'
1755 1775 7
1756 1776 2
1757 1777 4
1758 1778 8
1759 1779 9
1760 1780
1761 1781 Test explicit numeric revision
1762 1782 $ log 'rev(-2)'
1763 1783 $ log 'rev(-1)'
1764 1784 -1
1765 1785 $ log 'rev(0)'
1766 1786 0
1767 1787 $ log 'rev(9)'
1768 1788 9
1769 1789 $ log 'rev(10)'
1770 1790 $ log 'rev(tip)'
1771 1791 hg: parse error: rev expects a number
1772 1792 [255]
1773 1793
1774 1794 Test hexadecimal revision
1775 1795 $ log 'id(2)'
1776 1796 $ log 'id(5)'
1777 1797 2
1778 1798 $ hg --config experimental.revisions.prefixhexnode=yes log --template '{rev}\n' -r 'id(x5)'
1779 1799 2
1780 1800 $ hg --config experimental.revisions.prefixhexnode=yes log --template '{rev}\n' -r 'x5'
1781 1801 2
1782 1802 $ hg --config experimental.revisions.prefixhexnode=yes log --template '{rev}\n' -r 'id(x)'
1783 1803 $ hg --config experimental.revisions.prefixhexnode=yes log --template '{rev}\n' -r 'x'
1784 1804 abort: 00changelog.i@: ambiguous identifier!
1785 1805 [255]
1786 1806 $ log 'id(23268)'
1787 1807 4
1788 1808 $ log 'id(2785f51eece)'
1789 1809 0
1790 1810 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1791 1811 8
1792 1812 $ log 'id(d5d0dcbdc4a)'
1793 1813 $ log 'id(d5d0dcbdc4w)'
1794 1814 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1795 1815 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1796 1816 $ log 'id(1.0)'
1797 1817 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1798 1818
1799 1819 Test null revision
1800 1820 $ log '(null)'
1801 1821 -1
1802 1822 $ log '(null:0)'
1803 1823 -1
1804 1824 0
1805 1825 $ log '(0:null)'
1806 1826 0
1807 1827 -1
1808 1828 $ log 'null::0'
1809 1829 -1
1810 1830 0
1811 1831 $ log 'null:tip - 0:'
1812 1832 -1
1813 1833 $ log 'null: and null::' | head -1
1814 1834 -1
1815 1835 $ log 'null: or 0:' | head -2
1816 1836 -1
1817 1837 0
1818 1838 $ log 'ancestors(null)'
1819 1839 -1
1820 1840 $ log 'reverse(null:)' | tail -2
1821 1841 0
1822 1842 -1
1823 1843 $ log 'first(null:)'
1824 1844 -1
1825 1845 $ log 'min(null:)'
1826 1846 BROKEN: should be '-1'
1827 1847 $ log 'tip:null and all()' | tail -2
1828 1848 1
1829 1849 0
1830 1850
1831 1851 Test working-directory revision
1832 1852 $ hg debugrevspec 'wdir()'
1833 1853 2147483647
1834 1854 $ hg debugrevspec 'wdir()^'
1835 1855 9
1836 1856 $ hg up 7
1837 1857 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1838 1858 $ hg debugrevspec 'wdir()^'
1839 1859 7
1840 1860 $ hg debugrevspec 'wdir()^0'
1841 1861 2147483647
1842 1862 $ hg debugrevspec 'wdir()~3'
1843 1863 5
1844 1864 $ hg debugrevspec 'ancestors(wdir())'
1845 1865 0
1846 1866 1
1847 1867 2
1848 1868 3
1849 1869 4
1850 1870 5
1851 1871 6
1852 1872 7
1853 1873 2147483647
1854 1874 $ hg debugrevspec '0:wdir() & ancestor(wdir())'
1855 1875 2147483647
1856 1876 $ hg debugrevspec '0:wdir() & ancestor(.:wdir())'
1857 1877 4
1858 1878 $ hg debugrevspec '0:wdir() & ancestor(wdir(), wdir())'
1859 1879 2147483647
1860 1880 $ hg debugrevspec '0:wdir() & ancestor(wdir(), tip)'
1861 1881 4
1862 1882 $ hg debugrevspec 'null:wdir() & ancestor(wdir(), null)'
1863 1883 -1
1864 1884 $ hg debugrevspec 'wdir()~0'
1865 1885 2147483647
1866 1886 $ hg debugrevspec 'p1(wdir())'
1867 1887 7
1868 1888 $ hg debugrevspec 'p2(wdir())'
1869 1889 $ hg debugrevspec 'parents(wdir())'
1870 1890 7
1871 1891 $ hg debugrevspec 'wdir()^1'
1872 1892 7
1873 1893 $ hg debugrevspec 'wdir()^2'
1874 1894 $ hg debugrevspec 'wdir()^3'
1875 1895 hg: parse error: ^ expects a number 0, 1, or 2
1876 1896 [255]
1877 1897 For tests consistency
1878 1898 $ hg up 9
1879 1899 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1880 1900 $ hg debugrevspec 'tip or wdir()'
1881 1901 9
1882 1902 2147483647
1883 1903 $ hg debugrevspec '0:tip and wdir()'
1884 1904 $ log '0:wdir()' | tail -3
1885 1905 8
1886 1906 9
1887 1907 2147483647
1888 1908 $ log 'wdir():0' | head -3
1889 1909 2147483647
1890 1910 9
1891 1911 8
1892 1912 $ log 'wdir():wdir()'
1893 1913 2147483647
1894 1914 $ log '(all() + wdir()) & min(. + wdir())'
1895 1915 9
1896 1916 $ log '(all() + wdir()) & max(. + wdir())'
1897 1917 2147483647
1898 1918 $ log 'first(wdir() + .)'
1899 1919 2147483647
1900 1920 $ log 'last(. + wdir())'
1901 1921 2147483647
1902 1922
1903 1923 Test working-directory integer revision and node id
1904 1924 (BUG: '0:wdir()' is still needed to populate wdir revision)
1905 1925
1906 1926 $ hg debugrevspec '0:wdir() & 2147483647'
1907 1927 2147483647
1908 1928 $ hg debugrevspec '0:wdir() & rev(2147483647)'
1909 1929 2147483647
1910 1930 $ hg debugrevspec '0:wdir() & ffffffffffffffffffffffffffffffffffffffff'
1911 1931 2147483647
1912 1932 $ hg debugrevspec '0:wdir() & ffffffffffff'
1913 1933 2147483647
1914 1934 $ hg debugrevspec '0:wdir() & id(ffffffffffffffffffffffffffffffffffffffff)'
1915 1935 2147483647
1916 1936 $ hg debugrevspec '0:wdir() & id(ffffffffffff)'
1917 1937 2147483647
1918 1938
1919 1939 $ cd ..
1920 1940
1921 1941 Test short 'ff...' hash collision
1922 1942 (BUG: '0:wdir()' is still needed to populate wdir revision)
1923 1943
1924 1944 $ hg init wdir-hashcollision
1925 1945 $ cd wdir-hashcollision
1926 1946 $ cat <<EOF >> .hg/hgrc
1927 1947 > [experimental]
1928 1948 > evolution.createmarkers=True
1929 1949 > EOF
1930 1950 $ echo 0 > a
1931 1951 $ hg ci -qAm 0
1932 1952 $ for i in 2463 2961 6726 78127; do
1933 1953 > hg up -q 0
1934 1954 > echo $i > a
1935 1955 > hg ci -qm $i
1936 1956 > done
1937 1957 $ hg up -q null
1938 1958 $ hg log -r '0:wdir()' -T '{rev}:{node} {shortest(node, 3)}\n'
1939 1959 0:b4e73ffab476aa0ee32ed81ca51e07169844bc6a b4e
1940 1960 1:fffbae3886c8fbb2114296380d276fd37715d571 fffba
1941 1961 2:fffb6093b00943f91034b9bdad069402c834e572 fffb6
1942 1962 3:fff48a9b9de34a4d64120c29548214c67980ade3 fff4
1943 1963 4:ffff85cff0ff78504fcdc3c0bc10de0c65379249 ffff8
1944 1964 2147483647:ffffffffffffffffffffffffffffffffffffffff fffff
1945 1965 $ hg debugobsolete fffbae3886c8fbb2114296380d276fd37715d571
1946 1966 obsoleted 1 changesets
1947 1967
1948 1968 $ hg debugrevspec '0:wdir() & fff'
1949 1969 abort: 00changelog.i@fff: ambiguous identifier!
1950 1970 [255]
1951 1971 $ hg debugrevspec '0:wdir() & ffff'
1952 1972 abort: 00changelog.i@ffff: ambiguous identifier!
1953 1973 [255]
1954 1974 $ hg debugrevspec '0:wdir() & fffb'
1955 1975 abort: 00changelog.i@fffb: ambiguous identifier!
1956 1976 [255]
1957 1977 BROKEN should be '2' (node lookup uses unfiltered repo)
1958 1978 $ hg debugrevspec '0:wdir() & id(fffb)'
1959 1979 BROKEN should be '2' (node lookup uses unfiltered repo)
1960 1980 $ hg debugrevspec '0:wdir() & ffff8'
1961 1981 4
1962 1982 $ hg debugrevspec '0:wdir() & fffff'
1963 1983 2147483647
1964 1984
1965 1985 $ cd ..
1966 1986
1967 1987 Test branch() with wdir()
1968 1988
1969 1989 $ cd repo
1970 1990
1971 1991 $ log '0:wdir() & branch("literal:Γ©")'
1972 1992 8
1973 1993 9
1974 1994 2147483647
1975 1995 $ log '0:wdir() & branch("re:Γ©")'
1976 1996 8
1977 1997 9
1978 1998 2147483647
1979 1999 $ log '0:wdir() & branch("re:^a")'
1980 2000 0
1981 2001 2
1982 2002 $ log '0:wdir() & branch(8)'
1983 2003 8
1984 2004 9
1985 2005 2147483647
1986 2006
1987 2007 branch(wdir()) returns all revisions belonging to the working branch. The wdir
1988 2008 itself isn't returned unless it is explicitly populated.
1989 2009
1990 2010 $ log 'branch(wdir())'
1991 2011 8
1992 2012 9
1993 2013 $ log '0:wdir() & branch(wdir())'
1994 2014 8
1995 2015 9
1996 2016 2147483647
1997 2017
1998 2018 $ log 'outgoing()'
1999 2019 8
2000 2020 9
2001 2021 $ log 'outgoing("../remote1")'
2002 2022 8
2003 2023 9
2004 2024 $ log 'outgoing("../remote2")'
2005 2025 3
2006 2026 5
2007 2027 6
2008 2028 7
2009 2029 9
2010 2030 $ log 'p1(merge())'
2011 2031 5
2012 2032 $ log 'p2(merge())'
2013 2033 4
2014 2034 $ log 'parents(merge())'
2015 2035 4
2016 2036 5
2017 2037 $ log 'p1(branchpoint())'
2018 2038 0
2019 2039 2
2020 2040 $ log 'p2(branchpoint())'
2021 2041 $ log 'parents(branchpoint())'
2022 2042 0
2023 2043 2
2024 2044 $ log 'removes(a)'
2025 2045 2
2026 2046 6
2027 2047 $ log 'roots(all())'
2028 2048 0
2029 2049 $ log 'reverse(2 or 3 or 4 or 5)'
2030 2050 5
2031 2051 4
2032 2052 3
2033 2053 2
2034 2054 $ log 'reverse(all())'
2035 2055 9
2036 2056 8
2037 2057 7
2038 2058 6
2039 2059 5
2040 2060 4
2041 2061 3
2042 2062 2
2043 2063 1
2044 2064 0
2045 2065 $ log 'reverse(all()) & filelog(b)'
2046 2066 4
2047 2067 1
2048 2068 $ log 'rev(5)'
2049 2069 5
2050 2070 $ log 'sort(limit(reverse(all()), 3))'
2051 2071 7
2052 2072 8
2053 2073 9
2054 2074 $ log 'sort(2 or 3 or 4 or 5, date)'
2055 2075 2
2056 2076 3
2057 2077 5
2058 2078 4
2059 2079 $ log 'tagged()'
2060 2080 6
2061 2081 $ log 'tag()'
2062 2082 6
2063 2083 $ log 'tag(1.0)'
2064 2084 6
2065 2085 $ log 'tag(tip)'
2066 2086 9
2067 2087
2068 2088 Test order of revisions in compound expression
2069 2089 ----------------------------------------------
2070 2090
2071 2091 The general rule is that only the outermost (= leftmost) predicate can
2072 2092 enforce its ordering requirement. The other predicates should take the
2073 2093 ordering defined by it.
2074 2094
2075 2095 'A & B' should follow the order of 'A':
2076 2096
2077 2097 $ log '2:0 & 0::2'
2078 2098 2
2079 2099 1
2080 2100 0
2081 2101
2082 2102 'head()' combines sets in right order:
2083 2103
2084 2104 $ log '2:0 & head()'
2085 2105 2
2086 2106 1
2087 2107 0
2088 2108
2089 2109 'x:y' takes ordering parameter into account:
2090 2110
2091 2111 $ try -p optimized '3:0 & 0:3 & not 2:1'
2092 2112 * optimized:
2093 2113 (difference
2094 2114 (and
2095 2115 (range
2096 2116 (symbol '3')
2097 2117 (symbol '0'))
2098 2118 (range
2099 2119 (symbol '0')
2100 2120 (symbol '3')))
2101 2121 (range
2102 2122 (symbol '2')
2103 2123 (symbol '1')))
2104 2124 * set:
2105 2125 <filteredset
2106 2126 <filteredset
2107 2127 <spanset- 0:4>,
2108 2128 <spanset+ 0:4>>,
2109 2129 <not
2110 2130 <spanset+ 1:3>>>
2111 2131 3
2112 2132 0
2113 2133
2114 2134 'a + b', which is optimized to '_list(a b)', should take the ordering of
2115 2135 the left expression:
2116 2136
2117 2137 $ try --optimize '2:0 & (0 + 1 + 2)'
2118 2138 (and
2119 2139 (range
2120 2140 (symbol '2')
2121 2141 (symbol '0'))
2122 2142 (group
2123 2143 (or
2124 2144 (list
2125 2145 (symbol '0')
2126 2146 (symbol '1')
2127 2147 (symbol '2')))))
2128 2148 * optimized:
2129 2149 (and
2130 2150 (range
2131 2151 (symbol '2')
2132 2152 (symbol '0'))
2133 2153 (func
2134 2154 (symbol '_list')
2135 2155 (string '0\x001\x002')))
2136 2156 * set:
2137 2157 <filteredset
2138 2158 <spanset- 0:3>,
2139 2159 <baseset [0, 1, 2]>>
2140 2160 2
2141 2161 1
2142 2162 0
2143 2163
2144 2164 'A + B' should take the ordering of the left expression:
2145 2165
2146 2166 $ try --optimize '2:0 & (0:1 + 2)'
2147 2167 (and
2148 2168 (range
2149 2169 (symbol '2')
2150 2170 (symbol '0'))
2151 2171 (group
2152 2172 (or
2153 2173 (list
2154 2174 (range
2155 2175 (symbol '0')
2156 2176 (symbol '1'))
2157 2177 (symbol '2')))))
2158 2178 * optimized:
2159 2179 (and
2160 2180 (range
2161 2181 (symbol '2')
2162 2182 (symbol '0'))
2163 2183 (or
2164 2184 (list
2165 2185 (range
2166 2186 (symbol '0')
2167 2187 (symbol '1'))
2168 2188 (symbol '2'))))
2169 2189 * set:
2170 2190 <filteredset
2171 2191 <spanset- 0:3>,
2172 2192 <addset
2173 2193 <spanset+ 0:2>,
2174 2194 <baseset [2]>>>
2175 2195 2
2176 2196 1
2177 2197 0
2178 2198
2179 2199 '_intlist(a b)' should behave like 'a + b':
2180 2200
2181 2201 $ trylist --optimize '2:0 & %ld' 0 1 2
2182 2202 (and
2183 2203 (range
2184 2204 (symbol '2')
2185 2205 (symbol '0'))
2186 2206 (func
2187 2207 (symbol '_intlist')
2188 2208 (string '0\x001\x002')))
2189 2209 * optimized:
2190 2210 (andsmally
2191 2211 (range
2192 2212 (symbol '2')
2193 2213 (symbol '0'))
2194 2214 (func
2195 2215 (symbol '_intlist')
2196 2216 (string '0\x001\x002')))
2197 2217 * set:
2198 2218 <filteredset
2199 2219 <spanset- 0:3>,
2200 2220 <baseset+ [0, 1, 2]>>
2201 2221 2
2202 2222 1
2203 2223 0
2204 2224
2205 2225 $ trylist --optimize '%ld & 2:0' 0 2 1
2206 2226 (and
2207 2227 (func
2208 2228 (symbol '_intlist')
2209 2229 (string '0\x002\x001'))
2210 2230 (range
2211 2231 (symbol '2')
2212 2232 (symbol '0')))
2213 2233 * optimized:
2214 2234 (and
2215 2235 (func
2216 2236 (symbol '_intlist')
2217 2237 (string '0\x002\x001'))
2218 2238 (range
2219 2239 (symbol '2')
2220 2240 (symbol '0')))
2221 2241 * set:
2222 2242 <filteredset
2223 2243 <baseset [0, 2, 1]>,
2224 2244 <spanset- 0:3>>
2225 2245 0
2226 2246 2
2227 2247 1
2228 2248
2229 2249 '_hexlist(a b)' should behave like 'a + b':
2230 2250
2231 2251 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
2232 2252 (and
2233 2253 (range
2234 2254 (symbol '2')
2235 2255 (symbol '0'))
2236 2256 (func
2237 2257 (symbol '_hexlist')
2238 2258 (string '*'))) (glob)
2239 2259 * optimized:
2240 2260 (and
2241 2261 (range
2242 2262 (symbol '2')
2243 2263 (symbol '0'))
2244 2264 (func
2245 2265 (symbol '_hexlist')
2246 2266 (string '*'))) (glob)
2247 2267 * set:
2248 2268 <filteredset
2249 2269 <spanset- 0:3>,
2250 2270 <baseset [0, 1, 2]>>
2251 2271 2
2252 2272 1
2253 2273 0
2254 2274
2255 2275 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
2256 2276 (and
2257 2277 (func
2258 2278 (symbol '_hexlist')
2259 2279 (string '*')) (glob)
2260 2280 (range
2261 2281 (symbol '2')
2262 2282 (symbol '0')))
2263 2283 * optimized:
2264 2284 (andsmally
2265 2285 (func
2266 2286 (symbol '_hexlist')
2267 2287 (string '*')) (glob)
2268 2288 (range
2269 2289 (symbol '2')
2270 2290 (symbol '0')))
2271 2291 * set:
2272 2292 <baseset [0, 2, 1]>
2273 2293 0
2274 2294 2
2275 2295 1
2276 2296
2277 2297 '_list' should not go through the slow follow-order path if order doesn't
2278 2298 matter:
2279 2299
2280 2300 $ try -p optimized '2:0 & not (0 + 1)'
2281 2301 * optimized:
2282 2302 (difference
2283 2303 (range
2284 2304 (symbol '2')
2285 2305 (symbol '0'))
2286 2306 (func
2287 2307 (symbol '_list')
2288 2308 (string '0\x001')))
2289 2309 * set:
2290 2310 <filteredset
2291 2311 <spanset- 0:3>,
2292 2312 <not
2293 2313 <baseset [0, 1]>>>
2294 2314 2
2295 2315
2296 2316 $ try -p optimized '2:0 & not (0:2 & (0 + 1))'
2297 2317 * optimized:
2298 2318 (difference
2299 2319 (range
2300 2320 (symbol '2')
2301 2321 (symbol '0'))
2302 2322 (and
2303 2323 (range
2304 2324 (symbol '0')
2305 2325 (symbol '2'))
2306 2326 (func
2307 2327 (symbol '_list')
2308 2328 (string '0\x001'))))
2309 2329 * set:
2310 2330 <filteredset
2311 2331 <spanset- 0:3>,
2312 2332 <not
2313 2333 <baseset [0, 1]>>>
2314 2334 2
2315 2335
2316 2336 because 'present()' does nothing other than suppressing an error, the
2317 2337 ordering requirement should be forwarded to the nested expression
2318 2338
2319 2339 $ try -p optimized 'present(2 + 0 + 1)'
2320 2340 * optimized:
2321 2341 (func
2322 2342 (symbol 'present')
2323 2343 (func
2324 2344 (symbol '_list')
2325 2345 (string '2\x000\x001')))
2326 2346 * set:
2327 2347 <baseset [2, 0, 1]>
2328 2348 2
2329 2349 0
2330 2350 1
2331 2351
2332 2352 $ try --optimize '2:0 & present(0 + 1 + 2)'
2333 2353 (and
2334 2354 (range
2335 2355 (symbol '2')
2336 2356 (symbol '0'))
2337 2357 (func
2338 2358 (symbol 'present')
2339 2359 (or
2340 2360 (list
2341 2361 (symbol '0')
2342 2362 (symbol '1')
2343 2363 (symbol '2')))))
2344 2364 * optimized:
2345 2365 (and
2346 2366 (range
2347 2367 (symbol '2')
2348 2368 (symbol '0'))
2349 2369 (func
2350 2370 (symbol 'present')
2351 2371 (func
2352 2372 (symbol '_list')
2353 2373 (string '0\x001\x002'))))
2354 2374 * set:
2355 2375 <filteredset
2356 2376 <spanset- 0:3>,
2357 2377 <baseset [0, 1, 2]>>
2358 2378 2
2359 2379 1
2360 2380 0
2361 2381
2362 2382 'reverse()' should take effect only if it is the outermost expression:
2363 2383
2364 2384 $ try --optimize '0:2 & reverse(all())'
2365 2385 (and
2366 2386 (range
2367 2387 (symbol '0')
2368 2388 (symbol '2'))
2369 2389 (func
2370 2390 (symbol 'reverse')
2371 2391 (func
2372 2392 (symbol 'all')
2373 2393 None)))
2374 2394 * optimized:
2375 2395 (and
2376 2396 (range
2377 2397 (symbol '0')
2378 2398 (symbol '2'))
2379 2399 (func
2380 2400 (symbol 'reverse')
2381 2401 (func
2382 2402 (symbol 'all')
2383 2403 None)))
2384 2404 * set:
2385 2405 <filteredset
2386 2406 <spanset+ 0:3>,
2387 2407 <spanset+ 0:10>>
2388 2408 0
2389 2409 1
2390 2410 2
2391 2411
2392 2412 'sort()' should take effect only if it is the outermost expression:
2393 2413
2394 2414 $ try --optimize '0:2 & sort(all(), -rev)'
2395 2415 (and
2396 2416 (range
2397 2417 (symbol '0')
2398 2418 (symbol '2'))
2399 2419 (func
2400 2420 (symbol 'sort')
2401 2421 (list
2402 2422 (func
2403 2423 (symbol 'all')
2404 2424 None)
2405 2425 (negate
2406 2426 (symbol 'rev')))))
2407 2427 * optimized:
2408 2428 (and
2409 2429 (range
2410 2430 (symbol '0')
2411 2431 (symbol '2'))
2412 2432 (func
2413 2433 (symbol 'sort')
2414 2434 (list
2415 2435 (func
2416 2436 (symbol 'all')
2417 2437 None)
2418 2438 (string '-rev'))))
2419 2439 * set:
2420 2440 <filteredset
2421 2441 <spanset+ 0:3>,
2422 2442 <spanset+ 0:10>>
2423 2443 0
2424 2444 1
2425 2445 2
2426 2446
2427 2447 invalid argument passed to noop sort():
2428 2448
2429 2449 $ log '0:2 & sort()'
2430 2450 hg: parse error: sort requires one or two arguments
2431 2451 [255]
2432 2452 $ log '0:2 & sort(all(), -invalid)'
2433 2453 hg: parse error: unknown sort key '-invalid'
2434 2454 [255]
2435 2455
2436 2456 for 'A & f(B)', 'B' should not be affected by the order of 'A':
2437 2457
2438 2458 $ try --optimize '2:0 & first(1 + 0 + 2)'
2439 2459 (and
2440 2460 (range
2441 2461 (symbol '2')
2442 2462 (symbol '0'))
2443 2463 (func
2444 2464 (symbol 'first')
2445 2465 (or
2446 2466 (list
2447 2467 (symbol '1')
2448 2468 (symbol '0')
2449 2469 (symbol '2')))))
2450 2470 * optimized:
2451 2471 (and
2452 2472 (range
2453 2473 (symbol '2')
2454 2474 (symbol '0'))
2455 2475 (func
2456 2476 (symbol 'first')
2457 2477 (func
2458 2478 (symbol '_list')
2459 2479 (string '1\x000\x002'))))
2460 2480 * set:
2461 2481 <filteredset
2462 2482 <baseset [1]>,
2463 2483 <spanset- 0:3>>
2464 2484 1
2465 2485
2466 2486 $ try --optimize '2:0 & not last(0 + 2 + 1)'
2467 2487 (and
2468 2488 (range
2469 2489 (symbol '2')
2470 2490 (symbol '0'))
2471 2491 (not
2472 2492 (func
2473 2493 (symbol 'last')
2474 2494 (or
2475 2495 (list
2476 2496 (symbol '0')
2477 2497 (symbol '2')
2478 2498 (symbol '1'))))))
2479 2499 * optimized:
2480 2500 (difference
2481 2501 (range
2482 2502 (symbol '2')
2483 2503 (symbol '0'))
2484 2504 (func
2485 2505 (symbol 'last')
2486 2506 (func
2487 2507 (symbol '_list')
2488 2508 (string '0\x002\x001'))))
2489 2509 * set:
2490 2510 <filteredset
2491 2511 <spanset- 0:3>,
2492 2512 <not
2493 2513 <baseset [1]>>>
2494 2514 2
2495 2515 0
2496 2516
2497 2517 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
2498 2518
2499 2519 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
2500 2520 (and
2501 2521 (range
2502 2522 (symbol '2')
2503 2523 (symbol '0'))
2504 2524 (range
2505 2525 (group
2506 2526 (or
2507 2527 (list
2508 2528 (symbol '1')
2509 2529 (symbol '0')
2510 2530 (symbol '2'))))
2511 2531 (group
2512 2532 (or
2513 2533 (list
2514 2534 (symbol '0')
2515 2535 (symbol '2')
2516 2536 (symbol '1'))))))
2517 2537 * optimized:
2518 2538 (and
2519 2539 (range
2520 2540 (symbol '2')
2521 2541 (symbol '0'))
2522 2542 (range
2523 2543 (func
2524 2544 (symbol '_list')
2525 2545 (string '1\x000\x002'))
2526 2546 (func
2527 2547 (symbol '_list')
2528 2548 (string '0\x002\x001'))))
2529 2549 * set:
2530 2550 <filteredset
2531 2551 <spanset- 0:3>,
2532 2552 <baseset [1]>>
2533 2553 1
2534 2554
2535 2555 'A & B' can be rewritten as 'flipand(B, A)' by weight.
2536 2556
2537 2557 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
2538 2558 (and
2539 2559 (func
2540 2560 (symbol 'contains')
2541 2561 (string 'glob:*'))
2542 2562 (group
2543 2563 (or
2544 2564 (list
2545 2565 (symbol '2')
2546 2566 (symbol '0')
2547 2567 (symbol '1')))))
2548 2568 * optimized:
2549 2569 (andsmally
2550 2570 (func
2551 2571 (symbol 'contains')
2552 2572 (string 'glob:*'))
2553 2573 (func
2554 2574 (symbol '_list')
2555 2575 (string '2\x000\x001')))
2556 2576 * set:
2557 2577 <filteredset
2558 2578 <baseset+ [0, 1, 2]>,
2559 2579 <contains 'glob:*'>>
2560 2580 0
2561 2581 1
2562 2582 2
2563 2583
2564 2584 and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides
2565 2585 the order appropriately:
2566 2586
2567 2587 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
2568 2588 (and
2569 2589 (func
2570 2590 (symbol 'reverse')
2571 2591 (func
2572 2592 (symbol 'contains')
2573 2593 (string 'glob:*')))
2574 2594 (group
2575 2595 (or
2576 2596 (list
2577 2597 (symbol '0')
2578 2598 (symbol '2')
2579 2599 (symbol '1')))))
2580 2600 * optimized:
2581 2601 (andsmally
2582 2602 (func
2583 2603 (symbol 'reverse')
2584 2604 (func
2585 2605 (symbol 'contains')
2586 2606 (string 'glob:*')))
2587 2607 (func
2588 2608 (symbol '_list')
2589 2609 (string '0\x002\x001')))
2590 2610 * set:
2591 2611 <filteredset
2592 2612 <baseset- [0, 1, 2]>,
2593 2613 <contains 'glob:*'>>
2594 2614 2
2595 2615 1
2596 2616 0
2597 2617
2598 2618 test sort revset
2599 2619 --------------------------------------------
2600 2620
2601 2621 test when adding two unordered revsets
2602 2622
2603 2623 $ log 'sort(keyword(issue) or modifies(b))'
2604 2624 4
2605 2625 6
2606 2626
2607 2627 test when sorting a reversed collection in the same way it is
2608 2628
2609 2629 $ log 'sort(reverse(all()), -rev)'
2610 2630 9
2611 2631 8
2612 2632 7
2613 2633 6
2614 2634 5
2615 2635 4
2616 2636 3
2617 2637 2
2618 2638 1
2619 2639 0
2620 2640
2621 2641 test when sorting a reversed collection
2622 2642
2623 2643 $ log 'sort(reverse(all()), rev)'
2624 2644 0
2625 2645 1
2626 2646 2
2627 2647 3
2628 2648 4
2629 2649 5
2630 2650 6
2631 2651 7
2632 2652 8
2633 2653 9
2634 2654
2635 2655
2636 2656 test sorting two sorted collections in different orders
2637 2657
2638 2658 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
2639 2659 2
2640 2660 6
2641 2661 8
2642 2662 9
2643 2663
2644 2664 test sorting two sorted collections in different orders backwards
2645 2665
2646 2666 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
2647 2667 9
2648 2668 8
2649 2669 6
2650 2670 2
2651 2671
2652 2672 test empty sort key which is noop
2653 2673
2654 2674 $ log 'sort(0 + 2 + 1, "")'
2655 2675 0
2656 2676 2
2657 2677 1
2658 2678
2659 2679 test invalid sort keys
2660 2680
2661 2681 $ log 'sort(all(), -invalid)'
2662 2682 hg: parse error: unknown sort key '-invalid'
2663 2683 [255]
2664 2684
2665 2685 $ cd ..
2666 2686
2667 2687 test sorting by multiple keys including variable-length strings
2668 2688
2669 2689 $ hg init sorting
2670 2690 $ cd sorting
2671 2691 $ cat <<EOF >> .hg/hgrc
2672 2692 > [ui]
2673 2693 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
2674 2694 > [templatealias]
2675 2695 > p5(s) = pad(s, 5)
2676 2696 > EOF
2677 2697 $ hg branch -qf b12
2678 2698 $ hg ci -m m111 -u u112 -d '111 10800'
2679 2699 $ hg branch -qf b11
2680 2700 $ hg ci -m m12 -u u111 -d '112 7200'
2681 2701 $ hg branch -qf b111
2682 2702 $ hg ci -m m11 -u u12 -d '111 3600'
2683 2703 $ hg branch -qf b112
2684 2704 $ hg ci -m m111 -u u11 -d '120 0'
2685 2705 $ hg branch -qf b111
2686 2706 $ hg ci -m m112 -u u111 -d '110 14400'
2687 2707 created new head
2688 2708
2689 2709 compare revisions (has fast path):
2690 2710
2691 2711 $ hg log -r 'sort(all(), rev)'
2692 2712 0 b12 m111 u112 111 10800
2693 2713 1 b11 m12 u111 112 7200
2694 2714 2 b111 m11 u12 111 3600
2695 2715 3 b112 m111 u11 120 0
2696 2716 4 b111 m112 u111 110 14400
2697 2717
2698 2718 $ hg log -r 'sort(all(), -rev)'
2699 2719 4 b111 m112 u111 110 14400
2700 2720 3 b112 m111 u11 120 0
2701 2721 2 b111 m11 u12 111 3600
2702 2722 1 b11 m12 u111 112 7200
2703 2723 0 b12 m111 u112 111 10800
2704 2724
2705 2725 compare variable-length strings (issue5218):
2706 2726
2707 2727 $ hg log -r 'sort(all(), branch)'
2708 2728 1 b11 m12 u111 112 7200
2709 2729 2 b111 m11 u12 111 3600
2710 2730 4 b111 m112 u111 110 14400
2711 2731 3 b112 m111 u11 120 0
2712 2732 0 b12 m111 u112 111 10800
2713 2733
2714 2734 $ hg log -r 'sort(all(), -branch)'
2715 2735 0 b12 m111 u112 111 10800
2716 2736 3 b112 m111 u11 120 0
2717 2737 2 b111 m11 u12 111 3600
2718 2738 4 b111 m112 u111 110 14400
2719 2739 1 b11 m12 u111 112 7200
2720 2740
2721 2741 $ hg log -r 'sort(all(), desc)'
2722 2742 2 b111 m11 u12 111 3600
2723 2743 0 b12 m111 u112 111 10800
2724 2744 3 b112 m111 u11 120 0
2725 2745 4 b111 m112 u111 110 14400
2726 2746 1 b11 m12 u111 112 7200
2727 2747
2728 2748 $ hg log -r 'sort(all(), -desc)'
2729 2749 1 b11 m12 u111 112 7200
2730 2750 4 b111 m112 u111 110 14400
2731 2751 0 b12 m111 u112 111 10800
2732 2752 3 b112 m111 u11 120 0
2733 2753 2 b111 m11 u12 111 3600
2734 2754
2735 2755 $ hg log -r 'sort(all(), user)'
2736 2756 3 b112 m111 u11 120 0
2737 2757 1 b11 m12 u111 112 7200
2738 2758 4 b111 m112 u111 110 14400
2739 2759 0 b12 m111 u112 111 10800
2740 2760 2 b111 m11 u12 111 3600
2741 2761
2742 2762 $ hg log -r 'sort(all(), -user)'
2743 2763 2 b111 m11 u12 111 3600
2744 2764 0 b12 m111 u112 111 10800
2745 2765 1 b11 m12 u111 112 7200
2746 2766 4 b111 m112 u111 110 14400
2747 2767 3 b112 m111 u11 120 0
2748 2768
2749 2769 compare dates (tz offset should have no effect):
2750 2770
2751 2771 $ hg log -r 'sort(all(), date)'
2752 2772 4 b111 m112 u111 110 14400
2753 2773 0 b12 m111 u112 111 10800
2754 2774 2 b111 m11 u12 111 3600
2755 2775 1 b11 m12 u111 112 7200
2756 2776 3 b112 m111 u11 120 0
2757 2777
2758 2778 $ hg log -r 'sort(all(), -date)'
2759 2779 3 b112 m111 u11 120 0
2760 2780 1 b11 m12 u111 112 7200
2761 2781 0 b12 m111 u112 111 10800
2762 2782 2 b111 m11 u12 111 3600
2763 2783 4 b111 m112 u111 110 14400
2764 2784
2765 2785 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
2766 2786 because '-k' reverses the comparison, not the list itself:
2767 2787
2768 2788 $ hg log -r 'sort(0 + 2, date)'
2769 2789 0 b12 m111 u112 111 10800
2770 2790 2 b111 m11 u12 111 3600
2771 2791
2772 2792 $ hg log -r 'sort(0 + 2, -date)'
2773 2793 0 b12 m111 u112 111 10800
2774 2794 2 b111 m11 u12 111 3600
2775 2795
2776 2796 $ hg log -r 'reverse(sort(0 + 2, date))'
2777 2797 2 b111 m11 u12 111 3600
2778 2798 0 b12 m111 u112 111 10800
2779 2799
2780 2800 sort by multiple keys:
2781 2801
2782 2802 $ hg log -r 'sort(all(), "branch -rev")'
2783 2803 1 b11 m12 u111 112 7200
2784 2804 4 b111 m112 u111 110 14400
2785 2805 2 b111 m11 u12 111 3600
2786 2806 3 b112 m111 u11 120 0
2787 2807 0 b12 m111 u112 111 10800
2788 2808
2789 2809 $ hg log -r 'sort(all(), "-desc -date")'
2790 2810 1 b11 m12 u111 112 7200
2791 2811 4 b111 m112 u111 110 14400
2792 2812 3 b112 m111 u11 120 0
2793 2813 0 b12 m111 u112 111 10800
2794 2814 2 b111 m11 u12 111 3600
2795 2815
2796 2816 $ hg log -r 'sort(all(), "user -branch date rev")'
2797 2817 3 b112 m111 u11 120 0
2798 2818 4 b111 m112 u111 110 14400
2799 2819 1 b11 m12 u111 112 7200
2800 2820 0 b12 m111 u112 111 10800
2801 2821 2 b111 m11 u12 111 3600
2802 2822
2803 2823 toposort prioritises graph branches
2804 2824
2805 2825 $ hg up 2
2806 2826 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2807 2827 $ touch a
2808 2828 $ hg addremove
2809 2829 adding a
2810 2830 $ hg ci -m 't1' -u 'tu' -d '130 0'
2811 2831 created new head
2812 2832 $ echo 'a' >> a
2813 2833 $ hg ci -m 't2' -u 'tu' -d '130 0'
2814 2834 $ hg book book1
2815 2835 $ hg up 4
2816 2836 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2817 2837 (leaving bookmark book1)
2818 2838 $ touch a
2819 2839 $ hg addremove
2820 2840 adding a
2821 2841 $ hg ci -m 't3' -u 'tu' -d '130 0'
2822 2842
2823 2843 $ hg log -r 'sort(all(), topo)'
2824 2844 7 b111 t3 tu 130 0
2825 2845 4 b111 m112 u111 110 14400
2826 2846 3 b112 m111 u11 120 0
2827 2847 6 b111 t2 tu 130 0
2828 2848 5 b111 t1 tu 130 0
2829 2849 2 b111 m11 u12 111 3600
2830 2850 1 b11 m12 u111 112 7200
2831 2851 0 b12 m111 u112 111 10800
2832 2852
2833 2853 $ hg log -r 'sort(all(), -topo)'
2834 2854 0 b12 m111 u112 111 10800
2835 2855 1 b11 m12 u111 112 7200
2836 2856 2 b111 m11 u12 111 3600
2837 2857 5 b111 t1 tu 130 0
2838 2858 6 b111 t2 tu 130 0
2839 2859 3 b112 m111 u11 120 0
2840 2860 4 b111 m112 u111 110 14400
2841 2861 7 b111 t3 tu 130 0
2842 2862
2843 2863 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
2844 2864 6 b111 t2 tu 130 0
2845 2865 5 b111 t1 tu 130 0
2846 2866 7 b111 t3 tu 130 0
2847 2867 4 b111 m112 u111 110 14400
2848 2868 3 b112 m111 u11 120 0
2849 2869 2 b111 m11 u12 111 3600
2850 2870 1 b11 m12 u111 112 7200
2851 2871 0 b12 m111 u112 111 10800
2852 2872
2853 2873 topographical sorting can't be combined with other sort keys, and you can't
2854 2874 use the topo.firstbranch option when topo sort is not active:
2855 2875
2856 2876 $ hg log -r 'sort(all(), "topo user")'
2857 2877 hg: parse error: topo sort order cannot be combined with other sort keys
2858 2878 [255]
2859 2879
2860 2880 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
2861 2881 hg: parse error: topo.firstbranch can only be used when using the topo sort key
2862 2882 [255]
2863 2883
2864 2884 topo.firstbranch should accept any kind of expressions:
2865 2885
2866 2886 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
2867 2887 0 b12 m111 u112 111 10800
2868 2888
2869 2889 $ cd ..
2870 2890 $ cd repo
2871 2891
2872 2892 test multiline revset with errors
2873 2893
2874 2894 $ echo > multiline-revset
2875 2895 $ echo '. +' >> multiline-revset
2876 2896 $ echo '.^ +' >> multiline-revset
2877 2897 $ hg log -r "`cat multiline-revset`"
2878 2898 hg: parse error at 9: not a prefix: end
2879 2899 ( . + .^ +
2880 2900 ^ here)
2881 2901 [255]
2882 2902 $ hg debugrevspec -v 'revset(first(rev(0)))' -p all
2883 2903 * parsed:
2884 2904 (func
2885 2905 (symbol 'revset')
2886 2906 (func
2887 2907 (symbol 'first')
2888 2908 (func
2889 2909 (symbol 'rev')
2890 2910 (symbol '0'))))
2891 2911 * expanded:
2892 2912 (func
2893 2913 (symbol 'revset')
2894 2914 (func
2895 2915 (symbol 'first')
2896 2916 (func
2897 2917 (symbol 'rev')
2898 2918 (symbol '0'))))
2899 2919 * concatenated:
2900 2920 (func
2901 2921 (symbol 'revset')
2902 2922 (func
2903 2923 (symbol 'first')
2904 2924 (func
2905 2925 (symbol 'rev')
2906 2926 (symbol '0'))))
2907 2927 * analyzed:
2908 2928 (func
2909 2929 (symbol 'first')
2910 2930 (func
2911 2931 (symbol 'rev')
2912 2932 (symbol '0')))
2913 2933 * optimized:
2914 2934 (func
2915 2935 (symbol 'first')
2916 2936 (func
2917 2937 (symbol 'rev')
2918 2938 (symbol '0')))
2919 2939 * set:
2920 2940 <baseset+ [0]>
2921 2941 0
General Comments 0
You need to be logged in to leave comments. Login now