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