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