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