##// END OF EJS Templates
revset: wrap arguments of 'or' by 'list' node...
Yuya Nishihara -
r29929:b3845cab default
parent child Browse files
Show More
@@ -1,3731 +1,3738 b''
1 1 # revset.py - revision set queries for mercurial
2 2 #
3 3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import heapq
11 11 import re
12 12
13 13 from .i18n import _
14 14 from . import (
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 parser,
23 23 pathutil,
24 24 phases,
25 25 registrar,
26 26 repoview,
27 27 util,
28 28 )
29 29
30 30 def _revancestors(repo, revs, followfirst):
31 31 """Like revlog.ancestors(), but supports followfirst."""
32 32 if followfirst:
33 33 cut = 1
34 34 else:
35 35 cut = None
36 36 cl = repo.changelog
37 37
38 38 def iterate():
39 39 revs.sort(reverse=True)
40 40 irevs = iter(revs)
41 41 h = []
42 42
43 43 inputrev = next(irevs, None)
44 44 if inputrev is not None:
45 45 heapq.heappush(h, -inputrev)
46 46
47 47 seen = set()
48 48 while h:
49 49 current = -heapq.heappop(h)
50 50 if current == inputrev:
51 51 inputrev = next(irevs, None)
52 52 if inputrev is not None:
53 53 heapq.heappush(h, -inputrev)
54 54 if current not in seen:
55 55 seen.add(current)
56 56 yield current
57 57 for parent in cl.parentrevs(current)[:cut]:
58 58 if parent != node.nullrev:
59 59 heapq.heappush(h, -parent)
60 60
61 61 return generatorset(iterate(), iterasc=False)
62 62
63 63 def _revdescendants(repo, revs, followfirst):
64 64 """Like revlog.descendants() but supports followfirst."""
65 65 if followfirst:
66 66 cut = 1
67 67 else:
68 68 cut = None
69 69
70 70 def iterate():
71 71 cl = repo.changelog
72 72 # XXX this should be 'parentset.min()' assuming 'parentset' is a
73 73 # smartset (and if it is not, it should.)
74 74 first = min(revs)
75 75 nullrev = node.nullrev
76 76 if first == nullrev:
77 77 # Are there nodes with a null first parent and a non-null
78 78 # second one? Maybe. Do we care? Probably not.
79 79 for i in cl:
80 80 yield i
81 81 else:
82 82 seen = set(revs)
83 83 for i in cl.revs(first + 1):
84 84 for x in cl.parentrevs(i)[:cut]:
85 85 if x != nullrev and x in seen:
86 86 seen.add(i)
87 87 yield i
88 88 break
89 89
90 90 return generatorset(iterate(), iterasc=True)
91 91
92 92 def _reachablerootspure(repo, minroot, roots, heads, includepath):
93 93 """return (heads(::<roots> and ::<heads>))
94 94
95 95 If includepath is True, return (<roots>::<heads>)."""
96 96 if not roots:
97 97 return []
98 98 parentrevs = repo.changelog.parentrevs
99 99 roots = set(roots)
100 100 visit = list(heads)
101 101 reachable = set()
102 102 seen = {}
103 103 # prefetch all the things! (because python is slow)
104 104 reached = reachable.add
105 105 dovisit = visit.append
106 106 nextvisit = visit.pop
107 107 # open-code the post-order traversal due to the tiny size of
108 108 # sys.getrecursionlimit()
109 109 while visit:
110 110 rev = nextvisit()
111 111 if rev in roots:
112 112 reached(rev)
113 113 if not includepath:
114 114 continue
115 115 parents = parentrevs(rev)
116 116 seen[rev] = parents
117 117 for parent in parents:
118 118 if parent >= minroot and parent not in seen:
119 119 dovisit(parent)
120 120 if not reachable:
121 121 return baseset()
122 122 if not includepath:
123 123 return reachable
124 124 for rev in sorted(seen):
125 125 for parent in seen[rev]:
126 126 if parent in reachable:
127 127 reached(rev)
128 128 return reachable
129 129
130 130 def reachableroots(repo, roots, heads, includepath=False):
131 131 """return (heads(::<roots> and ::<heads>))
132 132
133 133 If includepath is True, return (<roots>::<heads>)."""
134 134 if not roots:
135 135 return baseset()
136 136 minroot = roots.min()
137 137 roots = list(roots)
138 138 heads = list(heads)
139 139 try:
140 140 revs = repo.changelog.reachableroots(minroot, heads, roots, includepath)
141 141 except AttributeError:
142 142 revs = _reachablerootspure(repo, minroot, roots, heads, includepath)
143 143 revs = baseset(revs)
144 144 revs.sort()
145 145 return revs
146 146
147 147 elements = {
148 148 # token-type: binding-strength, primary, prefix, infix, suffix
149 149 "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
150 150 "##": (20, None, None, ("_concat", 20), None),
151 151 "~": (18, None, None, ("ancestor", 18), None),
152 152 "^": (18, None, None, ("parent", 18), "parentpost"),
153 153 "-": (5, None, ("negate", 19), ("minus", 5), None),
154 154 "::": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
155 155 "..": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"),
156 156 ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"),
157 157 "not": (10, None, ("not", 10), None, None),
158 158 "!": (10, None, ("not", 10), None, None),
159 159 "and": (5, None, None, ("and", 5), None),
160 160 "&": (5, None, None, ("and", 5), None),
161 161 "%": (5, None, None, ("only", 5), "onlypost"),
162 162 "or": (4, None, None, ("or", 4), None),
163 163 "|": (4, None, None, ("or", 4), None),
164 164 "+": (4, None, None, ("or", 4), None),
165 165 "=": (3, None, None, ("keyvalue", 3), None),
166 166 ",": (2, None, None, ("list", 2), None),
167 167 ")": (0, None, None, None, None),
168 168 "symbol": (0, "symbol", None, None, None),
169 169 "string": (0, "string", None, None, None),
170 170 "end": (0, None, None, None, None),
171 171 }
172 172
173 173 keywords = set(['and', 'or', 'not'])
174 174
175 175 # default set of valid characters for the initial letter of symbols
176 176 _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
177 177 if c.isalnum() or c in '._@' or ord(c) > 127)
178 178
179 179 # default set of valid characters for non-initial letters of symbols
180 180 _symletters = set(c for c in [chr(i) for i in xrange(256)]
181 181 if c.isalnum() or c in '-._/@' or ord(c) > 127)
182 182
183 183 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
184 184 '''
185 185 Parse a revset statement into a stream of tokens
186 186
187 187 ``syminitletters`` is the set of valid characters for the initial
188 188 letter of symbols.
189 189
190 190 By default, character ``c`` is recognized as valid for initial
191 191 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
192 192
193 193 ``symletters`` is the set of valid characters for non-initial
194 194 letters of symbols.
195 195
196 196 By default, character ``c`` is recognized as valid for non-initial
197 197 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
198 198
199 199 Check that @ is a valid unquoted token character (issue3686):
200 200 >>> list(tokenize("@::"))
201 201 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
202 202
203 203 '''
204 204 if syminitletters is None:
205 205 syminitletters = _syminitletters
206 206 if symletters is None:
207 207 symletters = _symletters
208 208
209 209 if program and lookup:
210 210 # attempt to parse old-style ranges first to deal with
211 211 # things like old-tag which contain query metacharacters
212 212 parts = program.split(':', 1)
213 213 if all(lookup(sym) for sym in parts if sym):
214 214 if parts[0]:
215 215 yield ('symbol', parts[0], 0)
216 216 if len(parts) > 1:
217 217 s = len(parts[0])
218 218 yield (':', None, s)
219 219 if parts[1]:
220 220 yield ('symbol', parts[1], s + 1)
221 221 yield ('end', None, len(program))
222 222 return
223 223
224 224 pos, l = 0, len(program)
225 225 while pos < l:
226 226 c = program[pos]
227 227 if c.isspace(): # skip inter-token whitespace
228 228 pass
229 229 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
230 230 yield ('::', None, pos)
231 231 pos += 1 # skip ahead
232 232 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
233 233 yield ('..', None, pos)
234 234 pos += 1 # skip ahead
235 235 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
236 236 yield ('##', None, pos)
237 237 pos += 1 # skip ahead
238 238 elif c in "():=,-|&+!~^%": # handle simple operators
239 239 yield (c, None, pos)
240 240 elif (c in '"\'' or c == 'r' and
241 241 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
242 242 if c == 'r':
243 243 pos += 1
244 244 c = program[pos]
245 245 decode = lambda x: x
246 246 else:
247 247 decode = parser.unescapestr
248 248 pos += 1
249 249 s = pos
250 250 while pos < l: # find closing quote
251 251 d = program[pos]
252 252 if d == '\\': # skip over escaped characters
253 253 pos += 2
254 254 continue
255 255 if d == c:
256 256 yield ('string', decode(program[s:pos]), s)
257 257 break
258 258 pos += 1
259 259 else:
260 260 raise error.ParseError(_("unterminated string"), s)
261 261 # gather up a symbol/keyword
262 262 elif c in syminitletters:
263 263 s = pos
264 264 pos += 1
265 265 while pos < l: # find end of symbol
266 266 d = program[pos]
267 267 if d not in symletters:
268 268 break
269 269 if d == '.' and program[pos - 1] == '.': # special case for ..
270 270 pos -= 1
271 271 break
272 272 pos += 1
273 273 sym = program[s:pos]
274 274 if sym in keywords: # operator keywords
275 275 yield (sym, None, s)
276 276 elif '-' in sym:
277 277 # some jerk gave us foo-bar-baz, try to check if it's a symbol
278 278 if lookup and lookup(sym):
279 279 # looks like a real symbol
280 280 yield ('symbol', sym, s)
281 281 else:
282 282 # looks like an expression
283 283 parts = sym.split('-')
284 284 for p in parts[:-1]:
285 285 if p: # possible consecutive -
286 286 yield ('symbol', p, s)
287 287 s += len(p)
288 288 yield ('-', None, pos)
289 289 s += 1
290 290 if parts[-1]: # possible trailing -
291 291 yield ('symbol', parts[-1], s)
292 292 else:
293 293 yield ('symbol', sym, s)
294 294 pos -= 1
295 295 else:
296 296 raise error.ParseError(_("syntax error in revset '%s'") %
297 297 program, pos)
298 298 pos += 1
299 299 yield ('end', None, pos)
300 300
301 301 # helpers
302 302
303 303 def getsymbol(x):
304 304 if x and x[0] == 'symbol':
305 305 return x[1]
306 306 raise error.ParseError(_('not a symbol'))
307 307
308 308 def getstring(x, err):
309 309 if x and (x[0] == 'string' or x[0] == 'symbol'):
310 310 return x[1]
311 311 raise error.ParseError(err)
312 312
313 313 def getlist(x):
314 314 if not x:
315 315 return []
316 316 if x[0] == 'list':
317 317 return list(x[1:])
318 318 return [x]
319 319
320 320 def getargs(x, min, max, err):
321 321 l = getlist(x)
322 322 if len(l) < min or (max >= 0 and len(l) > max):
323 323 raise error.ParseError(err)
324 324 return l
325 325
326 326 def getargsdict(x, funcname, keys):
327 327 return parser.buildargsdict(getlist(x), funcname, keys.split(),
328 328 keyvaluenode='keyvalue', keynode='symbol')
329 329
330 330 def getset(repo, subset, x):
331 331 if not x:
332 332 raise error.ParseError(_("missing argument"))
333 333 s = methods[x[0]](repo, subset, *x[1:])
334 334 if util.safehasattr(s, 'isascending'):
335 335 return s
336 336 # else case should not happen, because all non-func are internal,
337 337 # ignoring for now.
338 338 if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols:
339 339 repo.ui.deprecwarn('revset "%s" uses list instead of smartset'
340 340 % x[1][1],
341 341 '3.9')
342 342 return baseset(s)
343 343
344 344 def _getrevsource(repo, r):
345 345 extra = repo[r].extra()
346 346 for label in ('source', 'transplant_source', 'rebase_source'):
347 347 if label in extra:
348 348 try:
349 349 return repo[extra[label]].rev()
350 350 except error.RepoLookupError:
351 351 pass
352 352 return None
353 353
354 354 # operator methods
355 355
356 356 def stringset(repo, subset, x):
357 357 x = repo[x].rev()
358 358 if (x in subset
359 359 or x == node.nullrev and isinstance(subset, fullreposet)):
360 360 return baseset([x])
361 361 return baseset()
362 362
363 363 def rangeset(repo, subset, x, y):
364 364 m = getset(repo, fullreposet(repo), x)
365 365 n = getset(repo, fullreposet(repo), y)
366 366
367 367 if not m or not n:
368 368 return baseset()
369 369 m, n = m.first(), n.last()
370 370
371 371 if m == n:
372 372 r = baseset([m])
373 373 elif n == node.wdirrev:
374 374 r = spanset(repo, m, len(repo)) + baseset([n])
375 375 elif m == node.wdirrev:
376 376 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
377 377 elif m < n:
378 378 r = spanset(repo, m, n + 1)
379 379 else:
380 380 r = spanset(repo, m, n - 1)
381 381 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
382 382 # necessary to ensure we preserve the order in subset.
383 383 #
384 384 # This has performance implication, carrying the sorting over when possible
385 385 # would be more efficient.
386 386 return r & subset
387 387
388 388 def dagrange(repo, subset, x, y):
389 389 r = fullreposet(repo)
390 390 xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
391 391 includepath=True)
392 392 return subset & xs
393 393
394 394 def andset(repo, subset, x, y):
395 395 return getset(repo, getset(repo, subset, x), y)
396 396
397 397 def differenceset(repo, subset, x, y):
398 398 return getset(repo, subset, x) - getset(repo, subset, y)
399 399
400 def orset(repo, subset, *xs):
400 def _orsetlist(repo, subset, xs):
401 401 assert xs
402 402 if len(xs) == 1:
403 403 return getset(repo, subset, xs[0])
404 404 p = len(xs) // 2
405 a = orset(repo, subset, *xs[:p])
406 b = orset(repo, subset, *xs[p:])
405 a = _orsetlist(repo, subset, xs[:p])
406 b = _orsetlist(repo, subset, xs[p:])
407 407 return a + b
408 408
409 def orset(repo, subset, x):
410 return _orsetlist(repo, subset, getlist(x))
411
409 412 def notset(repo, subset, x):
410 413 return subset - getset(repo, subset, x)
411 414
412 415 def listset(repo, subset, *xs):
413 416 raise error.ParseError(_("can't use a list in this context"),
414 417 hint=_('see hg help "revsets.x or y"'))
415 418
416 419 def keyvaluepair(repo, subset, k, v):
417 420 raise error.ParseError(_("can't use a key-value pair in this context"))
418 421
419 422 def func(repo, subset, a, b):
420 423 f = getsymbol(a)
421 424 if f in symbols:
422 425 return symbols[f](repo, subset, b)
423 426
424 427 keep = lambda fn: getattr(fn, '__doc__', None) is not None
425 428
426 429 syms = [s for (s, fn) in symbols.items() if keep(fn)]
427 430 raise error.UnknownIdentifier(f, syms)
428 431
429 432 # functions
430 433
431 434 # symbols are callables like:
432 435 # fn(repo, subset, x)
433 436 # with:
434 437 # repo - current repository instance
435 438 # subset - of revisions to be examined
436 439 # x - argument in tree form
437 440 symbols = {}
438 441
439 442 # symbols which can't be used for a DoS attack for any given input
440 443 # (e.g. those which accept regexes as plain strings shouldn't be included)
441 444 # functions that just return a lot of changesets (like all) don't count here
442 445 safesymbols = set()
443 446
444 447 predicate = registrar.revsetpredicate()
445 448
446 449 @predicate('_destupdate')
447 450 def _destupdate(repo, subset, x):
448 451 # experimental revset for update destination
449 452 args = getargsdict(x, 'limit', 'clean check')
450 453 return subset & baseset([destutil.destupdate(repo, **args)[0]])
451 454
452 455 @predicate('_destmerge')
453 456 def _destmerge(repo, subset, x):
454 457 # experimental revset for merge destination
455 458 sourceset = None
456 459 if x is not None:
457 460 sourceset = getset(repo, fullreposet(repo), x)
458 461 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
459 462
460 463 @predicate('adds(pattern)', safe=True)
461 464 def adds(repo, subset, x):
462 465 """Changesets that add a file matching pattern.
463 466
464 467 The pattern without explicit kind like ``glob:`` is expected to be
465 468 relative to the current directory and match against a file or a
466 469 directory.
467 470 """
468 471 # i18n: "adds" is a keyword
469 472 pat = getstring(x, _("adds requires a pattern"))
470 473 return checkstatus(repo, subset, pat, 1)
471 474
472 475 @predicate('ancestor(*changeset)', safe=True)
473 476 def ancestor(repo, subset, x):
474 477 """A greatest common ancestor of the changesets.
475 478
476 479 Accepts 0 or more changesets.
477 480 Will return empty list when passed no args.
478 481 Greatest common ancestor of a single changeset is that changeset.
479 482 """
480 483 # i18n: "ancestor" is a keyword
481 484 l = getlist(x)
482 485 rl = fullreposet(repo)
483 486 anc = None
484 487
485 488 # (getset(repo, rl, i) for i in l) generates a list of lists
486 489 for revs in (getset(repo, rl, i) for i in l):
487 490 for r in revs:
488 491 if anc is None:
489 492 anc = repo[r]
490 493 else:
491 494 anc = anc.ancestor(repo[r])
492 495
493 496 if anc is not None and anc.rev() in subset:
494 497 return baseset([anc.rev()])
495 498 return baseset()
496 499
497 500 def _ancestors(repo, subset, x, followfirst=False):
498 501 heads = getset(repo, fullreposet(repo), x)
499 502 if not heads:
500 503 return baseset()
501 504 s = _revancestors(repo, heads, followfirst)
502 505 return subset & s
503 506
504 507 @predicate('ancestors(set)', safe=True)
505 508 def ancestors(repo, subset, x):
506 509 """Changesets that are ancestors of a changeset in set.
507 510 """
508 511 return _ancestors(repo, subset, x)
509 512
510 513 @predicate('_firstancestors', safe=True)
511 514 def _firstancestors(repo, subset, x):
512 515 # ``_firstancestors(set)``
513 516 # Like ``ancestors(set)`` but follows only the first parents.
514 517 return _ancestors(repo, subset, x, followfirst=True)
515 518
516 519 def ancestorspec(repo, subset, x, n):
517 520 """``set~n``
518 521 Changesets that are the Nth ancestor (first parents only) of a changeset
519 522 in set.
520 523 """
521 524 try:
522 525 n = int(n[1])
523 526 except (TypeError, ValueError):
524 527 raise error.ParseError(_("~ expects a number"))
525 528 ps = set()
526 529 cl = repo.changelog
527 530 for r in getset(repo, fullreposet(repo), x):
528 531 for i in range(n):
529 532 r = cl.parentrevs(r)[0]
530 533 ps.add(r)
531 534 return subset & ps
532 535
533 536 @predicate('author(string)', safe=True)
534 537 def author(repo, subset, x):
535 538 """Alias for ``user(string)``.
536 539 """
537 540 # i18n: "author" is a keyword
538 541 n = encoding.lower(getstring(x, _("author requires a string")))
539 542 kind, pattern, matcher = _substringmatcher(n)
540 543 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())),
541 544 condrepr=('<user %r>', n))
542 545
543 546 @predicate('bisect(string)', safe=True)
544 547 def bisect(repo, subset, x):
545 548 """Changesets marked in the specified bisect status:
546 549
547 550 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
548 551 - ``goods``, ``bads`` : csets topologically good/bad
549 552 - ``range`` : csets taking part in the bisection
550 553 - ``pruned`` : csets that are goods, bads or skipped
551 554 - ``untested`` : csets whose fate is yet unknown
552 555 - ``ignored`` : csets ignored due to DAG topology
553 556 - ``current`` : the cset currently being bisected
554 557 """
555 558 # i18n: "bisect" is a keyword
556 559 status = getstring(x, _("bisect requires a string")).lower()
557 560 state = set(hbisect.get(repo, status))
558 561 return subset & state
559 562
560 563 # Backward-compatibility
561 564 # - no help entry so that we do not advertise it any more
562 565 @predicate('bisected', safe=True)
563 566 def bisected(repo, subset, x):
564 567 return bisect(repo, subset, x)
565 568
566 569 @predicate('bookmark([name])', safe=True)
567 570 def bookmark(repo, subset, x):
568 571 """The named bookmark or all bookmarks.
569 572
570 573 If `name` starts with `re:`, the remainder of the name is treated as
571 574 a regular expression. To match a bookmark that actually starts with `re:`,
572 575 use the prefix `literal:`.
573 576 """
574 577 # i18n: "bookmark" is a keyword
575 578 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
576 579 if args:
577 580 bm = getstring(args[0],
578 581 # i18n: "bookmark" is a keyword
579 582 _('the argument to bookmark must be a string'))
580 583 kind, pattern, matcher = util.stringmatcher(bm)
581 584 bms = set()
582 585 if kind == 'literal':
583 586 bmrev = repo._bookmarks.get(pattern, None)
584 587 if not bmrev:
585 588 raise error.RepoLookupError(_("bookmark '%s' does not exist")
586 589 % pattern)
587 590 bms.add(repo[bmrev].rev())
588 591 else:
589 592 matchrevs = set()
590 593 for name, bmrev in repo._bookmarks.iteritems():
591 594 if matcher(name):
592 595 matchrevs.add(bmrev)
593 596 if not matchrevs:
594 597 raise error.RepoLookupError(_("no bookmarks exist"
595 598 " that match '%s'") % pattern)
596 599 for bmrev in matchrevs:
597 600 bms.add(repo[bmrev].rev())
598 601 else:
599 602 bms = set([repo[r].rev()
600 603 for r in repo._bookmarks.values()])
601 604 bms -= set([node.nullrev])
602 605 return subset & bms
603 606
604 607 @predicate('branch(string or set)', safe=True)
605 608 def branch(repo, subset, x):
606 609 """
607 610 All changesets belonging to the given branch or the branches of the given
608 611 changesets.
609 612
610 613 If `string` starts with `re:`, the remainder of the name is treated as
611 614 a regular expression. To match a branch that actually starts with `re:`,
612 615 use the prefix `literal:`.
613 616 """
614 617 getbi = repo.revbranchcache().branchinfo
615 618
616 619 try:
617 620 b = getstring(x, '')
618 621 except error.ParseError:
619 622 # not a string, but another revspec, e.g. tip()
620 623 pass
621 624 else:
622 625 kind, pattern, matcher = util.stringmatcher(b)
623 626 if kind == 'literal':
624 627 # note: falls through to the revspec case if no branch with
625 628 # this name exists and pattern kind is not specified explicitly
626 629 if pattern in repo.branchmap():
627 630 return subset.filter(lambda r: matcher(getbi(r)[0]),
628 631 condrepr=('<branch %r>', b))
629 632 if b.startswith('literal:'):
630 633 raise error.RepoLookupError(_("branch '%s' does not exist")
631 634 % pattern)
632 635 else:
633 636 return subset.filter(lambda r: matcher(getbi(r)[0]),
634 637 condrepr=('<branch %r>', b))
635 638
636 639 s = getset(repo, fullreposet(repo), x)
637 640 b = set()
638 641 for r in s:
639 642 b.add(getbi(r)[0])
640 643 c = s.__contains__
641 644 return subset.filter(lambda r: c(r) or getbi(r)[0] in b,
642 645 condrepr=lambda: '<branch %r>' % sorted(b))
643 646
644 647 @predicate('bumped()', safe=True)
645 648 def bumped(repo, subset, x):
646 649 """Mutable changesets marked as successors of public changesets.
647 650
648 651 Only non-public and non-obsolete changesets can be `bumped`.
649 652 """
650 653 # i18n: "bumped" is a keyword
651 654 getargs(x, 0, 0, _("bumped takes no arguments"))
652 655 bumped = obsmod.getrevs(repo, 'bumped')
653 656 return subset & bumped
654 657
655 658 @predicate('bundle()', safe=True)
656 659 def bundle(repo, subset, x):
657 660 """Changesets in the bundle.
658 661
659 662 Bundle must be specified by the -R option."""
660 663
661 664 try:
662 665 bundlerevs = repo.changelog.bundlerevs
663 666 except AttributeError:
664 667 raise error.Abort(_("no bundle provided - specify with -R"))
665 668 return subset & bundlerevs
666 669
667 670 def checkstatus(repo, subset, pat, field):
668 671 hasset = matchmod.patkind(pat) == 'set'
669 672
670 673 mcache = [None]
671 674 def matches(x):
672 675 c = repo[x]
673 676 if not mcache[0] or hasset:
674 677 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
675 678 m = mcache[0]
676 679 fname = None
677 680 if not m.anypats() and len(m.files()) == 1:
678 681 fname = m.files()[0]
679 682 if fname is not None:
680 683 if fname not in c.files():
681 684 return False
682 685 else:
683 686 for f in c.files():
684 687 if m(f):
685 688 break
686 689 else:
687 690 return False
688 691 files = repo.status(c.p1().node(), c.node())[field]
689 692 if fname is not None:
690 693 if fname in files:
691 694 return True
692 695 else:
693 696 for f in files:
694 697 if m(f):
695 698 return True
696 699
697 700 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
698 701
699 702 def _children(repo, subset, parentset):
700 703 if not parentset:
701 704 return baseset()
702 705 cs = set()
703 706 pr = repo.changelog.parentrevs
704 707 minrev = parentset.min()
705 708 for r in subset:
706 709 if r <= minrev:
707 710 continue
708 711 for p in pr(r):
709 712 if p in parentset:
710 713 cs.add(r)
711 714 return baseset(cs)
712 715
713 716 @predicate('children(set)', safe=True)
714 717 def children(repo, subset, x):
715 718 """Child changesets of changesets in set.
716 719 """
717 720 s = getset(repo, fullreposet(repo), x)
718 721 cs = _children(repo, subset, s)
719 722 return subset & cs
720 723
721 724 @predicate('closed()', safe=True)
722 725 def closed(repo, subset, x):
723 726 """Changeset is closed.
724 727 """
725 728 # i18n: "closed" is a keyword
726 729 getargs(x, 0, 0, _("closed takes no arguments"))
727 730 return subset.filter(lambda r: repo[r].closesbranch(),
728 731 condrepr='<branch closed>')
729 732
730 733 @predicate('contains(pattern)')
731 734 def contains(repo, subset, x):
732 735 """The revision's manifest contains a file matching pattern (but might not
733 736 modify it). See :hg:`help patterns` for information about file patterns.
734 737
735 738 The pattern without explicit kind like ``glob:`` is expected to be
736 739 relative to the current directory and match against a file exactly
737 740 for efficiency.
738 741 """
739 742 # i18n: "contains" is a keyword
740 743 pat = getstring(x, _("contains requires a pattern"))
741 744
742 745 def matches(x):
743 746 if not matchmod.patkind(pat):
744 747 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
745 748 if pats in repo[x]:
746 749 return True
747 750 else:
748 751 c = repo[x]
749 752 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
750 753 for f in c.manifest():
751 754 if m(f):
752 755 return True
753 756 return False
754 757
755 758 return subset.filter(matches, condrepr=('<contains %r>', pat))
756 759
757 760 @predicate('converted([id])', safe=True)
758 761 def converted(repo, subset, x):
759 762 """Changesets converted from the given identifier in the old repository if
760 763 present, or all converted changesets if no identifier is specified.
761 764 """
762 765
763 766 # There is exactly no chance of resolving the revision, so do a simple
764 767 # string compare and hope for the best
765 768
766 769 rev = None
767 770 # i18n: "converted" is a keyword
768 771 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
769 772 if l:
770 773 # i18n: "converted" is a keyword
771 774 rev = getstring(l[0], _('converted requires a revision'))
772 775
773 776 def _matchvalue(r):
774 777 source = repo[r].extra().get('convert_revision', None)
775 778 return source is not None and (rev is None or source.startswith(rev))
776 779
777 780 return subset.filter(lambda r: _matchvalue(r),
778 781 condrepr=('<converted %r>', rev))
779 782
780 783 @predicate('date(interval)', safe=True)
781 784 def date(repo, subset, x):
782 785 """Changesets within the interval, see :hg:`help dates`.
783 786 """
784 787 # i18n: "date" is a keyword
785 788 ds = getstring(x, _("date requires a string"))
786 789 dm = util.matchdate(ds)
787 790 return subset.filter(lambda x: dm(repo[x].date()[0]),
788 791 condrepr=('<date %r>', ds))
789 792
790 793 @predicate('desc(string)', safe=True)
791 794 def desc(repo, subset, x):
792 795 """Search commit message for string. The match is case-insensitive.
793 796 """
794 797 # i18n: "desc" is a keyword
795 798 ds = encoding.lower(getstring(x, _("desc requires a string")))
796 799
797 800 def matches(x):
798 801 c = repo[x]
799 802 return ds in encoding.lower(c.description())
800 803
801 804 return subset.filter(matches, condrepr=('<desc %r>', ds))
802 805
803 806 def _descendants(repo, subset, x, followfirst=False):
804 807 roots = getset(repo, fullreposet(repo), x)
805 808 if not roots:
806 809 return baseset()
807 810 s = _revdescendants(repo, roots, followfirst)
808 811
809 812 # Both sets need to be ascending in order to lazily return the union
810 813 # in the correct order.
811 814 base = subset & roots
812 815 desc = subset & s
813 816 result = base + desc
814 817 if subset.isascending():
815 818 result.sort()
816 819 elif subset.isdescending():
817 820 result.sort(reverse=True)
818 821 else:
819 822 result = subset & result
820 823 return result
821 824
822 825 @predicate('descendants(set)', safe=True)
823 826 def descendants(repo, subset, x):
824 827 """Changesets which are descendants of changesets in set.
825 828 """
826 829 return _descendants(repo, subset, x)
827 830
828 831 @predicate('_firstdescendants', safe=True)
829 832 def _firstdescendants(repo, subset, x):
830 833 # ``_firstdescendants(set)``
831 834 # Like ``descendants(set)`` but follows only the first parents.
832 835 return _descendants(repo, subset, x, followfirst=True)
833 836
834 837 @predicate('destination([set])', safe=True)
835 838 def destination(repo, subset, x):
836 839 """Changesets that were created by a graft, transplant or rebase operation,
837 840 with the given revisions specified as the source. Omitting the optional set
838 841 is the same as passing all().
839 842 """
840 843 if x is not None:
841 844 sources = getset(repo, fullreposet(repo), x)
842 845 else:
843 846 sources = fullreposet(repo)
844 847
845 848 dests = set()
846 849
847 850 # subset contains all of the possible destinations that can be returned, so
848 851 # iterate over them and see if their source(s) were provided in the arg set.
849 852 # Even if the immediate src of r is not in the arg set, src's source (or
850 853 # further back) may be. Scanning back further than the immediate src allows
851 854 # transitive transplants and rebases to yield the same results as transitive
852 855 # grafts.
853 856 for r in subset:
854 857 src = _getrevsource(repo, r)
855 858 lineage = None
856 859
857 860 while src is not None:
858 861 if lineage is None:
859 862 lineage = list()
860 863
861 864 lineage.append(r)
862 865
863 866 # The visited lineage is a match if the current source is in the arg
864 867 # set. Since every candidate dest is visited by way of iterating
865 868 # subset, any dests further back in the lineage will be tested by a
866 869 # different iteration over subset. Likewise, if the src was already
867 870 # selected, the current lineage can be selected without going back
868 871 # further.
869 872 if src in sources or src in dests:
870 873 dests.update(lineage)
871 874 break
872 875
873 876 r = src
874 877 src = _getrevsource(repo, r)
875 878
876 879 return subset.filter(dests.__contains__,
877 880 condrepr=lambda: '<destination %r>' % sorted(dests))
878 881
879 882 @predicate('divergent()', safe=True)
880 883 def divergent(repo, subset, x):
881 884 """
882 885 Final successors of changesets with an alternative set of final successors.
883 886 """
884 887 # i18n: "divergent" is a keyword
885 888 getargs(x, 0, 0, _("divergent takes no arguments"))
886 889 divergent = obsmod.getrevs(repo, 'divergent')
887 890 return subset & divergent
888 891
889 892 @predicate('extinct()', safe=True)
890 893 def extinct(repo, subset, x):
891 894 """Obsolete changesets with obsolete descendants only.
892 895 """
893 896 # i18n: "extinct" is a keyword
894 897 getargs(x, 0, 0, _("extinct takes no arguments"))
895 898 extincts = obsmod.getrevs(repo, 'extinct')
896 899 return subset & extincts
897 900
898 901 @predicate('extra(label, [value])', safe=True)
899 902 def extra(repo, subset, x):
900 903 """Changesets with the given label in the extra metadata, with the given
901 904 optional value.
902 905
903 906 If `value` starts with `re:`, the remainder of the value is treated as
904 907 a regular expression. To match a value that actually starts with `re:`,
905 908 use the prefix `literal:`.
906 909 """
907 910 args = getargsdict(x, 'extra', 'label value')
908 911 if 'label' not in args:
909 912 # i18n: "extra" is a keyword
910 913 raise error.ParseError(_('extra takes at least 1 argument'))
911 914 # i18n: "extra" is a keyword
912 915 label = getstring(args['label'], _('first argument to extra must be '
913 916 'a string'))
914 917 value = None
915 918
916 919 if 'value' in args:
917 920 # i18n: "extra" is a keyword
918 921 value = getstring(args['value'], _('second argument to extra must be '
919 922 'a string'))
920 923 kind, value, matcher = util.stringmatcher(value)
921 924
922 925 def _matchvalue(r):
923 926 extra = repo[r].extra()
924 927 return label in extra and (value is None or matcher(extra[label]))
925 928
926 929 return subset.filter(lambda r: _matchvalue(r),
927 930 condrepr=('<extra[%r] %r>', label, value))
928 931
929 932 @predicate('filelog(pattern)', safe=True)
930 933 def filelog(repo, subset, x):
931 934 """Changesets connected to the specified filelog.
932 935
933 936 For performance reasons, visits only revisions mentioned in the file-level
934 937 filelog, rather than filtering through all changesets (much faster, but
935 938 doesn't include deletes or duplicate changes). For a slower, more accurate
936 939 result, use ``file()``.
937 940
938 941 The pattern without explicit kind like ``glob:`` is expected to be
939 942 relative to the current directory and match against a file exactly
940 943 for efficiency.
941 944
942 945 If some linkrev points to revisions filtered by the current repoview, we'll
943 946 work around it to return a non-filtered value.
944 947 """
945 948
946 949 # i18n: "filelog" is a keyword
947 950 pat = getstring(x, _("filelog requires a pattern"))
948 951 s = set()
949 952 cl = repo.changelog
950 953
951 954 if not matchmod.patkind(pat):
952 955 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
953 956 files = [f]
954 957 else:
955 958 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
956 959 files = (f for f in repo[None] if m(f))
957 960
958 961 for f in files:
959 962 fl = repo.file(f)
960 963 known = {}
961 964 scanpos = 0
962 965 for fr in list(fl):
963 966 fn = fl.node(fr)
964 967 if fn in known:
965 968 s.add(known[fn])
966 969 continue
967 970
968 971 lr = fl.linkrev(fr)
969 972 if lr in cl:
970 973 s.add(lr)
971 974 elif scanpos is not None:
972 975 # lowest matching changeset is filtered, scan further
973 976 # ahead in changelog
974 977 start = max(lr, scanpos) + 1
975 978 scanpos = None
976 979 for r in cl.revs(start):
977 980 # minimize parsing of non-matching entries
978 981 if f in cl.revision(r) and f in cl.readfiles(r):
979 982 try:
980 983 # try to use manifest delta fastpath
981 984 n = repo[r].filenode(f)
982 985 if n not in known:
983 986 if n == fn:
984 987 s.add(r)
985 988 scanpos = r
986 989 break
987 990 else:
988 991 known[n] = r
989 992 except error.ManifestLookupError:
990 993 # deletion in changelog
991 994 continue
992 995
993 996 return subset & s
994 997
995 998 @predicate('first(set, [n])', safe=True)
996 999 def first(repo, subset, x):
997 1000 """An alias for limit().
998 1001 """
999 1002 return limit(repo, subset, x)
1000 1003
1001 1004 def _follow(repo, subset, x, name, followfirst=False):
1002 1005 l = getargs(x, 0, 2, _("%s takes no arguments or a pattern "
1003 1006 "and an optional revset") % name)
1004 1007 c = repo['.']
1005 1008 if l:
1006 1009 x = getstring(l[0], _("%s expected a pattern") % name)
1007 1010 rev = None
1008 1011 if len(l) >= 2:
1009 1012 rev = getset(repo, fullreposet(repo), l[1]).last()
1010 1013 if rev is None:
1011 1014 raise error.RepoLookupError(
1012 1015 _("%s: starting revision set cannot be empty") % name)
1013 1016 c = repo[rev]
1014 1017 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
1015 1018 ctx=repo[rev], default='path')
1016 1019
1017 1020 files = c.manifest().walk(matcher)
1018 1021
1019 1022 s = set()
1020 1023 for fname in files:
1021 1024 fctx = c[fname]
1022 1025 s = s.union(set(c.rev() for c in fctx.ancestors(followfirst)))
1023 1026 # include the revision responsible for the most recent version
1024 1027 s.add(fctx.introrev())
1025 1028 else:
1026 1029 s = _revancestors(repo, baseset([c.rev()]), followfirst)
1027 1030
1028 1031 return subset & s
1029 1032
1030 1033 @predicate('follow([pattern[, startrev]])', safe=True)
1031 1034 def follow(repo, subset, x):
1032 1035 """
1033 1036 An alias for ``::.`` (ancestors of the working directory's first parent).
1034 1037 If pattern is specified, the histories of files matching given
1035 1038 pattern in the revision given by startrev are followed, including copies.
1036 1039 """
1037 1040 return _follow(repo, subset, x, 'follow')
1038 1041
1039 1042 @predicate('_followfirst', safe=True)
1040 1043 def _followfirst(repo, subset, x):
1041 1044 # ``followfirst([pattern[, startrev]])``
1042 1045 # Like ``follow([pattern[, startrev]])`` but follows only the first parent
1043 1046 # of every revisions or files revisions.
1044 1047 return _follow(repo, subset, x, '_followfirst', followfirst=True)
1045 1048
1046 1049 @predicate('all()', safe=True)
1047 1050 def getall(repo, subset, x):
1048 1051 """All changesets, the same as ``0:tip``.
1049 1052 """
1050 1053 # i18n: "all" is a keyword
1051 1054 getargs(x, 0, 0, _("all takes no arguments"))
1052 1055 return subset & spanset(repo) # drop "null" if any
1053 1056
1054 1057 @predicate('grep(regex)')
1055 1058 def grep(repo, subset, x):
1056 1059 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1057 1060 to ensure special escape characters are handled correctly. Unlike
1058 1061 ``keyword(string)``, the match is case-sensitive.
1059 1062 """
1060 1063 try:
1061 1064 # i18n: "grep" is a keyword
1062 1065 gr = re.compile(getstring(x, _("grep requires a string")))
1063 1066 except re.error as e:
1064 1067 raise error.ParseError(_('invalid match pattern: %s') % e)
1065 1068
1066 1069 def matches(x):
1067 1070 c = repo[x]
1068 1071 for e in c.files() + [c.user(), c.description()]:
1069 1072 if gr.search(e):
1070 1073 return True
1071 1074 return False
1072 1075
1073 1076 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1074 1077
1075 1078 @predicate('_matchfiles', safe=True)
1076 1079 def _matchfiles(repo, subset, x):
1077 1080 # _matchfiles takes a revset list of prefixed arguments:
1078 1081 #
1079 1082 # [p:foo, i:bar, x:baz]
1080 1083 #
1081 1084 # builds a match object from them and filters subset. Allowed
1082 1085 # prefixes are 'p:' for regular patterns, 'i:' for include
1083 1086 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1084 1087 # a revision identifier, or the empty string to reference the
1085 1088 # working directory, from which the match object is
1086 1089 # initialized. Use 'd:' to set the default matching mode, default
1087 1090 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1088 1091
1089 1092 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1090 1093 pats, inc, exc = [], [], []
1091 1094 rev, default = None, None
1092 1095 for arg in l:
1093 1096 s = getstring(arg, "_matchfiles requires string arguments")
1094 1097 prefix, value = s[:2], s[2:]
1095 1098 if prefix == 'p:':
1096 1099 pats.append(value)
1097 1100 elif prefix == 'i:':
1098 1101 inc.append(value)
1099 1102 elif prefix == 'x:':
1100 1103 exc.append(value)
1101 1104 elif prefix == 'r:':
1102 1105 if rev is not None:
1103 1106 raise error.ParseError('_matchfiles expected at most one '
1104 1107 'revision')
1105 1108 if value != '': # empty means working directory; leave rev as None
1106 1109 rev = value
1107 1110 elif prefix == 'd:':
1108 1111 if default is not None:
1109 1112 raise error.ParseError('_matchfiles expected at most one '
1110 1113 'default mode')
1111 1114 default = value
1112 1115 else:
1113 1116 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1114 1117 if not default:
1115 1118 default = 'glob'
1116 1119
1117 1120 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1118 1121 exclude=exc, ctx=repo[rev], default=default)
1119 1122
1120 1123 # This directly read the changelog data as creating changectx for all
1121 1124 # revisions is quite expensive.
1122 1125 getfiles = repo.changelog.readfiles
1123 1126 wdirrev = node.wdirrev
1124 1127 def matches(x):
1125 1128 if x == wdirrev:
1126 1129 files = repo[x].files()
1127 1130 else:
1128 1131 files = getfiles(x)
1129 1132 for f in files:
1130 1133 if m(f):
1131 1134 return True
1132 1135 return False
1133 1136
1134 1137 return subset.filter(matches,
1135 1138 condrepr=('<matchfiles patterns=%r, include=%r '
1136 1139 'exclude=%r, default=%r, rev=%r>',
1137 1140 pats, inc, exc, default, rev))
1138 1141
1139 1142 @predicate('file(pattern)', safe=True)
1140 1143 def hasfile(repo, subset, x):
1141 1144 """Changesets affecting files matched by pattern.
1142 1145
1143 1146 For a faster but less accurate result, consider using ``filelog()``
1144 1147 instead.
1145 1148
1146 1149 This predicate uses ``glob:`` as the default kind of pattern.
1147 1150 """
1148 1151 # i18n: "file" is a keyword
1149 1152 pat = getstring(x, _("file requires a pattern"))
1150 1153 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1151 1154
1152 1155 @predicate('head()', safe=True)
1153 1156 def head(repo, subset, x):
1154 1157 """Changeset is a named branch head.
1155 1158 """
1156 1159 # i18n: "head" is a keyword
1157 1160 getargs(x, 0, 0, _("head takes no arguments"))
1158 1161 hs = set()
1159 1162 cl = repo.changelog
1160 1163 for ls in repo.branchmap().itervalues():
1161 1164 hs.update(cl.rev(h) for h in ls)
1162 1165 return subset & baseset(hs)
1163 1166
1164 1167 @predicate('heads(set)', safe=True)
1165 1168 def heads(repo, subset, x):
1166 1169 """Members of set with no children in set.
1167 1170 """
1168 1171 s = getset(repo, subset, x)
1169 1172 ps = parents(repo, subset, x)
1170 1173 return s - ps
1171 1174
1172 1175 @predicate('hidden()', safe=True)
1173 1176 def hidden(repo, subset, x):
1174 1177 """Hidden changesets.
1175 1178 """
1176 1179 # i18n: "hidden" is a keyword
1177 1180 getargs(x, 0, 0, _("hidden takes no arguments"))
1178 1181 hiddenrevs = repoview.filterrevs(repo, 'visible')
1179 1182 return subset & hiddenrevs
1180 1183
1181 1184 @predicate('keyword(string)', safe=True)
1182 1185 def keyword(repo, subset, x):
1183 1186 """Search commit message, user name, and names of changed files for
1184 1187 string. The match is case-insensitive.
1185 1188 """
1186 1189 # i18n: "keyword" is a keyword
1187 1190 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1188 1191
1189 1192 def matches(r):
1190 1193 c = repo[r]
1191 1194 return any(kw in encoding.lower(t)
1192 1195 for t in c.files() + [c.user(), c.description()])
1193 1196
1194 1197 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1195 1198
1196 1199 @predicate('limit(set[, n[, offset]])', safe=True)
1197 1200 def limit(repo, subset, x):
1198 1201 """First n members of set, defaulting to 1, starting from offset.
1199 1202 """
1200 1203 args = getargsdict(x, 'limit', 'set n offset')
1201 1204 if 'set' not in args:
1202 1205 # i18n: "limit" is a keyword
1203 1206 raise error.ParseError(_("limit requires one to three arguments"))
1204 1207 try:
1205 1208 lim, ofs = 1, 0
1206 1209 if 'n' in args:
1207 1210 # i18n: "limit" is a keyword
1208 1211 lim = int(getstring(args['n'], _("limit requires a number")))
1209 1212 if 'offset' in args:
1210 1213 # i18n: "limit" is a keyword
1211 1214 ofs = int(getstring(args['offset'], _("limit requires a number")))
1212 1215 if ofs < 0:
1213 1216 raise error.ParseError(_("negative offset"))
1214 1217 except (TypeError, ValueError):
1215 1218 # i18n: "limit" is a keyword
1216 1219 raise error.ParseError(_("limit expects a number"))
1217 1220 os = getset(repo, fullreposet(repo), args['set'])
1218 1221 result = []
1219 1222 it = iter(os)
1220 1223 for x in xrange(ofs):
1221 1224 y = next(it, None)
1222 1225 if y is None:
1223 1226 break
1224 1227 for x in xrange(lim):
1225 1228 y = next(it, None)
1226 1229 if y is None:
1227 1230 break
1228 1231 elif y in subset:
1229 1232 result.append(y)
1230 1233 return baseset(result, datarepr=('<limit n=%d, offset=%d, %r, %r>',
1231 1234 lim, ofs, subset, os))
1232 1235
1233 1236 @predicate('last(set, [n])', safe=True)
1234 1237 def last(repo, subset, x):
1235 1238 """Last n members of set, defaulting to 1.
1236 1239 """
1237 1240 # i18n: "last" is a keyword
1238 1241 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1239 1242 try:
1240 1243 lim = 1
1241 1244 if len(l) == 2:
1242 1245 # i18n: "last" is a keyword
1243 1246 lim = int(getstring(l[1], _("last requires a number")))
1244 1247 except (TypeError, ValueError):
1245 1248 # i18n: "last" is a keyword
1246 1249 raise error.ParseError(_("last expects a number"))
1247 1250 os = getset(repo, fullreposet(repo), l[0])
1248 1251 os.reverse()
1249 1252 result = []
1250 1253 it = iter(os)
1251 1254 for x in xrange(lim):
1252 1255 y = next(it, None)
1253 1256 if y is None:
1254 1257 break
1255 1258 elif y in subset:
1256 1259 result.append(y)
1257 1260 return baseset(result, datarepr=('<last n=%d, %r, %r>', lim, subset, os))
1258 1261
1259 1262 @predicate('max(set)', safe=True)
1260 1263 def maxrev(repo, subset, x):
1261 1264 """Changeset with highest revision number in set.
1262 1265 """
1263 1266 os = getset(repo, fullreposet(repo), x)
1264 1267 try:
1265 1268 m = os.max()
1266 1269 if m in subset:
1267 1270 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1268 1271 except ValueError:
1269 1272 # os.max() throws a ValueError when the collection is empty.
1270 1273 # Same as python's max().
1271 1274 pass
1272 1275 return baseset(datarepr=('<max %r, %r>', subset, os))
1273 1276
1274 1277 @predicate('merge()', safe=True)
1275 1278 def merge(repo, subset, x):
1276 1279 """Changeset is a merge changeset.
1277 1280 """
1278 1281 # i18n: "merge" is a keyword
1279 1282 getargs(x, 0, 0, _("merge takes no arguments"))
1280 1283 cl = repo.changelog
1281 1284 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1282 1285 condrepr='<merge>')
1283 1286
1284 1287 @predicate('branchpoint()', safe=True)
1285 1288 def branchpoint(repo, subset, x):
1286 1289 """Changesets with more than one child.
1287 1290 """
1288 1291 # i18n: "branchpoint" is a keyword
1289 1292 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1290 1293 cl = repo.changelog
1291 1294 if not subset:
1292 1295 return baseset()
1293 1296 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1294 1297 # (and if it is not, it should.)
1295 1298 baserev = min(subset)
1296 1299 parentscount = [0]*(len(repo) - baserev)
1297 1300 for r in cl.revs(start=baserev + 1):
1298 1301 for p in cl.parentrevs(r):
1299 1302 if p >= baserev:
1300 1303 parentscount[p - baserev] += 1
1301 1304 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1302 1305 condrepr='<branchpoint>')
1303 1306
1304 1307 @predicate('min(set)', safe=True)
1305 1308 def minrev(repo, subset, x):
1306 1309 """Changeset with lowest revision number in set.
1307 1310 """
1308 1311 os = getset(repo, fullreposet(repo), x)
1309 1312 try:
1310 1313 m = os.min()
1311 1314 if m in subset:
1312 1315 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1313 1316 except ValueError:
1314 1317 # os.min() throws a ValueError when the collection is empty.
1315 1318 # Same as python's min().
1316 1319 pass
1317 1320 return baseset(datarepr=('<min %r, %r>', subset, os))
1318 1321
1319 1322 @predicate('modifies(pattern)', safe=True)
1320 1323 def modifies(repo, subset, x):
1321 1324 """Changesets modifying files matched by pattern.
1322 1325
1323 1326 The pattern without explicit kind like ``glob:`` is expected to be
1324 1327 relative to the current directory and match against a file or a
1325 1328 directory.
1326 1329 """
1327 1330 # i18n: "modifies" is a keyword
1328 1331 pat = getstring(x, _("modifies requires a pattern"))
1329 1332 return checkstatus(repo, subset, pat, 0)
1330 1333
1331 1334 @predicate('named(namespace)')
1332 1335 def named(repo, subset, x):
1333 1336 """The changesets in a given namespace.
1334 1337
1335 1338 If `namespace` starts with `re:`, the remainder of the string is treated as
1336 1339 a regular expression. To match a namespace that actually starts with `re:`,
1337 1340 use the prefix `literal:`.
1338 1341 """
1339 1342 # i18n: "named" is a keyword
1340 1343 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1341 1344
1342 1345 ns = getstring(args[0],
1343 1346 # i18n: "named" is a keyword
1344 1347 _('the argument to named must be a string'))
1345 1348 kind, pattern, matcher = util.stringmatcher(ns)
1346 1349 namespaces = set()
1347 1350 if kind == 'literal':
1348 1351 if pattern not in repo.names:
1349 1352 raise error.RepoLookupError(_("namespace '%s' does not exist")
1350 1353 % ns)
1351 1354 namespaces.add(repo.names[pattern])
1352 1355 else:
1353 1356 for name, ns in repo.names.iteritems():
1354 1357 if matcher(name):
1355 1358 namespaces.add(ns)
1356 1359 if not namespaces:
1357 1360 raise error.RepoLookupError(_("no namespace exists"
1358 1361 " that match '%s'") % pattern)
1359 1362
1360 1363 names = set()
1361 1364 for ns in namespaces:
1362 1365 for name in ns.listnames(repo):
1363 1366 if name not in ns.deprecated:
1364 1367 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1365 1368
1366 1369 names -= set([node.nullrev])
1367 1370 return subset & names
1368 1371
1369 1372 @predicate('id(string)', safe=True)
1370 1373 def node_(repo, subset, x):
1371 1374 """Revision non-ambiguously specified by the given hex string prefix.
1372 1375 """
1373 1376 # i18n: "id" is a keyword
1374 1377 l = getargs(x, 1, 1, _("id requires one argument"))
1375 1378 # i18n: "id" is a keyword
1376 1379 n = getstring(l[0], _("id requires a string"))
1377 1380 if len(n) == 40:
1378 1381 try:
1379 1382 rn = repo.changelog.rev(node.bin(n))
1380 1383 except (LookupError, TypeError):
1381 1384 rn = None
1382 1385 else:
1383 1386 rn = None
1384 1387 pm = repo.changelog._partialmatch(n)
1385 1388 if pm is not None:
1386 1389 rn = repo.changelog.rev(pm)
1387 1390
1388 1391 if rn is None:
1389 1392 return baseset()
1390 1393 result = baseset([rn])
1391 1394 return result & subset
1392 1395
1393 1396 @predicate('obsolete()', safe=True)
1394 1397 def obsolete(repo, subset, x):
1395 1398 """Mutable changeset with a newer version."""
1396 1399 # i18n: "obsolete" is a keyword
1397 1400 getargs(x, 0, 0, _("obsolete takes no arguments"))
1398 1401 obsoletes = obsmod.getrevs(repo, 'obsolete')
1399 1402 return subset & obsoletes
1400 1403
1401 1404 @predicate('only(set, [set])', safe=True)
1402 1405 def only(repo, subset, x):
1403 1406 """Changesets that are ancestors of the first set that are not ancestors
1404 1407 of any other head in the repo. If a second set is specified, the result
1405 1408 is ancestors of the first set that are not ancestors of the second set
1406 1409 (i.e. ::<set1> - ::<set2>).
1407 1410 """
1408 1411 cl = repo.changelog
1409 1412 # i18n: "only" is a keyword
1410 1413 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1411 1414 include = getset(repo, fullreposet(repo), args[0])
1412 1415 if len(args) == 1:
1413 1416 if not include:
1414 1417 return baseset()
1415 1418
1416 1419 descendants = set(_revdescendants(repo, include, False))
1417 1420 exclude = [rev for rev in cl.headrevs()
1418 1421 if not rev in descendants and not rev in include]
1419 1422 else:
1420 1423 exclude = getset(repo, fullreposet(repo), args[1])
1421 1424
1422 1425 results = set(cl.findmissingrevs(common=exclude, heads=include))
1423 1426 # XXX we should turn this into a baseset instead of a set, smartset may do
1424 1427 # some optimisations from the fact this is a baseset.
1425 1428 return subset & results
1426 1429
1427 1430 @predicate('origin([set])', safe=True)
1428 1431 def origin(repo, subset, x):
1429 1432 """
1430 1433 Changesets that were specified as a source for the grafts, transplants or
1431 1434 rebases that created the given revisions. Omitting the optional set is the
1432 1435 same as passing all(). If a changeset created by these operations is itself
1433 1436 specified as a source for one of these operations, only the source changeset
1434 1437 for the first operation is selected.
1435 1438 """
1436 1439 if x is not None:
1437 1440 dests = getset(repo, fullreposet(repo), x)
1438 1441 else:
1439 1442 dests = fullreposet(repo)
1440 1443
1441 1444 def _firstsrc(rev):
1442 1445 src = _getrevsource(repo, rev)
1443 1446 if src is None:
1444 1447 return None
1445 1448
1446 1449 while True:
1447 1450 prev = _getrevsource(repo, src)
1448 1451
1449 1452 if prev is None:
1450 1453 return src
1451 1454 src = prev
1452 1455
1453 1456 o = set([_firstsrc(r) for r in dests])
1454 1457 o -= set([None])
1455 1458 # XXX we should turn this into a baseset instead of a set, smartset may do
1456 1459 # some optimisations from the fact this is a baseset.
1457 1460 return subset & o
1458 1461
1459 1462 @predicate('outgoing([path])', safe=True)
1460 1463 def outgoing(repo, subset, x):
1461 1464 """Changesets not found in the specified destination repository, or the
1462 1465 default push location.
1463 1466 """
1464 1467 # Avoid cycles.
1465 1468 from . import (
1466 1469 discovery,
1467 1470 hg,
1468 1471 )
1469 1472 # i18n: "outgoing" is a keyword
1470 1473 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1471 1474 # i18n: "outgoing" is a keyword
1472 1475 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1473 1476 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1474 1477 dest, branches = hg.parseurl(dest)
1475 1478 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1476 1479 if revs:
1477 1480 revs = [repo.lookup(rev) for rev in revs]
1478 1481 other = hg.peer(repo, {}, dest)
1479 1482 repo.ui.pushbuffer()
1480 1483 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1481 1484 repo.ui.popbuffer()
1482 1485 cl = repo.changelog
1483 1486 o = set([cl.rev(r) for r in outgoing.missing])
1484 1487 return subset & o
1485 1488
1486 1489 @predicate('p1([set])', safe=True)
1487 1490 def p1(repo, subset, x):
1488 1491 """First parent of changesets in set, or the working directory.
1489 1492 """
1490 1493 if x is None:
1491 1494 p = repo[x].p1().rev()
1492 1495 if p >= 0:
1493 1496 return subset & baseset([p])
1494 1497 return baseset()
1495 1498
1496 1499 ps = set()
1497 1500 cl = repo.changelog
1498 1501 for r in getset(repo, fullreposet(repo), x):
1499 1502 ps.add(cl.parentrevs(r)[0])
1500 1503 ps -= set([node.nullrev])
1501 1504 # XXX we should turn this into a baseset instead of a set, smartset may do
1502 1505 # some optimisations from the fact this is a baseset.
1503 1506 return subset & ps
1504 1507
1505 1508 @predicate('p2([set])', safe=True)
1506 1509 def p2(repo, subset, x):
1507 1510 """Second parent of changesets in set, or the working directory.
1508 1511 """
1509 1512 if x is None:
1510 1513 ps = repo[x].parents()
1511 1514 try:
1512 1515 p = ps[1].rev()
1513 1516 if p >= 0:
1514 1517 return subset & baseset([p])
1515 1518 return baseset()
1516 1519 except IndexError:
1517 1520 return baseset()
1518 1521
1519 1522 ps = set()
1520 1523 cl = repo.changelog
1521 1524 for r in getset(repo, fullreposet(repo), x):
1522 1525 ps.add(cl.parentrevs(r)[1])
1523 1526 ps -= set([node.nullrev])
1524 1527 # XXX we should turn this into a baseset instead of a set, smartset may do
1525 1528 # some optimisations from the fact this is a baseset.
1526 1529 return subset & ps
1527 1530
1528 1531 @predicate('parents([set])', safe=True)
1529 1532 def parents(repo, subset, x):
1530 1533 """
1531 1534 The set of all parents for all changesets in set, or the working directory.
1532 1535 """
1533 1536 if x is None:
1534 1537 ps = set(p.rev() for p in repo[x].parents())
1535 1538 else:
1536 1539 ps = set()
1537 1540 cl = repo.changelog
1538 1541 up = ps.update
1539 1542 parentrevs = cl.parentrevs
1540 1543 for r in getset(repo, fullreposet(repo), x):
1541 1544 if r == node.wdirrev:
1542 1545 up(p.rev() for p in repo[r].parents())
1543 1546 else:
1544 1547 up(parentrevs(r))
1545 1548 ps -= set([node.nullrev])
1546 1549 return subset & ps
1547 1550
1548 1551 def _phase(repo, subset, target):
1549 1552 """helper to select all rev in phase <target>"""
1550 1553 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1551 1554 if repo._phasecache._phasesets:
1552 1555 s = repo._phasecache._phasesets[target] - repo.changelog.filteredrevs
1553 1556 s = baseset(s)
1554 1557 s.sort() # set are non ordered, so we enforce ascending
1555 1558 return subset & s
1556 1559 else:
1557 1560 phase = repo._phasecache.phase
1558 1561 condition = lambda r: phase(repo, r) == target
1559 1562 return subset.filter(condition, condrepr=('<phase %r>', target),
1560 1563 cache=False)
1561 1564
1562 1565 @predicate('draft()', safe=True)
1563 1566 def draft(repo, subset, x):
1564 1567 """Changeset in draft phase."""
1565 1568 # i18n: "draft" is a keyword
1566 1569 getargs(x, 0, 0, _("draft takes no arguments"))
1567 1570 target = phases.draft
1568 1571 return _phase(repo, subset, target)
1569 1572
1570 1573 @predicate('secret()', safe=True)
1571 1574 def secret(repo, subset, x):
1572 1575 """Changeset in secret phase."""
1573 1576 # i18n: "secret" is a keyword
1574 1577 getargs(x, 0, 0, _("secret takes no arguments"))
1575 1578 target = phases.secret
1576 1579 return _phase(repo, subset, target)
1577 1580
1578 1581 def parentspec(repo, subset, x, n):
1579 1582 """``set^0``
1580 1583 The set.
1581 1584 ``set^1`` (or ``set^``), ``set^2``
1582 1585 First or second parent, respectively, of all changesets in set.
1583 1586 """
1584 1587 try:
1585 1588 n = int(n[1])
1586 1589 if n not in (0, 1, 2):
1587 1590 raise ValueError
1588 1591 except (TypeError, ValueError):
1589 1592 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1590 1593 ps = set()
1591 1594 cl = repo.changelog
1592 1595 for r in getset(repo, fullreposet(repo), x):
1593 1596 if n == 0:
1594 1597 ps.add(r)
1595 1598 elif n == 1:
1596 1599 ps.add(cl.parentrevs(r)[0])
1597 1600 elif n == 2:
1598 1601 parents = cl.parentrevs(r)
1599 1602 if len(parents) > 1:
1600 1603 ps.add(parents[1])
1601 1604 return subset & ps
1602 1605
1603 1606 @predicate('present(set)', safe=True)
1604 1607 def present(repo, subset, x):
1605 1608 """An empty set, if any revision in set isn't found; otherwise,
1606 1609 all revisions in set.
1607 1610
1608 1611 If any of specified revisions is not present in the local repository,
1609 1612 the query is normally aborted. But this predicate allows the query
1610 1613 to continue even in such cases.
1611 1614 """
1612 1615 try:
1613 1616 return getset(repo, subset, x)
1614 1617 except error.RepoLookupError:
1615 1618 return baseset()
1616 1619
1617 1620 # for internal use
1618 1621 @predicate('_notpublic', safe=True)
1619 1622 def _notpublic(repo, subset, x):
1620 1623 getargs(x, 0, 0, "_notpublic takes no arguments")
1621 1624 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1622 1625 if repo._phasecache._phasesets:
1623 1626 s = set()
1624 1627 for u in repo._phasecache._phasesets[1:]:
1625 1628 s.update(u)
1626 1629 s = baseset(s - repo.changelog.filteredrevs)
1627 1630 s.sort()
1628 1631 return subset & s
1629 1632 else:
1630 1633 phase = repo._phasecache.phase
1631 1634 target = phases.public
1632 1635 condition = lambda r: phase(repo, r) != target
1633 1636 return subset.filter(condition, condrepr=('<phase %r>', target),
1634 1637 cache=False)
1635 1638
1636 1639 @predicate('public()', safe=True)
1637 1640 def public(repo, subset, x):
1638 1641 """Changeset in public phase."""
1639 1642 # i18n: "public" is a keyword
1640 1643 getargs(x, 0, 0, _("public takes no arguments"))
1641 1644 phase = repo._phasecache.phase
1642 1645 target = phases.public
1643 1646 condition = lambda r: phase(repo, r) == target
1644 1647 return subset.filter(condition, condrepr=('<phase %r>', target),
1645 1648 cache=False)
1646 1649
1647 1650 @predicate('remote([id [,path]])', safe=True)
1648 1651 def remote(repo, subset, x):
1649 1652 """Local revision that corresponds to the given identifier in a
1650 1653 remote repository, if present. Here, the '.' identifier is a
1651 1654 synonym for the current local branch.
1652 1655 """
1653 1656
1654 1657 from . import hg # avoid start-up nasties
1655 1658 # i18n: "remote" is a keyword
1656 1659 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1657 1660
1658 1661 q = '.'
1659 1662 if len(l) > 0:
1660 1663 # i18n: "remote" is a keyword
1661 1664 q = getstring(l[0], _("remote requires a string id"))
1662 1665 if q == '.':
1663 1666 q = repo['.'].branch()
1664 1667
1665 1668 dest = ''
1666 1669 if len(l) > 1:
1667 1670 # i18n: "remote" is a keyword
1668 1671 dest = getstring(l[1], _("remote requires a repository path"))
1669 1672 dest = repo.ui.expandpath(dest or 'default')
1670 1673 dest, branches = hg.parseurl(dest)
1671 1674 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1672 1675 if revs:
1673 1676 revs = [repo.lookup(rev) for rev in revs]
1674 1677 other = hg.peer(repo, {}, dest)
1675 1678 n = other.lookup(q)
1676 1679 if n in repo:
1677 1680 r = repo[n].rev()
1678 1681 if r in subset:
1679 1682 return baseset([r])
1680 1683 return baseset()
1681 1684
1682 1685 @predicate('removes(pattern)', safe=True)
1683 1686 def removes(repo, subset, x):
1684 1687 """Changesets which remove files matching pattern.
1685 1688
1686 1689 The pattern without explicit kind like ``glob:`` is expected to be
1687 1690 relative to the current directory and match against a file or a
1688 1691 directory.
1689 1692 """
1690 1693 # i18n: "removes" is a keyword
1691 1694 pat = getstring(x, _("removes requires a pattern"))
1692 1695 return checkstatus(repo, subset, pat, 2)
1693 1696
1694 1697 @predicate('rev(number)', safe=True)
1695 1698 def rev(repo, subset, x):
1696 1699 """Revision with the given numeric identifier.
1697 1700 """
1698 1701 # i18n: "rev" is a keyword
1699 1702 l = getargs(x, 1, 1, _("rev requires one argument"))
1700 1703 try:
1701 1704 # i18n: "rev" is a keyword
1702 1705 l = int(getstring(l[0], _("rev requires a number")))
1703 1706 except (TypeError, ValueError):
1704 1707 # i18n: "rev" is a keyword
1705 1708 raise error.ParseError(_("rev expects a number"))
1706 1709 if l not in repo.changelog and l != node.nullrev:
1707 1710 return baseset()
1708 1711 return subset & baseset([l])
1709 1712
1710 1713 @predicate('matching(revision [, field])', safe=True)
1711 1714 def matching(repo, subset, x):
1712 1715 """Changesets in which a given set of fields match the set of fields in the
1713 1716 selected revision or set.
1714 1717
1715 1718 To match more than one field pass the list of fields to match separated
1716 1719 by spaces (e.g. ``author description``).
1717 1720
1718 1721 Valid fields are most regular revision fields and some special fields.
1719 1722
1720 1723 Regular revision fields are ``description``, ``author``, ``branch``,
1721 1724 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1722 1725 and ``diff``.
1723 1726 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1724 1727 contents of the revision. Two revisions matching their ``diff`` will
1725 1728 also match their ``files``.
1726 1729
1727 1730 Special fields are ``summary`` and ``metadata``:
1728 1731 ``summary`` matches the first line of the description.
1729 1732 ``metadata`` is equivalent to matching ``description user date``
1730 1733 (i.e. it matches the main metadata fields).
1731 1734
1732 1735 ``metadata`` is the default field which is used when no fields are
1733 1736 specified. You can match more than one field at a time.
1734 1737 """
1735 1738 # i18n: "matching" is a keyword
1736 1739 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1737 1740
1738 1741 revs = getset(repo, fullreposet(repo), l[0])
1739 1742
1740 1743 fieldlist = ['metadata']
1741 1744 if len(l) > 1:
1742 1745 fieldlist = getstring(l[1],
1743 1746 # i18n: "matching" is a keyword
1744 1747 _("matching requires a string "
1745 1748 "as its second argument")).split()
1746 1749
1747 1750 # Make sure that there are no repeated fields,
1748 1751 # expand the 'special' 'metadata' field type
1749 1752 # and check the 'files' whenever we check the 'diff'
1750 1753 fields = []
1751 1754 for field in fieldlist:
1752 1755 if field == 'metadata':
1753 1756 fields += ['user', 'description', 'date']
1754 1757 elif field == 'diff':
1755 1758 # a revision matching the diff must also match the files
1756 1759 # since matching the diff is very costly, make sure to
1757 1760 # also match the files first
1758 1761 fields += ['files', 'diff']
1759 1762 else:
1760 1763 if field == 'author':
1761 1764 field = 'user'
1762 1765 fields.append(field)
1763 1766 fields = set(fields)
1764 1767 if 'summary' in fields and 'description' in fields:
1765 1768 # If a revision matches its description it also matches its summary
1766 1769 fields.discard('summary')
1767 1770
1768 1771 # We may want to match more than one field
1769 1772 # Not all fields take the same amount of time to be matched
1770 1773 # Sort the selected fields in order of increasing matching cost
1771 1774 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1772 1775 'files', 'description', 'substate', 'diff']
1773 1776 def fieldkeyfunc(f):
1774 1777 try:
1775 1778 return fieldorder.index(f)
1776 1779 except ValueError:
1777 1780 # assume an unknown field is very costly
1778 1781 return len(fieldorder)
1779 1782 fields = list(fields)
1780 1783 fields.sort(key=fieldkeyfunc)
1781 1784
1782 1785 # Each field will be matched with its own "getfield" function
1783 1786 # which will be added to the getfieldfuncs array of functions
1784 1787 getfieldfuncs = []
1785 1788 _funcs = {
1786 1789 'user': lambda r: repo[r].user(),
1787 1790 'branch': lambda r: repo[r].branch(),
1788 1791 'date': lambda r: repo[r].date(),
1789 1792 'description': lambda r: repo[r].description(),
1790 1793 'files': lambda r: repo[r].files(),
1791 1794 'parents': lambda r: repo[r].parents(),
1792 1795 'phase': lambda r: repo[r].phase(),
1793 1796 'substate': lambda r: repo[r].substate,
1794 1797 'summary': lambda r: repo[r].description().splitlines()[0],
1795 1798 'diff': lambda r: list(repo[r].diff(git=True),)
1796 1799 }
1797 1800 for info in fields:
1798 1801 getfield = _funcs.get(info, None)
1799 1802 if getfield is None:
1800 1803 raise error.ParseError(
1801 1804 # i18n: "matching" is a keyword
1802 1805 _("unexpected field name passed to matching: %s") % info)
1803 1806 getfieldfuncs.append(getfield)
1804 1807 # convert the getfield array of functions into a "getinfo" function
1805 1808 # which returns an array of field values (or a single value if there
1806 1809 # is only one field to match)
1807 1810 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1808 1811
1809 1812 def matches(x):
1810 1813 for rev in revs:
1811 1814 target = getinfo(rev)
1812 1815 match = True
1813 1816 for n, f in enumerate(getfieldfuncs):
1814 1817 if target[n] != f(x):
1815 1818 match = False
1816 1819 if match:
1817 1820 return True
1818 1821 return False
1819 1822
1820 1823 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1821 1824
1822 1825 @predicate('reverse(set)', safe=True)
1823 1826 def reverse(repo, subset, x):
1824 1827 """Reverse order of set.
1825 1828 """
1826 1829 l = getset(repo, subset, x)
1827 1830 l.reverse()
1828 1831 return l
1829 1832
1830 1833 @predicate('roots(set)', safe=True)
1831 1834 def roots(repo, subset, x):
1832 1835 """Changesets in set with no parent changeset in set.
1833 1836 """
1834 1837 s = getset(repo, fullreposet(repo), x)
1835 1838 parents = repo.changelog.parentrevs
1836 1839 def filter(r):
1837 1840 for p in parents(r):
1838 1841 if 0 <= p and p in s:
1839 1842 return False
1840 1843 return True
1841 1844 return subset & s.filter(filter, condrepr='<roots>')
1842 1845
1843 1846 _sortkeyfuncs = {
1844 1847 'rev': lambda c: c.rev(),
1845 1848 'branch': lambda c: c.branch(),
1846 1849 'desc': lambda c: c.description(),
1847 1850 'user': lambda c: c.user(),
1848 1851 'author': lambda c: c.user(),
1849 1852 'date': lambda c: c.date()[0],
1850 1853 }
1851 1854
1852 1855 def _getsortargs(x):
1853 1856 """Parse sort options into (set, [(key, reverse)], opts)"""
1854 1857 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1855 1858 if 'set' not in args:
1856 1859 # i18n: "sort" is a keyword
1857 1860 raise error.ParseError(_('sort requires one or two arguments'))
1858 1861 keys = "rev"
1859 1862 if 'keys' in args:
1860 1863 # i18n: "sort" is a keyword
1861 1864 keys = getstring(args['keys'], _("sort spec must be a string"))
1862 1865
1863 1866 keyflags = []
1864 1867 for k in keys.split():
1865 1868 fk = k
1866 1869 reverse = (k[0] == '-')
1867 1870 if reverse:
1868 1871 k = k[1:]
1869 1872 if k not in _sortkeyfuncs and k != 'topo':
1870 1873 raise error.ParseError(_("unknown sort key %r") % fk)
1871 1874 keyflags.append((k, reverse))
1872 1875
1873 1876 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1874 1877 # i18n: "topo" is a keyword
1875 1878 raise error.ParseError(_('topo sort order cannot be combined '
1876 1879 'with other sort keys'))
1877 1880
1878 1881 opts = {}
1879 1882 if 'topo.firstbranch' in args:
1880 1883 if any(k == 'topo' for k, reverse in keyflags):
1881 1884 opts['topo.firstbranch'] = args['topo.firstbranch']
1882 1885 else:
1883 1886 # i18n: "topo" and "topo.firstbranch" are keywords
1884 1887 raise error.ParseError(_('topo.firstbranch can only be used '
1885 1888 'when using the topo sort key'))
1886 1889
1887 1890 return args['set'], keyflags, opts
1888 1891
1889 1892 @predicate('sort(set[, [-]key... [, ...]])', safe=True)
1890 1893 def sort(repo, subset, x):
1891 1894 """Sort set by keys. The default sort order is ascending, specify a key
1892 1895 as ``-key`` to sort in descending order.
1893 1896
1894 1897 The keys can be:
1895 1898
1896 1899 - ``rev`` for the revision number,
1897 1900 - ``branch`` for the branch name,
1898 1901 - ``desc`` for the commit message (description),
1899 1902 - ``user`` for user name (``author`` can be used as an alias),
1900 1903 - ``date`` for the commit date
1901 1904 - ``topo`` for a reverse topographical sort
1902 1905
1903 1906 The ``topo`` sort order cannot be combined with other sort keys. This sort
1904 1907 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1905 1908 specifies what topographical branches to prioritize in the sort.
1906 1909
1907 1910 """
1908 1911 s, keyflags, opts = _getsortargs(x)
1909 1912 revs = getset(repo, subset, s)
1910 1913
1911 1914 if not keyflags:
1912 1915 return revs
1913 1916 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1914 1917 revs.sort(reverse=keyflags[0][1])
1915 1918 return revs
1916 1919 elif keyflags[0][0] == "topo":
1917 1920 firstbranch = ()
1918 1921 if 'topo.firstbranch' in opts:
1919 1922 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1920 1923 revs = baseset(_toposort(revs, repo.changelog.parentrevs, firstbranch),
1921 1924 istopo=True)
1922 1925 if keyflags[0][1]:
1923 1926 revs.reverse()
1924 1927 return revs
1925 1928
1926 1929 # sort() is guaranteed to be stable
1927 1930 ctxs = [repo[r] for r in revs]
1928 1931 for k, reverse in reversed(keyflags):
1929 1932 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1930 1933 return baseset([c.rev() for c in ctxs])
1931 1934
1932 1935 def _toposort(revs, parentsfunc, firstbranch=()):
1933 1936 """Yield revisions from heads to roots one (topo) branch at a time.
1934 1937
1935 1938 This function aims to be used by a graph generator that wishes to minimize
1936 1939 the number of parallel branches and their interleaving.
1937 1940
1938 1941 Example iteration order (numbers show the "true" order in a changelog):
1939 1942
1940 1943 o 4
1941 1944 |
1942 1945 o 1
1943 1946 |
1944 1947 | o 3
1945 1948 | |
1946 1949 | o 2
1947 1950 |/
1948 1951 o 0
1949 1952
1950 1953 Note that the ancestors of merges are understood by the current
1951 1954 algorithm to be on the same branch. This means no reordering will
1952 1955 occur behind a merge.
1953 1956 """
1954 1957
1955 1958 ### Quick summary of the algorithm
1956 1959 #
1957 1960 # This function is based around a "retention" principle. We keep revisions
1958 1961 # in memory until we are ready to emit a whole branch that immediately
1959 1962 # "merges" into an existing one. This reduces the number of parallel
1960 1963 # branches with interleaved revisions.
1961 1964 #
1962 1965 # During iteration revs are split into two groups:
1963 1966 # A) revision already emitted
1964 1967 # B) revision in "retention". They are stored as different subgroups.
1965 1968 #
1966 1969 # for each REV, we do the following logic:
1967 1970 #
1968 1971 # 1) if REV is a parent of (A), we will emit it. If there is a
1969 1972 # retention group ((B) above) that is blocked on REV being
1970 1973 # available, we emit all the revisions out of that retention
1971 1974 # group first.
1972 1975 #
1973 1976 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be
1974 1977 # available, if such subgroup exist, we add REV to it and the subgroup is
1975 1978 # now awaiting for REV.parents() to be available.
1976 1979 #
1977 1980 # 3) finally if no such group existed in (B), we create a new subgroup.
1978 1981 #
1979 1982 #
1980 1983 # To bootstrap the algorithm, we emit the tipmost revision (which
1981 1984 # puts it in group (A) from above).
1982 1985
1983 1986 revs.sort(reverse=True)
1984 1987
1985 1988 # Set of parents of revision that have been emitted. They can be considered
1986 1989 # unblocked as the graph generator is already aware of them so there is no
1987 1990 # need to delay the revisions that reference them.
1988 1991 #
1989 1992 # If someone wants to prioritize a branch over the others, pre-filling this
1990 1993 # set will force all other branches to wait until this branch is ready to be
1991 1994 # emitted.
1992 1995 unblocked = set(firstbranch)
1993 1996
1994 1997 # list of groups waiting to be displayed, each group is defined by:
1995 1998 #
1996 1999 # (revs: lists of revs waiting to be displayed,
1997 2000 # blocked: set of that cannot be displayed before those in 'revs')
1998 2001 #
1999 2002 # The second value ('blocked') correspond to parents of any revision in the
2000 2003 # group ('revs') that is not itself contained in the group. The main idea
2001 2004 # of this algorithm is to delay as much as possible the emission of any
2002 2005 # revision. This means waiting for the moment we are about to display
2003 2006 # these parents to display the revs in a group.
2004 2007 #
2005 2008 # This first implementation is smart until it encounters a merge: it will
2006 2009 # emit revs as soon as any parent is about to be emitted and can grow an
2007 2010 # arbitrary number of revs in 'blocked'. In practice this mean we properly
2008 2011 # retains new branches but gives up on any special ordering for ancestors
2009 2012 # of merges. The implementation can be improved to handle this better.
2010 2013 #
2011 2014 # The first subgroup is special. It corresponds to all the revision that
2012 2015 # were already emitted. The 'revs' lists is expected to be empty and the
2013 2016 # 'blocked' set contains the parents revisions of already emitted revision.
2014 2017 #
2015 2018 # You could pre-seed the <parents> set of groups[0] to a specific
2016 2019 # changesets to select what the first emitted branch should be.
2017 2020 groups = [([], unblocked)]
2018 2021 pendingheap = []
2019 2022 pendingset = set()
2020 2023
2021 2024 heapq.heapify(pendingheap)
2022 2025 heappop = heapq.heappop
2023 2026 heappush = heapq.heappush
2024 2027 for currentrev in revs:
2025 2028 # Heap works with smallest element, we want highest so we invert
2026 2029 if currentrev not in pendingset:
2027 2030 heappush(pendingheap, -currentrev)
2028 2031 pendingset.add(currentrev)
2029 2032 # iterates on pending rev until after the current rev have been
2030 2033 # processed.
2031 2034 rev = None
2032 2035 while rev != currentrev:
2033 2036 rev = -heappop(pendingheap)
2034 2037 pendingset.remove(rev)
2035 2038
2036 2039 # Seek for a subgroup blocked, waiting for the current revision.
2037 2040 matching = [i for i, g in enumerate(groups) if rev in g[1]]
2038 2041
2039 2042 if matching:
2040 2043 # The main idea is to gather together all sets that are blocked
2041 2044 # on the same revision.
2042 2045 #
2043 2046 # Groups are merged when a common blocking ancestor is
2044 2047 # observed. For example, given two groups:
2045 2048 #
2046 2049 # revs [5, 4] waiting for 1
2047 2050 # revs [3, 2] waiting for 1
2048 2051 #
2049 2052 # These two groups will be merged when we process
2050 2053 # 1. In theory, we could have merged the groups when
2051 2054 # we added 2 to the group it is now in (we could have
2052 2055 # noticed the groups were both blocked on 1 then), but
2053 2056 # the way it works now makes the algorithm simpler.
2054 2057 #
2055 2058 # We also always keep the oldest subgroup first. We can
2056 2059 # probably improve the behavior by having the longest set
2057 2060 # first. That way, graph algorithms could minimise the length
2058 2061 # of parallel lines their drawing. This is currently not done.
2059 2062 targetidx = matching.pop(0)
2060 2063 trevs, tparents = groups[targetidx]
2061 2064 for i in matching:
2062 2065 gr = groups[i]
2063 2066 trevs.extend(gr[0])
2064 2067 tparents |= gr[1]
2065 2068 # delete all merged subgroups (except the one we kept)
2066 2069 # (starting from the last subgroup for performance and
2067 2070 # sanity reasons)
2068 2071 for i in reversed(matching):
2069 2072 del groups[i]
2070 2073 else:
2071 2074 # This is a new head. We create a new subgroup for it.
2072 2075 targetidx = len(groups)
2073 2076 groups.append(([], set([rev])))
2074 2077
2075 2078 gr = groups[targetidx]
2076 2079
2077 2080 # We now add the current nodes to this subgroups. This is done
2078 2081 # after the subgroup merging because all elements from a subgroup
2079 2082 # that relied on this rev must precede it.
2080 2083 #
2081 2084 # we also update the <parents> set to include the parents of the
2082 2085 # new nodes.
2083 2086 if rev == currentrev: # only display stuff in rev
2084 2087 gr[0].append(rev)
2085 2088 gr[1].remove(rev)
2086 2089 parents = [p for p in parentsfunc(rev) if p > node.nullrev]
2087 2090 gr[1].update(parents)
2088 2091 for p in parents:
2089 2092 if p not in pendingset:
2090 2093 pendingset.add(p)
2091 2094 heappush(pendingheap, -p)
2092 2095
2093 2096 # Look for a subgroup to display
2094 2097 #
2095 2098 # When unblocked is empty (if clause), we were not waiting for any
2096 2099 # revisions during the first iteration (if no priority was given) or
2097 2100 # if we emitted a whole disconnected set of the graph (reached a
2098 2101 # root). In that case we arbitrarily take the oldest known
2099 2102 # subgroup. The heuristic could probably be better.
2100 2103 #
2101 2104 # Otherwise (elif clause) if the subgroup is blocked on
2102 2105 # a revision we just emitted, we can safely emit it as
2103 2106 # well.
2104 2107 if not unblocked:
2105 2108 if len(groups) > 1: # display other subset
2106 2109 targetidx = 1
2107 2110 gr = groups[1]
2108 2111 elif not gr[1] & unblocked:
2109 2112 gr = None
2110 2113
2111 2114 if gr is not None:
2112 2115 # update the set of awaited revisions with the one from the
2113 2116 # subgroup
2114 2117 unblocked |= gr[1]
2115 2118 # output all revisions in the subgroup
2116 2119 for r in gr[0]:
2117 2120 yield r
2118 2121 # delete the subgroup that you just output
2119 2122 # unless it is groups[0] in which case you just empty it.
2120 2123 if targetidx:
2121 2124 del groups[targetidx]
2122 2125 else:
2123 2126 gr[0][:] = []
2124 2127 # Check if we have some subgroup waiting for revisions we are not going to
2125 2128 # iterate over
2126 2129 for g in groups:
2127 2130 for r in g[0]:
2128 2131 yield r
2129 2132
2130 2133 @predicate('subrepo([pattern])')
2131 2134 def subrepo(repo, subset, x):
2132 2135 """Changesets that add, modify or remove the given subrepo. If no subrepo
2133 2136 pattern is named, any subrepo changes are returned.
2134 2137 """
2135 2138 # i18n: "subrepo" is a keyword
2136 2139 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
2137 2140 pat = None
2138 2141 if len(args) != 0:
2139 2142 pat = getstring(args[0], _("subrepo requires a pattern"))
2140 2143
2141 2144 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
2142 2145
2143 2146 def submatches(names):
2144 2147 k, p, m = util.stringmatcher(pat)
2145 2148 for name in names:
2146 2149 if m(name):
2147 2150 yield name
2148 2151
2149 2152 def matches(x):
2150 2153 c = repo[x]
2151 2154 s = repo.status(c.p1().node(), c.node(), match=m)
2152 2155
2153 2156 if pat is None:
2154 2157 return s.added or s.modified or s.removed
2155 2158
2156 2159 if s.added:
2157 2160 return any(submatches(c.substate.keys()))
2158 2161
2159 2162 if s.modified:
2160 2163 subs = set(c.p1().substate.keys())
2161 2164 subs.update(c.substate.keys())
2162 2165
2163 2166 for path in submatches(subs):
2164 2167 if c.p1().substate.get(path) != c.substate.get(path):
2165 2168 return True
2166 2169
2167 2170 if s.removed:
2168 2171 return any(submatches(c.p1().substate.keys()))
2169 2172
2170 2173 return False
2171 2174
2172 2175 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2173 2176
2174 2177 def _substringmatcher(pattern):
2175 2178 kind, pattern, matcher = util.stringmatcher(pattern)
2176 2179 if kind == 'literal':
2177 2180 matcher = lambda s: pattern in s
2178 2181 return kind, pattern, matcher
2179 2182
2180 2183 @predicate('tag([name])', safe=True)
2181 2184 def tag(repo, subset, x):
2182 2185 """The specified tag by name, or all tagged revisions if no name is given.
2183 2186
2184 2187 If `name` starts with `re:`, the remainder of the name is treated as
2185 2188 a regular expression. To match a tag that actually starts with `re:`,
2186 2189 use the prefix `literal:`.
2187 2190 """
2188 2191 # i18n: "tag" is a keyword
2189 2192 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2190 2193 cl = repo.changelog
2191 2194 if args:
2192 2195 pattern = getstring(args[0],
2193 2196 # i18n: "tag" is a keyword
2194 2197 _('the argument to tag must be a string'))
2195 2198 kind, pattern, matcher = util.stringmatcher(pattern)
2196 2199 if kind == 'literal':
2197 2200 # avoid resolving all tags
2198 2201 tn = repo._tagscache.tags.get(pattern, None)
2199 2202 if tn is None:
2200 2203 raise error.RepoLookupError(_("tag '%s' does not exist")
2201 2204 % pattern)
2202 2205 s = set([repo[tn].rev()])
2203 2206 else:
2204 2207 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
2205 2208 else:
2206 2209 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
2207 2210 return subset & s
2208 2211
2209 2212 @predicate('tagged', safe=True)
2210 2213 def tagged(repo, subset, x):
2211 2214 return tag(repo, subset, x)
2212 2215
2213 2216 @predicate('unstable()', safe=True)
2214 2217 def unstable(repo, subset, x):
2215 2218 """Non-obsolete changesets with obsolete ancestors.
2216 2219 """
2217 2220 # i18n: "unstable" is a keyword
2218 2221 getargs(x, 0, 0, _("unstable takes no arguments"))
2219 2222 unstables = obsmod.getrevs(repo, 'unstable')
2220 2223 return subset & unstables
2221 2224
2222 2225
2223 2226 @predicate('user(string)', safe=True)
2224 2227 def user(repo, subset, x):
2225 2228 """User name contains string. The match is case-insensitive.
2226 2229
2227 2230 If `string` starts with `re:`, the remainder of the string is treated as
2228 2231 a regular expression. To match a user that actually contains `re:`, use
2229 2232 the prefix `literal:`.
2230 2233 """
2231 2234 return author(repo, subset, x)
2232 2235
2233 2236 # experimental
2234 2237 @predicate('wdir', safe=True)
2235 2238 def wdir(repo, subset, x):
2236 2239 # i18n: "wdir" is a keyword
2237 2240 getargs(x, 0, 0, _("wdir takes no arguments"))
2238 2241 if node.wdirrev in subset or isinstance(subset, fullreposet):
2239 2242 return baseset([node.wdirrev])
2240 2243 return baseset()
2241 2244
2242 2245 # for internal use
2243 2246 @predicate('_list', safe=True)
2244 2247 def _list(repo, subset, x):
2245 2248 s = getstring(x, "internal error")
2246 2249 if not s:
2247 2250 return baseset()
2248 2251 # remove duplicates here. it's difficult for caller to deduplicate sets
2249 2252 # because different symbols can point to the same rev.
2250 2253 cl = repo.changelog
2251 2254 ls = []
2252 2255 seen = set()
2253 2256 for t in s.split('\0'):
2254 2257 try:
2255 2258 # fast path for integer revision
2256 2259 r = int(t)
2257 2260 if str(r) != t or r not in cl:
2258 2261 raise ValueError
2259 2262 revs = [r]
2260 2263 except ValueError:
2261 2264 revs = stringset(repo, subset, t)
2262 2265
2263 2266 for r in revs:
2264 2267 if r in seen:
2265 2268 continue
2266 2269 if (r in subset
2267 2270 or r == node.nullrev and isinstance(subset, fullreposet)):
2268 2271 ls.append(r)
2269 2272 seen.add(r)
2270 2273 return baseset(ls)
2271 2274
2272 2275 # for internal use
2273 2276 @predicate('_intlist', safe=True)
2274 2277 def _intlist(repo, subset, x):
2275 2278 s = getstring(x, "internal error")
2276 2279 if not s:
2277 2280 return baseset()
2278 2281 ls = [int(r) for r in s.split('\0')]
2279 2282 s = subset
2280 2283 return baseset([r for r in ls if r in s])
2281 2284
2282 2285 # for internal use
2283 2286 @predicate('_hexlist', safe=True)
2284 2287 def _hexlist(repo, subset, x):
2285 2288 s = getstring(x, "internal error")
2286 2289 if not s:
2287 2290 return baseset()
2288 2291 cl = repo.changelog
2289 2292 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2290 2293 s = subset
2291 2294 return baseset([r for r in ls if r in s])
2292 2295
2293 2296 methods = {
2294 2297 "range": rangeset,
2295 2298 "dagrange": dagrange,
2296 2299 "string": stringset,
2297 2300 "symbol": stringset,
2298 2301 "and": andset,
2299 2302 "or": orset,
2300 2303 "not": notset,
2301 2304 "difference": differenceset,
2302 2305 "list": listset,
2303 2306 "keyvalue": keyvaluepair,
2304 2307 "func": func,
2305 2308 "ancestor": ancestorspec,
2306 2309 "parent": parentspec,
2307 2310 "parentpost": p1,
2308 2311 }
2309 2312
2310 2313 def _matchonly(revs, bases):
2311 2314 """
2312 2315 >>> f = lambda *args: _matchonly(*map(parse, args))
2313 2316 >>> f('ancestors(A)', 'not ancestors(B)')
2314 2317 ('list', ('symbol', 'A'), ('symbol', 'B'))
2315 2318 """
2316 2319 if (revs is not None
2317 2320 and revs[0] == 'func'
2318 2321 and getsymbol(revs[1]) == 'ancestors'
2319 2322 and bases is not None
2320 2323 and bases[0] == 'not'
2321 2324 and bases[1][0] == 'func'
2322 2325 and getsymbol(bases[1][1]) == 'ancestors'):
2323 2326 return ('list', revs[2], bases[1][2])
2324 2327
2325 2328 def _fixops(x):
2326 2329 """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
2327 2330 handled well by our simple top-down parser"""
2328 2331 if not isinstance(x, tuple):
2329 2332 return x
2330 2333
2331 2334 op = x[0]
2332 2335 if op == 'parent':
2333 2336 # x^:y means (x^) : y, not x ^ (:y)
2334 2337 # x^: means (x^) :, not x ^ (:)
2335 2338 post = ('parentpost', x[1])
2336 2339 if x[2][0] == 'dagrangepre':
2337 2340 return _fixops(('dagrange', post, x[2][1]))
2338 2341 elif x[2][0] == 'rangepre':
2339 2342 return _fixops(('range', post, x[2][1]))
2340 2343 elif x[2][0] == 'rangeall':
2341 2344 return _fixops(('rangepost', post))
2345 elif op == 'or':
2346 # make number of arguments deterministic:
2347 # x + y + z -> (or x y z) -> (or (list x y z))
2348 return (op, _fixops(('list',) + x[1:]))
2342 2349
2343 2350 return (op,) + tuple(_fixops(y) for y in x[1:])
2344 2351
2345 2352 def _analyze(x):
2346 2353 if x is None:
2347 2354 return x
2348 2355
2349 2356 op = x[0]
2350 2357 if op == 'minus':
2351 2358 return _analyze(('and', x[1], ('not', x[2])))
2352 2359 elif op == 'only':
2353 2360 t = ('func', ('symbol', 'only'), ('list', x[1], x[2]))
2354 2361 return _analyze(t)
2355 2362 elif op == 'onlypost':
2356 2363 return _analyze(('func', ('symbol', 'only'), x[1]))
2357 2364 elif op == 'dagrangepre':
2358 2365 return _analyze(('func', ('symbol', 'ancestors'), x[1]))
2359 2366 elif op == 'dagrangepost':
2360 2367 return _analyze(('func', ('symbol', 'descendants'), x[1]))
2361 2368 elif op == 'rangeall':
2362 2369 return _analyze(('range', ('string', '0'), ('string', 'tip')))
2363 2370 elif op == 'rangepre':
2364 2371 return _analyze(('range', ('string', '0'), x[1]))
2365 2372 elif op == 'rangepost':
2366 2373 return _analyze(('range', x[1], ('string', 'tip')))
2367 2374 elif op == 'negate':
2368 2375 s = getstring(x[1], _("can't negate that"))
2369 2376 return _analyze(('string', '-' + s))
2370 2377 elif op in ('string', 'symbol'):
2371 2378 return x
2372 2379 elif op == 'and':
2373 2380 ta = _analyze(x[1])
2374 2381 tb = _analyze(x[2])
2375 2382 return (op, ta, tb)
2376 2383 elif op == 'or':
2377 return (op,) + tuple(_analyze(y) for y in x[1:])
2384 return (op, _analyze(x[1]))
2378 2385 elif op == 'not':
2379 2386 return (op, _analyze(x[1]))
2380 2387 elif op == 'parentpost':
2381 2388 return (op, _analyze(x[1]))
2382 2389 elif op == 'group':
2383 2390 return _analyze(x[1])
2384 2391 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2385 2392 ta = _analyze(x[1])
2386 2393 tb = _analyze(x[2])
2387 2394 return (op, ta, tb)
2388 2395 elif op == 'list':
2389 2396 return (op,) + tuple(_analyze(y) for y in x[1:])
2390 2397 elif op == 'keyvalue':
2391 2398 return (op, x[1], _analyze(x[2]))
2392 2399 elif op == 'func':
2393 2400 return (op, x[1], _analyze(x[2]))
2394 2401 raise ValueError('invalid operator %r' % op)
2395 2402
2396 2403 def analyze(x):
2397 2404 """Transform raw parsed tree to evaluatable tree which can be fed to
2398 2405 optimize() or getset()
2399 2406
2400 2407 All pseudo operations should be mapped to real operations or functions
2401 2408 defined in methods or symbols table respectively.
2402 2409 """
2403 2410 return _analyze(x)
2404 2411
2405 2412 def _optimize(x, small):
2406 2413 if x is None:
2407 2414 return 0, x
2408 2415
2409 2416 smallbonus = 1
2410 2417 if small:
2411 2418 smallbonus = .5
2412 2419
2413 2420 op = x[0]
2414 2421 if op in ('string', 'symbol'):
2415 2422 return smallbonus, x # single revisions are small
2416 2423 elif op == 'and':
2417 2424 wa, ta = _optimize(x[1], True)
2418 2425 wb, tb = _optimize(x[2], True)
2419 2426 w = min(wa, wb)
2420 2427
2421 2428 # (::x and not ::y)/(not ::y and ::x) have a fast path
2422 2429 tm = _matchonly(ta, tb) or _matchonly(tb, ta)
2423 2430 if tm:
2424 2431 return w, ('func', ('symbol', 'only'), tm)
2425 2432
2426 2433 if tb is not None and tb[0] == 'not':
2427 2434 return wa, ('difference', ta, tb[1])
2428 2435
2429 2436 if wa > wb:
2430 2437 return w, (op, tb, ta)
2431 2438 return w, (op, ta, tb)
2432 2439 elif op == 'or':
2433 2440 # fast path for machine-generated expression, that is likely to have
2434 2441 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2435 2442 ws, ts, ss = [], [], []
2436 2443 def flushss():
2437 2444 if not ss:
2438 2445 return
2439 2446 if len(ss) == 1:
2440 2447 w, t = ss[0]
2441 2448 else:
2442 2449 s = '\0'.join(t[1] for w, t in ss)
2443 2450 y = ('func', ('symbol', '_list'), ('string', s))
2444 2451 w, t = _optimize(y, False)
2445 2452 ws.append(w)
2446 2453 ts.append(t)
2447 2454 del ss[:]
2448 for y in x[1:]:
2455 for y in getlist(x[1]):
2449 2456 w, t = _optimize(y, False)
2450 2457 if t is not None and (t[0] == 'string' or t[0] == 'symbol'):
2451 2458 ss.append((w, t))
2452 2459 continue
2453 2460 flushss()
2454 2461 ws.append(w)
2455 2462 ts.append(t)
2456 2463 flushss()
2457 2464 if len(ts) == 1:
2458 2465 return ws[0], ts[0] # 'or' operation is fully optimized out
2459 2466 # we can't reorder trees by weight because it would change the order.
2460 2467 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2461 2468 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2462 return max(ws), (op,) + tuple(ts)
2469 return max(ws), (op, ('list',) + tuple(ts))
2463 2470 elif op == 'not':
2464 2471 # Optimize not public() to _notpublic() because we have a fast version
2465 2472 if x[1] == ('func', ('symbol', 'public'), None):
2466 2473 newsym = ('func', ('symbol', '_notpublic'), None)
2467 2474 o = _optimize(newsym, not small)
2468 2475 return o[0], o[1]
2469 2476 else:
2470 2477 o = _optimize(x[1], not small)
2471 2478 return o[0], (op, o[1])
2472 2479 elif op == 'parentpost':
2473 2480 o = _optimize(x[1], small)
2474 2481 return o[0], (op, o[1])
2475 2482 elif op in ('dagrange', 'range', 'parent', 'ancestor'):
2476 2483 wa, ta = _optimize(x[1], small)
2477 2484 wb, tb = _optimize(x[2], small)
2478 2485 return wa + wb, (op, ta, tb)
2479 2486 elif op == 'list':
2480 2487 ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
2481 2488 return sum(ws), (op,) + ts
2482 2489 elif op == 'keyvalue':
2483 2490 w, t = _optimize(x[2], small)
2484 2491 return w, (op, x[1], t)
2485 2492 elif op == 'func':
2486 2493 f = getsymbol(x[1])
2487 2494 wa, ta = _optimize(x[2], small)
2488 2495 if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep',
2489 2496 'keyword', 'outgoing', 'user'):
2490 2497 w = 10 # slow
2491 2498 elif f in ('modifies', 'adds', 'removes'):
2492 2499 w = 30 # slower
2493 2500 elif f == "contains":
2494 2501 w = 100 # very slow
2495 2502 elif f == "ancestor":
2496 2503 w = 1 * smallbonus
2497 2504 elif f in ('reverse', 'limit', 'first', '_intlist'):
2498 2505 w = 0
2499 2506 elif f == "sort":
2500 2507 w = 10 # assume most sorts look at changelog
2501 2508 else:
2502 2509 w = 1
2503 2510 return w + wa, (op, x[1], ta)
2504 2511 raise ValueError('invalid operator %r' % op)
2505 2512
2506 2513 def optimize(tree):
2507 2514 """Optimize evaluatable tree
2508 2515
2509 2516 All pseudo operations should be transformed beforehand.
2510 2517 """
2511 2518 _weight, newtree = _optimize(tree, small=True)
2512 2519 return newtree
2513 2520
2514 2521 # the set of valid characters for the initial letter of symbols in
2515 2522 # alias declarations and definitions
2516 2523 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2517 2524 if c.isalnum() or c in '._@$' or ord(c) > 127)
2518 2525
2519 2526 def _parsewith(spec, lookup=None, syminitletters=None):
2520 2527 """Generate a parse tree of given spec with given tokenizing options
2521 2528
2522 2529 >>> _parsewith('foo($1)', syminitletters=_aliassyminitletters)
2523 2530 ('func', ('symbol', 'foo'), ('symbol', '$1'))
2524 2531 >>> _parsewith('$1')
2525 2532 Traceback (most recent call last):
2526 2533 ...
2527 2534 ParseError: ("syntax error in revset '$1'", 0)
2528 2535 >>> _parsewith('foo bar')
2529 2536 Traceback (most recent call last):
2530 2537 ...
2531 2538 ParseError: ('invalid token', 4)
2532 2539 """
2533 2540 p = parser.parser(elements)
2534 2541 tree, pos = p.parse(tokenize(spec, lookup=lookup,
2535 2542 syminitletters=syminitletters))
2536 2543 if pos != len(spec):
2537 2544 raise error.ParseError(_('invalid token'), pos)
2538 2545 return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
2539 2546
2540 2547 class _aliasrules(parser.basealiasrules):
2541 2548 """Parsing and expansion rule set of revset aliases"""
2542 2549 _section = _('revset alias')
2543 2550
2544 2551 @staticmethod
2545 2552 def _parse(spec):
2546 2553 """Parse alias declaration/definition ``spec``
2547 2554
2548 2555 This allows symbol names to use also ``$`` as an initial letter
2549 2556 (for backward compatibility), and callers of this function should
2550 2557 examine whether ``$`` is used also for unexpected symbols or not.
2551 2558 """
2552 2559 return _parsewith(spec, syminitletters=_aliassyminitletters)
2553 2560
2554 2561 @staticmethod
2555 2562 def _trygetfunc(tree):
2556 2563 if tree[0] == 'func' and tree[1][0] == 'symbol':
2557 2564 return tree[1][1], getlist(tree[2])
2558 2565
2559 2566 def expandaliases(ui, tree):
2560 2567 aliases = _aliasrules.buildmap(ui.configitems('revsetalias'))
2561 2568 tree = _aliasrules.expand(aliases, tree)
2562 2569 # warn about problematic (but not referred) aliases
2563 2570 for name, alias in sorted(aliases.iteritems()):
2564 2571 if alias.error and not alias.warned:
2565 2572 ui.warn(_('warning: %s\n') % (alias.error))
2566 2573 alias.warned = True
2567 2574 return tree
2568 2575
2569 2576 def foldconcat(tree):
2570 2577 """Fold elements to be concatenated by `##`
2571 2578 """
2572 2579 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2573 2580 return tree
2574 2581 if tree[0] == '_concat':
2575 2582 pending = [tree]
2576 2583 l = []
2577 2584 while pending:
2578 2585 e = pending.pop()
2579 2586 if e[0] == '_concat':
2580 2587 pending.extend(reversed(e[1:]))
2581 2588 elif e[0] in ('string', 'symbol'):
2582 2589 l.append(e[1])
2583 2590 else:
2584 2591 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2585 2592 raise error.ParseError(msg)
2586 2593 return ('string', ''.join(l))
2587 2594 else:
2588 2595 return tuple(foldconcat(t) for t in tree)
2589 2596
2590 2597 def parse(spec, lookup=None):
2591 2598 return _parsewith(spec, lookup=lookup)
2592 2599
2593 2600 def posttreebuilthook(tree, repo):
2594 2601 # hook for extensions to execute code on the optimized tree
2595 2602 pass
2596 2603
2597 2604 def match(ui, spec, repo=None):
2598 2605 """Create a matcher for a single revision spec."""
2599 2606 return matchany(ui, [spec], repo=repo)
2600 2607
2601 2608 def matchany(ui, specs, repo=None):
2602 2609 """Create a matcher that will include any revisions matching one of the
2603 2610 given specs"""
2604 2611 if not specs:
2605 2612 def mfunc(repo, subset=None):
2606 2613 return baseset()
2607 2614 return mfunc
2608 2615 if not all(specs):
2609 2616 raise error.ParseError(_("empty query"))
2610 2617 lookup = None
2611 2618 if repo:
2612 2619 lookup = repo.__contains__
2613 2620 if len(specs) == 1:
2614 2621 tree = parse(specs[0], lookup)
2615 2622 else:
2616 tree = ('or',) + tuple(parse(s, lookup) for s in specs)
2623 tree = ('or', ('list',) + tuple(parse(s, lookup) for s in specs))
2617 2624
2618 2625 if ui:
2619 2626 tree = expandaliases(ui, tree)
2620 2627 tree = foldconcat(tree)
2621 2628 tree = analyze(tree)
2622 2629 tree = optimize(tree)
2623 2630 posttreebuilthook(tree, repo)
2624 2631 return makematcher(tree)
2625 2632
2626 2633 def makematcher(tree):
2627 2634 """Create a matcher from an evaluatable tree"""
2628 2635 def mfunc(repo, subset=None):
2629 2636 if subset is None:
2630 2637 subset = fullreposet(repo)
2631 2638 if util.safehasattr(subset, 'isascending'):
2632 2639 result = getset(repo, subset, tree)
2633 2640 else:
2634 2641 result = getset(repo, baseset(subset), tree)
2635 2642 return result
2636 2643 return mfunc
2637 2644
2638 2645 def formatspec(expr, *args):
2639 2646 '''
2640 2647 This is a convenience function for using revsets internally, and
2641 2648 escapes arguments appropriately. Aliases are intentionally ignored
2642 2649 so that intended expression behavior isn't accidentally subverted.
2643 2650
2644 2651 Supported arguments:
2645 2652
2646 2653 %r = revset expression, parenthesized
2647 2654 %d = int(arg), no quoting
2648 2655 %s = string(arg), escaped and single-quoted
2649 2656 %b = arg.branch(), escaped and single-quoted
2650 2657 %n = hex(arg), single-quoted
2651 2658 %% = a literal '%'
2652 2659
2653 2660 Prefixing the type with 'l' specifies a parenthesized list of that type.
2654 2661
2655 2662 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2656 2663 '(10 or 11):: and ((this()) or (that()))'
2657 2664 >>> formatspec('%d:: and not %d::', 10, 20)
2658 2665 '10:: and not 20::'
2659 2666 >>> formatspec('%ld or %ld', [], [1])
2660 2667 "_list('') or 1"
2661 2668 >>> formatspec('keyword(%s)', 'foo\\xe9')
2662 2669 "keyword('foo\\\\xe9')"
2663 2670 >>> b = lambda: 'default'
2664 2671 >>> b.branch = b
2665 2672 >>> formatspec('branch(%b)', b)
2666 2673 "branch('default')"
2667 2674 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2668 2675 "root(_list('a\\x00b\\x00c\\x00d'))"
2669 2676 '''
2670 2677
2671 2678 def quote(s):
2672 2679 return repr(str(s))
2673 2680
2674 2681 def argtype(c, arg):
2675 2682 if c == 'd':
2676 2683 return str(int(arg))
2677 2684 elif c == 's':
2678 2685 return quote(arg)
2679 2686 elif c == 'r':
2680 2687 parse(arg) # make sure syntax errors are confined
2681 2688 return '(%s)' % arg
2682 2689 elif c == 'n':
2683 2690 return quote(node.hex(arg))
2684 2691 elif c == 'b':
2685 2692 return quote(arg.branch())
2686 2693
2687 2694 def listexp(s, t):
2688 2695 l = len(s)
2689 2696 if l == 0:
2690 2697 return "_list('')"
2691 2698 elif l == 1:
2692 2699 return argtype(t, s[0])
2693 2700 elif t == 'd':
2694 2701 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2695 2702 elif t == 's':
2696 2703 return "_list('%s')" % "\0".join(s)
2697 2704 elif t == 'n':
2698 2705 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2699 2706 elif t == 'b':
2700 2707 return "_list('%s')" % "\0".join(a.branch() for a in s)
2701 2708
2702 2709 m = l // 2
2703 2710 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2704 2711
2705 2712 ret = ''
2706 2713 pos = 0
2707 2714 arg = 0
2708 2715 while pos < len(expr):
2709 2716 c = expr[pos]
2710 2717 if c == '%':
2711 2718 pos += 1
2712 2719 d = expr[pos]
2713 2720 if d == '%':
2714 2721 ret += d
2715 2722 elif d in 'dsnbr':
2716 2723 ret += argtype(d, args[arg])
2717 2724 arg += 1
2718 2725 elif d == 'l':
2719 2726 # a list of some type
2720 2727 pos += 1
2721 2728 d = expr[pos]
2722 2729 ret += listexp(list(args[arg]), d)
2723 2730 arg += 1
2724 2731 else:
2725 2732 raise error.Abort(_('unexpected revspec format character %s')
2726 2733 % d)
2727 2734 else:
2728 2735 ret += c
2729 2736 pos += 1
2730 2737
2731 2738 return ret
2732 2739
2733 2740 def prettyformat(tree):
2734 2741 return parser.prettyformat(tree, ('string', 'symbol'))
2735 2742
2736 2743 def depth(tree):
2737 2744 if isinstance(tree, tuple):
2738 2745 return max(map(depth, tree)) + 1
2739 2746 else:
2740 2747 return 0
2741 2748
2742 2749 def funcsused(tree):
2743 2750 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2744 2751 return set()
2745 2752 else:
2746 2753 funcs = set()
2747 2754 for s in tree[1:]:
2748 2755 funcs |= funcsused(s)
2749 2756 if tree[0] == 'func':
2750 2757 funcs.add(tree[1][1])
2751 2758 return funcs
2752 2759
2753 2760 def _formatsetrepr(r):
2754 2761 """Format an optional printable representation of a set
2755 2762
2756 2763 ======== =================================
2757 2764 type(r) example
2758 2765 ======== =================================
2759 2766 tuple ('<not %r>', other)
2760 2767 str '<branch closed>'
2761 2768 callable lambda: '<branch %r>' % sorted(b)
2762 2769 object other
2763 2770 ======== =================================
2764 2771 """
2765 2772 if r is None:
2766 2773 return ''
2767 2774 elif isinstance(r, tuple):
2768 2775 return r[0] % r[1:]
2769 2776 elif isinstance(r, str):
2770 2777 return r
2771 2778 elif callable(r):
2772 2779 return r()
2773 2780 else:
2774 2781 return repr(r)
2775 2782
2776 2783 class abstractsmartset(object):
2777 2784
2778 2785 def __nonzero__(self):
2779 2786 """True if the smartset is not empty"""
2780 2787 raise NotImplementedError()
2781 2788
2782 2789 def __contains__(self, rev):
2783 2790 """provide fast membership testing"""
2784 2791 raise NotImplementedError()
2785 2792
2786 2793 def __iter__(self):
2787 2794 """iterate the set in the order it is supposed to be iterated"""
2788 2795 raise NotImplementedError()
2789 2796
2790 2797 # Attributes containing a function to perform a fast iteration in a given
2791 2798 # direction. A smartset can have none, one, or both defined.
2792 2799 #
2793 2800 # Default value is None instead of a function returning None to avoid
2794 2801 # initializing an iterator just for testing if a fast method exists.
2795 2802 fastasc = None
2796 2803 fastdesc = None
2797 2804
2798 2805 def isascending(self):
2799 2806 """True if the set will iterate in ascending order"""
2800 2807 raise NotImplementedError()
2801 2808
2802 2809 def isdescending(self):
2803 2810 """True if the set will iterate in descending order"""
2804 2811 raise NotImplementedError()
2805 2812
2806 2813 def istopo(self):
2807 2814 """True if the set will iterate in topographical order"""
2808 2815 raise NotImplementedError()
2809 2816
2810 2817 @util.cachefunc
2811 2818 def min(self):
2812 2819 """return the minimum element in the set"""
2813 2820 if self.fastasc is not None:
2814 2821 for r in self.fastasc():
2815 2822 return r
2816 2823 raise ValueError('arg is an empty sequence')
2817 2824 return min(self)
2818 2825
2819 2826 @util.cachefunc
2820 2827 def max(self):
2821 2828 """return the maximum element in the set"""
2822 2829 if self.fastdesc is not None:
2823 2830 for r in self.fastdesc():
2824 2831 return r
2825 2832 raise ValueError('arg is an empty sequence')
2826 2833 return max(self)
2827 2834
2828 2835 def first(self):
2829 2836 """return the first element in the set (user iteration perspective)
2830 2837
2831 2838 Return None if the set is empty"""
2832 2839 raise NotImplementedError()
2833 2840
2834 2841 def last(self):
2835 2842 """return the last element in the set (user iteration perspective)
2836 2843
2837 2844 Return None if the set is empty"""
2838 2845 raise NotImplementedError()
2839 2846
2840 2847 def __len__(self):
2841 2848 """return the length of the smartsets
2842 2849
2843 2850 This can be expensive on smartset that could be lazy otherwise."""
2844 2851 raise NotImplementedError()
2845 2852
2846 2853 def reverse(self):
2847 2854 """reverse the expected iteration order"""
2848 2855 raise NotImplementedError()
2849 2856
2850 2857 def sort(self, reverse=True):
2851 2858 """get the set to iterate in an ascending or descending order"""
2852 2859 raise NotImplementedError()
2853 2860
2854 2861 def __and__(self, other):
2855 2862 """Returns a new object with the intersection of the two collections.
2856 2863
2857 2864 This is part of the mandatory API for smartset."""
2858 2865 if isinstance(other, fullreposet):
2859 2866 return self
2860 2867 return self.filter(other.__contains__, condrepr=other, cache=False)
2861 2868
2862 2869 def __add__(self, other):
2863 2870 """Returns a new object with the union of the two collections.
2864 2871
2865 2872 This is part of the mandatory API for smartset."""
2866 2873 return addset(self, other)
2867 2874
2868 2875 def __sub__(self, other):
2869 2876 """Returns a new object with the substraction of the two collections.
2870 2877
2871 2878 This is part of the mandatory API for smartset."""
2872 2879 c = other.__contains__
2873 2880 return self.filter(lambda r: not c(r), condrepr=('<not %r>', other),
2874 2881 cache=False)
2875 2882
2876 2883 def filter(self, condition, condrepr=None, cache=True):
2877 2884 """Returns this smartset filtered by condition as a new smartset.
2878 2885
2879 2886 `condition` is a callable which takes a revision number and returns a
2880 2887 boolean. Optional `condrepr` provides a printable representation of
2881 2888 the given `condition`.
2882 2889
2883 2890 This is part of the mandatory API for smartset."""
2884 2891 # builtin cannot be cached. but do not needs to
2885 2892 if cache and util.safehasattr(condition, 'func_code'):
2886 2893 condition = util.cachefunc(condition)
2887 2894 return filteredset(self, condition, condrepr)
2888 2895
2889 2896 class baseset(abstractsmartset):
2890 2897 """Basic data structure that represents a revset and contains the basic
2891 2898 operation that it should be able to perform.
2892 2899
2893 2900 Every method in this class should be implemented by any smartset class.
2894 2901 """
2895 2902 def __init__(self, data=(), datarepr=None, istopo=False):
2896 2903 """
2897 2904 datarepr: a tuple of (format, obj, ...), a function or an object that
2898 2905 provides a printable representation of the given data.
2899 2906 """
2900 2907 self._ascending = None
2901 2908 self._istopo = istopo
2902 2909 if not isinstance(data, list):
2903 2910 if isinstance(data, set):
2904 2911 self._set = data
2905 2912 # set has no order we pick one for stability purpose
2906 2913 self._ascending = True
2907 2914 data = list(data)
2908 2915 self._list = data
2909 2916 self._datarepr = datarepr
2910 2917
2911 2918 @util.propertycache
2912 2919 def _set(self):
2913 2920 return set(self._list)
2914 2921
2915 2922 @util.propertycache
2916 2923 def _asclist(self):
2917 2924 asclist = self._list[:]
2918 2925 asclist.sort()
2919 2926 return asclist
2920 2927
2921 2928 def __iter__(self):
2922 2929 if self._ascending is None:
2923 2930 return iter(self._list)
2924 2931 elif self._ascending:
2925 2932 return iter(self._asclist)
2926 2933 else:
2927 2934 return reversed(self._asclist)
2928 2935
2929 2936 def fastasc(self):
2930 2937 return iter(self._asclist)
2931 2938
2932 2939 def fastdesc(self):
2933 2940 return reversed(self._asclist)
2934 2941
2935 2942 @util.propertycache
2936 2943 def __contains__(self):
2937 2944 return self._set.__contains__
2938 2945
2939 2946 def __nonzero__(self):
2940 2947 return bool(self._list)
2941 2948
2942 2949 def sort(self, reverse=False):
2943 2950 self._ascending = not bool(reverse)
2944 2951 self._istopo = False
2945 2952
2946 2953 def reverse(self):
2947 2954 if self._ascending is None:
2948 2955 self._list.reverse()
2949 2956 else:
2950 2957 self._ascending = not self._ascending
2951 2958 self._istopo = False
2952 2959
2953 2960 def __len__(self):
2954 2961 return len(self._list)
2955 2962
2956 2963 def isascending(self):
2957 2964 """Returns True if the collection is ascending order, False if not.
2958 2965
2959 2966 This is part of the mandatory API for smartset."""
2960 2967 if len(self) <= 1:
2961 2968 return True
2962 2969 return self._ascending is not None and self._ascending
2963 2970
2964 2971 def isdescending(self):
2965 2972 """Returns True if the collection is descending order, False if not.
2966 2973
2967 2974 This is part of the mandatory API for smartset."""
2968 2975 if len(self) <= 1:
2969 2976 return True
2970 2977 return self._ascending is not None and not self._ascending
2971 2978
2972 2979 def istopo(self):
2973 2980 """Is the collection is in topographical order or not.
2974 2981
2975 2982 This is part of the mandatory API for smartset."""
2976 2983 if len(self) <= 1:
2977 2984 return True
2978 2985 return self._istopo
2979 2986
2980 2987 def first(self):
2981 2988 if self:
2982 2989 if self._ascending is None:
2983 2990 return self._list[0]
2984 2991 elif self._ascending:
2985 2992 return self._asclist[0]
2986 2993 else:
2987 2994 return self._asclist[-1]
2988 2995 return None
2989 2996
2990 2997 def last(self):
2991 2998 if self:
2992 2999 if self._ascending is None:
2993 3000 return self._list[-1]
2994 3001 elif self._ascending:
2995 3002 return self._asclist[-1]
2996 3003 else:
2997 3004 return self._asclist[0]
2998 3005 return None
2999 3006
3000 3007 def __repr__(self):
3001 3008 d = {None: '', False: '-', True: '+'}[self._ascending]
3002 3009 s = _formatsetrepr(self._datarepr)
3003 3010 if not s:
3004 3011 l = self._list
3005 3012 # if _list has been built from a set, it might have a different
3006 3013 # order from one python implementation to another.
3007 3014 # We fallback to the sorted version for a stable output.
3008 3015 if self._ascending is not None:
3009 3016 l = self._asclist
3010 3017 s = repr(l)
3011 3018 return '<%s%s %s>' % (type(self).__name__, d, s)
3012 3019
3013 3020 class filteredset(abstractsmartset):
3014 3021 """Duck type for baseset class which iterates lazily over the revisions in
3015 3022 the subset and contains a function which tests for membership in the
3016 3023 revset
3017 3024 """
3018 3025 def __init__(self, subset, condition=lambda x: True, condrepr=None):
3019 3026 """
3020 3027 condition: a function that decide whether a revision in the subset
3021 3028 belongs to the revset or not.
3022 3029 condrepr: a tuple of (format, obj, ...), a function or an object that
3023 3030 provides a printable representation of the given condition.
3024 3031 """
3025 3032 self._subset = subset
3026 3033 self._condition = condition
3027 3034 self._condrepr = condrepr
3028 3035
3029 3036 def __contains__(self, x):
3030 3037 return x in self._subset and self._condition(x)
3031 3038
3032 3039 def __iter__(self):
3033 3040 return self._iterfilter(self._subset)
3034 3041
3035 3042 def _iterfilter(self, it):
3036 3043 cond = self._condition
3037 3044 for x in it:
3038 3045 if cond(x):
3039 3046 yield x
3040 3047
3041 3048 @property
3042 3049 def fastasc(self):
3043 3050 it = self._subset.fastasc
3044 3051 if it is None:
3045 3052 return None
3046 3053 return lambda: self._iterfilter(it())
3047 3054
3048 3055 @property
3049 3056 def fastdesc(self):
3050 3057 it = self._subset.fastdesc
3051 3058 if it is None:
3052 3059 return None
3053 3060 return lambda: self._iterfilter(it())
3054 3061
3055 3062 def __nonzero__(self):
3056 3063 fast = None
3057 3064 candidates = [self.fastasc if self.isascending() else None,
3058 3065 self.fastdesc if self.isdescending() else None,
3059 3066 self.fastasc,
3060 3067 self.fastdesc]
3061 3068 for candidate in candidates:
3062 3069 if candidate is not None:
3063 3070 fast = candidate
3064 3071 break
3065 3072
3066 3073 if fast is not None:
3067 3074 it = fast()
3068 3075 else:
3069 3076 it = self
3070 3077
3071 3078 for r in it:
3072 3079 return True
3073 3080 return False
3074 3081
3075 3082 def __len__(self):
3076 3083 # Basic implementation to be changed in future patches.
3077 3084 # until this gets improved, we use generator expression
3078 3085 # here, since list compr is free to call __len__ again
3079 3086 # causing infinite recursion
3080 3087 l = baseset(r for r in self)
3081 3088 return len(l)
3082 3089
3083 3090 def sort(self, reverse=False):
3084 3091 self._subset.sort(reverse=reverse)
3085 3092
3086 3093 def reverse(self):
3087 3094 self._subset.reverse()
3088 3095
3089 3096 def isascending(self):
3090 3097 return self._subset.isascending()
3091 3098
3092 3099 def isdescending(self):
3093 3100 return self._subset.isdescending()
3094 3101
3095 3102 def istopo(self):
3096 3103 return self._subset.istopo()
3097 3104
3098 3105 def first(self):
3099 3106 for x in self:
3100 3107 return x
3101 3108 return None
3102 3109
3103 3110 def last(self):
3104 3111 it = None
3105 3112 if self.isascending():
3106 3113 it = self.fastdesc
3107 3114 elif self.isdescending():
3108 3115 it = self.fastasc
3109 3116 if it is not None:
3110 3117 for x in it():
3111 3118 return x
3112 3119 return None #empty case
3113 3120 else:
3114 3121 x = None
3115 3122 for x in self:
3116 3123 pass
3117 3124 return x
3118 3125
3119 3126 def __repr__(self):
3120 3127 xs = [repr(self._subset)]
3121 3128 s = _formatsetrepr(self._condrepr)
3122 3129 if s:
3123 3130 xs.append(s)
3124 3131 return '<%s %s>' % (type(self).__name__, ', '.join(xs))
3125 3132
3126 3133 def _iterordered(ascending, iter1, iter2):
3127 3134 """produce an ordered iteration from two iterators with the same order
3128 3135
3129 3136 The ascending is used to indicated the iteration direction.
3130 3137 """
3131 3138 choice = max
3132 3139 if ascending:
3133 3140 choice = min
3134 3141
3135 3142 val1 = None
3136 3143 val2 = None
3137 3144 try:
3138 3145 # Consume both iterators in an ordered way until one is empty
3139 3146 while True:
3140 3147 if val1 is None:
3141 3148 val1 = next(iter1)
3142 3149 if val2 is None:
3143 3150 val2 = next(iter2)
3144 3151 n = choice(val1, val2)
3145 3152 yield n
3146 3153 if val1 == n:
3147 3154 val1 = None
3148 3155 if val2 == n:
3149 3156 val2 = None
3150 3157 except StopIteration:
3151 3158 # Flush any remaining values and consume the other one
3152 3159 it = iter2
3153 3160 if val1 is not None:
3154 3161 yield val1
3155 3162 it = iter1
3156 3163 elif val2 is not None:
3157 3164 # might have been equality and both are empty
3158 3165 yield val2
3159 3166 for val in it:
3160 3167 yield val
3161 3168
3162 3169 class addset(abstractsmartset):
3163 3170 """Represent the addition of two sets
3164 3171
3165 3172 Wrapper structure for lazily adding two structures without losing much
3166 3173 performance on the __contains__ method
3167 3174
3168 3175 If the ascending attribute is set, that means the two structures are
3169 3176 ordered in either an ascending or descending way. Therefore, we can add
3170 3177 them maintaining the order by iterating over both at the same time
3171 3178
3172 3179 >>> xs = baseset([0, 3, 2])
3173 3180 >>> ys = baseset([5, 2, 4])
3174 3181
3175 3182 >>> rs = addset(xs, ys)
3176 3183 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3177 3184 (True, True, False, True, 0, 4)
3178 3185 >>> rs = addset(xs, baseset([]))
3179 3186 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3180 3187 (True, True, False, 0, 2)
3181 3188 >>> rs = addset(baseset([]), baseset([]))
3182 3189 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3183 3190 (False, False, None, None)
3184 3191
3185 3192 iterate unsorted:
3186 3193 >>> rs = addset(xs, ys)
3187 3194 >>> # (use generator because pypy could call len())
3188 3195 >>> list(x for x in rs) # without _genlist
3189 3196 [0, 3, 2, 5, 4]
3190 3197 >>> assert not rs._genlist
3191 3198 >>> len(rs)
3192 3199 5
3193 3200 >>> [x for x in rs] # with _genlist
3194 3201 [0, 3, 2, 5, 4]
3195 3202 >>> assert rs._genlist
3196 3203
3197 3204 iterate ascending:
3198 3205 >>> rs = addset(xs, ys, ascending=True)
3199 3206 >>> # (use generator because pypy could call len())
3200 3207 >>> list(x for x in rs), list(x for x in rs.fastasc()) # without _asclist
3201 3208 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3202 3209 >>> assert not rs._asclist
3203 3210 >>> len(rs)
3204 3211 5
3205 3212 >>> [x for x in rs], [x for x in rs.fastasc()]
3206 3213 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3207 3214 >>> assert rs._asclist
3208 3215
3209 3216 iterate descending:
3210 3217 >>> rs = addset(xs, ys, ascending=False)
3211 3218 >>> # (use generator because pypy could call len())
3212 3219 >>> list(x for x in rs), list(x for x in rs.fastdesc()) # without _asclist
3213 3220 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3214 3221 >>> assert not rs._asclist
3215 3222 >>> len(rs)
3216 3223 5
3217 3224 >>> [x for x in rs], [x for x in rs.fastdesc()]
3218 3225 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3219 3226 >>> assert rs._asclist
3220 3227
3221 3228 iterate ascending without fastasc:
3222 3229 >>> rs = addset(xs, generatorset(ys), ascending=True)
3223 3230 >>> assert rs.fastasc is None
3224 3231 >>> [x for x in rs]
3225 3232 [0, 2, 3, 4, 5]
3226 3233
3227 3234 iterate descending without fastdesc:
3228 3235 >>> rs = addset(generatorset(xs), ys, ascending=False)
3229 3236 >>> assert rs.fastdesc is None
3230 3237 >>> [x for x in rs]
3231 3238 [5, 4, 3, 2, 0]
3232 3239 """
3233 3240 def __init__(self, revs1, revs2, ascending=None):
3234 3241 self._r1 = revs1
3235 3242 self._r2 = revs2
3236 3243 self._iter = None
3237 3244 self._ascending = ascending
3238 3245 self._genlist = None
3239 3246 self._asclist = None
3240 3247
3241 3248 def __len__(self):
3242 3249 return len(self._list)
3243 3250
3244 3251 def __nonzero__(self):
3245 3252 return bool(self._r1) or bool(self._r2)
3246 3253
3247 3254 @util.propertycache
3248 3255 def _list(self):
3249 3256 if not self._genlist:
3250 3257 self._genlist = baseset(iter(self))
3251 3258 return self._genlist
3252 3259
3253 3260 def __iter__(self):
3254 3261 """Iterate over both collections without repeating elements
3255 3262
3256 3263 If the ascending attribute is not set, iterate over the first one and
3257 3264 then over the second one checking for membership on the first one so we
3258 3265 dont yield any duplicates.
3259 3266
3260 3267 If the ascending attribute is set, iterate over both collections at the
3261 3268 same time, yielding only one value at a time in the given order.
3262 3269 """
3263 3270 if self._ascending is None:
3264 3271 if self._genlist:
3265 3272 return iter(self._genlist)
3266 3273 def arbitraryordergen():
3267 3274 for r in self._r1:
3268 3275 yield r
3269 3276 inr1 = self._r1.__contains__
3270 3277 for r in self._r2:
3271 3278 if not inr1(r):
3272 3279 yield r
3273 3280 return arbitraryordergen()
3274 3281 # try to use our own fast iterator if it exists
3275 3282 self._trysetasclist()
3276 3283 if self._ascending:
3277 3284 attr = 'fastasc'
3278 3285 else:
3279 3286 attr = 'fastdesc'
3280 3287 it = getattr(self, attr)
3281 3288 if it is not None:
3282 3289 return it()
3283 3290 # maybe half of the component supports fast
3284 3291 # get iterator for _r1
3285 3292 iter1 = getattr(self._r1, attr)
3286 3293 if iter1 is None:
3287 3294 # let's avoid side effect (not sure it matters)
3288 3295 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3289 3296 else:
3290 3297 iter1 = iter1()
3291 3298 # get iterator for _r2
3292 3299 iter2 = getattr(self._r2, attr)
3293 3300 if iter2 is None:
3294 3301 # let's avoid side effect (not sure it matters)
3295 3302 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3296 3303 else:
3297 3304 iter2 = iter2()
3298 3305 return _iterordered(self._ascending, iter1, iter2)
3299 3306
3300 3307 def _trysetasclist(self):
3301 3308 """populate the _asclist attribute if possible and necessary"""
3302 3309 if self._genlist is not None and self._asclist is None:
3303 3310 self._asclist = sorted(self._genlist)
3304 3311
3305 3312 @property
3306 3313 def fastasc(self):
3307 3314 self._trysetasclist()
3308 3315 if self._asclist is not None:
3309 3316 return self._asclist.__iter__
3310 3317 iter1 = self._r1.fastasc
3311 3318 iter2 = self._r2.fastasc
3312 3319 if None in (iter1, iter2):
3313 3320 return None
3314 3321 return lambda: _iterordered(True, iter1(), iter2())
3315 3322
3316 3323 @property
3317 3324 def fastdesc(self):
3318 3325 self._trysetasclist()
3319 3326 if self._asclist is not None:
3320 3327 return self._asclist.__reversed__
3321 3328 iter1 = self._r1.fastdesc
3322 3329 iter2 = self._r2.fastdesc
3323 3330 if None in (iter1, iter2):
3324 3331 return None
3325 3332 return lambda: _iterordered(False, iter1(), iter2())
3326 3333
3327 3334 def __contains__(self, x):
3328 3335 return x in self._r1 or x in self._r2
3329 3336
3330 3337 def sort(self, reverse=False):
3331 3338 """Sort the added set
3332 3339
3333 3340 For this we use the cached list with all the generated values and if we
3334 3341 know they are ascending or descending we can sort them in a smart way.
3335 3342 """
3336 3343 self._ascending = not reverse
3337 3344
3338 3345 def isascending(self):
3339 3346 return self._ascending is not None and self._ascending
3340 3347
3341 3348 def isdescending(self):
3342 3349 return self._ascending is not None and not self._ascending
3343 3350
3344 3351 def istopo(self):
3345 3352 # not worth the trouble asserting if the two sets combined are still
3346 3353 # in topographical order. Use the sort() predicate to explicitly sort
3347 3354 # again instead.
3348 3355 return False
3349 3356
3350 3357 def reverse(self):
3351 3358 if self._ascending is None:
3352 3359 self._list.reverse()
3353 3360 else:
3354 3361 self._ascending = not self._ascending
3355 3362
3356 3363 def first(self):
3357 3364 for x in self:
3358 3365 return x
3359 3366 return None
3360 3367
3361 3368 def last(self):
3362 3369 self.reverse()
3363 3370 val = self.first()
3364 3371 self.reverse()
3365 3372 return val
3366 3373
3367 3374 def __repr__(self):
3368 3375 d = {None: '', False: '-', True: '+'}[self._ascending]
3369 3376 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3370 3377
3371 3378 class generatorset(abstractsmartset):
3372 3379 """Wrap a generator for lazy iteration
3373 3380
3374 3381 Wrapper structure for generators that provides lazy membership and can
3375 3382 be iterated more than once.
3376 3383 When asked for membership it generates values until either it finds the
3377 3384 requested one or has gone through all the elements in the generator
3378 3385 """
3379 3386 def __init__(self, gen, iterasc=None):
3380 3387 """
3381 3388 gen: a generator producing the values for the generatorset.
3382 3389 """
3383 3390 self._gen = gen
3384 3391 self._asclist = None
3385 3392 self._cache = {}
3386 3393 self._genlist = []
3387 3394 self._finished = False
3388 3395 self._ascending = True
3389 3396 if iterasc is not None:
3390 3397 if iterasc:
3391 3398 self.fastasc = self._iterator
3392 3399 self.__contains__ = self._asccontains
3393 3400 else:
3394 3401 self.fastdesc = self._iterator
3395 3402 self.__contains__ = self._desccontains
3396 3403
3397 3404 def __nonzero__(self):
3398 3405 # Do not use 'for r in self' because it will enforce the iteration
3399 3406 # order (default ascending), possibly unrolling a whole descending
3400 3407 # iterator.
3401 3408 if self._genlist:
3402 3409 return True
3403 3410 for r in self._consumegen():
3404 3411 return True
3405 3412 return False
3406 3413
3407 3414 def __contains__(self, x):
3408 3415 if x in self._cache:
3409 3416 return self._cache[x]
3410 3417
3411 3418 # Use new values only, as existing values would be cached.
3412 3419 for l in self._consumegen():
3413 3420 if l == x:
3414 3421 return True
3415 3422
3416 3423 self._cache[x] = False
3417 3424 return False
3418 3425
3419 3426 def _asccontains(self, x):
3420 3427 """version of contains optimised for ascending generator"""
3421 3428 if x in self._cache:
3422 3429 return self._cache[x]
3423 3430
3424 3431 # Use new values only, as existing values would be cached.
3425 3432 for l in self._consumegen():
3426 3433 if l == x:
3427 3434 return True
3428 3435 if l > x:
3429 3436 break
3430 3437
3431 3438 self._cache[x] = False
3432 3439 return False
3433 3440
3434 3441 def _desccontains(self, x):
3435 3442 """version of contains optimised for descending generator"""
3436 3443 if x in self._cache:
3437 3444 return self._cache[x]
3438 3445
3439 3446 # Use new values only, as existing values would be cached.
3440 3447 for l in self._consumegen():
3441 3448 if l == x:
3442 3449 return True
3443 3450 if l < x:
3444 3451 break
3445 3452
3446 3453 self._cache[x] = False
3447 3454 return False
3448 3455
3449 3456 def __iter__(self):
3450 3457 if self._ascending:
3451 3458 it = self.fastasc
3452 3459 else:
3453 3460 it = self.fastdesc
3454 3461 if it is not None:
3455 3462 return it()
3456 3463 # we need to consume the iterator
3457 3464 for x in self._consumegen():
3458 3465 pass
3459 3466 # recall the same code
3460 3467 return iter(self)
3461 3468
3462 3469 def _iterator(self):
3463 3470 if self._finished:
3464 3471 return iter(self._genlist)
3465 3472
3466 3473 # We have to use this complex iteration strategy to allow multiple
3467 3474 # iterations at the same time. We need to be able to catch revision
3468 3475 # removed from _consumegen and added to genlist in another instance.
3469 3476 #
3470 3477 # Getting rid of it would provide an about 15% speed up on this
3471 3478 # iteration.
3472 3479 genlist = self._genlist
3473 3480 nextrev = self._consumegen().next
3474 3481 _len = len # cache global lookup
3475 3482 def gen():
3476 3483 i = 0
3477 3484 while True:
3478 3485 if i < _len(genlist):
3479 3486 yield genlist[i]
3480 3487 else:
3481 3488 yield nextrev()
3482 3489 i += 1
3483 3490 return gen()
3484 3491
3485 3492 def _consumegen(self):
3486 3493 cache = self._cache
3487 3494 genlist = self._genlist.append
3488 3495 for item in self._gen:
3489 3496 cache[item] = True
3490 3497 genlist(item)
3491 3498 yield item
3492 3499 if not self._finished:
3493 3500 self._finished = True
3494 3501 asc = self._genlist[:]
3495 3502 asc.sort()
3496 3503 self._asclist = asc
3497 3504 self.fastasc = asc.__iter__
3498 3505 self.fastdesc = asc.__reversed__
3499 3506
3500 3507 def __len__(self):
3501 3508 for x in self._consumegen():
3502 3509 pass
3503 3510 return len(self._genlist)
3504 3511
3505 3512 def sort(self, reverse=False):
3506 3513 self._ascending = not reverse
3507 3514
3508 3515 def reverse(self):
3509 3516 self._ascending = not self._ascending
3510 3517
3511 3518 def isascending(self):
3512 3519 return self._ascending
3513 3520
3514 3521 def isdescending(self):
3515 3522 return not self._ascending
3516 3523
3517 3524 def istopo(self):
3518 3525 # not worth the trouble asserting if the two sets combined are still
3519 3526 # in topographical order. Use the sort() predicate to explicitly sort
3520 3527 # again instead.
3521 3528 return False
3522 3529
3523 3530 def first(self):
3524 3531 if self._ascending:
3525 3532 it = self.fastasc
3526 3533 else:
3527 3534 it = self.fastdesc
3528 3535 if it is None:
3529 3536 # we need to consume all and try again
3530 3537 for x in self._consumegen():
3531 3538 pass
3532 3539 return self.first()
3533 3540 return next(it(), None)
3534 3541
3535 3542 def last(self):
3536 3543 if self._ascending:
3537 3544 it = self.fastdesc
3538 3545 else:
3539 3546 it = self.fastasc
3540 3547 if it is None:
3541 3548 # we need to consume all and try again
3542 3549 for x in self._consumegen():
3543 3550 pass
3544 3551 return self.first()
3545 3552 return next(it(), None)
3546 3553
3547 3554 def __repr__(self):
3548 3555 d = {False: '-', True: '+'}[self._ascending]
3549 3556 return '<%s%s>' % (type(self).__name__, d)
3550 3557
3551 3558 class spanset(abstractsmartset):
3552 3559 """Duck type for baseset class which represents a range of revisions and
3553 3560 can work lazily and without having all the range in memory
3554 3561
3555 3562 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3556 3563 notable points:
3557 3564 - when x < y it will be automatically descending,
3558 3565 - revision filtered with this repoview will be skipped.
3559 3566
3560 3567 """
3561 3568 def __init__(self, repo, start=0, end=None):
3562 3569 """
3563 3570 start: first revision included the set
3564 3571 (default to 0)
3565 3572 end: first revision excluded (last+1)
3566 3573 (default to len(repo)
3567 3574
3568 3575 Spanset will be descending if `end` < `start`.
3569 3576 """
3570 3577 if end is None:
3571 3578 end = len(repo)
3572 3579 self._ascending = start <= end
3573 3580 if not self._ascending:
3574 3581 start, end = end + 1, start +1
3575 3582 self._start = start
3576 3583 self._end = end
3577 3584 self._hiddenrevs = repo.changelog.filteredrevs
3578 3585
3579 3586 def sort(self, reverse=False):
3580 3587 self._ascending = not reverse
3581 3588
3582 3589 def reverse(self):
3583 3590 self._ascending = not self._ascending
3584 3591
3585 3592 def istopo(self):
3586 3593 # not worth the trouble asserting if the two sets combined are still
3587 3594 # in topographical order. Use the sort() predicate to explicitly sort
3588 3595 # again instead.
3589 3596 return False
3590 3597
3591 3598 def _iterfilter(self, iterrange):
3592 3599 s = self._hiddenrevs
3593 3600 for r in iterrange:
3594 3601 if r not in s:
3595 3602 yield r
3596 3603
3597 3604 def __iter__(self):
3598 3605 if self._ascending:
3599 3606 return self.fastasc()
3600 3607 else:
3601 3608 return self.fastdesc()
3602 3609
3603 3610 def fastasc(self):
3604 3611 iterrange = xrange(self._start, self._end)
3605 3612 if self._hiddenrevs:
3606 3613 return self._iterfilter(iterrange)
3607 3614 return iter(iterrange)
3608 3615
3609 3616 def fastdesc(self):
3610 3617 iterrange = xrange(self._end - 1, self._start - 1, -1)
3611 3618 if self._hiddenrevs:
3612 3619 return self._iterfilter(iterrange)
3613 3620 return iter(iterrange)
3614 3621
3615 3622 def __contains__(self, rev):
3616 3623 hidden = self._hiddenrevs
3617 3624 return ((self._start <= rev < self._end)
3618 3625 and not (hidden and rev in hidden))
3619 3626
3620 3627 def __nonzero__(self):
3621 3628 for r in self:
3622 3629 return True
3623 3630 return False
3624 3631
3625 3632 def __len__(self):
3626 3633 if not self._hiddenrevs:
3627 3634 return abs(self._end - self._start)
3628 3635 else:
3629 3636 count = 0
3630 3637 start = self._start
3631 3638 end = self._end
3632 3639 for rev in self._hiddenrevs:
3633 3640 if (end < rev <= start) or (start <= rev < end):
3634 3641 count += 1
3635 3642 return abs(self._end - self._start) - count
3636 3643
3637 3644 def isascending(self):
3638 3645 return self._ascending
3639 3646
3640 3647 def isdescending(self):
3641 3648 return not self._ascending
3642 3649
3643 3650 def first(self):
3644 3651 if self._ascending:
3645 3652 it = self.fastasc
3646 3653 else:
3647 3654 it = self.fastdesc
3648 3655 for x in it():
3649 3656 return x
3650 3657 return None
3651 3658
3652 3659 def last(self):
3653 3660 if self._ascending:
3654 3661 it = self.fastdesc
3655 3662 else:
3656 3663 it = self.fastasc
3657 3664 for x in it():
3658 3665 return x
3659 3666 return None
3660 3667
3661 3668 def __repr__(self):
3662 3669 d = {False: '-', True: '+'}[self._ascending]
3663 3670 return '<%s%s %d:%d>' % (type(self).__name__, d,
3664 3671 self._start, self._end - 1)
3665 3672
3666 3673 class fullreposet(spanset):
3667 3674 """a set containing all revisions in the repo
3668 3675
3669 3676 This class exists to host special optimization and magic to handle virtual
3670 3677 revisions such as "null".
3671 3678 """
3672 3679
3673 3680 def __init__(self, repo):
3674 3681 super(fullreposet, self).__init__(repo)
3675 3682
3676 3683 def __and__(self, other):
3677 3684 """As self contains the whole repo, all of the other set should also be
3678 3685 in self. Therefore `self & other = other`.
3679 3686
3680 3687 This boldly assumes the other contains valid revs only.
3681 3688 """
3682 3689 # other not a smartset, make is so
3683 3690 if not util.safehasattr(other, 'isascending'):
3684 3691 # filter out hidden revision
3685 3692 # (this boldly assumes all smartset are pure)
3686 3693 #
3687 3694 # `other` was used with "&", let's assume this is a set like
3688 3695 # object.
3689 3696 other = baseset(other - self._hiddenrevs)
3690 3697
3691 3698 # XXX As fullreposet is also used as bootstrap, this is wrong.
3692 3699 #
3693 3700 # With a giveme312() revset returning [3,1,2], this makes
3694 3701 # 'hg log -r "giveme312()"' -> 1, 2, 3 (wrong)
3695 3702 # We cannot just drop it because other usage still need to sort it:
3696 3703 # 'hg log -r "all() and giveme312()"' -> 1, 2, 3 (right)
3697 3704 #
3698 3705 # There is also some faulty revset implementations that rely on it
3699 3706 # (eg: children as of its state in e8075329c5fb)
3700 3707 #
3701 3708 # When we fix the two points above we can move this into the if clause
3702 3709 other.sort(reverse=self.isdescending())
3703 3710 return other
3704 3711
3705 3712 def prettyformatset(revs):
3706 3713 lines = []
3707 3714 rs = repr(revs)
3708 3715 p = 0
3709 3716 while p < len(rs):
3710 3717 q = rs.find('<', p + 1)
3711 3718 if q < 0:
3712 3719 q = len(rs)
3713 3720 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3714 3721 assert l >= 0
3715 3722 lines.append((l, rs[p:q].rstrip()))
3716 3723 p = q
3717 3724 return '\n'.join(' ' * l + s for l, s in lines)
3718 3725
3719 3726 def loadpredicate(ui, extname, registrarobj):
3720 3727 """Load revset predicates from specified registrarobj
3721 3728 """
3722 3729 for name, func in registrarobj._table.iteritems():
3723 3730 symbols[name] = func
3724 3731 if func._safe:
3725 3732 safesymbols.add(name)
3726 3733
3727 3734 # load built-in predicates explicitly to setup safesymbols
3728 3735 loadpredicate(None, None, predicate)
3729 3736
3730 3737 # tell hggettext to extract docstrings from these functions:
3731 3738 i18nfunctions = symbols.values()
@@ -1,3419 +1,3426 b''
1 1 @ (34) head
2 2 |
3 3 | o (33) head
4 4 | |
5 5 o | (32) expand
6 6 |\ \
7 7 | o \ (31) expand
8 8 | |\ \
9 9 | | o \ (30) expand
10 10 | | |\ \
11 11 | | | o | (29) regular commit
12 12 | | | | |
13 13 | | o | | (28) merge zero known
14 14 | | |\ \ \
15 15 o | | | | | (27) collapse
16 16 |/ / / / /
17 17 | | o---+ (26) merge one known; far right
18 18 | | | | |
19 19 +---o | | (25) merge one known; far left
20 20 | | | | |
21 21 | | o | | (24) merge one known; immediate right
22 22 | | |\| |
23 23 | | o | | (23) merge one known; immediate left
24 24 | |/| | |
25 25 +---o---+ (22) merge two known; one far left, one far right
26 26 | | / /
27 27 o | | | (21) expand
28 28 |\ \ \ \
29 29 | o---+-+ (20) merge two known; two far right
30 30 | / / /
31 31 o | | | (19) expand
32 32 |\ \ \ \
33 33 +---+---o (18) merge two known; two far left
34 34 | | | |
35 35 | o | | (17) expand
36 36 | |\ \ \
37 37 | | o---+ (16) merge two known; one immediate right, one near right
38 38 | | |/ /
39 39 o | | | (15) expand
40 40 |\ \ \ \
41 41 | o-----+ (14) merge two known; one immediate right, one far right
42 42 | |/ / /
43 43 o | | | (13) expand
44 44 |\ \ \ \
45 45 +---o | | (12) merge two known; one immediate right, one far left
46 46 | | |/ /
47 47 | o | | (11) expand
48 48 | |\ \ \
49 49 | | o---+ (10) merge two known; one immediate left, one near right
50 50 | |/ / /
51 51 o | | | (9) expand
52 52 |\ \ \ \
53 53 | o-----+ (8) merge two known; one immediate left, one far right
54 54 |/ / / /
55 55 o | | | (7) expand
56 56 |\ \ \ \
57 57 +---o | | (6) merge two known; one immediate left, one far left
58 58 | |/ / /
59 59 | o | | (5) expand
60 60 | |\ \ \
61 61 | | o | | (4) merge two known; one immediate left, one immediate right
62 62 | |/|/ /
63 63 | o / / (3) collapse
64 64 |/ / /
65 65 o / / (2) collapse
66 66 |/ /
67 67 o / (1) collapse
68 68 |/
69 69 o (0) root
70 70
71 71
72 72 $ commit()
73 73 > {
74 74 > rev=$1
75 75 > msg=$2
76 76 > shift 2
77 77 > if [ "$#" -gt 0 ]; then
78 78 > hg debugsetparents "$@"
79 79 > fi
80 80 > echo $rev > a
81 81 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
82 82 > }
83 83
84 84 $ cat > printrevset.py <<EOF
85 85 > from mercurial import extensions, revset, commands, cmdutil
86 86 >
87 87 > def uisetup(ui):
88 88 > def printrevset(orig, ui, repo, *pats, **opts):
89 89 > if opts.get('print_revset'):
90 90 > expr = cmdutil.getgraphlogrevs(repo, pats, opts)[1]
91 91 > if expr:
92 92 > tree = revset.parse(expr)
93 93 > else:
94 94 > tree = []
95 95 > ui.write('%r\n' % (opts.get('rev', []),))
96 96 > ui.write(revset.prettyformat(tree) + '\n')
97 97 > return 0
98 98 > return orig(ui, repo, *pats, **opts)
99 99 > entry = extensions.wrapcommand(commands.table, 'log', printrevset)
100 100 > entry[1].append(('', 'print-revset', False,
101 101 > 'print generated revset and exit (DEPRECATED)'))
102 102 > EOF
103 103
104 104 $ echo "[extensions]" >> $HGRCPATH
105 105 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
106 106
107 107 $ hg init repo
108 108 $ cd repo
109 109
110 110 Empty repo:
111 111
112 112 $ hg log -G
113 113
114 114
115 115 Building DAG:
116 116
117 117 $ commit 0 "root"
118 118 $ commit 1 "collapse" 0
119 119 $ commit 2 "collapse" 1
120 120 $ commit 3 "collapse" 2
121 121 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
122 122 $ commit 5 "expand" 3 4
123 123 $ commit 6 "merge two known; one immediate left, one far left" 2 5
124 124 $ commit 7 "expand" 2 5
125 125 $ commit 8 "merge two known; one immediate left, one far right" 0 7
126 126 $ commit 9 "expand" 7 8
127 127 $ commit 10 "merge two known; one immediate left, one near right" 0 6
128 128 $ commit 11 "expand" 6 10
129 129 $ commit 12 "merge two known; one immediate right, one far left" 1 9
130 130 $ commit 13 "expand" 9 11
131 131 $ commit 14 "merge two known; one immediate right, one far right" 0 12
132 132 $ commit 15 "expand" 13 14
133 133 $ commit 16 "merge two known; one immediate right, one near right" 0 1
134 134 $ commit 17 "expand" 12 16
135 135 $ commit 18 "merge two known; two far left" 1 15
136 136 $ commit 19 "expand" 15 17
137 137 $ commit 20 "merge two known; two far right" 0 18
138 138 $ commit 21 "expand" 19 20
139 139 $ commit 22 "merge two known; one far left, one far right" 18 21
140 140 $ commit 23 "merge one known; immediate left" 1 22
141 141 $ commit 24 "merge one known; immediate right" 0 23
142 142 $ commit 25 "merge one known; far left" 21 24
143 143 $ commit 26 "merge one known; far right" 18 25
144 144 $ commit 27 "collapse" 21
145 145 $ commit 28 "merge zero known" 1 26
146 146 $ commit 29 "regular commit" 0
147 147 $ commit 30 "expand" 28 29
148 148 $ commit 31 "expand" 21 30
149 149 $ commit 32 "expand" 27 31
150 150 $ commit 33 "head" 18
151 151 $ commit 34 "head" 32
152 152
153 153
154 154 $ hg log -G -q
155 155 @ 34:fea3ac5810e0
156 156 |
157 157 | o 33:68608f5145f9
158 158 | |
159 159 o | 32:d06dffa21a31
160 160 |\ \
161 161 | o \ 31:621d83e11f67
162 162 | |\ \
163 163 | | o \ 30:6e11cd4b648f
164 164 | | |\ \
165 165 | | | o | 29:cd9bb2be7593
166 166 | | | | |
167 167 | | o | | 28:44ecd0b9ae99
168 168 | | |\ \ \
169 169 o | | | | | 27:886ed638191b
170 170 |/ / / / /
171 171 | | o---+ 26:7f25b6c2f0b9
172 172 | | | | |
173 173 +---o | | 25:91da8ed57247
174 174 | | | | |
175 175 | | o | | 24:a9c19a3d96b7
176 176 | | |\| |
177 177 | | o | | 23:a01cddf0766d
178 178 | |/| | |
179 179 +---o---+ 22:e0d9cccacb5d
180 180 | | / /
181 181 o | | | 21:d42a756af44d
182 182 |\ \ \ \
183 183 | o---+-+ 20:d30ed6450e32
184 184 | / / /
185 185 o | | | 19:31ddc2c1573b
186 186 |\ \ \ \
187 187 +---+---o 18:1aa84d96232a
188 188 | | | |
189 189 | o | | 17:44765d7c06e0
190 190 | |\ \ \
191 191 | | o---+ 16:3677d192927d
192 192 | | |/ /
193 193 o | | | 15:1dda3f72782d
194 194 |\ \ \ \
195 195 | o-----+ 14:8eac370358ef
196 196 | |/ / /
197 197 o | | | 13:22d8966a97e3
198 198 |\ \ \ \
199 199 +---o | | 12:86b91144a6e9
200 200 | | |/ /
201 201 | o | | 11:832d76e6bdf2
202 202 | |\ \ \
203 203 | | o---+ 10:74c64d036d72
204 204 | |/ / /
205 205 o | | | 9:7010c0af0a35
206 206 |\ \ \ \
207 207 | o-----+ 8:7a0b11f71937
208 208 |/ / / /
209 209 o | | | 7:b632bb1b1224
210 210 |\ \ \ \
211 211 +---o | | 6:b105a072e251
212 212 | |/ / /
213 213 | o | | 5:4409d547b708
214 214 | |\ \ \
215 215 | | o | | 4:26a8bac39d9f
216 216 | |/|/ /
217 217 | o / / 3:27eef8ed80b4
218 218 |/ / /
219 219 o / / 2:3d9a33b8d1e1
220 220 |/ /
221 221 o / 1:6db2ef61d156
222 222 |/
223 223 o 0:e6eb3150255d
224 224
225 225
226 226 $ hg log -G
227 227 @ changeset: 34:fea3ac5810e0
228 228 | tag: tip
229 229 | parent: 32:d06dffa21a31
230 230 | user: test
231 231 | date: Thu Jan 01 00:00:34 1970 +0000
232 232 | summary: (34) head
233 233 |
234 234 | o changeset: 33:68608f5145f9
235 235 | | parent: 18:1aa84d96232a
236 236 | | user: test
237 237 | | date: Thu Jan 01 00:00:33 1970 +0000
238 238 | | summary: (33) head
239 239 | |
240 240 o | changeset: 32:d06dffa21a31
241 241 |\ \ parent: 27:886ed638191b
242 242 | | | parent: 31:621d83e11f67
243 243 | | | user: test
244 244 | | | date: Thu Jan 01 00:00:32 1970 +0000
245 245 | | | summary: (32) expand
246 246 | | |
247 247 | o | changeset: 31:621d83e11f67
248 248 | |\ \ parent: 21:d42a756af44d
249 249 | | | | parent: 30:6e11cd4b648f
250 250 | | | | user: test
251 251 | | | | date: Thu Jan 01 00:00:31 1970 +0000
252 252 | | | | summary: (31) expand
253 253 | | | |
254 254 | | o | changeset: 30:6e11cd4b648f
255 255 | | |\ \ parent: 28:44ecd0b9ae99
256 256 | | | | | parent: 29:cd9bb2be7593
257 257 | | | | | user: test
258 258 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
259 259 | | | | | summary: (30) expand
260 260 | | | | |
261 261 | | | o | changeset: 29:cd9bb2be7593
262 262 | | | | | parent: 0:e6eb3150255d
263 263 | | | | | user: test
264 264 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
265 265 | | | | | summary: (29) regular commit
266 266 | | | | |
267 267 | | o | | changeset: 28:44ecd0b9ae99
268 268 | | |\ \ \ parent: 1:6db2ef61d156
269 269 | | | | | | parent: 26:7f25b6c2f0b9
270 270 | | | | | | user: test
271 271 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
272 272 | | | | | | summary: (28) merge zero known
273 273 | | | | | |
274 274 o | | | | | changeset: 27:886ed638191b
275 275 |/ / / / / parent: 21:d42a756af44d
276 276 | | | | | user: test
277 277 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
278 278 | | | | | summary: (27) collapse
279 279 | | | | |
280 280 | | o---+ changeset: 26:7f25b6c2f0b9
281 281 | | | | | parent: 18:1aa84d96232a
282 282 | | | | | parent: 25:91da8ed57247
283 283 | | | | | user: test
284 284 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
285 285 | | | | | summary: (26) merge one known; far right
286 286 | | | | |
287 287 +---o | | changeset: 25:91da8ed57247
288 288 | | | | | parent: 21:d42a756af44d
289 289 | | | | | parent: 24:a9c19a3d96b7
290 290 | | | | | user: test
291 291 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
292 292 | | | | | summary: (25) merge one known; far left
293 293 | | | | |
294 294 | | o | | changeset: 24:a9c19a3d96b7
295 295 | | |\| | parent: 0:e6eb3150255d
296 296 | | | | | parent: 23:a01cddf0766d
297 297 | | | | | user: test
298 298 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
299 299 | | | | | summary: (24) merge one known; immediate right
300 300 | | | | |
301 301 | | o | | changeset: 23:a01cddf0766d
302 302 | |/| | | parent: 1:6db2ef61d156
303 303 | | | | | parent: 22:e0d9cccacb5d
304 304 | | | | | user: test
305 305 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
306 306 | | | | | summary: (23) merge one known; immediate left
307 307 | | | | |
308 308 +---o---+ changeset: 22:e0d9cccacb5d
309 309 | | | | parent: 18:1aa84d96232a
310 310 | | / / parent: 21:d42a756af44d
311 311 | | | | user: test
312 312 | | | | date: Thu Jan 01 00:00:22 1970 +0000
313 313 | | | | summary: (22) merge two known; one far left, one far right
314 314 | | | |
315 315 o | | | changeset: 21:d42a756af44d
316 316 |\ \ \ \ parent: 19:31ddc2c1573b
317 317 | | | | | parent: 20:d30ed6450e32
318 318 | | | | | user: test
319 319 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
320 320 | | | | | summary: (21) expand
321 321 | | | | |
322 322 | o---+-+ changeset: 20:d30ed6450e32
323 323 | | | | parent: 0:e6eb3150255d
324 324 | / / / parent: 18:1aa84d96232a
325 325 | | | | user: test
326 326 | | | | date: Thu Jan 01 00:00:20 1970 +0000
327 327 | | | | summary: (20) merge two known; two far right
328 328 | | | |
329 329 o | | | changeset: 19:31ddc2c1573b
330 330 |\ \ \ \ parent: 15:1dda3f72782d
331 331 | | | | | parent: 17:44765d7c06e0
332 332 | | | | | user: test
333 333 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
334 334 | | | | | summary: (19) expand
335 335 | | | | |
336 336 +---+---o changeset: 18:1aa84d96232a
337 337 | | | | parent: 1:6db2ef61d156
338 338 | | | | parent: 15:1dda3f72782d
339 339 | | | | user: test
340 340 | | | | date: Thu Jan 01 00:00:18 1970 +0000
341 341 | | | | summary: (18) merge two known; two far left
342 342 | | | |
343 343 | o | | changeset: 17:44765d7c06e0
344 344 | |\ \ \ parent: 12:86b91144a6e9
345 345 | | | | | parent: 16:3677d192927d
346 346 | | | | | user: test
347 347 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
348 348 | | | | | summary: (17) expand
349 349 | | | | |
350 350 | | o---+ changeset: 16:3677d192927d
351 351 | | | | | parent: 0:e6eb3150255d
352 352 | | |/ / parent: 1:6db2ef61d156
353 353 | | | | user: test
354 354 | | | | date: Thu Jan 01 00:00:16 1970 +0000
355 355 | | | | summary: (16) merge two known; one immediate right, one near right
356 356 | | | |
357 357 o | | | changeset: 15:1dda3f72782d
358 358 |\ \ \ \ parent: 13:22d8966a97e3
359 359 | | | | | parent: 14:8eac370358ef
360 360 | | | | | user: test
361 361 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
362 362 | | | | | summary: (15) expand
363 363 | | | | |
364 364 | o-----+ changeset: 14:8eac370358ef
365 365 | | | | | parent: 0:e6eb3150255d
366 366 | |/ / / parent: 12:86b91144a6e9
367 367 | | | | user: test
368 368 | | | | date: Thu Jan 01 00:00:14 1970 +0000
369 369 | | | | summary: (14) merge two known; one immediate right, one far right
370 370 | | | |
371 371 o | | | changeset: 13:22d8966a97e3
372 372 |\ \ \ \ parent: 9:7010c0af0a35
373 373 | | | | | parent: 11:832d76e6bdf2
374 374 | | | | | user: test
375 375 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
376 376 | | | | | summary: (13) expand
377 377 | | | | |
378 378 +---o | | changeset: 12:86b91144a6e9
379 379 | | |/ / parent: 1:6db2ef61d156
380 380 | | | | parent: 9:7010c0af0a35
381 381 | | | | user: test
382 382 | | | | date: Thu Jan 01 00:00:12 1970 +0000
383 383 | | | | summary: (12) merge two known; one immediate right, one far left
384 384 | | | |
385 385 | o | | changeset: 11:832d76e6bdf2
386 386 | |\ \ \ parent: 6:b105a072e251
387 387 | | | | | parent: 10:74c64d036d72
388 388 | | | | | user: test
389 389 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
390 390 | | | | | summary: (11) expand
391 391 | | | | |
392 392 | | o---+ changeset: 10:74c64d036d72
393 393 | | | | | parent: 0:e6eb3150255d
394 394 | |/ / / parent: 6:b105a072e251
395 395 | | | | user: test
396 396 | | | | date: Thu Jan 01 00:00:10 1970 +0000
397 397 | | | | summary: (10) merge two known; one immediate left, one near right
398 398 | | | |
399 399 o | | | changeset: 9:7010c0af0a35
400 400 |\ \ \ \ parent: 7:b632bb1b1224
401 401 | | | | | parent: 8:7a0b11f71937
402 402 | | | | | user: test
403 403 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
404 404 | | | | | summary: (9) expand
405 405 | | | | |
406 406 | o-----+ changeset: 8:7a0b11f71937
407 407 | | | | | parent: 0:e6eb3150255d
408 408 |/ / / / parent: 7:b632bb1b1224
409 409 | | | | user: test
410 410 | | | | date: Thu Jan 01 00:00:08 1970 +0000
411 411 | | | | summary: (8) merge two known; one immediate left, one far right
412 412 | | | |
413 413 o | | | changeset: 7:b632bb1b1224
414 414 |\ \ \ \ parent: 2:3d9a33b8d1e1
415 415 | | | | | parent: 5:4409d547b708
416 416 | | | | | user: test
417 417 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
418 418 | | | | | summary: (7) expand
419 419 | | | | |
420 420 +---o | | changeset: 6:b105a072e251
421 421 | |/ / / parent: 2:3d9a33b8d1e1
422 422 | | | | parent: 5:4409d547b708
423 423 | | | | user: test
424 424 | | | | date: Thu Jan 01 00:00:06 1970 +0000
425 425 | | | | summary: (6) merge two known; one immediate left, one far left
426 426 | | | |
427 427 | o | | changeset: 5:4409d547b708
428 428 | |\ \ \ parent: 3:27eef8ed80b4
429 429 | | | | | parent: 4:26a8bac39d9f
430 430 | | | | | user: test
431 431 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
432 432 | | | | | summary: (5) expand
433 433 | | | | |
434 434 | | o | | changeset: 4:26a8bac39d9f
435 435 | |/|/ / parent: 1:6db2ef61d156
436 436 | | | | parent: 3:27eef8ed80b4
437 437 | | | | user: test
438 438 | | | | date: Thu Jan 01 00:00:04 1970 +0000
439 439 | | | | summary: (4) merge two known; one immediate left, one immediate right
440 440 | | | |
441 441 | o | | changeset: 3:27eef8ed80b4
442 442 |/ / / user: test
443 443 | | | date: Thu Jan 01 00:00:03 1970 +0000
444 444 | | | summary: (3) collapse
445 445 | | |
446 446 o | | changeset: 2:3d9a33b8d1e1
447 447 |/ / user: test
448 448 | | date: Thu Jan 01 00:00:02 1970 +0000
449 449 | | summary: (2) collapse
450 450 | |
451 451 o | changeset: 1:6db2ef61d156
452 452 |/ user: test
453 453 | date: Thu Jan 01 00:00:01 1970 +0000
454 454 | summary: (1) collapse
455 455 |
456 456 o changeset: 0:e6eb3150255d
457 457 user: test
458 458 date: Thu Jan 01 00:00:00 1970 +0000
459 459 summary: (0) root
460 460
461 461
462 462 File glog:
463 463 $ hg log -G a
464 464 @ changeset: 34:fea3ac5810e0
465 465 | tag: tip
466 466 | parent: 32:d06dffa21a31
467 467 | user: test
468 468 | date: Thu Jan 01 00:00:34 1970 +0000
469 469 | summary: (34) head
470 470 |
471 471 | o changeset: 33:68608f5145f9
472 472 | | parent: 18:1aa84d96232a
473 473 | | user: test
474 474 | | date: Thu Jan 01 00:00:33 1970 +0000
475 475 | | summary: (33) head
476 476 | |
477 477 o | changeset: 32:d06dffa21a31
478 478 |\ \ parent: 27:886ed638191b
479 479 | | | parent: 31:621d83e11f67
480 480 | | | user: test
481 481 | | | date: Thu Jan 01 00:00:32 1970 +0000
482 482 | | | summary: (32) expand
483 483 | | |
484 484 | o | changeset: 31:621d83e11f67
485 485 | |\ \ parent: 21:d42a756af44d
486 486 | | | | parent: 30:6e11cd4b648f
487 487 | | | | user: test
488 488 | | | | date: Thu Jan 01 00:00:31 1970 +0000
489 489 | | | | summary: (31) expand
490 490 | | | |
491 491 | | o | changeset: 30:6e11cd4b648f
492 492 | | |\ \ parent: 28:44ecd0b9ae99
493 493 | | | | | parent: 29:cd9bb2be7593
494 494 | | | | | user: test
495 495 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
496 496 | | | | | summary: (30) expand
497 497 | | | | |
498 498 | | | o | changeset: 29:cd9bb2be7593
499 499 | | | | | parent: 0:e6eb3150255d
500 500 | | | | | user: test
501 501 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
502 502 | | | | | summary: (29) regular commit
503 503 | | | | |
504 504 | | o | | changeset: 28:44ecd0b9ae99
505 505 | | |\ \ \ parent: 1:6db2ef61d156
506 506 | | | | | | parent: 26:7f25b6c2f0b9
507 507 | | | | | | user: test
508 508 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
509 509 | | | | | | summary: (28) merge zero known
510 510 | | | | | |
511 511 o | | | | | changeset: 27:886ed638191b
512 512 |/ / / / / parent: 21:d42a756af44d
513 513 | | | | | user: test
514 514 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
515 515 | | | | | summary: (27) collapse
516 516 | | | | |
517 517 | | o---+ changeset: 26:7f25b6c2f0b9
518 518 | | | | | parent: 18:1aa84d96232a
519 519 | | | | | parent: 25:91da8ed57247
520 520 | | | | | user: test
521 521 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
522 522 | | | | | summary: (26) merge one known; far right
523 523 | | | | |
524 524 +---o | | changeset: 25:91da8ed57247
525 525 | | | | | parent: 21:d42a756af44d
526 526 | | | | | parent: 24:a9c19a3d96b7
527 527 | | | | | user: test
528 528 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
529 529 | | | | | summary: (25) merge one known; far left
530 530 | | | | |
531 531 | | o | | changeset: 24:a9c19a3d96b7
532 532 | | |\| | parent: 0:e6eb3150255d
533 533 | | | | | parent: 23:a01cddf0766d
534 534 | | | | | user: test
535 535 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
536 536 | | | | | summary: (24) merge one known; immediate right
537 537 | | | | |
538 538 | | o | | changeset: 23:a01cddf0766d
539 539 | |/| | | parent: 1:6db2ef61d156
540 540 | | | | | parent: 22:e0d9cccacb5d
541 541 | | | | | user: test
542 542 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
543 543 | | | | | summary: (23) merge one known; immediate left
544 544 | | | | |
545 545 +---o---+ changeset: 22:e0d9cccacb5d
546 546 | | | | parent: 18:1aa84d96232a
547 547 | | / / parent: 21:d42a756af44d
548 548 | | | | user: test
549 549 | | | | date: Thu Jan 01 00:00:22 1970 +0000
550 550 | | | | summary: (22) merge two known; one far left, one far right
551 551 | | | |
552 552 o | | | changeset: 21:d42a756af44d
553 553 |\ \ \ \ parent: 19:31ddc2c1573b
554 554 | | | | | parent: 20:d30ed6450e32
555 555 | | | | | user: test
556 556 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
557 557 | | | | | summary: (21) expand
558 558 | | | | |
559 559 | o---+-+ changeset: 20:d30ed6450e32
560 560 | | | | parent: 0:e6eb3150255d
561 561 | / / / parent: 18:1aa84d96232a
562 562 | | | | user: test
563 563 | | | | date: Thu Jan 01 00:00:20 1970 +0000
564 564 | | | | summary: (20) merge two known; two far right
565 565 | | | |
566 566 o | | | changeset: 19:31ddc2c1573b
567 567 |\ \ \ \ parent: 15:1dda3f72782d
568 568 | | | | | parent: 17:44765d7c06e0
569 569 | | | | | user: test
570 570 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
571 571 | | | | | summary: (19) expand
572 572 | | | | |
573 573 +---+---o changeset: 18:1aa84d96232a
574 574 | | | | parent: 1:6db2ef61d156
575 575 | | | | parent: 15:1dda3f72782d
576 576 | | | | user: test
577 577 | | | | date: Thu Jan 01 00:00:18 1970 +0000
578 578 | | | | summary: (18) merge two known; two far left
579 579 | | | |
580 580 | o | | changeset: 17:44765d7c06e0
581 581 | |\ \ \ parent: 12:86b91144a6e9
582 582 | | | | | parent: 16:3677d192927d
583 583 | | | | | user: test
584 584 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
585 585 | | | | | summary: (17) expand
586 586 | | | | |
587 587 | | o---+ changeset: 16:3677d192927d
588 588 | | | | | parent: 0:e6eb3150255d
589 589 | | |/ / parent: 1:6db2ef61d156
590 590 | | | | user: test
591 591 | | | | date: Thu Jan 01 00:00:16 1970 +0000
592 592 | | | | summary: (16) merge two known; one immediate right, one near right
593 593 | | | |
594 594 o | | | changeset: 15:1dda3f72782d
595 595 |\ \ \ \ parent: 13:22d8966a97e3
596 596 | | | | | parent: 14:8eac370358ef
597 597 | | | | | user: test
598 598 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
599 599 | | | | | summary: (15) expand
600 600 | | | | |
601 601 | o-----+ changeset: 14:8eac370358ef
602 602 | | | | | parent: 0:e6eb3150255d
603 603 | |/ / / parent: 12:86b91144a6e9
604 604 | | | | user: test
605 605 | | | | date: Thu Jan 01 00:00:14 1970 +0000
606 606 | | | | summary: (14) merge two known; one immediate right, one far right
607 607 | | | |
608 608 o | | | changeset: 13:22d8966a97e3
609 609 |\ \ \ \ parent: 9:7010c0af0a35
610 610 | | | | | parent: 11:832d76e6bdf2
611 611 | | | | | user: test
612 612 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
613 613 | | | | | summary: (13) expand
614 614 | | | | |
615 615 +---o | | changeset: 12:86b91144a6e9
616 616 | | |/ / parent: 1:6db2ef61d156
617 617 | | | | parent: 9:7010c0af0a35
618 618 | | | | user: test
619 619 | | | | date: Thu Jan 01 00:00:12 1970 +0000
620 620 | | | | summary: (12) merge two known; one immediate right, one far left
621 621 | | | |
622 622 | o | | changeset: 11:832d76e6bdf2
623 623 | |\ \ \ parent: 6:b105a072e251
624 624 | | | | | parent: 10:74c64d036d72
625 625 | | | | | user: test
626 626 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
627 627 | | | | | summary: (11) expand
628 628 | | | | |
629 629 | | o---+ changeset: 10:74c64d036d72
630 630 | | | | | parent: 0:e6eb3150255d
631 631 | |/ / / parent: 6:b105a072e251
632 632 | | | | user: test
633 633 | | | | date: Thu Jan 01 00:00:10 1970 +0000
634 634 | | | | summary: (10) merge two known; one immediate left, one near right
635 635 | | | |
636 636 o | | | changeset: 9:7010c0af0a35
637 637 |\ \ \ \ parent: 7:b632bb1b1224
638 638 | | | | | parent: 8:7a0b11f71937
639 639 | | | | | user: test
640 640 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
641 641 | | | | | summary: (9) expand
642 642 | | | | |
643 643 | o-----+ changeset: 8:7a0b11f71937
644 644 | | | | | parent: 0:e6eb3150255d
645 645 |/ / / / parent: 7:b632bb1b1224
646 646 | | | | user: test
647 647 | | | | date: Thu Jan 01 00:00:08 1970 +0000
648 648 | | | | summary: (8) merge two known; one immediate left, one far right
649 649 | | | |
650 650 o | | | changeset: 7:b632bb1b1224
651 651 |\ \ \ \ parent: 2:3d9a33b8d1e1
652 652 | | | | | parent: 5:4409d547b708
653 653 | | | | | user: test
654 654 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
655 655 | | | | | summary: (7) expand
656 656 | | | | |
657 657 +---o | | changeset: 6:b105a072e251
658 658 | |/ / / parent: 2:3d9a33b8d1e1
659 659 | | | | parent: 5:4409d547b708
660 660 | | | | user: test
661 661 | | | | date: Thu Jan 01 00:00:06 1970 +0000
662 662 | | | | summary: (6) merge two known; one immediate left, one far left
663 663 | | | |
664 664 | o | | changeset: 5:4409d547b708
665 665 | |\ \ \ parent: 3:27eef8ed80b4
666 666 | | | | | parent: 4:26a8bac39d9f
667 667 | | | | | user: test
668 668 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
669 669 | | | | | summary: (5) expand
670 670 | | | | |
671 671 | | o | | changeset: 4:26a8bac39d9f
672 672 | |/|/ / parent: 1:6db2ef61d156
673 673 | | | | parent: 3:27eef8ed80b4
674 674 | | | | user: test
675 675 | | | | date: Thu Jan 01 00:00:04 1970 +0000
676 676 | | | | summary: (4) merge two known; one immediate left, one immediate right
677 677 | | | |
678 678 | o | | changeset: 3:27eef8ed80b4
679 679 |/ / / user: test
680 680 | | | date: Thu Jan 01 00:00:03 1970 +0000
681 681 | | | summary: (3) collapse
682 682 | | |
683 683 o | | changeset: 2:3d9a33b8d1e1
684 684 |/ / user: test
685 685 | | date: Thu Jan 01 00:00:02 1970 +0000
686 686 | | summary: (2) collapse
687 687 | |
688 688 o | changeset: 1:6db2ef61d156
689 689 |/ user: test
690 690 | date: Thu Jan 01 00:00:01 1970 +0000
691 691 | summary: (1) collapse
692 692 |
693 693 o changeset: 0:e6eb3150255d
694 694 user: test
695 695 date: Thu Jan 01 00:00:00 1970 +0000
696 696 summary: (0) root
697 697
698 698
699 699 File glog per revset:
700 700
701 701 $ hg log -G -r 'file("a")'
702 702 @ changeset: 34:fea3ac5810e0
703 703 | tag: tip
704 704 | parent: 32:d06dffa21a31
705 705 | user: test
706 706 | date: Thu Jan 01 00:00:34 1970 +0000
707 707 | summary: (34) head
708 708 |
709 709 | o changeset: 33:68608f5145f9
710 710 | | parent: 18:1aa84d96232a
711 711 | | user: test
712 712 | | date: Thu Jan 01 00:00:33 1970 +0000
713 713 | | summary: (33) head
714 714 | |
715 715 o | changeset: 32:d06dffa21a31
716 716 |\ \ parent: 27:886ed638191b
717 717 | | | parent: 31:621d83e11f67
718 718 | | | user: test
719 719 | | | date: Thu Jan 01 00:00:32 1970 +0000
720 720 | | | summary: (32) expand
721 721 | | |
722 722 | o | changeset: 31:621d83e11f67
723 723 | |\ \ parent: 21:d42a756af44d
724 724 | | | | parent: 30:6e11cd4b648f
725 725 | | | | user: test
726 726 | | | | date: Thu Jan 01 00:00:31 1970 +0000
727 727 | | | | summary: (31) expand
728 728 | | | |
729 729 | | o | changeset: 30:6e11cd4b648f
730 730 | | |\ \ parent: 28:44ecd0b9ae99
731 731 | | | | | parent: 29:cd9bb2be7593
732 732 | | | | | user: test
733 733 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
734 734 | | | | | summary: (30) expand
735 735 | | | | |
736 736 | | | o | changeset: 29:cd9bb2be7593
737 737 | | | | | parent: 0:e6eb3150255d
738 738 | | | | | user: test
739 739 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
740 740 | | | | | summary: (29) regular commit
741 741 | | | | |
742 742 | | o | | changeset: 28:44ecd0b9ae99
743 743 | | |\ \ \ parent: 1:6db2ef61d156
744 744 | | | | | | parent: 26:7f25b6c2f0b9
745 745 | | | | | | user: test
746 746 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
747 747 | | | | | | summary: (28) merge zero known
748 748 | | | | | |
749 749 o | | | | | changeset: 27:886ed638191b
750 750 |/ / / / / parent: 21:d42a756af44d
751 751 | | | | | user: test
752 752 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
753 753 | | | | | summary: (27) collapse
754 754 | | | | |
755 755 | | o---+ changeset: 26:7f25b6c2f0b9
756 756 | | | | | parent: 18:1aa84d96232a
757 757 | | | | | parent: 25:91da8ed57247
758 758 | | | | | user: test
759 759 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
760 760 | | | | | summary: (26) merge one known; far right
761 761 | | | | |
762 762 +---o | | changeset: 25:91da8ed57247
763 763 | | | | | parent: 21:d42a756af44d
764 764 | | | | | parent: 24:a9c19a3d96b7
765 765 | | | | | user: test
766 766 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
767 767 | | | | | summary: (25) merge one known; far left
768 768 | | | | |
769 769 | | o | | changeset: 24:a9c19a3d96b7
770 770 | | |\| | parent: 0:e6eb3150255d
771 771 | | | | | parent: 23:a01cddf0766d
772 772 | | | | | user: test
773 773 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
774 774 | | | | | summary: (24) merge one known; immediate right
775 775 | | | | |
776 776 | | o | | changeset: 23:a01cddf0766d
777 777 | |/| | | parent: 1:6db2ef61d156
778 778 | | | | | parent: 22:e0d9cccacb5d
779 779 | | | | | user: test
780 780 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
781 781 | | | | | summary: (23) merge one known; immediate left
782 782 | | | | |
783 783 +---o---+ changeset: 22:e0d9cccacb5d
784 784 | | | | parent: 18:1aa84d96232a
785 785 | | / / parent: 21:d42a756af44d
786 786 | | | | user: test
787 787 | | | | date: Thu Jan 01 00:00:22 1970 +0000
788 788 | | | | summary: (22) merge two known; one far left, one far right
789 789 | | | |
790 790 o | | | changeset: 21:d42a756af44d
791 791 |\ \ \ \ parent: 19:31ddc2c1573b
792 792 | | | | | parent: 20:d30ed6450e32
793 793 | | | | | user: test
794 794 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
795 795 | | | | | summary: (21) expand
796 796 | | | | |
797 797 | o---+-+ changeset: 20:d30ed6450e32
798 798 | | | | parent: 0:e6eb3150255d
799 799 | / / / parent: 18:1aa84d96232a
800 800 | | | | user: test
801 801 | | | | date: Thu Jan 01 00:00:20 1970 +0000
802 802 | | | | summary: (20) merge two known; two far right
803 803 | | | |
804 804 o | | | changeset: 19:31ddc2c1573b
805 805 |\ \ \ \ parent: 15:1dda3f72782d
806 806 | | | | | parent: 17:44765d7c06e0
807 807 | | | | | user: test
808 808 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
809 809 | | | | | summary: (19) expand
810 810 | | | | |
811 811 +---+---o changeset: 18:1aa84d96232a
812 812 | | | | parent: 1:6db2ef61d156
813 813 | | | | parent: 15:1dda3f72782d
814 814 | | | | user: test
815 815 | | | | date: Thu Jan 01 00:00:18 1970 +0000
816 816 | | | | summary: (18) merge two known; two far left
817 817 | | | |
818 818 | o | | changeset: 17:44765d7c06e0
819 819 | |\ \ \ parent: 12:86b91144a6e9
820 820 | | | | | parent: 16:3677d192927d
821 821 | | | | | user: test
822 822 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
823 823 | | | | | summary: (17) expand
824 824 | | | | |
825 825 | | o---+ changeset: 16:3677d192927d
826 826 | | | | | parent: 0:e6eb3150255d
827 827 | | |/ / parent: 1:6db2ef61d156
828 828 | | | | user: test
829 829 | | | | date: Thu Jan 01 00:00:16 1970 +0000
830 830 | | | | summary: (16) merge two known; one immediate right, one near right
831 831 | | | |
832 832 o | | | changeset: 15:1dda3f72782d
833 833 |\ \ \ \ parent: 13:22d8966a97e3
834 834 | | | | | parent: 14:8eac370358ef
835 835 | | | | | user: test
836 836 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
837 837 | | | | | summary: (15) expand
838 838 | | | | |
839 839 | o-----+ changeset: 14:8eac370358ef
840 840 | | | | | parent: 0:e6eb3150255d
841 841 | |/ / / parent: 12:86b91144a6e9
842 842 | | | | user: test
843 843 | | | | date: Thu Jan 01 00:00:14 1970 +0000
844 844 | | | | summary: (14) merge two known; one immediate right, one far right
845 845 | | | |
846 846 o | | | changeset: 13:22d8966a97e3
847 847 |\ \ \ \ parent: 9:7010c0af0a35
848 848 | | | | | parent: 11:832d76e6bdf2
849 849 | | | | | user: test
850 850 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
851 851 | | | | | summary: (13) expand
852 852 | | | | |
853 853 +---o | | changeset: 12:86b91144a6e9
854 854 | | |/ / parent: 1:6db2ef61d156
855 855 | | | | parent: 9:7010c0af0a35
856 856 | | | | user: test
857 857 | | | | date: Thu Jan 01 00:00:12 1970 +0000
858 858 | | | | summary: (12) merge two known; one immediate right, one far left
859 859 | | | |
860 860 | o | | changeset: 11:832d76e6bdf2
861 861 | |\ \ \ parent: 6:b105a072e251
862 862 | | | | | parent: 10:74c64d036d72
863 863 | | | | | user: test
864 864 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
865 865 | | | | | summary: (11) expand
866 866 | | | | |
867 867 | | o---+ changeset: 10:74c64d036d72
868 868 | | | | | parent: 0:e6eb3150255d
869 869 | |/ / / parent: 6:b105a072e251
870 870 | | | | user: test
871 871 | | | | date: Thu Jan 01 00:00:10 1970 +0000
872 872 | | | | summary: (10) merge two known; one immediate left, one near right
873 873 | | | |
874 874 o | | | changeset: 9:7010c0af0a35
875 875 |\ \ \ \ parent: 7:b632bb1b1224
876 876 | | | | | parent: 8:7a0b11f71937
877 877 | | | | | user: test
878 878 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
879 879 | | | | | summary: (9) expand
880 880 | | | | |
881 881 | o-----+ changeset: 8:7a0b11f71937
882 882 | | | | | parent: 0:e6eb3150255d
883 883 |/ / / / parent: 7:b632bb1b1224
884 884 | | | | user: test
885 885 | | | | date: Thu Jan 01 00:00:08 1970 +0000
886 886 | | | | summary: (8) merge two known; one immediate left, one far right
887 887 | | | |
888 888 o | | | changeset: 7:b632bb1b1224
889 889 |\ \ \ \ parent: 2:3d9a33b8d1e1
890 890 | | | | | parent: 5:4409d547b708
891 891 | | | | | user: test
892 892 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
893 893 | | | | | summary: (7) expand
894 894 | | | | |
895 895 +---o | | changeset: 6:b105a072e251
896 896 | |/ / / parent: 2:3d9a33b8d1e1
897 897 | | | | parent: 5:4409d547b708
898 898 | | | | user: test
899 899 | | | | date: Thu Jan 01 00:00:06 1970 +0000
900 900 | | | | summary: (6) merge two known; one immediate left, one far left
901 901 | | | |
902 902 | o | | changeset: 5:4409d547b708
903 903 | |\ \ \ parent: 3:27eef8ed80b4
904 904 | | | | | parent: 4:26a8bac39d9f
905 905 | | | | | user: test
906 906 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
907 907 | | | | | summary: (5) expand
908 908 | | | | |
909 909 | | o | | changeset: 4:26a8bac39d9f
910 910 | |/|/ / parent: 1:6db2ef61d156
911 911 | | | | parent: 3:27eef8ed80b4
912 912 | | | | user: test
913 913 | | | | date: Thu Jan 01 00:00:04 1970 +0000
914 914 | | | | summary: (4) merge two known; one immediate left, one immediate right
915 915 | | | |
916 916 | o | | changeset: 3:27eef8ed80b4
917 917 |/ / / user: test
918 918 | | | date: Thu Jan 01 00:00:03 1970 +0000
919 919 | | | summary: (3) collapse
920 920 | | |
921 921 o | | changeset: 2:3d9a33b8d1e1
922 922 |/ / user: test
923 923 | | date: Thu Jan 01 00:00:02 1970 +0000
924 924 | | summary: (2) collapse
925 925 | |
926 926 o | changeset: 1:6db2ef61d156
927 927 |/ user: test
928 928 | date: Thu Jan 01 00:00:01 1970 +0000
929 929 | summary: (1) collapse
930 930 |
931 931 o changeset: 0:e6eb3150255d
932 932 user: test
933 933 date: Thu Jan 01 00:00:00 1970 +0000
934 934 summary: (0) root
935 935
936 936
937 937
938 938 File glog per revset (only merges):
939 939
940 940 $ hg log -G -r 'file("a")' -m
941 941 o changeset: 32:d06dffa21a31
942 942 |\ parent: 27:886ed638191b
943 943 | : parent: 31:621d83e11f67
944 944 | : user: test
945 945 | : date: Thu Jan 01 00:00:32 1970 +0000
946 946 | : summary: (32) expand
947 947 | :
948 948 o : changeset: 31:621d83e11f67
949 949 |\: parent: 21:d42a756af44d
950 950 | : parent: 30:6e11cd4b648f
951 951 | : user: test
952 952 | : date: Thu Jan 01 00:00:31 1970 +0000
953 953 | : summary: (31) expand
954 954 | :
955 955 o : changeset: 30:6e11cd4b648f
956 956 |\ \ parent: 28:44ecd0b9ae99
957 957 | ~ : parent: 29:cd9bb2be7593
958 958 | : user: test
959 959 | : date: Thu Jan 01 00:00:30 1970 +0000
960 960 | : summary: (30) expand
961 961 | /
962 962 o : changeset: 28:44ecd0b9ae99
963 963 |\ \ parent: 1:6db2ef61d156
964 964 | ~ : parent: 26:7f25b6c2f0b9
965 965 | : user: test
966 966 | : date: Thu Jan 01 00:00:28 1970 +0000
967 967 | : summary: (28) merge zero known
968 968 | /
969 969 o : changeset: 26:7f25b6c2f0b9
970 970 |\ \ parent: 18:1aa84d96232a
971 971 | | : parent: 25:91da8ed57247
972 972 | | : user: test
973 973 | | : date: Thu Jan 01 00:00:26 1970 +0000
974 974 | | : summary: (26) merge one known; far right
975 975 | | :
976 976 | o : changeset: 25:91da8ed57247
977 977 | |\: parent: 21:d42a756af44d
978 978 | | : parent: 24:a9c19a3d96b7
979 979 | | : user: test
980 980 | | : date: Thu Jan 01 00:00:25 1970 +0000
981 981 | | : summary: (25) merge one known; far left
982 982 | | :
983 983 | o : changeset: 24:a9c19a3d96b7
984 984 | |\ \ parent: 0:e6eb3150255d
985 985 | | ~ : parent: 23:a01cddf0766d
986 986 | | : user: test
987 987 | | : date: Thu Jan 01 00:00:24 1970 +0000
988 988 | | : summary: (24) merge one known; immediate right
989 989 | | /
990 990 | o : changeset: 23:a01cddf0766d
991 991 | |\ \ parent: 1:6db2ef61d156
992 992 | | ~ : parent: 22:e0d9cccacb5d
993 993 | | : user: test
994 994 | | : date: Thu Jan 01 00:00:23 1970 +0000
995 995 | | : summary: (23) merge one known; immediate left
996 996 | | /
997 997 | o : changeset: 22:e0d9cccacb5d
998 998 |/:/ parent: 18:1aa84d96232a
999 999 | : parent: 21:d42a756af44d
1000 1000 | : user: test
1001 1001 | : date: Thu Jan 01 00:00:22 1970 +0000
1002 1002 | : summary: (22) merge two known; one far left, one far right
1003 1003 | :
1004 1004 | o changeset: 21:d42a756af44d
1005 1005 | |\ parent: 19:31ddc2c1573b
1006 1006 | | | parent: 20:d30ed6450e32
1007 1007 | | | user: test
1008 1008 | | | date: Thu Jan 01 00:00:21 1970 +0000
1009 1009 | | | summary: (21) expand
1010 1010 | | |
1011 1011 +---o changeset: 20:d30ed6450e32
1012 1012 | | | parent: 0:e6eb3150255d
1013 1013 | | ~ parent: 18:1aa84d96232a
1014 1014 | | user: test
1015 1015 | | date: Thu Jan 01 00:00:20 1970 +0000
1016 1016 | | summary: (20) merge two known; two far right
1017 1017 | |
1018 1018 | o changeset: 19:31ddc2c1573b
1019 1019 | |\ parent: 15:1dda3f72782d
1020 1020 | | | parent: 17:44765d7c06e0
1021 1021 | | | user: test
1022 1022 | | | date: Thu Jan 01 00:00:19 1970 +0000
1023 1023 | | | summary: (19) expand
1024 1024 | | |
1025 1025 o | | changeset: 18:1aa84d96232a
1026 1026 |\| | parent: 1:6db2ef61d156
1027 1027 ~ | | parent: 15:1dda3f72782d
1028 1028 | | user: test
1029 1029 | | date: Thu Jan 01 00:00:18 1970 +0000
1030 1030 | | summary: (18) merge two known; two far left
1031 1031 / /
1032 1032 | o changeset: 17:44765d7c06e0
1033 1033 | |\ parent: 12:86b91144a6e9
1034 1034 | | | parent: 16:3677d192927d
1035 1035 | | | user: test
1036 1036 | | | date: Thu Jan 01 00:00:17 1970 +0000
1037 1037 | | | summary: (17) expand
1038 1038 | | |
1039 1039 | | o changeset: 16:3677d192927d
1040 1040 | | |\ parent: 0:e6eb3150255d
1041 1041 | | ~ ~ parent: 1:6db2ef61d156
1042 1042 | | user: test
1043 1043 | | date: Thu Jan 01 00:00:16 1970 +0000
1044 1044 | | summary: (16) merge two known; one immediate right, one near right
1045 1045 | |
1046 1046 o | changeset: 15:1dda3f72782d
1047 1047 |\ \ parent: 13:22d8966a97e3
1048 1048 | | | parent: 14:8eac370358ef
1049 1049 | | | user: test
1050 1050 | | | date: Thu Jan 01 00:00:15 1970 +0000
1051 1051 | | | summary: (15) expand
1052 1052 | | |
1053 1053 | o | changeset: 14:8eac370358ef
1054 1054 | |\| parent: 0:e6eb3150255d
1055 1055 | ~ | parent: 12:86b91144a6e9
1056 1056 | | user: test
1057 1057 | | date: Thu Jan 01 00:00:14 1970 +0000
1058 1058 | | summary: (14) merge two known; one immediate right, one far right
1059 1059 | /
1060 1060 o | changeset: 13:22d8966a97e3
1061 1061 |\ \ parent: 9:7010c0af0a35
1062 1062 | | | parent: 11:832d76e6bdf2
1063 1063 | | | user: test
1064 1064 | | | date: Thu Jan 01 00:00:13 1970 +0000
1065 1065 | | | summary: (13) expand
1066 1066 | | |
1067 1067 +---o changeset: 12:86b91144a6e9
1068 1068 | | | parent: 1:6db2ef61d156
1069 1069 | | ~ parent: 9:7010c0af0a35
1070 1070 | | user: test
1071 1071 | | date: Thu Jan 01 00:00:12 1970 +0000
1072 1072 | | summary: (12) merge two known; one immediate right, one far left
1073 1073 | |
1074 1074 | o changeset: 11:832d76e6bdf2
1075 1075 | |\ parent: 6:b105a072e251
1076 1076 | | | parent: 10:74c64d036d72
1077 1077 | | | user: test
1078 1078 | | | date: Thu Jan 01 00:00:11 1970 +0000
1079 1079 | | | summary: (11) expand
1080 1080 | | |
1081 1081 | | o changeset: 10:74c64d036d72
1082 1082 | |/| parent: 0:e6eb3150255d
1083 1083 | | ~ parent: 6:b105a072e251
1084 1084 | | user: test
1085 1085 | | date: Thu Jan 01 00:00:10 1970 +0000
1086 1086 | | summary: (10) merge two known; one immediate left, one near right
1087 1087 | |
1088 1088 o | changeset: 9:7010c0af0a35
1089 1089 |\ \ parent: 7:b632bb1b1224
1090 1090 | | | parent: 8:7a0b11f71937
1091 1091 | | | user: test
1092 1092 | | | date: Thu Jan 01 00:00:09 1970 +0000
1093 1093 | | | summary: (9) expand
1094 1094 | | |
1095 1095 | o | changeset: 8:7a0b11f71937
1096 1096 |/| | parent: 0:e6eb3150255d
1097 1097 | ~ | parent: 7:b632bb1b1224
1098 1098 | | user: test
1099 1099 | | date: Thu Jan 01 00:00:08 1970 +0000
1100 1100 | | summary: (8) merge two known; one immediate left, one far right
1101 1101 | /
1102 1102 o | changeset: 7:b632bb1b1224
1103 1103 |\ \ parent: 2:3d9a33b8d1e1
1104 1104 | ~ | parent: 5:4409d547b708
1105 1105 | | user: test
1106 1106 | | date: Thu Jan 01 00:00:07 1970 +0000
1107 1107 | | summary: (7) expand
1108 1108 | /
1109 1109 | o changeset: 6:b105a072e251
1110 1110 |/| parent: 2:3d9a33b8d1e1
1111 1111 | ~ parent: 5:4409d547b708
1112 1112 | user: test
1113 1113 | date: Thu Jan 01 00:00:06 1970 +0000
1114 1114 | summary: (6) merge two known; one immediate left, one far left
1115 1115 |
1116 1116 o changeset: 5:4409d547b708
1117 1117 |\ parent: 3:27eef8ed80b4
1118 1118 | ~ parent: 4:26a8bac39d9f
1119 1119 | user: test
1120 1120 | date: Thu Jan 01 00:00:05 1970 +0000
1121 1121 | summary: (5) expand
1122 1122 |
1123 1123 o changeset: 4:26a8bac39d9f
1124 1124 |\ parent: 1:6db2ef61d156
1125 1125 ~ ~ parent: 3:27eef8ed80b4
1126 1126 user: test
1127 1127 date: Thu Jan 01 00:00:04 1970 +0000
1128 1128 summary: (4) merge two known; one immediate left, one immediate right
1129 1129
1130 1130
1131 1131
1132 1132 Empty revision range - display nothing:
1133 1133 $ hg log -G -r 1..0
1134 1134
1135 1135 $ cd ..
1136 1136
1137 1137 #if no-outer-repo
1138 1138
1139 1139 From outer space:
1140 1140 $ hg log -G -l1 repo
1141 1141 @ changeset: 34:fea3ac5810e0
1142 1142 | tag: tip
1143 1143 ~ parent: 32:d06dffa21a31
1144 1144 user: test
1145 1145 date: Thu Jan 01 00:00:34 1970 +0000
1146 1146 summary: (34) head
1147 1147
1148 1148 $ hg log -G -l1 repo/a
1149 1149 @ changeset: 34:fea3ac5810e0
1150 1150 | tag: tip
1151 1151 ~ parent: 32:d06dffa21a31
1152 1152 user: test
1153 1153 date: Thu Jan 01 00:00:34 1970 +0000
1154 1154 summary: (34) head
1155 1155
1156 1156 $ hg log -G -l1 repo/missing
1157 1157
1158 1158 #endif
1159 1159
1160 1160 File log with revs != cset revs:
1161 1161 $ hg init flog
1162 1162 $ cd flog
1163 1163 $ echo one >one
1164 1164 $ hg add one
1165 1165 $ hg commit -mone
1166 1166 $ echo two >two
1167 1167 $ hg add two
1168 1168 $ hg commit -mtwo
1169 1169 $ echo more >two
1170 1170 $ hg commit -mmore
1171 1171 $ hg log -G two
1172 1172 @ changeset: 2:12c28321755b
1173 1173 | tag: tip
1174 1174 | user: test
1175 1175 | date: Thu Jan 01 00:00:00 1970 +0000
1176 1176 | summary: more
1177 1177 |
1178 1178 o changeset: 1:5ac72c0599bf
1179 1179 | user: test
1180 1180 ~ date: Thu Jan 01 00:00:00 1970 +0000
1181 1181 summary: two
1182 1182
1183 1183
1184 1184 Issue1896: File log with explicit style
1185 1185 $ hg log -G --style=default one
1186 1186 o changeset: 0:3d578b4a1f53
1187 1187 user: test
1188 1188 date: Thu Jan 01 00:00:00 1970 +0000
1189 1189 summary: one
1190 1190
1191 1191 Issue2395: glog --style header and footer
1192 1192 $ hg log -G --style=xml one
1193 1193 <?xml version="1.0"?>
1194 1194 <log>
1195 1195 o <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1">
1196 1196 <author email="test">test</author>
1197 1197 <date>1970-01-01T00:00:00+00:00</date>
1198 1198 <msg xml:space="preserve">one</msg>
1199 1199 </logentry>
1200 1200 </log>
1201 1201
1202 1202 $ cd ..
1203 1203
1204 1204 Incoming and outgoing:
1205 1205
1206 1206 $ hg clone -U -r31 repo repo2
1207 1207 adding changesets
1208 1208 adding manifests
1209 1209 adding file changes
1210 1210 added 31 changesets with 31 changes to 1 files
1211 1211 $ cd repo2
1212 1212
1213 1213 $ hg incoming --graph ../repo
1214 1214 comparing with ../repo
1215 1215 searching for changes
1216 1216 o changeset: 34:fea3ac5810e0
1217 1217 | tag: tip
1218 1218 | parent: 32:d06dffa21a31
1219 1219 | user: test
1220 1220 | date: Thu Jan 01 00:00:34 1970 +0000
1221 1221 | summary: (34) head
1222 1222 |
1223 1223 | o changeset: 33:68608f5145f9
1224 1224 | parent: 18:1aa84d96232a
1225 1225 | user: test
1226 1226 | date: Thu Jan 01 00:00:33 1970 +0000
1227 1227 | summary: (33) head
1228 1228 |
1229 1229 o changeset: 32:d06dffa21a31
1230 1230 | parent: 27:886ed638191b
1231 1231 | parent: 31:621d83e11f67
1232 1232 | user: test
1233 1233 | date: Thu Jan 01 00:00:32 1970 +0000
1234 1234 | summary: (32) expand
1235 1235 |
1236 1236 o changeset: 27:886ed638191b
1237 1237 parent: 21:d42a756af44d
1238 1238 user: test
1239 1239 date: Thu Jan 01 00:00:27 1970 +0000
1240 1240 summary: (27) collapse
1241 1241
1242 1242 $ cd ..
1243 1243
1244 1244 $ hg -R repo outgoing --graph repo2
1245 1245 comparing with repo2
1246 1246 searching for changes
1247 1247 @ changeset: 34:fea3ac5810e0
1248 1248 | tag: tip
1249 1249 | parent: 32:d06dffa21a31
1250 1250 | user: test
1251 1251 | date: Thu Jan 01 00:00:34 1970 +0000
1252 1252 | summary: (34) head
1253 1253 |
1254 1254 | o changeset: 33:68608f5145f9
1255 1255 | parent: 18:1aa84d96232a
1256 1256 | user: test
1257 1257 | date: Thu Jan 01 00:00:33 1970 +0000
1258 1258 | summary: (33) head
1259 1259 |
1260 1260 o changeset: 32:d06dffa21a31
1261 1261 | parent: 27:886ed638191b
1262 1262 | parent: 31:621d83e11f67
1263 1263 | user: test
1264 1264 | date: Thu Jan 01 00:00:32 1970 +0000
1265 1265 | summary: (32) expand
1266 1266 |
1267 1267 o changeset: 27:886ed638191b
1268 1268 parent: 21:d42a756af44d
1269 1269 user: test
1270 1270 date: Thu Jan 01 00:00:27 1970 +0000
1271 1271 summary: (27) collapse
1272 1272
1273 1273
1274 1274 File + limit with revs != cset revs:
1275 1275 $ cd repo
1276 1276 $ touch b
1277 1277 $ hg ci -Aqm0
1278 1278 $ hg log -G -l2 a
1279 1279 o changeset: 34:fea3ac5810e0
1280 1280 | parent: 32:d06dffa21a31
1281 1281 ~ user: test
1282 1282 date: Thu Jan 01 00:00:34 1970 +0000
1283 1283 summary: (34) head
1284 1284
1285 1285 o changeset: 33:68608f5145f9
1286 1286 | parent: 18:1aa84d96232a
1287 1287 ~ user: test
1288 1288 date: Thu Jan 01 00:00:33 1970 +0000
1289 1289 summary: (33) head
1290 1290
1291 1291
1292 1292 File + limit + -ra:b, (b - a) < limit:
1293 1293 $ hg log -G -l3000 -r32:tip a
1294 1294 o changeset: 34:fea3ac5810e0
1295 1295 | parent: 32:d06dffa21a31
1296 1296 | user: test
1297 1297 | date: Thu Jan 01 00:00:34 1970 +0000
1298 1298 | summary: (34) head
1299 1299 |
1300 1300 | o changeset: 33:68608f5145f9
1301 1301 | | parent: 18:1aa84d96232a
1302 1302 | ~ user: test
1303 1303 | date: Thu Jan 01 00:00:33 1970 +0000
1304 1304 | summary: (33) head
1305 1305 |
1306 1306 o changeset: 32:d06dffa21a31
1307 1307 |\ parent: 27:886ed638191b
1308 1308 ~ ~ parent: 31:621d83e11f67
1309 1309 user: test
1310 1310 date: Thu Jan 01 00:00:32 1970 +0000
1311 1311 summary: (32) expand
1312 1312
1313 1313
1314 1314 Point out a common and an uncommon unshown parent
1315 1315
1316 1316 $ hg log -G -r 'rev(8) or rev(9)'
1317 1317 o changeset: 9:7010c0af0a35
1318 1318 |\ parent: 7:b632bb1b1224
1319 1319 | ~ parent: 8:7a0b11f71937
1320 1320 | user: test
1321 1321 | date: Thu Jan 01 00:00:09 1970 +0000
1322 1322 | summary: (9) expand
1323 1323 |
1324 1324 o changeset: 8:7a0b11f71937
1325 1325 |\ parent: 0:e6eb3150255d
1326 1326 ~ ~ parent: 7:b632bb1b1224
1327 1327 user: test
1328 1328 date: Thu Jan 01 00:00:08 1970 +0000
1329 1329 summary: (8) merge two known; one immediate left, one far right
1330 1330
1331 1331
1332 1332 File + limit + -ra:b, b < tip:
1333 1333
1334 1334 $ hg log -G -l1 -r32:34 a
1335 1335 o changeset: 34:fea3ac5810e0
1336 1336 | parent: 32:d06dffa21a31
1337 1337 ~ user: test
1338 1338 date: Thu Jan 01 00:00:34 1970 +0000
1339 1339 summary: (34) head
1340 1340
1341 1341
1342 1342 file(File) + limit + -ra:b, b < tip:
1343 1343
1344 1344 $ hg log -G -l1 -r32:34 -r 'file("a")'
1345 1345 o changeset: 34:fea3ac5810e0
1346 1346 | parent: 32:d06dffa21a31
1347 1347 ~ user: test
1348 1348 date: Thu Jan 01 00:00:34 1970 +0000
1349 1349 summary: (34) head
1350 1350
1351 1351
1352 1352 limit(file(File) and a::b), b < tip:
1353 1353
1354 1354 $ hg log -G -r 'limit(file("a") and 32::34, 1)'
1355 1355 o changeset: 32:d06dffa21a31
1356 1356 |\ parent: 27:886ed638191b
1357 1357 ~ ~ parent: 31:621d83e11f67
1358 1358 user: test
1359 1359 date: Thu Jan 01 00:00:32 1970 +0000
1360 1360 summary: (32) expand
1361 1361
1362 1362
1363 1363 File + limit + -ra:b, b < tip:
1364 1364
1365 1365 $ hg log -G -r 'limit(file("a") and 34::32, 1)'
1366 1366
1367 1367 File + limit + -ra:b, b < tip, (b - a) < limit:
1368 1368
1369 1369 $ hg log -G -l10 -r33:34 a
1370 1370 o changeset: 34:fea3ac5810e0
1371 1371 | parent: 32:d06dffa21a31
1372 1372 ~ user: test
1373 1373 date: Thu Jan 01 00:00:34 1970 +0000
1374 1374 summary: (34) head
1375 1375
1376 1376 o changeset: 33:68608f5145f9
1377 1377 | parent: 18:1aa84d96232a
1378 1378 ~ user: test
1379 1379 date: Thu Jan 01 00:00:33 1970 +0000
1380 1380 summary: (33) head
1381 1381
1382 1382
1383 1383 Do not crash or produce strange graphs if history is buggy
1384 1384
1385 1385 $ hg branch branch
1386 1386 marked working directory as branch branch
1387 1387 (branches are permanent and global, did you want a bookmark?)
1388 1388 $ commit 36 "buggy merge: identical parents" 35 35
1389 1389 $ hg log -G -l5
1390 1390 @ changeset: 36:08a19a744424
1391 1391 | branch: branch
1392 1392 | tag: tip
1393 1393 | parent: 35:9159c3644c5e
1394 1394 | parent: 35:9159c3644c5e
1395 1395 | user: test
1396 1396 | date: Thu Jan 01 00:00:36 1970 +0000
1397 1397 | summary: (36) buggy merge: identical parents
1398 1398 |
1399 1399 o changeset: 35:9159c3644c5e
1400 1400 | user: test
1401 1401 | date: Thu Jan 01 00:00:00 1970 +0000
1402 1402 | summary: 0
1403 1403 |
1404 1404 o changeset: 34:fea3ac5810e0
1405 1405 | parent: 32:d06dffa21a31
1406 1406 | user: test
1407 1407 | date: Thu Jan 01 00:00:34 1970 +0000
1408 1408 | summary: (34) head
1409 1409 |
1410 1410 | o changeset: 33:68608f5145f9
1411 1411 | | parent: 18:1aa84d96232a
1412 1412 | ~ user: test
1413 1413 | date: Thu Jan 01 00:00:33 1970 +0000
1414 1414 | summary: (33) head
1415 1415 |
1416 1416 o changeset: 32:d06dffa21a31
1417 1417 |\ parent: 27:886ed638191b
1418 1418 ~ ~ parent: 31:621d83e11f67
1419 1419 user: test
1420 1420 date: Thu Jan 01 00:00:32 1970 +0000
1421 1421 summary: (32) expand
1422 1422
1423 1423
1424 1424 Test log -G options
1425 1425
1426 1426 $ testlog() {
1427 1427 > hg log -G --print-revset "$@"
1428 1428 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1429 1429 > | sed 's/.*nodetag/nodetag/' > log.nodes
1430 1430 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1431 1431 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1432 1432 > (cmp log.nodes glog.nodes || diff -u log.nodes glog.nodes) \
1433 1433 > | grep '^[-+@ ]' || :
1434 1434 > }
1435 1435
1436 1436 glog always reorders nodes which explains the difference with log
1437 1437
1438 1438 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1439 1439 ['27', '25', '21', '34', '32', '31']
1440 1440 []
1441 1441 --- log.nodes * (glob)
1442 1442 +++ glog.nodes * (glob)
1443 1443 @@ -1,6 +1,6 @@
1444 1444 -nodetag 27
1445 1445 -nodetag 25
1446 1446 -nodetag 21
1447 1447 nodetag 34
1448 1448 nodetag 32
1449 1449 nodetag 31
1450 1450 +nodetag 27
1451 1451 +nodetag 25
1452 1452 +nodetag 21
1453 1453 $ testlog -u test -u not-a-user
1454 1454 []
1455 1455 (group
1456 1456 (group
1457 1457 (or
1458 (func
1459 ('symbol', 'user')
1460 ('string', 'test'))
1461 (func
1462 ('symbol', 'user')
1463 ('string', 'not-a-user')))))
1458 (list
1459 (func
1460 ('symbol', 'user')
1461 ('string', 'test'))
1462 (func
1463 ('symbol', 'user')
1464 ('string', 'not-a-user'))))))
1464 1465 $ testlog -b not-a-branch
1465 1466 abort: unknown revision 'not-a-branch'!
1466 1467 abort: unknown revision 'not-a-branch'!
1467 1468 abort: unknown revision 'not-a-branch'!
1468 1469 $ testlog -b 35 -b 36 --only-branch branch
1469 1470 []
1470 1471 (group
1471 1472 (group
1472 1473 (or
1473 (func
1474 ('symbol', 'branch')
1475 ('string', 'default'))
1476 (func
1477 ('symbol', 'branch')
1478 ('string', 'branch'))
1479 (func
1480 ('symbol', 'branch')
1481 ('string', 'branch')))))
1474 (list
1475 (func
1476 ('symbol', 'branch')
1477 ('string', 'default'))
1478 (func
1479 ('symbol', 'branch')
1480 ('string', 'branch'))
1481 (func
1482 ('symbol', 'branch')
1483 ('string', 'branch'))))))
1482 1484 $ testlog -k expand -k merge
1483 1485 []
1484 1486 (group
1485 1487 (group
1486 1488 (or
1487 (func
1488 ('symbol', 'keyword')
1489 ('string', 'expand'))
1490 (func
1491 ('symbol', 'keyword')
1492 ('string', 'merge')))))
1489 (list
1490 (func
1491 ('symbol', 'keyword')
1492 ('string', 'expand'))
1493 (func
1494 ('symbol', 'keyword')
1495 ('string', 'merge'))))))
1493 1496 $ testlog --only-merges
1494 1497 []
1495 1498 (group
1496 1499 (func
1497 1500 ('symbol', 'merge')
1498 1501 None))
1499 1502 $ testlog --no-merges
1500 1503 []
1501 1504 (group
1502 1505 (not
1503 1506 (func
1504 1507 ('symbol', 'merge')
1505 1508 None)))
1506 1509 $ testlog --date '2 0 to 4 0'
1507 1510 []
1508 1511 (group
1509 1512 (func
1510 1513 ('symbol', 'date')
1511 1514 ('string', '2 0 to 4 0')))
1512 1515 $ hg log -G -d 'brace ) in a date'
1513 1516 abort: invalid date: 'brace ) in a date'
1514 1517 [255]
1515 1518 $ testlog --prune 31 --prune 32
1516 1519 []
1517 1520 (group
1518 1521 (group
1519 1522 (and
1520 1523 (not
1521 1524 (group
1522 1525 (or
1523 ('string', '31')
1524 (func
1525 ('symbol', 'ancestors')
1526 ('string', '31')))))
1526 (list
1527 ('string', '31')
1528 (func
1529 ('symbol', 'ancestors')
1530 ('string', '31'))))))
1527 1531 (not
1528 1532 (group
1529 1533 (or
1530 ('string', '32')
1531 (func
1532 ('symbol', 'ancestors')
1533 ('string', '32'))))))))
1534 (list
1535 ('string', '32')
1536 (func
1537 ('symbol', 'ancestors')
1538 ('string', '32')))))))))
1534 1539
1535 1540 Dedicated repo for --follow and paths filtering. The g is crafted to
1536 1541 have 2 filelog topological heads in a linear changeset graph.
1537 1542
1538 1543 $ cd ..
1539 1544 $ hg init follow
1540 1545 $ cd follow
1541 1546 $ testlog --follow
1542 1547 []
1543 1548 []
1544 1549 $ testlog -rnull
1545 1550 ['null']
1546 1551 []
1547 1552 $ echo a > a
1548 1553 $ echo aa > aa
1549 1554 $ echo f > f
1550 1555 $ hg ci -Am "add a" a aa f
1551 1556 $ hg cp a b
1552 1557 $ hg cp f g
1553 1558 $ hg ci -m "copy a b"
1554 1559 $ mkdir dir
1555 1560 $ hg mv b dir
1556 1561 $ echo g >> g
1557 1562 $ echo f >> f
1558 1563 $ hg ci -m "mv b dir/b"
1559 1564 $ hg mv a b
1560 1565 $ hg cp -f f g
1561 1566 $ echo a > d
1562 1567 $ hg add d
1563 1568 $ hg ci -m "mv a b; add d"
1564 1569 $ hg mv dir/b e
1565 1570 $ hg ci -m "mv dir/b e"
1566 1571 $ hg log -G --template '({rev}) {desc|firstline}\n'
1567 1572 @ (4) mv dir/b e
1568 1573 |
1569 1574 o (3) mv a b; add d
1570 1575 |
1571 1576 o (2) mv b dir/b
1572 1577 |
1573 1578 o (1) copy a b
1574 1579 |
1575 1580 o (0) add a
1576 1581
1577 1582
1578 1583 $ testlog a
1579 1584 []
1580 1585 (group
1581 1586 (group
1582 1587 (func
1583 1588 ('symbol', 'filelog')
1584 1589 ('string', 'a'))))
1585 1590 $ testlog a b
1586 1591 []
1587 1592 (group
1588 1593 (group
1589 1594 (or
1590 (func
1591 ('symbol', 'filelog')
1592 ('string', 'a'))
1593 (func
1594 ('symbol', 'filelog')
1595 ('string', 'b')))))
1595 (list
1596 (func
1597 ('symbol', 'filelog')
1598 ('string', 'a'))
1599 (func
1600 ('symbol', 'filelog')
1601 ('string', 'b'))))))
1596 1602
1597 1603 Test falling back to slow path for non-existing files
1598 1604
1599 1605 $ testlog a c
1600 1606 []
1601 1607 (group
1602 1608 (func
1603 1609 ('symbol', '_matchfiles')
1604 1610 (list
1605 1611 ('string', 'r:')
1606 1612 ('string', 'd:relpath')
1607 1613 ('string', 'p:a')
1608 1614 ('string', 'p:c'))))
1609 1615
1610 1616 Test multiple --include/--exclude/paths
1611 1617
1612 1618 $ testlog --include a --include e --exclude b --exclude e a e
1613 1619 []
1614 1620 (group
1615 1621 (func
1616 1622 ('symbol', '_matchfiles')
1617 1623 (list
1618 1624 ('string', 'r:')
1619 1625 ('string', 'd:relpath')
1620 1626 ('string', 'p:a')
1621 1627 ('string', 'p:e')
1622 1628 ('string', 'i:a')
1623 1629 ('string', 'i:e')
1624 1630 ('string', 'x:b')
1625 1631 ('string', 'x:e'))))
1626 1632
1627 1633 Test glob expansion of pats
1628 1634
1629 1635 $ expandglobs=`$PYTHON -c "import mercurial.util; \
1630 1636 > print mercurial.util.expandglobs and 'true' or 'false'"`
1631 1637 $ if [ $expandglobs = "true" ]; then
1632 1638 > testlog 'a*';
1633 1639 > else
1634 1640 > testlog a*;
1635 1641 > fi;
1636 1642 []
1637 1643 (group
1638 1644 (group
1639 1645 (func
1640 1646 ('symbol', 'filelog')
1641 1647 ('string', 'aa'))))
1642 1648
1643 1649 Test --follow on a non-existent directory
1644 1650
1645 1651 $ testlog -f dir
1646 1652 abort: cannot follow file not in parent revision: "dir"
1647 1653 abort: cannot follow file not in parent revision: "dir"
1648 1654 abort: cannot follow file not in parent revision: "dir"
1649 1655
1650 1656 Test --follow on a directory
1651 1657
1652 1658 $ hg up -q '.^'
1653 1659 $ testlog -f dir
1654 1660 []
1655 1661 (group
1656 1662 (and
1657 1663 (func
1658 1664 ('symbol', 'ancestors')
1659 1665 ('symbol', '.'))
1660 1666 (func
1661 1667 ('symbol', '_matchfiles')
1662 1668 (list
1663 1669 ('string', 'r:')
1664 1670 ('string', 'd:relpath')
1665 1671 ('string', 'p:dir')))))
1666 1672 $ hg up -q tip
1667 1673
1668 1674 Test --follow on file not in parent revision
1669 1675
1670 1676 $ testlog -f a
1671 1677 abort: cannot follow file not in parent revision: "a"
1672 1678 abort: cannot follow file not in parent revision: "a"
1673 1679 abort: cannot follow file not in parent revision: "a"
1674 1680
1675 1681 Test --follow and patterns
1676 1682
1677 1683 $ testlog -f 'glob:*'
1678 1684 []
1679 1685 (group
1680 1686 (and
1681 1687 (func
1682 1688 ('symbol', 'ancestors')
1683 1689 ('symbol', '.'))
1684 1690 (func
1685 1691 ('symbol', '_matchfiles')
1686 1692 (list
1687 1693 ('string', 'r:')
1688 1694 ('string', 'd:relpath')
1689 1695 ('string', 'p:glob:*')))))
1690 1696
1691 1697 Test --follow on a single rename
1692 1698
1693 1699 $ hg up -q 2
1694 1700 $ testlog -f a
1695 1701 []
1696 1702 (group
1697 1703 (group
1698 1704 (func
1699 1705 ('symbol', 'follow')
1700 1706 ('string', 'a'))))
1701 1707
1702 1708 Test --follow and multiple renames
1703 1709
1704 1710 $ hg up -q tip
1705 1711 $ testlog -f e
1706 1712 []
1707 1713 (group
1708 1714 (group
1709 1715 (func
1710 1716 ('symbol', 'follow')
1711 1717 ('string', 'e'))))
1712 1718
1713 1719 Test --follow and multiple filelog heads
1714 1720
1715 1721 $ hg up -q 2
1716 1722 $ testlog -f g
1717 1723 []
1718 1724 (group
1719 1725 (group
1720 1726 (func
1721 1727 ('symbol', 'follow')
1722 1728 ('string', 'g'))))
1723 1729 $ cat log.nodes
1724 1730 nodetag 2
1725 1731 nodetag 1
1726 1732 nodetag 0
1727 1733 $ hg up -q tip
1728 1734 $ testlog -f g
1729 1735 []
1730 1736 (group
1731 1737 (group
1732 1738 (func
1733 1739 ('symbol', 'follow')
1734 1740 ('string', 'g'))))
1735 1741 $ cat log.nodes
1736 1742 nodetag 3
1737 1743 nodetag 2
1738 1744 nodetag 0
1739 1745
1740 1746 Test --follow and multiple files
1741 1747
1742 1748 $ testlog -f g e
1743 1749 []
1744 1750 (group
1745 1751 (group
1746 1752 (or
1747 (func
1748 ('symbol', 'follow')
1749 ('string', 'g'))
1750 (func
1751 ('symbol', 'follow')
1752 ('string', 'e')))))
1753 (list
1754 (func
1755 ('symbol', 'follow')
1756 ('string', 'g'))
1757 (func
1758 ('symbol', 'follow')
1759 ('string', 'e'))))))
1753 1760 $ cat log.nodes
1754 1761 nodetag 4
1755 1762 nodetag 3
1756 1763 nodetag 2
1757 1764 nodetag 1
1758 1765 nodetag 0
1759 1766
1760 1767 Test --follow null parent
1761 1768
1762 1769 $ hg up -q null
1763 1770 $ testlog -f
1764 1771 []
1765 1772 []
1766 1773
1767 1774 Test --follow-first
1768 1775
1769 1776 $ hg up -q 3
1770 1777 $ echo ee > e
1771 1778 $ hg ci -Am "add another e" e
1772 1779 created new head
1773 1780 $ hg merge --tool internal:other 4
1774 1781 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1775 1782 (branch merge, don't forget to commit)
1776 1783 $ echo merge > e
1777 1784 $ hg ci -m "merge 5 and 4"
1778 1785 $ testlog --follow-first
1779 1786 []
1780 1787 (group
1781 1788 (func
1782 1789 ('symbol', '_firstancestors')
1783 1790 (func
1784 1791 ('symbol', 'rev')
1785 1792 ('symbol', '6'))))
1786 1793
1787 1794 Cannot compare with log --follow-first FILE as it never worked
1788 1795
1789 1796 $ hg log -G --print-revset --follow-first e
1790 1797 []
1791 1798 (group
1792 1799 (group
1793 1800 (func
1794 1801 ('symbol', '_followfirst')
1795 1802 ('string', 'e'))))
1796 1803 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1797 1804 @ 6 merge 5 and 4
1798 1805 |\
1799 1806 | ~
1800 1807 o 5 add another e
1801 1808 |
1802 1809 ~
1803 1810
1804 1811 Test --copies
1805 1812
1806 1813 $ hg log -G --copies --template "{rev} {desc|firstline} \
1807 1814 > copies: {file_copies_switch}\n"
1808 1815 @ 6 merge 5 and 4 copies:
1809 1816 |\
1810 1817 | o 5 add another e copies:
1811 1818 | |
1812 1819 o | 4 mv dir/b e copies: e (dir/b)
1813 1820 |/
1814 1821 o 3 mv a b; add d copies: b (a)g (f)
1815 1822 |
1816 1823 o 2 mv b dir/b copies: dir/b (b)
1817 1824 |
1818 1825 o 1 copy a b copies: b (a)g (f)
1819 1826 |
1820 1827 o 0 add a copies:
1821 1828
1822 1829 Test "set:..." and parent revision
1823 1830
1824 1831 $ hg up -q 4
1825 1832 $ testlog "set:copied()"
1826 1833 []
1827 1834 (group
1828 1835 (func
1829 1836 ('symbol', '_matchfiles')
1830 1837 (list
1831 1838 ('string', 'r:')
1832 1839 ('string', 'd:relpath')
1833 1840 ('string', 'p:set:copied()'))))
1834 1841 $ testlog --include "set:copied()"
1835 1842 []
1836 1843 (group
1837 1844 (func
1838 1845 ('symbol', '_matchfiles')
1839 1846 (list
1840 1847 ('string', 'r:')
1841 1848 ('string', 'd:relpath')
1842 1849 ('string', 'i:set:copied()'))))
1843 1850 $ testlog -r "sort(file('set:copied()'), -rev)"
1844 1851 ["sort(file('set:copied()'), -rev)"]
1845 1852 []
1846 1853
1847 1854 Test --removed
1848 1855
1849 1856 $ testlog --removed
1850 1857 []
1851 1858 []
1852 1859 $ testlog --removed a
1853 1860 []
1854 1861 (group
1855 1862 (func
1856 1863 ('symbol', '_matchfiles')
1857 1864 (list
1858 1865 ('string', 'r:')
1859 1866 ('string', 'd:relpath')
1860 1867 ('string', 'p:a'))))
1861 1868 $ testlog --removed --follow a
1862 1869 []
1863 1870 (group
1864 1871 (and
1865 1872 (func
1866 1873 ('symbol', 'ancestors')
1867 1874 ('symbol', '.'))
1868 1875 (func
1869 1876 ('symbol', '_matchfiles')
1870 1877 (list
1871 1878 ('string', 'r:')
1872 1879 ('string', 'd:relpath')
1873 1880 ('string', 'p:a')))))
1874 1881
1875 1882 Test --patch and --stat with --follow and --follow-first
1876 1883
1877 1884 $ hg up -q 3
1878 1885 $ hg log -G --git --patch b
1879 1886 o changeset: 1:216d4c92cf98
1880 1887 | user: test
1881 1888 ~ date: Thu Jan 01 00:00:00 1970 +0000
1882 1889 summary: copy a b
1883 1890
1884 1891 diff --git a/a b/b
1885 1892 copy from a
1886 1893 copy to b
1887 1894
1888 1895
1889 1896 $ hg log -G --git --stat b
1890 1897 o changeset: 1:216d4c92cf98
1891 1898 | user: test
1892 1899 ~ date: Thu Jan 01 00:00:00 1970 +0000
1893 1900 summary: copy a b
1894 1901
1895 1902 b | 0
1896 1903 1 files changed, 0 insertions(+), 0 deletions(-)
1897 1904
1898 1905
1899 1906 $ hg log -G --git --patch --follow b
1900 1907 o changeset: 1:216d4c92cf98
1901 1908 | user: test
1902 1909 | date: Thu Jan 01 00:00:00 1970 +0000
1903 1910 | summary: copy a b
1904 1911 |
1905 1912 | diff --git a/a b/b
1906 1913 | copy from a
1907 1914 | copy to b
1908 1915 |
1909 1916 o changeset: 0:f8035bb17114
1910 1917 user: test
1911 1918 date: Thu Jan 01 00:00:00 1970 +0000
1912 1919 summary: add a
1913 1920
1914 1921 diff --git a/a b/a
1915 1922 new file mode 100644
1916 1923 --- /dev/null
1917 1924 +++ b/a
1918 1925 @@ -0,0 +1,1 @@
1919 1926 +a
1920 1927
1921 1928
1922 1929 $ hg log -G --git --stat --follow b
1923 1930 o changeset: 1:216d4c92cf98
1924 1931 | user: test
1925 1932 | date: Thu Jan 01 00:00:00 1970 +0000
1926 1933 | summary: copy a b
1927 1934 |
1928 1935 | b | 0
1929 1936 | 1 files changed, 0 insertions(+), 0 deletions(-)
1930 1937 |
1931 1938 o changeset: 0:f8035bb17114
1932 1939 user: test
1933 1940 date: Thu Jan 01 00:00:00 1970 +0000
1934 1941 summary: add a
1935 1942
1936 1943 a | 1 +
1937 1944 1 files changed, 1 insertions(+), 0 deletions(-)
1938 1945
1939 1946
1940 1947 $ hg up -q 6
1941 1948 $ hg log -G --git --patch --follow-first e
1942 1949 @ changeset: 6:fc281d8ff18d
1943 1950 |\ tag: tip
1944 1951 | ~ parent: 5:99b31f1c2782
1945 1952 | parent: 4:17d952250a9d
1946 1953 | user: test
1947 1954 | date: Thu Jan 01 00:00:00 1970 +0000
1948 1955 | summary: merge 5 and 4
1949 1956 |
1950 1957 | diff --git a/e b/e
1951 1958 | --- a/e
1952 1959 | +++ b/e
1953 1960 | @@ -1,1 +1,1 @@
1954 1961 | -ee
1955 1962 | +merge
1956 1963 |
1957 1964 o changeset: 5:99b31f1c2782
1958 1965 | parent: 3:5918b8d165d1
1959 1966 ~ user: test
1960 1967 date: Thu Jan 01 00:00:00 1970 +0000
1961 1968 summary: add another e
1962 1969
1963 1970 diff --git a/e b/e
1964 1971 new file mode 100644
1965 1972 --- /dev/null
1966 1973 +++ b/e
1967 1974 @@ -0,0 +1,1 @@
1968 1975 +ee
1969 1976
1970 1977
1971 1978 Test old-style --rev
1972 1979
1973 1980 $ hg tag 'foo-bar'
1974 1981 $ testlog -r 'foo-bar'
1975 1982 ['foo-bar']
1976 1983 []
1977 1984
1978 1985 Test --follow and forward --rev
1979 1986
1980 1987 $ hg up -q 6
1981 1988 $ echo g > g
1982 1989 $ hg ci -Am 'add g' g
1983 1990 created new head
1984 1991 $ hg up -q 2
1985 1992 $ hg log -G --template "{rev} {desc|firstline}\n"
1986 1993 o 8 add g
1987 1994 |
1988 1995 | o 7 Added tag foo-bar for changeset fc281d8ff18d
1989 1996 |/
1990 1997 o 6 merge 5 and 4
1991 1998 |\
1992 1999 | o 5 add another e
1993 2000 | |
1994 2001 o | 4 mv dir/b e
1995 2002 |/
1996 2003 o 3 mv a b; add d
1997 2004 |
1998 2005 @ 2 mv b dir/b
1999 2006 |
2000 2007 o 1 copy a b
2001 2008 |
2002 2009 o 0 add a
2003 2010
2004 2011 $ hg archive -r 7 archive
2005 2012 $ grep changessincelatesttag archive/.hg_archival.txt
2006 2013 changessincelatesttag: 1
2007 2014 $ rm -r archive
2008 2015
2009 2016 changessincelatesttag with no prior tag
2010 2017 $ hg archive -r 4 archive
2011 2018 $ grep changessincelatesttag archive/.hg_archival.txt
2012 2019 changessincelatesttag: 5
2013 2020
2014 2021 $ hg export 'all()'
2015 2022 # HG changeset patch
2016 2023 # User test
2017 2024 # Date 0 0
2018 2025 # Thu Jan 01 00:00:00 1970 +0000
2019 2026 # Node ID f8035bb17114da16215af3436ec5222428ace8ee
2020 2027 # Parent 0000000000000000000000000000000000000000
2021 2028 add a
2022 2029
2023 2030 diff -r 000000000000 -r f8035bb17114 a
2024 2031 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2025 2032 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2026 2033 @@ -0,0 +1,1 @@
2027 2034 +a
2028 2035 diff -r 000000000000 -r f8035bb17114 aa
2029 2036 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2030 2037 +++ b/aa Thu Jan 01 00:00:00 1970 +0000
2031 2038 @@ -0,0 +1,1 @@
2032 2039 +aa
2033 2040 diff -r 000000000000 -r f8035bb17114 f
2034 2041 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2035 2042 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2036 2043 @@ -0,0 +1,1 @@
2037 2044 +f
2038 2045 # HG changeset patch
2039 2046 # User test
2040 2047 # Date 0 0
2041 2048 # Thu Jan 01 00:00:00 1970 +0000
2042 2049 # Node ID 216d4c92cf98ff2b4641d508b76b529f3d424c92
2043 2050 # Parent f8035bb17114da16215af3436ec5222428ace8ee
2044 2051 copy a b
2045 2052
2046 2053 diff -r f8035bb17114 -r 216d4c92cf98 b
2047 2054 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2048 2055 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2049 2056 @@ -0,0 +1,1 @@
2050 2057 +a
2051 2058 diff -r f8035bb17114 -r 216d4c92cf98 g
2052 2059 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2053 2060 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2054 2061 @@ -0,0 +1,1 @@
2055 2062 +f
2056 2063 # HG changeset patch
2057 2064 # User test
2058 2065 # Date 0 0
2059 2066 # Thu Jan 01 00:00:00 1970 +0000
2060 2067 # Node ID bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2061 2068 # Parent 216d4c92cf98ff2b4641d508b76b529f3d424c92
2062 2069 mv b dir/b
2063 2070
2064 2071 diff -r 216d4c92cf98 -r bb573313a9e8 b
2065 2072 --- a/b Thu Jan 01 00:00:00 1970 +0000
2066 2073 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2067 2074 @@ -1,1 +0,0 @@
2068 2075 -a
2069 2076 diff -r 216d4c92cf98 -r bb573313a9e8 dir/b
2070 2077 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2071 2078 +++ b/dir/b Thu Jan 01 00:00:00 1970 +0000
2072 2079 @@ -0,0 +1,1 @@
2073 2080 +a
2074 2081 diff -r 216d4c92cf98 -r bb573313a9e8 f
2075 2082 --- a/f Thu Jan 01 00:00:00 1970 +0000
2076 2083 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2077 2084 @@ -1,1 +1,2 @@
2078 2085 f
2079 2086 +f
2080 2087 diff -r 216d4c92cf98 -r bb573313a9e8 g
2081 2088 --- a/g Thu Jan 01 00:00:00 1970 +0000
2082 2089 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2083 2090 @@ -1,1 +1,2 @@
2084 2091 f
2085 2092 +g
2086 2093 # HG changeset patch
2087 2094 # User test
2088 2095 # Date 0 0
2089 2096 # Thu Jan 01 00:00:00 1970 +0000
2090 2097 # Node ID 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2091 2098 # Parent bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2092 2099 mv a b; add d
2093 2100
2094 2101 diff -r bb573313a9e8 -r 5918b8d165d1 a
2095 2102 --- a/a Thu Jan 01 00:00:00 1970 +0000
2096 2103 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2097 2104 @@ -1,1 +0,0 @@
2098 2105 -a
2099 2106 diff -r bb573313a9e8 -r 5918b8d165d1 b
2100 2107 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2101 2108 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2102 2109 @@ -0,0 +1,1 @@
2103 2110 +a
2104 2111 diff -r bb573313a9e8 -r 5918b8d165d1 d
2105 2112 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2106 2113 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2107 2114 @@ -0,0 +1,1 @@
2108 2115 +a
2109 2116 diff -r bb573313a9e8 -r 5918b8d165d1 g
2110 2117 --- a/g Thu Jan 01 00:00:00 1970 +0000
2111 2118 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2112 2119 @@ -1,2 +1,2 @@
2113 2120 f
2114 2121 -g
2115 2122 +f
2116 2123 # HG changeset patch
2117 2124 # User test
2118 2125 # Date 0 0
2119 2126 # Thu Jan 01 00:00:00 1970 +0000
2120 2127 # Node ID 17d952250a9d03cc3dc77b199ab60e959b9b0260
2121 2128 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2122 2129 mv dir/b e
2123 2130
2124 2131 diff -r 5918b8d165d1 -r 17d952250a9d dir/b
2125 2132 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2126 2133 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2127 2134 @@ -1,1 +0,0 @@
2128 2135 -a
2129 2136 diff -r 5918b8d165d1 -r 17d952250a9d e
2130 2137 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2131 2138 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2132 2139 @@ -0,0 +1,1 @@
2133 2140 +a
2134 2141 # HG changeset patch
2135 2142 # User test
2136 2143 # Date 0 0
2137 2144 # Thu Jan 01 00:00:00 1970 +0000
2138 2145 # Node ID 99b31f1c2782e2deb1723cef08930f70fc84b37b
2139 2146 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2140 2147 add another e
2141 2148
2142 2149 diff -r 5918b8d165d1 -r 99b31f1c2782 e
2143 2150 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2144 2151 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2145 2152 @@ -0,0 +1,1 @@
2146 2153 +ee
2147 2154 # HG changeset patch
2148 2155 # User test
2149 2156 # Date 0 0
2150 2157 # Thu Jan 01 00:00:00 1970 +0000
2151 2158 # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
2152 2159 # Parent 99b31f1c2782e2deb1723cef08930f70fc84b37b
2153 2160 # Parent 17d952250a9d03cc3dc77b199ab60e959b9b0260
2154 2161 merge 5 and 4
2155 2162
2156 2163 diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
2157 2164 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2158 2165 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2159 2166 @@ -1,1 +0,0 @@
2160 2167 -a
2161 2168 diff -r 99b31f1c2782 -r fc281d8ff18d e
2162 2169 --- a/e Thu Jan 01 00:00:00 1970 +0000
2163 2170 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2164 2171 @@ -1,1 +1,1 @@
2165 2172 -ee
2166 2173 +merge
2167 2174 # HG changeset patch
2168 2175 # User test
2169 2176 # Date 0 0
2170 2177 # Thu Jan 01 00:00:00 1970 +0000
2171 2178 # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
2172 2179 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2173 2180 Added tag foo-bar for changeset fc281d8ff18d
2174 2181
2175 2182 diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
2176 2183 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2177 2184 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2178 2185 @@ -0,0 +1,1 @@
2179 2186 +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
2180 2187 # HG changeset patch
2181 2188 # User test
2182 2189 # Date 0 0
2183 2190 # Thu Jan 01 00:00:00 1970 +0000
2184 2191 # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
2185 2192 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2186 2193 add g
2187 2194
2188 2195 diff -r fc281d8ff18d -r 24c2e826ddeb g
2189 2196 --- a/g Thu Jan 01 00:00:00 1970 +0000
2190 2197 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2191 2198 @@ -1,2 +1,1 @@
2192 2199 -f
2193 2200 -f
2194 2201 +g
2195 2202 $ testlog --follow -r6 -r8 -r5 -r7 -r4
2196 2203 ['6', '8', '5', '7', '4']
2197 2204 (group
2198 2205 (func
2199 2206 ('symbol', 'descendants')
2200 2207 (func
2201 2208 ('symbol', 'rev')
2202 2209 ('symbol', '6'))))
2203 2210
2204 2211 Test --follow-first and forward --rev
2205 2212
2206 2213 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
2207 2214 ['6', '8', '5', '7', '4']
2208 2215 (group
2209 2216 (func
2210 2217 ('symbol', '_firstdescendants')
2211 2218 (func
2212 2219 ('symbol', 'rev')
2213 2220 ('symbol', '6'))))
2214 2221 --- log.nodes * (glob)
2215 2222 +++ glog.nodes * (glob)
2216 2223 @@ -1,3 +1,3 @@
2217 2224 -nodetag 6
2218 2225 nodetag 8
2219 2226 nodetag 7
2220 2227 +nodetag 6
2221 2228
2222 2229 Test --follow and backward --rev
2223 2230
2224 2231 $ testlog --follow -r6 -r5 -r7 -r8 -r4
2225 2232 ['6', '5', '7', '8', '4']
2226 2233 (group
2227 2234 (func
2228 2235 ('symbol', 'ancestors')
2229 2236 (func
2230 2237 ('symbol', 'rev')
2231 2238 ('symbol', '6'))))
2232 2239
2233 2240 Test --follow-first and backward --rev
2234 2241
2235 2242 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2236 2243 ['6', '5', '7', '8', '4']
2237 2244 (group
2238 2245 (func
2239 2246 ('symbol', '_firstancestors')
2240 2247 (func
2241 2248 ('symbol', 'rev')
2242 2249 ('symbol', '6'))))
2243 2250
2244 2251 Test --follow with --rev of graphlog extension
2245 2252
2246 2253 $ hg --config extensions.graphlog= glog -qfr1
2247 2254 o 1:216d4c92cf98
2248 2255 |
2249 2256 o 0:f8035bb17114
2250 2257
2251 2258
2252 2259 Test subdir
2253 2260
2254 2261 $ hg up -q 3
2255 2262 $ cd dir
2256 2263 $ testlog .
2257 2264 []
2258 2265 (group
2259 2266 (func
2260 2267 ('symbol', '_matchfiles')
2261 2268 (list
2262 2269 ('string', 'r:')
2263 2270 ('string', 'd:relpath')
2264 2271 ('string', 'p:.'))))
2265 2272 $ testlog ../b
2266 2273 []
2267 2274 (group
2268 2275 (group
2269 2276 (func
2270 2277 ('symbol', 'filelog')
2271 2278 ('string', '../b'))))
2272 2279 $ testlog -f ../b
2273 2280 []
2274 2281 (group
2275 2282 (group
2276 2283 (func
2277 2284 ('symbol', 'follow')
2278 2285 ('string', 'b'))))
2279 2286 $ cd ..
2280 2287
2281 2288 Test --hidden
2282 2289 (enable obsolete)
2283 2290
2284 2291 $ cat >> $HGRCPATH << EOF
2285 2292 > [experimental]
2286 2293 > evolution=createmarkers
2287 2294 > EOF
2288 2295
2289 2296 $ hg debugobsolete `hg id --debug -i -r 8`
2290 2297 $ testlog
2291 2298 []
2292 2299 []
2293 2300 $ testlog --hidden
2294 2301 []
2295 2302 []
2296 2303 $ hg log -G --template '{rev} {desc}\n'
2297 2304 o 7 Added tag foo-bar for changeset fc281d8ff18d
2298 2305 |
2299 2306 o 6 merge 5 and 4
2300 2307 |\
2301 2308 | o 5 add another e
2302 2309 | |
2303 2310 o | 4 mv dir/b e
2304 2311 |/
2305 2312 @ 3 mv a b; add d
2306 2313 |
2307 2314 o 2 mv b dir/b
2308 2315 |
2309 2316 o 1 copy a b
2310 2317 |
2311 2318 o 0 add a
2312 2319
2313 2320
2314 2321 A template without trailing newline should do something sane
2315 2322
2316 2323 $ hg log -G -r ::2 --template '{rev} {desc}'
2317 2324 o 2 mv b dir/b
2318 2325 |
2319 2326 o 1 copy a b
2320 2327 |
2321 2328 o 0 add a
2322 2329
2323 2330
2324 2331 Extra newlines must be preserved
2325 2332
2326 2333 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2327 2334 o
2328 2335 | 2 mv b dir/b
2329 2336 |
2330 2337 o
2331 2338 | 1 copy a b
2332 2339 |
2333 2340 o
2334 2341 0 add a
2335 2342
2336 2343
2337 2344 The almost-empty template should do something sane too ...
2338 2345
2339 2346 $ hg log -G -r ::2 --template '\n'
2340 2347 o
2341 2348 |
2342 2349 o
2343 2350 |
2344 2351 o
2345 2352
2346 2353
2347 2354 issue3772
2348 2355
2349 2356 $ hg log -G -r :null
2350 2357 o changeset: 0:f8035bb17114
2351 2358 | user: test
2352 2359 | date: Thu Jan 01 00:00:00 1970 +0000
2353 2360 | summary: add a
2354 2361 |
2355 2362 o changeset: -1:000000000000
2356 2363 user:
2357 2364 date: Thu Jan 01 00:00:00 1970 +0000
2358 2365
2359 2366 $ hg log -G -r null:null
2360 2367 o changeset: -1:000000000000
2361 2368 user:
2362 2369 date: Thu Jan 01 00:00:00 1970 +0000
2363 2370
2364 2371
2365 2372 should not draw line down to null due to the magic of fullreposet
2366 2373
2367 2374 $ hg log -G -r 'all()' | tail -6
2368 2375 |
2369 2376 o changeset: 0:f8035bb17114
2370 2377 user: test
2371 2378 date: Thu Jan 01 00:00:00 1970 +0000
2372 2379 summary: add a
2373 2380
2374 2381
2375 2382 $ hg log -G -r 'branch(default)' | tail -6
2376 2383 |
2377 2384 o changeset: 0:f8035bb17114
2378 2385 user: test
2379 2386 date: Thu Jan 01 00:00:00 1970 +0000
2380 2387 summary: add a
2381 2388
2382 2389
2383 2390 working-directory revision
2384 2391
2385 2392 $ hg log -G -qr '. + wdir()'
2386 2393 o 2147483647:ffffffffffff
2387 2394 |
2388 2395 @ 3:5918b8d165d1
2389 2396 |
2390 2397 ~
2391 2398
2392 2399 node template with changeset_printer:
2393 2400
2394 2401 $ hg log -Gqr 5:7 --config ui.graphnodetemplate='{rev}'
2395 2402 7 7:02dbb8e276b8
2396 2403 |
2397 2404 6 6:fc281d8ff18d
2398 2405 |\
2399 2406 | ~
2400 2407 5 5:99b31f1c2782
2401 2408 |
2402 2409 ~
2403 2410
2404 2411 node template with changeset_templater (shared cache variable):
2405 2412
2406 2413 $ hg log -Gr 5:7 -T '{latesttag % "{rev} {tag}+{distance}"}\n' \
2407 2414 > --config ui.graphnodetemplate='{ifeq(latesttagdistance, 0, "#", graphnode)}'
2408 2415 o 7 foo-bar+1
2409 2416 |
2410 2417 # 6 foo-bar+0
2411 2418 |\
2412 2419 | ~
2413 2420 o 5 null+5
2414 2421 |
2415 2422 ~
2416 2423
2417 2424 label() should just work in node template:
2418 2425
2419 2426 $ hg log -Gqr 7 --config extensions.color= --color=debug \
2420 2427 > --config ui.graphnodetemplate='{label("branch.{branch}", rev)}'
2421 2428 [branch.default|7] [log.node|7:02dbb8e276b8]
2422 2429 |
2423 2430 ~
2424 2431
2425 2432 $ cd ..
2426 2433
2427 2434 change graph edge styling
2428 2435
2429 2436 $ cd repo
2430 2437 $ cat << EOF >> $HGRCPATH
2431 2438 > [experimental]
2432 2439 > graphstyle.parent = |
2433 2440 > graphstyle.grandparent = :
2434 2441 > graphstyle.missing =
2435 2442 > EOF
2436 2443 $ hg log -G -r 'file("a")' -m
2437 2444 @ changeset: 36:08a19a744424
2438 2445 : branch: branch
2439 2446 : tag: tip
2440 2447 : parent: 35:9159c3644c5e
2441 2448 : parent: 35:9159c3644c5e
2442 2449 : user: test
2443 2450 : date: Thu Jan 01 00:00:36 1970 +0000
2444 2451 : summary: (36) buggy merge: identical parents
2445 2452 :
2446 2453 o changeset: 32:d06dffa21a31
2447 2454 |\ parent: 27:886ed638191b
2448 2455 | : parent: 31:621d83e11f67
2449 2456 | : user: test
2450 2457 | : date: Thu Jan 01 00:00:32 1970 +0000
2451 2458 | : summary: (32) expand
2452 2459 | :
2453 2460 o : changeset: 31:621d83e11f67
2454 2461 |\: parent: 21:d42a756af44d
2455 2462 | : parent: 30:6e11cd4b648f
2456 2463 | : user: test
2457 2464 | : date: Thu Jan 01 00:00:31 1970 +0000
2458 2465 | : summary: (31) expand
2459 2466 | :
2460 2467 o : changeset: 30:6e11cd4b648f
2461 2468 |\ \ parent: 28:44ecd0b9ae99
2462 2469 | ~ : parent: 29:cd9bb2be7593
2463 2470 | : user: test
2464 2471 | : date: Thu Jan 01 00:00:30 1970 +0000
2465 2472 | : summary: (30) expand
2466 2473 | /
2467 2474 o : changeset: 28:44ecd0b9ae99
2468 2475 |\ \ parent: 1:6db2ef61d156
2469 2476 | ~ : parent: 26:7f25b6c2f0b9
2470 2477 | : user: test
2471 2478 | : date: Thu Jan 01 00:00:28 1970 +0000
2472 2479 | : summary: (28) merge zero known
2473 2480 | /
2474 2481 o : changeset: 26:7f25b6c2f0b9
2475 2482 |\ \ parent: 18:1aa84d96232a
2476 2483 | | : parent: 25:91da8ed57247
2477 2484 | | : user: test
2478 2485 | | : date: Thu Jan 01 00:00:26 1970 +0000
2479 2486 | | : summary: (26) merge one known; far right
2480 2487 | | :
2481 2488 | o : changeset: 25:91da8ed57247
2482 2489 | |\: parent: 21:d42a756af44d
2483 2490 | | : parent: 24:a9c19a3d96b7
2484 2491 | | : user: test
2485 2492 | | : date: Thu Jan 01 00:00:25 1970 +0000
2486 2493 | | : summary: (25) merge one known; far left
2487 2494 | | :
2488 2495 | o : changeset: 24:a9c19a3d96b7
2489 2496 | |\ \ parent: 0:e6eb3150255d
2490 2497 | | ~ : parent: 23:a01cddf0766d
2491 2498 | | : user: test
2492 2499 | | : date: Thu Jan 01 00:00:24 1970 +0000
2493 2500 | | : summary: (24) merge one known; immediate right
2494 2501 | | /
2495 2502 | o : changeset: 23:a01cddf0766d
2496 2503 | |\ \ parent: 1:6db2ef61d156
2497 2504 | | ~ : parent: 22:e0d9cccacb5d
2498 2505 | | : user: test
2499 2506 | | : date: Thu Jan 01 00:00:23 1970 +0000
2500 2507 | | : summary: (23) merge one known; immediate left
2501 2508 | | /
2502 2509 | o : changeset: 22:e0d9cccacb5d
2503 2510 |/:/ parent: 18:1aa84d96232a
2504 2511 | : parent: 21:d42a756af44d
2505 2512 | : user: test
2506 2513 | : date: Thu Jan 01 00:00:22 1970 +0000
2507 2514 | : summary: (22) merge two known; one far left, one far right
2508 2515 | :
2509 2516 | o changeset: 21:d42a756af44d
2510 2517 | |\ parent: 19:31ddc2c1573b
2511 2518 | | | parent: 20:d30ed6450e32
2512 2519 | | | user: test
2513 2520 | | | date: Thu Jan 01 00:00:21 1970 +0000
2514 2521 | | | summary: (21) expand
2515 2522 | | |
2516 2523 +---o changeset: 20:d30ed6450e32
2517 2524 | | | parent: 0:e6eb3150255d
2518 2525 | | ~ parent: 18:1aa84d96232a
2519 2526 | | user: test
2520 2527 | | date: Thu Jan 01 00:00:20 1970 +0000
2521 2528 | | summary: (20) merge two known; two far right
2522 2529 | |
2523 2530 | o changeset: 19:31ddc2c1573b
2524 2531 | |\ parent: 15:1dda3f72782d
2525 2532 | | | parent: 17:44765d7c06e0
2526 2533 | | | user: test
2527 2534 | | | date: Thu Jan 01 00:00:19 1970 +0000
2528 2535 | | | summary: (19) expand
2529 2536 | | |
2530 2537 o | | changeset: 18:1aa84d96232a
2531 2538 |\| | parent: 1:6db2ef61d156
2532 2539 ~ | | parent: 15:1dda3f72782d
2533 2540 | | user: test
2534 2541 | | date: Thu Jan 01 00:00:18 1970 +0000
2535 2542 | | summary: (18) merge two known; two far left
2536 2543 / /
2537 2544 | o changeset: 17:44765d7c06e0
2538 2545 | |\ parent: 12:86b91144a6e9
2539 2546 | | | parent: 16:3677d192927d
2540 2547 | | | user: test
2541 2548 | | | date: Thu Jan 01 00:00:17 1970 +0000
2542 2549 | | | summary: (17) expand
2543 2550 | | |
2544 2551 | | o changeset: 16:3677d192927d
2545 2552 | | |\ parent: 0:e6eb3150255d
2546 2553 | | ~ ~ parent: 1:6db2ef61d156
2547 2554 | | user: test
2548 2555 | | date: Thu Jan 01 00:00:16 1970 +0000
2549 2556 | | summary: (16) merge two known; one immediate right, one near right
2550 2557 | |
2551 2558 o | changeset: 15:1dda3f72782d
2552 2559 |\ \ parent: 13:22d8966a97e3
2553 2560 | | | parent: 14:8eac370358ef
2554 2561 | | | user: test
2555 2562 | | | date: Thu Jan 01 00:00:15 1970 +0000
2556 2563 | | | summary: (15) expand
2557 2564 | | |
2558 2565 | o | changeset: 14:8eac370358ef
2559 2566 | |\| parent: 0:e6eb3150255d
2560 2567 | ~ | parent: 12:86b91144a6e9
2561 2568 | | user: test
2562 2569 | | date: Thu Jan 01 00:00:14 1970 +0000
2563 2570 | | summary: (14) merge two known; one immediate right, one far right
2564 2571 | /
2565 2572 o | changeset: 13:22d8966a97e3
2566 2573 |\ \ parent: 9:7010c0af0a35
2567 2574 | | | parent: 11:832d76e6bdf2
2568 2575 | | | user: test
2569 2576 | | | date: Thu Jan 01 00:00:13 1970 +0000
2570 2577 | | | summary: (13) expand
2571 2578 | | |
2572 2579 +---o changeset: 12:86b91144a6e9
2573 2580 | | | parent: 1:6db2ef61d156
2574 2581 | | ~ parent: 9:7010c0af0a35
2575 2582 | | user: test
2576 2583 | | date: Thu Jan 01 00:00:12 1970 +0000
2577 2584 | | summary: (12) merge two known; one immediate right, one far left
2578 2585 | |
2579 2586 | o changeset: 11:832d76e6bdf2
2580 2587 | |\ parent: 6:b105a072e251
2581 2588 | | | parent: 10:74c64d036d72
2582 2589 | | | user: test
2583 2590 | | | date: Thu Jan 01 00:00:11 1970 +0000
2584 2591 | | | summary: (11) expand
2585 2592 | | |
2586 2593 | | o changeset: 10:74c64d036d72
2587 2594 | |/| parent: 0:e6eb3150255d
2588 2595 | | ~ parent: 6:b105a072e251
2589 2596 | | user: test
2590 2597 | | date: Thu Jan 01 00:00:10 1970 +0000
2591 2598 | | summary: (10) merge two known; one immediate left, one near right
2592 2599 | |
2593 2600 o | changeset: 9:7010c0af0a35
2594 2601 |\ \ parent: 7:b632bb1b1224
2595 2602 | | | parent: 8:7a0b11f71937
2596 2603 | | | user: test
2597 2604 | | | date: Thu Jan 01 00:00:09 1970 +0000
2598 2605 | | | summary: (9) expand
2599 2606 | | |
2600 2607 | o | changeset: 8:7a0b11f71937
2601 2608 |/| | parent: 0:e6eb3150255d
2602 2609 | ~ | parent: 7:b632bb1b1224
2603 2610 | | user: test
2604 2611 | | date: Thu Jan 01 00:00:08 1970 +0000
2605 2612 | | summary: (8) merge two known; one immediate left, one far right
2606 2613 | /
2607 2614 o | changeset: 7:b632bb1b1224
2608 2615 |\ \ parent: 2:3d9a33b8d1e1
2609 2616 | ~ | parent: 5:4409d547b708
2610 2617 | | user: test
2611 2618 | | date: Thu Jan 01 00:00:07 1970 +0000
2612 2619 | | summary: (7) expand
2613 2620 | /
2614 2621 | o changeset: 6:b105a072e251
2615 2622 |/| parent: 2:3d9a33b8d1e1
2616 2623 | ~ parent: 5:4409d547b708
2617 2624 | user: test
2618 2625 | date: Thu Jan 01 00:00:06 1970 +0000
2619 2626 | summary: (6) merge two known; one immediate left, one far left
2620 2627 |
2621 2628 o changeset: 5:4409d547b708
2622 2629 |\ parent: 3:27eef8ed80b4
2623 2630 | ~ parent: 4:26a8bac39d9f
2624 2631 | user: test
2625 2632 | date: Thu Jan 01 00:00:05 1970 +0000
2626 2633 | summary: (5) expand
2627 2634 |
2628 2635 o changeset: 4:26a8bac39d9f
2629 2636 |\ parent: 1:6db2ef61d156
2630 2637 ~ ~ parent: 3:27eef8ed80b4
2631 2638 user: test
2632 2639 date: Thu Jan 01 00:00:04 1970 +0000
2633 2640 summary: (4) merge two known; one immediate left, one immediate right
2634 2641
2635 2642
2636 2643 Setting HGPLAIN ignores graphmod styling:
2637 2644
2638 2645 $ HGPLAIN=1 hg log -G -r 'file("a")' -m
2639 2646 @ changeset: 36:08a19a744424
2640 2647 | branch: branch
2641 2648 | tag: tip
2642 2649 | parent: 35:9159c3644c5e
2643 2650 | parent: 35:9159c3644c5e
2644 2651 | user: test
2645 2652 | date: Thu Jan 01 00:00:36 1970 +0000
2646 2653 | summary: (36) buggy merge: identical parents
2647 2654 |
2648 2655 o changeset: 32:d06dffa21a31
2649 2656 |\ parent: 27:886ed638191b
2650 2657 | | parent: 31:621d83e11f67
2651 2658 | | user: test
2652 2659 | | date: Thu Jan 01 00:00:32 1970 +0000
2653 2660 | | summary: (32) expand
2654 2661 | |
2655 2662 o | changeset: 31:621d83e11f67
2656 2663 |\| parent: 21:d42a756af44d
2657 2664 | | parent: 30:6e11cd4b648f
2658 2665 | | user: test
2659 2666 | | date: Thu Jan 01 00:00:31 1970 +0000
2660 2667 | | summary: (31) expand
2661 2668 | |
2662 2669 o | changeset: 30:6e11cd4b648f
2663 2670 |\ \ parent: 28:44ecd0b9ae99
2664 2671 | | | parent: 29:cd9bb2be7593
2665 2672 | | | user: test
2666 2673 | | | date: Thu Jan 01 00:00:30 1970 +0000
2667 2674 | | | summary: (30) expand
2668 2675 | | |
2669 2676 o | | changeset: 28:44ecd0b9ae99
2670 2677 |\ \ \ parent: 1:6db2ef61d156
2671 2678 | | | | parent: 26:7f25b6c2f0b9
2672 2679 | | | | user: test
2673 2680 | | | | date: Thu Jan 01 00:00:28 1970 +0000
2674 2681 | | | | summary: (28) merge zero known
2675 2682 | | | |
2676 2683 o | | | changeset: 26:7f25b6c2f0b9
2677 2684 |\ \ \ \ parent: 18:1aa84d96232a
2678 2685 | | | | | parent: 25:91da8ed57247
2679 2686 | | | | | user: test
2680 2687 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
2681 2688 | | | | | summary: (26) merge one known; far right
2682 2689 | | | | |
2683 2690 | o-----+ changeset: 25:91da8ed57247
2684 2691 | | | | | parent: 21:d42a756af44d
2685 2692 | | | | | parent: 24:a9c19a3d96b7
2686 2693 | | | | | user: test
2687 2694 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
2688 2695 | | | | | summary: (25) merge one known; far left
2689 2696 | | | | |
2690 2697 | o | | | changeset: 24:a9c19a3d96b7
2691 2698 | |\ \ \ \ parent: 0:e6eb3150255d
2692 2699 | | | | | | parent: 23:a01cddf0766d
2693 2700 | | | | | | user: test
2694 2701 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
2695 2702 | | | | | | summary: (24) merge one known; immediate right
2696 2703 | | | | | |
2697 2704 | o---+ | | changeset: 23:a01cddf0766d
2698 2705 | | | | | | parent: 1:6db2ef61d156
2699 2706 | | | | | | parent: 22:e0d9cccacb5d
2700 2707 | | | | | | user: test
2701 2708 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
2702 2709 | | | | | | summary: (23) merge one known; immediate left
2703 2710 | | | | | |
2704 2711 | o-------+ changeset: 22:e0d9cccacb5d
2705 2712 | | | | | | parent: 18:1aa84d96232a
2706 2713 |/ / / / / parent: 21:d42a756af44d
2707 2714 | | | | | user: test
2708 2715 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
2709 2716 | | | | | summary: (22) merge two known; one far left, one far right
2710 2717 | | | | |
2711 2718 | | | | o changeset: 21:d42a756af44d
2712 2719 | | | | |\ parent: 19:31ddc2c1573b
2713 2720 | | | | | | parent: 20:d30ed6450e32
2714 2721 | | | | | | user: test
2715 2722 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
2716 2723 | | | | | | summary: (21) expand
2717 2724 | | | | | |
2718 2725 +-+-------o changeset: 20:d30ed6450e32
2719 2726 | | | | | parent: 0:e6eb3150255d
2720 2727 | | | | | parent: 18:1aa84d96232a
2721 2728 | | | | | user: test
2722 2729 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
2723 2730 | | | | | summary: (20) merge two known; two far right
2724 2731 | | | | |
2725 2732 | | | | o changeset: 19:31ddc2c1573b
2726 2733 | | | | |\ parent: 15:1dda3f72782d
2727 2734 | | | | | | parent: 17:44765d7c06e0
2728 2735 | | | | | | user: test
2729 2736 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
2730 2737 | | | | | | summary: (19) expand
2731 2738 | | | | | |
2732 2739 o---+---+ | changeset: 18:1aa84d96232a
2733 2740 | | | | | parent: 1:6db2ef61d156
2734 2741 / / / / / parent: 15:1dda3f72782d
2735 2742 | | | | | user: test
2736 2743 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
2737 2744 | | | | | summary: (18) merge two known; two far left
2738 2745 | | | | |
2739 2746 | | | | o changeset: 17:44765d7c06e0
2740 2747 | | | | |\ parent: 12:86b91144a6e9
2741 2748 | | | | | | parent: 16:3677d192927d
2742 2749 | | | | | | user: test
2743 2750 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
2744 2751 | | | | | | summary: (17) expand
2745 2752 | | | | | |
2746 2753 +-+-------o changeset: 16:3677d192927d
2747 2754 | | | | | parent: 0:e6eb3150255d
2748 2755 | | | | | parent: 1:6db2ef61d156
2749 2756 | | | | | user: test
2750 2757 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
2751 2758 | | | | | summary: (16) merge two known; one immediate right, one near right
2752 2759 | | | | |
2753 2760 | | | o | changeset: 15:1dda3f72782d
2754 2761 | | | |\ \ parent: 13:22d8966a97e3
2755 2762 | | | | | | parent: 14:8eac370358ef
2756 2763 | | | | | | user: test
2757 2764 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
2758 2765 | | | | | | summary: (15) expand
2759 2766 | | | | | |
2760 2767 +-------o | changeset: 14:8eac370358ef
2761 2768 | | | | |/ parent: 0:e6eb3150255d
2762 2769 | | | | | parent: 12:86b91144a6e9
2763 2770 | | | | | user: test
2764 2771 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
2765 2772 | | | | | summary: (14) merge two known; one immediate right, one far right
2766 2773 | | | | |
2767 2774 | | | o | changeset: 13:22d8966a97e3
2768 2775 | | | |\ \ parent: 9:7010c0af0a35
2769 2776 | | | | | | parent: 11:832d76e6bdf2
2770 2777 | | | | | | user: test
2771 2778 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
2772 2779 | | | | | | summary: (13) expand
2773 2780 | | | | | |
2774 2781 | +---+---o changeset: 12:86b91144a6e9
2775 2782 | | | | | parent: 1:6db2ef61d156
2776 2783 | | | | | parent: 9:7010c0af0a35
2777 2784 | | | | | user: test
2778 2785 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
2779 2786 | | | | | summary: (12) merge two known; one immediate right, one far left
2780 2787 | | | | |
2781 2788 | | | | o changeset: 11:832d76e6bdf2
2782 2789 | | | | |\ parent: 6:b105a072e251
2783 2790 | | | | | | parent: 10:74c64d036d72
2784 2791 | | | | | | user: test
2785 2792 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
2786 2793 | | | | | | summary: (11) expand
2787 2794 | | | | | |
2788 2795 +---------o changeset: 10:74c64d036d72
2789 2796 | | | | |/ parent: 0:e6eb3150255d
2790 2797 | | | | | parent: 6:b105a072e251
2791 2798 | | | | | user: test
2792 2799 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
2793 2800 | | | | | summary: (10) merge two known; one immediate left, one near right
2794 2801 | | | | |
2795 2802 | | | o | changeset: 9:7010c0af0a35
2796 2803 | | | |\ \ parent: 7:b632bb1b1224
2797 2804 | | | | | | parent: 8:7a0b11f71937
2798 2805 | | | | | | user: test
2799 2806 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
2800 2807 | | | | | | summary: (9) expand
2801 2808 | | | | | |
2802 2809 +-------o | changeset: 8:7a0b11f71937
2803 2810 | | | |/ / parent: 0:e6eb3150255d
2804 2811 | | | | | parent: 7:b632bb1b1224
2805 2812 | | | | | user: test
2806 2813 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
2807 2814 | | | | | summary: (8) merge two known; one immediate left, one far right
2808 2815 | | | | |
2809 2816 | | | o | changeset: 7:b632bb1b1224
2810 2817 | | | |\ \ parent: 2:3d9a33b8d1e1
2811 2818 | | | | | | parent: 5:4409d547b708
2812 2819 | | | | | | user: test
2813 2820 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
2814 2821 | | | | | | summary: (7) expand
2815 2822 | | | | | |
2816 2823 | | | +---o changeset: 6:b105a072e251
2817 2824 | | | | |/ parent: 2:3d9a33b8d1e1
2818 2825 | | | | | parent: 5:4409d547b708
2819 2826 | | | | | user: test
2820 2827 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
2821 2828 | | | | | summary: (6) merge two known; one immediate left, one far left
2822 2829 | | | | |
2823 2830 | | | o | changeset: 5:4409d547b708
2824 2831 | | | |\ \ parent: 3:27eef8ed80b4
2825 2832 | | | | | | parent: 4:26a8bac39d9f
2826 2833 | | | | | | user: test
2827 2834 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
2828 2835 | | | | | | summary: (5) expand
2829 2836 | | | | | |
2830 2837 | +---o | | changeset: 4:26a8bac39d9f
2831 2838 | | | |/ / parent: 1:6db2ef61d156
2832 2839 | | | | | parent: 3:27eef8ed80b4
2833 2840 | | | | | user: test
2834 2841 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
2835 2842 | | | | | summary: (4) merge two known; one immediate left, one immediate right
2836 2843 | | | | |
2837 2844
2838 2845 .. unless HGPLAINEXCEPT=graph is set:
2839 2846
2840 2847 $ HGPLAIN=1 HGPLAINEXCEPT=graph hg log -G -r 'file("a")' -m
2841 2848 @ changeset: 36:08a19a744424
2842 2849 : branch: branch
2843 2850 : tag: tip
2844 2851 : parent: 35:9159c3644c5e
2845 2852 : parent: 35:9159c3644c5e
2846 2853 : user: test
2847 2854 : date: Thu Jan 01 00:00:36 1970 +0000
2848 2855 : summary: (36) buggy merge: identical parents
2849 2856 :
2850 2857 o changeset: 32:d06dffa21a31
2851 2858 |\ parent: 27:886ed638191b
2852 2859 | : parent: 31:621d83e11f67
2853 2860 | : user: test
2854 2861 | : date: Thu Jan 01 00:00:32 1970 +0000
2855 2862 | : summary: (32) expand
2856 2863 | :
2857 2864 o : changeset: 31:621d83e11f67
2858 2865 |\: parent: 21:d42a756af44d
2859 2866 | : parent: 30:6e11cd4b648f
2860 2867 | : user: test
2861 2868 | : date: Thu Jan 01 00:00:31 1970 +0000
2862 2869 | : summary: (31) expand
2863 2870 | :
2864 2871 o : changeset: 30:6e11cd4b648f
2865 2872 |\ \ parent: 28:44ecd0b9ae99
2866 2873 | ~ : parent: 29:cd9bb2be7593
2867 2874 | : user: test
2868 2875 | : date: Thu Jan 01 00:00:30 1970 +0000
2869 2876 | : summary: (30) expand
2870 2877 | /
2871 2878 o : changeset: 28:44ecd0b9ae99
2872 2879 |\ \ parent: 1:6db2ef61d156
2873 2880 | ~ : parent: 26:7f25b6c2f0b9
2874 2881 | : user: test
2875 2882 | : date: Thu Jan 01 00:00:28 1970 +0000
2876 2883 | : summary: (28) merge zero known
2877 2884 | /
2878 2885 o : changeset: 26:7f25b6c2f0b9
2879 2886 |\ \ parent: 18:1aa84d96232a
2880 2887 | | : parent: 25:91da8ed57247
2881 2888 | | : user: test
2882 2889 | | : date: Thu Jan 01 00:00:26 1970 +0000
2883 2890 | | : summary: (26) merge one known; far right
2884 2891 | | :
2885 2892 | o : changeset: 25:91da8ed57247
2886 2893 | |\: parent: 21:d42a756af44d
2887 2894 | | : parent: 24:a9c19a3d96b7
2888 2895 | | : user: test
2889 2896 | | : date: Thu Jan 01 00:00:25 1970 +0000
2890 2897 | | : summary: (25) merge one known; far left
2891 2898 | | :
2892 2899 | o : changeset: 24:a9c19a3d96b7
2893 2900 | |\ \ parent: 0:e6eb3150255d
2894 2901 | | ~ : parent: 23:a01cddf0766d
2895 2902 | | : user: test
2896 2903 | | : date: Thu Jan 01 00:00:24 1970 +0000
2897 2904 | | : summary: (24) merge one known; immediate right
2898 2905 | | /
2899 2906 | o : changeset: 23:a01cddf0766d
2900 2907 | |\ \ parent: 1:6db2ef61d156
2901 2908 | | ~ : parent: 22:e0d9cccacb5d
2902 2909 | | : user: test
2903 2910 | | : date: Thu Jan 01 00:00:23 1970 +0000
2904 2911 | | : summary: (23) merge one known; immediate left
2905 2912 | | /
2906 2913 | o : changeset: 22:e0d9cccacb5d
2907 2914 |/:/ parent: 18:1aa84d96232a
2908 2915 | : parent: 21:d42a756af44d
2909 2916 | : user: test
2910 2917 | : date: Thu Jan 01 00:00:22 1970 +0000
2911 2918 | : summary: (22) merge two known; one far left, one far right
2912 2919 | :
2913 2920 | o changeset: 21:d42a756af44d
2914 2921 | |\ parent: 19:31ddc2c1573b
2915 2922 | | | parent: 20:d30ed6450e32
2916 2923 | | | user: test
2917 2924 | | | date: Thu Jan 01 00:00:21 1970 +0000
2918 2925 | | | summary: (21) expand
2919 2926 | | |
2920 2927 +---o changeset: 20:d30ed6450e32
2921 2928 | | | parent: 0:e6eb3150255d
2922 2929 | | ~ parent: 18:1aa84d96232a
2923 2930 | | user: test
2924 2931 | | date: Thu Jan 01 00:00:20 1970 +0000
2925 2932 | | summary: (20) merge two known; two far right
2926 2933 | |
2927 2934 | o changeset: 19:31ddc2c1573b
2928 2935 | |\ parent: 15:1dda3f72782d
2929 2936 | | | parent: 17:44765d7c06e0
2930 2937 | | | user: test
2931 2938 | | | date: Thu Jan 01 00:00:19 1970 +0000
2932 2939 | | | summary: (19) expand
2933 2940 | | |
2934 2941 o | | changeset: 18:1aa84d96232a
2935 2942 |\| | parent: 1:6db2ef61d156
2936 2943 ~ | | parent: 15:1dda3f72782d
2937 2944 | | user: test
2938 2945 | | date: Thu Jan 01 00:00:18 1970 +0000
2939 2946 | | summary: (18) merge two known; two far left
2940 2947 / /
2941 2948 | o changeset: 17:44765d7c06e0
2942 2949 | |\ parent: 12:86b91144a6e9
2943 2950 | | | parent: 16:3677d192927d
2944 2951 | | | user: test
2945 2952 | | | date: Thu Jan 01 00:00:17 1970 +0000
2946 2953 | | | summary: (17) expand
2947 2954 | | |
2948 2955 | | o changeset: 16:3677d192927d
2949 2956 | | |\ parent: 0:e6eb3150255d
2950 2957 | | ~ ~ parent: 1:6db2ef61d156
2951 2958 | | user: test
2952 2959 | | date: Thu Jan 01 00:00:16 1970 +0000
2953 2960 | | summary: (16) merge two known; one immediate right, one near right
2954 2961 | |
2955 2962 o | changeset: 15:1dda3f72782d
2956 2963 |\ \ parent: 13:22d8966a97e3
2957 2964 | | | parent: 14:8eac370358ef
2958 2965 | | | user: test
2959 2966 | | | date: Thu Jan 01 00:00:15 1970 +0000
2960 2967 | | | summary: (15) expand
2961 2968 | | |
2962 2969 | o | changeset: 14:8eac370358ef
2963 2970 | |\| parent: 0:e6eb3150255d
2964 2971 | ~ | parent: 12:86b91144a6e9
2965 2972 | | user: test
2966 2973 | | date: Thu Jan 01 00:00:14 1970 +0000
2967 2974 | | summary: (14) merge two known; one immediate right, one far right
2968 2975 | /
2969 2976 o | changeset: 13:22d8966a97e3
2970 2977 |\ \ parent: 9:7010c0af0a35
2971 2978 | | | parent: 11:832d76e6bdf2
2972 2979 | | | user: test
2973 2980 | | | date: Thu Jan 01 00:00:13 1970 +0000
2974 2981 | | | summary: (13) expand
2975 2982 | | |
2976 2983 +---o changeset: 12:86b91144a6e9
2977 2984 | | | parent: 1:6db2ef61d156
2978 2985 | | ~ parent: 9:7010c0af0a35
2979 2986 | | user: test
2980 2987 | | date: Thu Jan 01 00:00:12 1970 +0000
2981 2988 | | summary: (12) merge two known; one immediate right, one far left
2982 2989 | |
2983 2990 | o changeset: 11:832d76e6bdf2
2984 2991 | |\ parent: 6:b105a072e251
2985 2992 | | | parent: 10:74c64d036d72
2986 2993 | | | user: test
2987 2994 | | | date: Thu Jan 01 00:00:11 1970 +0000
2988 2995 | | | summary: (11) expand
2989 2996 | | |
2990 2997 | | o changeset: 10:74c64d036d72
2991 2998 | |/| parent: 0:e6eb3150255d
2992 2999 | | ~ parent: 6:b105a072e251
2993 3000 | | user: test
2994 3001 | | date: Thu Jan 01 00:00:10 1970 +0000
2995 3002 | | summary: (10) merge two known; one immediate left, one near right
2996 3003 | |
2997 3004 o | changeset: 9:7010c0af0a35
2998 3005 |\ \ parent: 7:b632bb1b1224
2999 3006 | | | parent: 8:7a0b11f71937
3000 3007 | | | user: test
3001 3008 | | | date: Thu Jan 01 00:00:09 1970 +0000
3002 3009 | | | summary: (9) expand
3003 3010 | | |
3004 3011 | o | changeset: 8:7a0b11f71937
3005 3012 |/| | parent: 0:e6eb3150255d
3006 3013 | ~ | parent: 7:b632bb1b1224
3007 3014 | | user: test
3008 3015 | | date: Thu Jan 01 00:00:08 1970 +0000
3009 3016 | | summary: (8) merge two known; one immediate left, one far right
3010 3017 | /
3011 3018 o | changeset: 7:b632bb1b1224
3012 3019 |\ \ parent: 2:3d9a33b8d1e1
3013 3020 | ~ | parent: 5:4409d547b708
3014 3021 | | user: test
3015 3022 | | date: Thu Jan 01 00:00:07 1970 +0000
3016 3023 | | summary: (7) expand
3017 3024 | /
3018 3025 | o changeset: 6:b105a072e251
3019 3026 |/| parent: 2:3d9a33b8d1e1
3020 3027 | ~ parent: 5:4409d547b708
3021 3028 | user: test
3022 3029 | date: Thu Jan 01 00:00:06 1970 +0000
3023 3030 | summary: (6) merge two known; one immediate left, one far left
3024 3031 |
3025 3032 o changeset: 5:4409d547b708
3026 3033 |\ parent: 3:27eef8ed80b4
3027 3034 | ~ parent: 4:26a8bac39d9f
3028 3035 | user: test
3029 3036 | date: Thu Jan 01 00:00:05 1970 +0000
3030 3037 | summary: (5) expand
3031 3038 |
3032 3039 o changeset: 4:26a8bac39d9f
3033 3040 |\ parent: 1:6db2ef61d156
3034 3041 ~ ~ parent: 3:27eef8ed80b4
3035 3042 user: test
3036 3043 date: Thu Jan 01 00:00:04 1970 +0000
3037 3044 summary: (4) merge two known; one immediate left, one immediate right
3038 3045
3039 3046 Draw only part of a grandparent line differently with "<N><char>"; only the
3040 3047 last N lines (for positive N) or everything but the first N lines (for
3041 3048 negative N) along the current node use the style, the rest of the edge uses
3042 3049 the parent edge styling.
3043 3050
3044 3051 Last 3 lines:
3045 3052
3046 3053 $ cat << EOF >> $HGRCPATH
3047 3054 > [experimental]
3048 3055 > graphstyle.parent = !
3049 3056 > graphstyle.grandparent = 3.
3050 3057 > graphstyle.missing =
3051 3058 > EOF
3052 3059 $ hg log -G -r '36:18 & file("a")' -m
3053 3060 @ changeset: 36:08a19a744424
3054 3061 ! branch: branch
3055 3062 ! tag: tip
3056 3063 ! parent: 35:9159c3644c5e
3057 3064 ! parent: 35:9159c3644c5e
3058 3065 ! user: test
3059 3066 . date: Thu Jan 01 00:00:36 1970 +0000
3060 3067 . summary: (36) buggy merge: identical parents
3061 3068 .
3062 3069 o changeset: 32:d06dffa21a31
3063 3070 !\ parent: 27:886ed638191b
3064 3071 ! ! parent: 31:621d83e11f67
3065 3072 ! ! user: test
3066 3073 ! . date: Thu Jan 01 00:00:32 1970 +0000
3067 3074 ! . summary: (32) expand
3068 3075 ! .
3069 3076 o ! changeset: 31:621d83e11f67
3070 3077 !\! parent: 21:d42a756af44d
3071 3078 ! ! parent: 30:6e11cd4b648f
3072 3079 ! ! user: test
3073 3080 ! ! date: Thu Jan 01 00:00:31 1970 +0000
3074 3081 ! ! summary: (31) expand
3075 3082 ! !
3076 3083 o ! changeset: 30:6e11cd4b648f
3077 3084 !\ \ parent: 28:44ecd0b9ae99
3078 3085 ! ~ ! parent: 29:cd9bb2be7593
3079 3086 ! ! user: test
3080 3087 ! ! date: Thu Jan 01 00:00:30 1970 +0000
3081 3088 ! ! summary: (30) expand
3082 3089 ! /
3083 3090 o ! changeset: 28:44ecd0b9ae99
3084 3091 !\ \ parent: 1:6db2ef61d156
3085 3092 ! ~ ! parent: 26:7f25b6c2f0b9
3086 3093 ! ! user: test
3087 3094 ! ! date: Thu Jan 01 00:00:28 1970 +0000
3088 3095 ! ! summary: (28) merge zero known
3089 3096 ! /
3090 3097 o ! changeset: 26:7f25b6c2f0b9
3091 3098 !\ \ parent: 18:1aa84d96232a
3092 3099 ! ! ! parent: 25:91da8ed57247
3093 3100 ! ! ! user: test
3094 3101 ! ! ! date: Thu Jan 01 00:00:26 1970 +0000
3095 3102 ! ! ! summary: (26) merge one known; far right
3096 3103 ! ! !
3097 3104 ! o ! changeset: 25:91da8ed57247
3098 3105 ! !\! parent: 21:d42a756af44d
3099 3106 ! ! ! parent: 24:a9c19a3d96b7
3100 3107 ! ! ! user: test
3101 3108 ! ! ! date: Thu Jan 01 00:00:25 1970 +0000
3102 3109 ! ! ! summary: (25) merge one known; far left
3103 3110 ! ! !
3104 3111 ! o ! changeset: 24:a9c19a3d96b7
3105 3112 ! !\ \ parent: 0:e6eb3150255d
3106 3113 ! ! ~ ! parent: 23:a01cddf0766d
3107 3114 ! ! ! user: test
3108 3115 ! ! ! date: Thu Jan 01 00:00:24 1970 +0000
3109 3116 ! ! ! summary: (24) merge one known; immediate right
3110 3117 ! ! /
3111 3118 ! o ! changeset: 23:a01cddf0766d
3112 3119 ! !\ \ parent: 1:6db2ef61d156
3113 3120 ! ! ~ ! parent: 22:e0d9cccacb5d
3114 3121 ! ! ! user: test
3115 3122 ! ! ! date: Thu Jan 01 00:00:23 1970 +0000
3116 3123 ! ! ! summary: (23) merge one known; immediate left
3117 3124 ! ! /
3118 3125 ! o ! changeset: 22:e0d9cccacb5d
3119 3126 !/!/ parent: 18:1aa84d96232a
3120 3127 ! ! parent: 21:d42a756af44d
3121 3128 ! ! user: test
3122 3129 ! ! date: Thu Jan 01 00:00:22 1970 +0000
3123 3130 ! ! summary: (22) merge two known; one far left, one far right
3124 3131 ! !
3125 3132 ! o changeset: 21:d42a756af44d
3126 3133 ! !\ parent: 19:31ddc2c1573b
3127 3134 ! ! ! parent: 20:d30ed6450e32
3128 3135 ! ! ! user: test
3129 3136 ! ! ! date: Thu Jan 01 00:00:21 1970 +0000
3130 3137 ! ! ! summary: (21) expand
3131 3138 ! ! !
3132 3139 +---o changeset: 20:d30ed6450e32
3133 3140 ! ! | parent: 0:e6eb3150255d
3134 3141 ! ! ~ parent: 18:1aa84d96232a
3135 3142 ! ! user: test
3136 3143 ! ! date: Thu Jan 01 00:00:20 1970 +0000
3137 3144 ! ! summary: (20) merge two known; two far right
3138 3145 ! !
3139 3146 ! o changeset: 19:31ddc2c1573b
3140 3147 ! |\ parent: 15:1dda3f72782d
3141 3148 ! ~ ~ parent: 17:44765d7c06e0
3142 3149 ! user: test
3143 3150 ! date: Thu Jan 01 00:00:19 1970 +0000
3144 3151 ! summary: (19) expand
3145 3152 !
3146 3153 o changeset: 18:1aa84d96232a
3147 3154 |\ parent: 1:6db2ef61d156
3148 3155 ~ ~ parent: 15:1dda3f72782d
3149 3156 user: test
3150 3157 date: Thu Jan 01 00:00:18 1970 +0000
3151 3158 summary: (18) merge two known; two far left
3152 3159
3153 3160 All but the first 3 lines:
3154 3161
3155 3162 $ cat << EOF >> $HGRCPATH
3156 3163 > [experimental]
3157 3164 > graphstyle.parent = !
3158 3165 > graphstyle.grandparent = -3.
3159 3166 > graphstyle.missing =
3160 3167 > EOF
3161 3168 $ hg log -G -r '36:18 & file("a")' -m
3162 3169 @ changeset: 36:08a19a744424
3163 3170 ! branch: branch
3164 3171 ! tag: tip
3165 3172 . parent: 35:9159c3644c5e
3166 3173 . parent: 35:9159c3644c5e
3167 3174 . user: test
3168 3175 . date: Thu Jan 01 00:00:36 1970 +0000
3169 3176 . summary: (36) buggy merge: identical parents
3170 3177 .
3171 3178 o changeset: 32:d06dffa21a31
3172 3179 !\ parent: 27:886ed638191b
3173 3180 ! ! parent: 31:621d83e11f67
3174 3181 ! . user: test
3175 3182 ! . date: Thu Jan 01 00:00:32 1970 +0000
3176 3183 ! . summary: (32) expand
3177 3184 ! .
3178 3185 o ! changeset: 31:621d83e11f67
3179 3186 !\! parent: 21:d42a756af44d
3180 3187 ! ! parent: 30:6e11cd4b648f
3181 3188 ! ! user: test
3182 3189 ! ! date: Thu Jan 01 00:00:31 1970 +0000
3183 3190 ! ! summary: (31) expand
3184 3191 ! !
3185 3192 o ! changeset: 30:6e11cd4b648f
3186 3193 !\ \ parent: 28:44ecd0b9ae99
3187 3194 ! ~ ! parent: 29:cd9bb2be7593
3188 3195 ! ! user: test
3189 3196 ! ! date: Thu Jan 01 00:00:30 1970 +0000
3190 3197 ! ! summary: (30) expand
3191 3198 ! /
3192 3199 o ! changeset: 28:44ecd0b9ae99
3193 3200 !\ \ parent: 1:6db2ef61d156
3194 3201 ! ~ ! parent: 26:7f25b6c2f0b9
3195 3202 ! ! user: test
3196 3203 ! ! date: Thu Jan 01 00:00:28 1970 +0000
3197 3204 ! ! summary: (28) merge zero known
3198 3205 ! /
3199 3206 o ! changeset: 26:7f25b6c2f0b9
3200 3207 !\ \ parent: 18:1aa84d96232a
3201 3208 ! ! ! parent: 25:91da8ed57247
3202 3209 ! ! ! user: test
3203 3210 ! ! ! date: Thu Jan 01 00:00:26 1970 +0000
3204 3211 ! ! ! summary: (26) merge one known; far right
3205 3212 ! ! !
3206 3213 ! o ! changeset: 25:91da8ed57247
3207 3214 ! !\! parent: 21:d42a756af44d
3208 3215 ! ! ! parent: 24:a9c19a3d96b7
3209 3216 ! ! ! user: test
3210 3217 ! ! ! date: Thu Jan 01 00:00:25 1970 +0000
3211 3218 ! ! ! summary: (25) merge one known; far left
3212 3219 ! ! !
3213 3220 ! o ! changeset: 24:a9c19a3d96b7
3214 3221 ! !\ \ parent: 0:e6eb3150255d
3215 3222 ! ! ~ ! parent: 23:a01cddf0766d
3216 3223 ! ! ! user: test
3217 3224 ! ! ! date: Thu Jan 01 00:00:24 1970 +0000
3218 3225 ! ! ! summary: (24) merge one known; immediate right
3219 3226 ! ! /
3220 3227 ! o ! changeset: 23:a01cddf0766d
3221 3228 ! !\ \ parent: 1:6db2ef61d156
3222 3229 ! ! ~ ! parent: 22:e0d9cccacb5d
3223 3230 ! ! ! user: test
3224 3231 ! ! ! date: Thu Jan 01 00:00:23 1970 +0000
3225 3232 ! ! ! summary: (23) merge one known; immediate left
3226 3233 ! ! /
3227 3234 ! o ! changeset: 22:e0d9cccacb5d
3228 3235 !/!/ parent: 18:1aa84d96232a
3229 3236 ! ! parent: 21:d42a756af44d
3230 3237 ! ! user: test
3231 3238 ! ! date: Thu Jan 01 00:00:22 1970 +0000
3232 3239 ! ! summary: (22) merge two known; one far left, one far right
3233 3240 ! !
3234 3241 ! o changeset: 21:d42a756af44d
3235 3242 ! !\ parent: 19:31ddc2c1573b
3236 3243 ! ! ! parent: 20:d30ed6450e32
3237 3244 ! ! ! user: test
3238 3245 ! ! ! date: Thu Jan 01 00:00:21 1970 +0000
3239 3246 ! ! ! summary: (21) expand
3240 3247 ! ! !
3241 3248 +---o changeset: 20:d30ed6450e32
3242 3249 ! ! | parent: 0:e6eb3150255d
3243 3250 ! ! ~ parent: 18:1aa84d96232a
3244 3251 ! ! user: test
3245 3252 ! ! date: Thu Jan 01 00:00:20 1970 +0000
3246 3253 ! ! summary: (20) merge two known; two far right
3247 3254 ! !
3248 3255 ! o changeset: 19:31ddc2c1573b
3249 3256 ! |\ parent: 15:1dda3f72782d
3250 3257 ! ~ ~ parent: 17:44765d7c06e0
3251 3258 ! user: test
3252 3259 ! date: Thu Jan 01 00:00:19 1970 +0000
3253 3260 ! summary: (19) expand
3254 3261 !
3255 3262 o changeset: 18:1aa84d96232a
3256 3263 |\ parent: 1:6db2ef61d156
3257 3264 ~ ~ parent: 15:1dda3f72782d
3258 3265 user: test
3259 3266 date: Thu Jan 01 00:00:18 1970 +0000
3260 3267 summary: (18) merge two known; two far left
3261 3268
3262 3269 $ cd ..
3263 3270
3264 3271 Change graph shorten, test better with graphstyle.missing not none
3265 3272
3266 3273 $ cd repo
3267 3274 $ cat << EOF >> $HGRCPATH
3268 3275 > [experimental]
3269 3276 > graphstyle.parent = |
3270 3277 > graphstyle.grandparent = :
3271 3278 > graphstyle.missing = '
3272 3279 > graphshorten = true
3273 3280 > EOF
3274 3281 $ hg log -G -r 'file("a")' -m -T '{rev} {desc}'
3275 3282 @ 36 (36) buggy merge: identical parents
3276 3283 o 32 (32) expand
3277 3284 |\
3278 3285 o : 31 (31) expand
3279 3286 |\:
3280 3287 o : 30 (30) expand
3281 3288 |\ \
3282 3289 o \ \ 28 (28) merge zero known
3283 3290 |\ \ \
3284 3291 o \ \ \ 26 (26) merge one known; far right
3285 3292 |\ \ \ \
3286 3293 | o-----+ 25 (25) merge one known; far left
3287 3294 | o ' ' : 24 (24) merge one known; immediate right
3288 3295 | |\ \ \ \
3289 3296 | o---+ ' : 23 (23) merge one known; immediate left
3290 3297 | o-------+ 22 (22) merge two known; one far left, one far right
3291 3298 |/ / / / /
3292 3299 | ' ' ' o 21 (21) expand
3293 3300 | ' ' ' |\
3294 3301 +-+-------o 20 (20) merge two known; two far right
3295 3302 | ' ' ' o 19 (19) expand
3296 3303 | ' ' ' |\
3297 3304 o---+---+ | 18 (18) merge two known; two far left
3298 3305 / / / / /
3299 3306 ' ' ' | o 17 (17) expand
3300 3307 ' ' ' | |\
3301 3308 +-+-------o 16 (16) merge two known; one immediate right, one near right
3302 3309 ' ' ' o | 15 (15) expand
3303 3310 ' ' ' |\ \
3304 3311 +-------o | 14 (14) merge two known; one immediate right, one far right
3305 3312 ' ' ' | |/
3306 3313 ' ' ' o | 13 (13) expand
3307 3314 ' ' ' |\ \
3308 3315 ' +---+---o 12 (12) merge two known; one immediate right, one far left
3309 3316 ' ' ' | o 11 (11) expand
3310 3317 ' ' ' | |\
3311 3318 +---------o 10 (10) merge two known; one immediate left, one near right
3312 3319 ' ' ' | |/
3313 3320 ' ' ' o | 9 (9) expand
3314 3321 ' ' ' |\ \
3315 3322 +-------o | 8 (8) merge two known; one immediate left, one far right
3316 3323 ' ' ' |/ /
3317 3324 ' ' ' o | 7 (7) expand
3318 3325 ' ' ' |\ \
3319 3326 ' ' ' +---o 6 (6) merge two known; one immediate left, one far left
3320 3327 ' ' ' | '/
3321 3328 ' ' ' o ' 5 (5) expand
3322 3329 ' ' ' |\ \
3323 3330 ' +---o ' ' 4 (4) merge two known; one immediate left, one immediate right
3324 3331 ' ' ' '/ /
3325 3332
3326 3333 behavior with newlines
3327 3334
3328 3335 $ hg log -G -r ::2 -T '{rev} {desc}'
3329 3336 o 2 (2) collapse
3330 3337 o 1 (1) collapse
3331 3338 o 0 (0) root
3332 3339
3333 3340 $ hg log -G -r ::2 -T '{rev} {desc}\n'
3334 3341 o 2 (2) collapse
3335 3342 o 1 (1) collapse
3336 3343 o 0 (0) root
3337 3344
3338 3345 $ hg log -G -r ::2 -T '{rev} {desc}\n\n'
3339 3346 o 2 (2) collapse
3340 3347 |
3341 3348 o 1 (1) collapse
3342 3349 |
3343 3350 o 0 (0) root
3344 3351
3345 3352
3346 3353 $ hg log -G -r ::2 -T '\n{rev} {desc}'
3347 3354 o
3348 3355 | 2 (2) collapse
3349 3356 o
3350 3357 | 1 (1) collapse
3351 3358 o
3352 3359 0 (0) root
3353 3360
3354 3361 $ hg log -G -r ::2 -T '{rev} {desc}\n\n\n'
3355 3362 o 2 (2) collapse
3356 3363 |
3357 3364 |
3358 3365 o 1 (1) collapse
3359 3366 |
3360 3367 |
3361 3368 o 0 (0) root
3362 3369
3363 3370
3364 3371 $ cd ..
3365 3372
3366 3373 When inserting extra line nodes to handle more than 2 parents, ensure that
3367 3374 the right node styles are used (issue5174):
3368 3375
3369 3376 $ hg init repo-issue5174
3370 3377 $ cd repo-issue5174
3371 3378 $ echo a > f0
3372 3379 $ hg ci -Aqm 0
3373 3380 $ echo a > f1
3374 3381 $ hg ci -Aqm 1
3375 3382 $ echo a > f2
3376 3383 $ hg ci -Aqm 2
3377 3384 $ hg co ".^"
3378 3385 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
3379 3386 $ echo a > f3
3380 3387 $ hg ci -Aqm 3
3381 3388 $ hg co ".^^"
3382 3389 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
3383 3390 $ echo a > f4
3384 3391 $ hg ci -Aqm 4
3385 3392 $ hg merge -r 2
3386 3393 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
3387 3394 (branch merge, don't forget to commit)
3388 3395 $ hg ci -qm 5
3389 3396 $ hg merge -r 3
3390 3397 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3391 3398 (branch merge, don't forget to commit)
3392 3399 $ hg ci -qm 6
3393 3400 $ hg log -G -r '0 | 1 | 2 | 6'
3394 3401 @ changeset: 6:851fe89689ad
3395 3402 :\ tag: tip
3396 3403 : : parent: 5:4f1e3cf15f5d
3397 3404 : : parent: 3:b74ba7084d2d
3398 3405 : : user: test
3399 3406 : : date: Thu Jan 01 00:00:00 1970 +0000
3400 3407 : : summary: 6
3401 3408 : :
3402 3409 : \
3403 3410 : :\
3404 3411 : o : changeset: 2:3e6599df4cce
3405 3412 : :/ user: test
3406 3413 : : date: Thu Jan 01 00:00:00 1970 +0000
3407 3414 : : summary: 2
3408 3415 : :
3409 3416 : o changeset: 1:bd9a55143933
3410 3417 :/ user: test
3411 3418 : date: Thu Jan 01 00:00:00 1970 +0000
3412 3419 : summary: 1
3413 3420 :
3414 3421 o changeset: 0:870a5edc339c
3415 3422 user: test
3416 3423 date: Thu Jan 01 00:00:00 1970 +0000
3417 3424 summary: 0
3418 3425
3419 3426
@@ -1,3350 +1,3391 b''
1 1 $ HGENCODING=utf-8
2 2 $ export HGENCODING
3 3 $ cat > testrevset.py << EOF
4 4 > import mercurial.revset
5 5 >
6 6 > baseset = mercurial.revset.baseset
7 7 >
8 8 > def r3232(repo, subset, x):
9 9 > """"simple revset that return [3,2,3,2]
10 10 >
11 11 > revisions duplicated on purpose.
12 12 > """
13 13 > if 3 not in subset:
14 14 > if 2 in subset:
15 15 > return baseset([2,2])
16 16 > return baseset()
17 17 > return baseset([3,3,2,2])
18 18 >
19 19 > mercurial.revset.symbols['r3232'] = r3232
20 20 > EOF
21 21 $ cat >> $HGRCPATH << EOF
22 22 > [extensions]
23 23 > testrevset=$TESTTMP/testrevset.py
24 24 > EOF
25 25
26 26 $ try() {
27 27 > hg debugrevspec --debug "$@"
28 28 > }
29 29
30 30 $ log() {
31 31 > hg log --template '{rev}\n' -r "$1"
32 32 > }
33 33
34 34 extension to build '_intlist()' and '_hexlist()', which is necessary because
35 35 these predicates use '\0' as a separator:
36 36
37 37 $ cat <<EOF > debugrevlistspec.py
38 38 > from __future__ import absolute_import
39 39 > from mercurial import (
40 40 > cmdutil,
41 41 > node as nodemod,
42 42 > revset,
43 43 > )
44 44 > cmdtable = {}
45 45 > command = cmdutil.command(cmdtable)
46 46 > @command('debugrevlistspec',
47 47 > [('', 'optimize', None, 'print parsed tree after optimizing'),
48 48 > ('', 'bin', None, 'unhexlify arguments')])
49 49 > def debugrevlistspec(ui, repo, fmt, *args, **opts):
50 50 > if opts['bin']:
51 51 > args = map(nodemod.bin, args)
52 52 > expr = revset.formatspec(fmt, list(args))
53 53 > if ui.verbose:
54 54 > tree = revset.parse(expr, lookup=repo.__contains__)
55 55 > ui.note(revset.prettyformat(tree), "\n")
56 56 > if opts["optimize"]:
57 57 > opttree = revset.optimize(revset.analyze(tree))
58 58 > ui.note("* optimized:\n", revset.prettyformat(opttree), "\n")
59 59 > func = revset.match(ui, expr, repo)
60 60 > revs = func(repo)
61 61 > if ui.verbose:
62 62 > ui.note("* set:\n", revset.prettyformatset(revs), "\n")
63 63 > for c in revs:
64 64 > ui.write("%s\n" % c)
65 65 > EOF
66 66 $ cat <<EOF >> $HGRCPATH
67 67 > [extensions]
68 68 > debugrevlistspec = $TESTTMP/debugrevlistspec.py
69 69 > EOF
70 70 $ trylist() {
71 71 > hg debugrevlistspec --debug "$@"
72 72 > }
73 73
74 74 $ hg init repo
75 75 $ cd repo
76 76
77 77 $ echo a > a
78 78 $ hg branch a
79 79 marked working directory as branch a
80 80 (branches are permanent and global, did you want a bookmark?)
81 81 $ hg ci -Aqm0
82 82
83 83 $ echo b > b
84 84 $ hg branch b
85 85 marked working directory as branch b
86 86 $ hg ci -Aqm1
87 87
88 88 $ rm a
89 89 $ hg branch a-b-c-
90 90 marked working directory as branch a-b-c-
91 91 $ hg ci -Aqm2 -u Bob
92 92
93 93 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
94 94 2
95 95 $ hg log -r "extra('branch')" --template '{rev}\n'
96 96 0
97 97 1
98 98 2
99 99 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
100 100 0 a
101 101 2 a-b-c-
102 102
103 103 $ hg co 1
104 104 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 105 $ hg branch +a+b+c+
106 106 marked working directory as branch +a+b+c+
107 107 $ hg ci -Aqm3
108 108
109 109 $ hg co 2 # interleave
110 110 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
111 111 $ echo bb > b
112 112 $ hg branch -- -a-b-c-
113 113 marked working directory as branch -a-b-c-
114 114 $ hg ci -Aqm4 -d "May 12 2005"
115 115
116 116 $ hg co 3
117 117 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 118 $ hg branch !a/b/c/
119 119 marked working directory as branch !a/b/c/
120 120 $ hg ci -Aqm"5 bug"
121 121
122 122 $ hg merge 4
123 123 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
124 124 (branch merge, don't forget to commit)
125 125 $ hg branch _a_b_c_
126 126 marked working directory as branch _a_b_c_
127 127 $ hg ci -Aqm"6 issue619"
128 128
129 129 $ hg branch .a.b.c.
130 130 marked working directory as branch .a.b.c.
131 131 $ hg ci -Aqm7
132 132
133 133 $ hg branch all
134 134 marked working directory as branch all
135 135
136 136 $ hg co 4
137 137 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
138 138 $ hg branch Γ©
139 139 marked working directory as branch \xc3\xa9 (esc)
140 140 $ hg ci -Aqm9
141 141
142 142 $ hg tag -r6 1.0
143 143 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
144 144
145 145 $ hg clone --quiet -U -r 7 . ../remote1
146 146 $ hg clone --quiet -U -r 8 . ../remote2
147 147 $ echo "[paths]" >> .hg/hgrc
148 148 $ echo "default = ../remote1" >> .hg/hgrc
149 149
150 150 trivial
151 151
152 152 $ try 0:1
153 153 (range
154 154 ('symbol', '0')
155 155 ('symbol', '1'))
156 156 * set:
157 157 <spanset+ 0:1>
158 158 0
159 159 1
160 160 $ try --optimize :
161 161 (rangeall
162 162 None)
163 163 * optimized:
164 164 (range
165 165 ('string', '0')
166 166 ('string', 'tip'))
167 167 * set:
168 168 <spanset+ 0:9>
169 169 0
170 170 1
171 171 2
172 172 3
173 173 4
174 174 5
175 175 6
176 176 7
177 177 8
178 178 9
179 179 $ try 3::6
180 180 (dagrange
181 181 ('symbol', '3')
182 182 ('symbol', '6'))
183 183 * set:
184 184 <baseset+ [3, 5, 6]>
185 185 3
186 186 5
187 187 6
188 188 $ try '0|1|2'
189 189 (or
190 ('symbol', '0')
191 ('symbol', '1')
192 ('symbol', '2'))
190 (list
191 ('symbol', '0')
192 ('symbol', '1')
193 ('symbol', '2')))
193 194 * set:
194 195 <baseset [0, 1, 2]>
195 196 0
196 197 1
197 198 2
198 199
199 200 names that should work without quoting
200 201
201 202 $ try a
202 203 ('symbol', 'a')
203 204 * set:
204 205 <baseset [0]>
205 206 0
206 207 $ try b-a
207 208 (minus
208 209 ('symbol', 'b')
209 210 ('symbol', 'a'))
210 211 * set:
211 212 <filteredset
212 213 <baseset [1]>,
213 214 <not
214 215 <baseset [0]>>>
215 216 1
216 217 $ try _a_b_c_
217 218 ('symbol', '_a_b_c_')
218 219 * set:
219 220 <baseset [6]>
220 221 6
221 222 $ try _a_b_c_-a
222 223 (minus
223 224 ('symbol', '_a_b_c_')
224 225 ('symbol', 'a'))
225 226 * set:
226 227 <filteredset
227 228 <baseset [6]>,
228 229 <not
229 230 <baseset [0]>>>
230 231 6
231 232 $ try .a.b.c.
232 233 ('symbol', '.a.b.c.')
233 234 * set:
234 235 <baseset [7]>
235 236 7
236 237 $ try .a.b.c.-a
237 238 (minus
238 239 ('symbol', '.a.b.c.')
239 240 ('symbol', 'a'))
240 241 * set:
241 242 <filteredset
242 243 <baseset [7]>,
243 244 <not
244 245 <baseset [0]>>>
245 246 7
246 247
247 248 names that should be caught by fallback mechanism
248 249
249 250 $ try -- '-a-b-c-'
250 251 ('symbol', '-a-b-c-')
251 252 * set:
252 253 <baseset [4]>
253 254 4
254 255 $ log -a-b-c-
255 256 4
256 257 $ try '+a+b+c+'
257 258 ('symbol', '+a+b+c+')
258 259 * set:
259 260 <baseset [3]>
260 261 3
261 262 $ try '+a+b+c+:'
262 263 (rangepost
263 264 ('symbol', '+a+b+c+'))
264 265 * set:
265 266 <spanset+ 3:9>
266 267 3
267 268 4
268 269 5
269 270 6
270 271 7
271 272 8
272 273 9
273 274 $ try ':+a+b+c+'
274 275 (rangepre
275 276 ('symbol', '+a+b+c+'))
276 277 * set:
277 278 <spanset+ 0:3>
278 279 0
279 280 1
280 281 2
281 282 3
282 283 $ try -- '-a-b-c-:+a+b+c+'
283 284 (range
284 285 ('symbol', '-a-b-c-')
285 286 ('symbol', '+a+b+c+'))
286 287 * set:
287 288 <spanset- 3:4>
288 289 4
289 290 3
290 291 $ log '-a-b-c-:+a+b+c+'
291 292 4
292 293 3
293 294
294 295 $ try -- -a-b-c--a # complains
295 296 (minus
296 297 (minus
297 298 (minus
298 299 (negate
299 300 ('symbol', 'a'))
300 301 ('symbol', 'b'))
301 302 ('symbol', 'c'))
302 303 (negate
303 304 ('symbol', 'a')))
304 305 abort: unknown revision '-a'!
305 306 [255]
306 307 $ try Γ©
307 308 ('symbol', '\xc3\xa9')
308 309 * set:
309 310 <baseset [9]>
310 311 9
311 312
312 313 no quoting needed
313 314
314 315 $ log ::a-b-c-
315 316 0
316 317 1
317 318 2
318 319
319 320 quoting needed
320 321
321 322 $ try '"-a-b-c-"-a'
322 323 (minus
323 324 ('string', '-a-b-c-')
324 325 ('symbol', 'a'))
325 326 * set:
326 327 <filteredset
327 328 <baseset [4]>,
328 329 <not
329 330 <baseset [0]>>>
330 331 4
331 332
332 333 $ log '1 or 2'
333 334 1
334 335 2
335 336 $ log '1|2'
336 337 1
337 338 2
338 339 $ log '1 and 2'
339 340 $ log '1&2'
340 341 $ try '1&2|3' # precedence - and is higher
341 342 (or
342 (and
343 ('symbol', '1')
344 ('symbol', '2'))
345 ('symbol', '3'))
343 (list
344 (and
345 ('symbol', '1')
346 ('symbol', '2'))
347 ('symbol', '3')))
346 348 * set:
347 349 <addset
348 350 <baseset []>,
349 351 <baseset [3]>>
350 352 3
351 353 $ try '1|2&3'
352 354 (or
353 ('symbol', '1')
354 (and
355 ('symbol', '2')
356 ('symbol', '3')))
355 (list
356 ('symbol', '1')
357 (and
358 ('symbol', '2')
359 ('symbol', '3'))))
357 360 * set:
358 361 <addset
359 362 <baseset [1]>,
360 363 <baseset []>>
361 364 1
362 365 $ try '1&2&3' # associativity
363 366 (and
364 367 (and
365 368 ('symbol', '1')
366 369 ('symbol', '2'))
367 370 ('symbol', '3'))
368 371 * set:
369 372 <baseset []>
370 373 $ try '1|(2|3)'
371 374 (or
372 ('symbol', '1')
373 (group
374 (or
375 ('symbol', '2')
376 ('symbol', '3'))))
375 (list
376 ('symbol', '1')
377 (group
378 (or
379 (list
380 ('symbol', '2')
381 ('symbol', '3'))))))
377 382 * set:
378 383 <addset
379 384 <baseset [1]>,
380 385 <baseset [2, 3]>>
381 386 1
382 387 2
383 388 3
384 389 $ log '1.0' # tag
385 390 6
386 391 $ log 'a' # branch
387 392 0
388 393 $ log '2785f51ee'
389 394 0
390 395 $ log 'date(2005)'
391 396 4
392 397 $ log 'date(this is a test)'
393 398 hg: parse error at 10: unexpected token: symbol
394 399 [255]
395 400 $ log 'date()'
396 401 hg: parse error: date requires a string
397 402 [255]
398 403 $ log 'date'
399 404 abort: unknown revision 'date'!
400 405 [255]
401 406 $ log 'date('
402 407 hg: parse error at 5: not a prefix: end
403 408 [255]
404 409 $ log 'date("\xy")'
405 410 hg: parse error: invalid \x escape
406 411 [255]
407 412 $ log 'date(tip)'
408 413 abort: invalid date: 'tip'
409 414 [255]
410 415 $ log '0:date'
411 416 abort: unknown revision 'date'!
412 417 [255]
413 418 $ log '::"date"'
414 419 abort: unknown revision 'date'!
415 420 [255]
416 421 $ hg book date -r 4
417 422 $ log '0:date'
418 423 0
419 424 1
420 425 2
421 426 3
422 427 4
423 428 $ log '::date'
424 429 0
425 430 1
426 431 2
427 432 4
428 433 $ log '::"date"'
429 434 0
430 435 1
431 436 2
432 437 4
433 438 $ log 'date(2005) and 1::'
434 439 4
435 440 $ hg book -d date
436 441
437 442 function name should be a symbol
438 443
439 444 $ log '"date"(2005)'
440 445 hg: parse error: not a symbol
441 446 [255]
442 447
443 448 keyword arguments
444 449
445 450 $ log 'extra(branch, value=a)'
446 451 0
447 452
448 453 $ log 'extra(branch, a, b)'
449 454 hg: parse error: extra takes at most 2 arguments
450 455 [255]
451 456 $ log 'extra(a, label=b)'
452 457 hg: parse error: extra got multiple values for keyword argument 'label'
453 458 [255]
454 459 $ log 'extra(label=branch, default)'
455 460 hg: parse error: extra got an invalid argument
456 461 [255]
457 462 $ log 'extra(branch, foo+bar=baz)'
458 463 hg: parse error: extra got an invalid argument
459 464 [255]
460 465 $ log 'extra(unknown=branch)'
461 466 hg: parse error: extra got an unexpected keyword argument 'unknown'
462 467 [255]
463 468
464 469 $ try 'foo=bar|baz'
465 470 (keyvalue
466 471 ('symbol', 'foo')
467 472 (or
468 ('symbol', 'bar')
469 ('symbol', 'baz')))
473 (list
474 ('symbol', 'bar')
475 ('symbol', 'baz'))))
470 476 hg: parse error: can't use a key-value pair in this context
471 477 [255]
472 478
473 479 right-hand side should be optimized recursively
474 480
475 481 $ try --optimize 'foo=(not public())'
476 482 (keyvalue
477 483 ('symbol', 'foo')
478 484 (group
479 485 (not
480 486 (func
481 487 ('symbol', 'public')
482 488 None))))
483 489 * optimized:
484 490 (keyvalue
485 491 ('symbol', 'foo')
486 492 (func
487 493 ('symbol', '_notpublic')
488 494 None))
489 495 hg: parse error: can't use a key-value pair in this context
490 496 [255]
491 497
492 498 parsed tree at stages:
493 499
494 500 $ hg debugrevspec -p all '()'
495 501 * parsed:
496 502 (group
497 503 None)
498 504 * expanded:
499 505 (group
500 506 None)
501 507 * concatenated:
502 508 (group
503 509 None)
504 510 * analyzed:
505 511 None
506 512 * optimized:
507 513 None
508 514 hg: parse error: missing argument
509 515 [255]
510 516
511 517 $ hg debugrevspec --no-optimized -p all '()'
512 518 * parsed:
513 519 (group
514 520 None)
515 521 * expanded:
516 522 (group
517 523 None)
518 524 * concatenated:
519 525 (group
520 526 None)
521 527 * analyzed:
522 528 None
523 529 hg: parse error: missing argument
524 530 [255]
525 531
526 532 $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1'
527 533 * parsed:
528 534 (minus
529 535 (group
530 536 (or
531 ('symbol', '0')
532 ('symbol', '1')))
537 (list
538 ('symbol', '0')
539 ('symbol', '1'))))
533 540 ('symbol', '1'))
534 541 * analyzed:
535 542 (and
536 543 (or
537 ('symbol', '0')
538 ('symbol', '1'))
544 (list
545 ('symbol', '0')
546 ('symbol', '1')))
539 547 (not
540 548 ('symbol', '1')))
541 549 * optimized:
542 550 (difference
543 551 (func
544 552 ('symbol', '_list')
545 553 ('string', '0\x001'))
546 554 ('symbol', '1'))
547 555 0
548 556
549 557 $ hg debugrevspec -p unknown '0'
550 558 abort: invalid stage name: unknown
551 559 [255]
552 560
553 561 $ hg debugrevspec -p all --optimize '0'
554 562 abort: cannot use --optimize with --show-stage
555 563 [255]
556 564
557 565 verify optimized tree:
558 566
559 567 $ hg debugrevspec --verify '0|1'
560 568
561 569 $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2'
562 570 * analyzed:
563 571 (and
564 572 (func
565 573 ('symbol', 'r3232')
566 574 None)
567 575 ('symbol', '2'))
568 576 * optimized:
569 577 (and
570 578 ('symbol', '2')
571 579 (func
572 580 ('symbol', 'r3232')
573 581 None))
574 582 * analyzed set:
575 583 <baseset [2]>
576 584 * optimized set:
577 585 <baseset [2, 2]>
578 586 --- analyzed
579 587 +++ optimized
580 588 2
581 589 +2
582 590 [1]
583 591
584 592 $ hg debugrevspec --no-optimized --verify-optimized '0'
585 593 abort: cannot use --verify-optimized with --no-optimized
586 594 [255]
587 595
588 596 Test that symbols only get parsed as functions if there's an opening
589 597 parenthesis.
590 598
591 599 $ hg book only -r 9
592 600 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
593 601 8
594 602 9
595 603
596 604 infix/suffix resolution of ^ operator (issue2884):
597 605
598 606 x^:y means (x^):y
599 607
600 608 $ try '1^:2'
601 609 (range
602 610 (parentpost
603 611 ('symbol', '1'))
604 612 ('symbol', '2'))
605 613 * set:
606 614 <spanset+ 0:2>
607 615 0
608 616 1
609 617 2
610 618
611 619 $ try '1^::2'
612 620 (dagrange
613 621 (parentpost
614 622 ('symbol', '1'))
615 623 ('symbol', '2'))
616 624 * set:
617 625 <baseset+ [0, 1, 2]>
618 626 0
619 627 1
620 628 2
621 629
622 630 $ try '9^:'
623 631 (rangepost
624 632 (parentpost
625 633 ('symbol', '9')))
626 634 * set:
627 635 <spanset+ 8:9>
628 636 8
629 637 9
630 638
631 639 x^:y should be resolved before omitting group operators
632 640
633 641 $ try '1^(:2)'
634 642 (parent
635 643 ('symbol', '1')
636 644 (group
637 645 (rangepre
638 646 ('symbol', '2'))))
639 647 hg: parse error: ^ expects a number 0, 1, or 2
640 648 [255]
641 649
642 650 x^:y should be resolved recursively
643 651
644 652 $ try 'sort(1^:2)'
645 653 (func
646 654 ('symbol', 'sort')
647 655 (range
648 656 (parentpost
649 657 ('symbol', '1'))
650 658 ('symbol', '2')))
651 659 * set:
652 660 <spanset+ 0:2>
653 661 0
654 662 1
655 663 2
656 664
657 665 $ try '(3^:4)^:2'
658 666 (range
659 667 (parentpost
660 668 (group
661 669 (range
662 670 (parentpost
663 671 ('symbol', '3'))
664 672 ('symbol', '4'))))
665 673 ('symbol', '2'))
666 674 * set:
667 675 <spanset+ 0:2>
668 676 0
669 677 1
670 678 2
671 679
672 680 $ try '(3^::4)^::2'
673 681 (dagrange
674 682 (parentpost
675 683 (group
676 684 (dagrange
677 685 (parentpost
678 686 ('symbol', '3'))
679 687 ('symbol', '4'))))
680 688 ('symbol', '2'))
681 689 * set:
682 690 <baseset+ [0, 1, 2]>
683 691 0
684 692 1
685 693 2
686 694
687 695 $ try '(9^:)^:'
688 696 (rangepost
689 697 (parentpost
690 698 (group
691 699 (rangepost
692 700 (parentpost
693 701 ('symbol', '9'))))))
694 702 * set:
695 703 <spanset+ 4:9>
696 704 4
697 705 5
698 706 6
699 707 7
700 708 8
701 709 9
702 710
703 711 x^ in alias should also be resolved
704 712
705 713 $ try 'A' --config 'revsetalias.A=1^:2'
706 714 ('symbol', 'A')
707 715 * expanded:
708 716 (range
709 717 (parentpost
710 718 ('symbol', '1'))
711 719 ('symbol', '2'))
712 720 * set:
713 721 <spanset+ 0:2>
714 722 0
715 723 1
716 724 2
717 725
718 726 $ try 'A:2' --config 'revsetalias.A=1^'
719 727 (range
720 728 ('symbol', 'A')
721 729 ('symbol', '2'))
722 730 * expanded:
723 731 (range
724 732 (parentpost
725 733 ('symbol', '1'))
726 734 ('symbol', '2'))
727 735 * set:
728 736 <spanset+ 0:2>
729 737 0
730 738 1
731 739 2
732 740
733 741 but not beyond the boundary of alias expansion, because the resolution should
734 742 be made at the parsing stage
735 743
736 744 $ try '1^A' --config 'revsetalias.A=:2'
737 745 (parent
738 746 ('symbol', '1')
739 747 ('symbol', 'A'))
740 748 * expanded:
741 749 (parent
742 750 ('symbol', '1')
743 751 (rangepre
744 752 ('symbol', '2')))
745 753 hg: parse error: ^ expects a number 0, 1, or 2
746 754 [255]
747 755
748 756 ancestor can accept 0 or more arguments
749 757
750 758 $ log 'ancestor()'
751 759 $ log 'ancestor(1)'
752 760 1
753 761 $ log 'ancestor(4,5)'
754 762 1
755 763 $ log 'ancestor(4,5) and 4'
756 764 $ log 'ancestor(0,0,1,3)'
757 765 0
758 766 $ log 'ancestor(3,1,5,3,5,1)'
759 767 1
760 768 $ log 'ancestor(0,1,3,5)'
761 769 0
762 770 $ log 'ancestor(1,2,3,4,5)'
763 771 1
764 772
765 773 test ancestors
766 774
767 775 $ log 'ancestors(5)'
768 776 0
769 777 1
770 778 3
771 779 5
772 780 $ log 'ancestor(ancestors(5))'
773 781 0
774 782 $ log '::r3232()'
775 783 0
776 784 1
777 785 2
778 786 3
779 787
780 788 $ log 'author(bob)'
781 789 2
782 790 $ log 'author("re:bob|test")'
783 791 0
784 792 1
785 793 2
786 794 3
787 795 4
788 796 5
789 797 6
790 798 7
791 799 8
792 800 9
793 801 $ log 'branch(Γ©)'
794 802 8
795 803 9
796 804 $ log 'branch(a)'
797 805 0
798 806 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
799 807 0 a
800 808 2 a-b-c-
801 809 3 +a+b+c+
802 810 4 -a-b-c-
803 811 5 !a/b/c/
804 812 6 _a_b_c_
805 813 7 .a.b.c.
806 814 $ log 'children(ancestor(4,5))'
807 815 2
808 816 3
809 817 $ log 'closed()'
810 818 $ log 'contains(a)'
811 819 0
812 820 1
813 821 3
814 822 5
815 823 $ log 'contains("../repo/a")'
816 824 0
817 825 1
818 826 3
819 827 5
820 828 $ log 'desc(B)'
821 829 5
822 830 $ log 'descendants(2 or 3)'
823 831 2
824 832 3
825 833 4
826 834 5
827 835 6
828 836 7
829 837 8
830 838 9
831 839 $ log 'file("b*")'
832 840 1
833 841 4
834 842 $ log 'filelog("b")'
835 843 1
836 844 4
837 845 $ log 'filelog("../repo/b")'
838 846 1
839 847 4
840 848 $ log 'follow()'
841 849 0
842 850 1
843 851 2
844 852 4
845 853 8
846 854 9
847 855 $ log 'grep("issue\d+")'
848 856 6
849 857 $ try 'grep("(")' # invalid regular expression
850 858 (func
851 859 ('symbol', 'grep')
852 860 ('string', '('))
853 861 hg: parse error: invalid match pattern: unbalanced parenthesis
854 862 [255]
855 863 $ try 'grep("\bissue\d+")'
856 864 (func
857 865 ('symbol', 'grep')
858 866 ('string', '\x08issue\\d+'))
859 867 * set:
860 868 <filteredset
861 869 <fullreposet+ 0:9>,
862 870 <grep '\x08issue\\d+'>>
863 871 $ try 'grep(r"\bissue\d+")'
864 872 (func
865 873 ('symbol', 'grep')
866 874 ('string', '\\bissue\\d+'))
867 875 * set:
868 876 <filteredset
869 877 <fullreposet+ 0:9>,
870 878 <grep '\\bissue\\d+'>>
871 879 6
872 880 $ try 'grep(r"\")'
873 881 hg: parse error at 7: unterminated string
874 882 [255]
875 883 $ log 'head()'
876 884 0
877 885 1
878 886 2
879 887 3
880 888 4
881 889 5
882 890 6
883 891 7
884 892 9
885 893 $ log 'heads(6::)'
886 894 7
887 895 $ log 'keyword(issue)'
888 896 6
889 897 $ log 'keyword("test a")'
890 898 $ log 'limit(head(), 1)'
891 899 0
892 900 $ log 'limit(author("re:bob|test"), 3, 5)'
893 901 5
894 902 6
895 903 7
896 904 $ log 'limit(author("re:bob|test"), offset=6)'
897 905 6
898 906 $ log 'limit(author("re:bob|test"), offset=10)'
899 907 $ log 'limit(all(), 1, -1)'
900 908 hg: parse error: negative offset
901 909 [255]
902 910 $ log 'matching(6)'
903 911 6
904 912 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
905 913 6
906 914 7
907 915
908 916 Testing min and max
909 917
910 918 max: simple
911 919
912 920 $ log 'max(contains(a))'
913 921 5
914 922
915 923 max: simple on unordered set)
916 924
917 925 $ log 'max((4+0+2+5+7) and contains(a))'
918 926 5
919 927
920 928 max: no result
921 929
922 930 $ log 'max(contains(stringthatdoesnotappearanywhere))'
923 931
924 932 max: no result on unordered set
925 933
926 934 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
927 935
928 936 min: simple
929 937
930 938 $ log 'min(contains(a))'
931 939 0
932 940
933 941 min: simple on unordered set
934 942
935 943 $ log 'min((4+0+2+5+7) and contains(a))'
936 944 0
937 945
938 946 min: empty
939 947
940 948 $ log 'min(contains(stringthatdoesnotappearanywhere))'
941 949
942 950 min: empty on unordered set
943 951
944 952 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
945 953
946 954
947 955 $ log 'merge()'
948 956 6
949 957 $ log 'branchpoint()'
950 958 1
951 959 4
952 960 $ log 'modifies(b)'
953 961 4
954 962 $ log 'modifies("path:b")'
955 963 4
956 964 $ log 'modifies("*")'
957 965 4
958 966 6
959 967 $ log 'modifies("set:modified()")'
960 968 4
961 969 $ log 'id(5)'
962 970 2
963 971 $ log 'only(9)'
964 972 8
965 973 9
966 974 $ log 'only(8)'
967 975 8
968 976 $ log 'only(9, 5)'
969 977 2
970 978 4
971 979 8
972 980 9
973 981 $ log 'only(7 + 9, 5 + 2)'
974 982 4
975 983 6
976 984 7
977 985 8
978 986 9
979 987
980 988 Test empty set input
981 989 $ log 'only(p2())'
982 990 $ log 'only(p1(), p2())'
983 991 0
984 992 1
985 993 2
986 994 4
987 995 8
988 996 9
989 997
990 998 Test '%' operator
991 999
992 1000 $ log '9%'
993 1001 8
994 1002 9
995 1003 $ log '9%5'
996 1004 2
997 1005 4
998 1006 8
999 1007 9
1000 1008 $ log '(7 + 9)%(5 + 2)'
1001 1009 4
1002 1010 6
1003 1011 7
1004 1012 8
1005 1013 9
1006 1014
1007 1015 Test opreand of '%' is optimized recursively (issue4670)
1008 1016
1009 1017 $ try --optimize '8:9-8%'
1010 1018 (onlypost
1011 1019 (minus
1012 1020 (range
1013 1021 ('symbol', '8')
1014 1022 ('symbol', '9'))
1015 1023 ('symbol', '8')))
1016 1024 * optimized:
1017 1025 (func
1018 1026 ('symbol', 'only')
1019 1027 (difference
1020 1028 (range
1021 1029 ('symbol', '8')
1022 1030 ('symbol', '9'))
1023 1031 ('symbol', '8')))
1024 1032 * set:
1025 1033 <baseset+ [8, 9]>
1026 1034 8
1027 1035 9
1028 1036 $ try --optimize '(9)%(5)'
1029 1037 (only
1030 1038 (group
1031 1039 ('symbol', '9'))
1032 1040 (group
1033 1041 ('symbol', '5')))
1034 1042 * optimized:
1035 1043 (func
1036 1044 ('symbol', 'only')
1037 1045 (list
1038 1046 ('symbol', '9')
1039 1047 ('symbol', '5')))
1040 1048 * set:
1041 1049 <baseset+ [2, 4, 8, 9]>
1042 1050 2
1043 1051 4
1044 1052 8
1045 1053 9
1046 1054
1047 1055 Test the order of operations
1048 1056
1049 1057 $ log '7 + 9%5 + 2'
1050 1058 7
1051 1059 2
1052 1060 4
1053 1061 8
1054 1062 9
1055 1063
1056 1064 Test explicit numeric revision
1057 1065 $ log 'rev(-2)'
1058 1066 $ log 'rev(-1)'
1059 1067 -1
1060 1068 $ log 'rev(0)'
1061 1069 0
1062 1070 $ log 'rev(9)'
1063 1071 9
1064 1072 $ log 'rev(10)'
1065 1073 $ log 'rev(tip)'
1066 1074 hg: parse error: rev expects a number
1067 1075 [255]
1068 1076
1069 1077 Test hexadecimal revision
1070 1078 $ log 'id(2)'
1071 1079 abort: 00changelog.i@2: ambiguous identifier!
1072 1080 [255]
1073 1081 $ log 'id(23268)'
1074 1082 4
1075 1083 $ log 'id(2785f51eece)'
1076 1084 0
1077 1085 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
1078 1086 8
1079 1087 $ log 'id(d5d0dcbdc4a)'
1080 1088 $ log 'id(d5d0dcbdc4w)'
1081 1089 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
1082 1090 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
1083 1091 $ log 'id(1.0)'
1084 1092 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
1085 1093
1086 1094 Test null revision
1087 1095 $ log '(null)'
1088 1096 -1
1089 1097 $ log '(null:0)'
1090 1098 -1
1091 1099 0
1092 1100 $ log '(0:null)'
1093 1101 0
1094 1102 -1
1095 1103 $ log 'null::0'
1096 1104 -1
1097 1105 0
1098 1106 $ log 'null:tip - 0:'
1099 1107 -1
1100 1108 $ log 'null: and null::' | head -1
1101 1109 -1
1102 1110 $ log 'null: or 0:' | head -2
1103 1111 -1
1104 1112 0
1105 1113 $ log 'ancestors(null)'
1106 1114 -1
1107 1115 $ log 'reverse(null:)' | tail -2
1108 1116 0
1109 1117 -1
1110 1118 BROKEN: should be '-1'
1111 1119 $ log 'first(null:)'
1112 1120 BROKEN: should be '-1'
1113 1121 $ log 'min(null:)'
1114 1122 $ log 'tip:null and all()' | tail -2
1115 1123 1
1116 1124 0
1117 1125
1118 1126 Test working-directory revision
1119 1127 $ hg debugrevspec 'wdir()'
1120 1128 2147483647
1121 1129 $ hg debugrevspec 'tip or wdir()'
1122 1130 9
1123 1131 2147483647
1124 1132 $ hg debugrevspec '0:tip and wdir()'
1125 1133 $ log '0:wdir()' | tail -3
1126 1134 8
1127 1135 9
1128 1136 2147483647
1129 1137 $ log 'wdir():0' | head -3
1130 1138 2147483647
1131 1139 9
1132 1140 8
1133 1141 $ log 'wdir():wdir()'
1134 1142 2147483647
1135 1143 $ log '(all() + wdir()) & min(. + wdir())'
1136 1144 9
1137 1145 $ log '(all() + wdir()) & max(. + wdir())'
1138 1146 2147483647
1139 1147 $ log '(all() + wdir()) & first(wdir() + .)'
1140 1148 2147483647
1141 1149 $ log '(all() + wdir()) & last(. + wdir())'
1142 1150 2147483647
1143 1151
1144 1152 $ log 'outgoing()'
1145 1153 8
1146 1154 9
1147 1155 $ log 'outgoing("../remote1")'
1148 1156 8
1149 1157 9
1150 1158 $ log 'outgoing("../remote2")'
1151 1159 3
1152 1160 5
1153 1161 6
1154 1162 7
1155 1163 9
1156 1164 $ log 'p1(merge())'
1157 1165 5
1158 1166 $ log 'p2(merge())'
1159 1167 4
1160 1168 $ log 'parents(merge())'
1161 1169 4
1162 1170 5
1163 1171 $ log 'p1(branchpoint())'
1164 1172 0
1165 1173 2
1166 1174 $ log 'p2(branchpoint())'
1167 1175 $ log 'parents(branchpoint())'
1168 1176 0
1169 1177 2
1170 1178 $ log 'removes(a)'
1171 1179 2
1172 1180 6
1173 1181 $ log 'roots(all())'
1174 1182 0
1175 1183 $ log 'reverse(2 or 3 or 4 or 5)'
1176 1184 5
1177 1185 4
1178 1186 3
1179 1187 2
1180 1188 $ log 'reverse(all())'
1181 1189 9
1182 1190 8
1183 1191 7
1184 1192 6
1185 1193 5
1186 1194 4
1187 1195 3
1188 1196 2
1189 1197 1
1190 1198 0
1191 1199 $ log 'reverse(all()) & filelog(b)'
1192 1200 4
1193 1201 1
1194 1202 $ log 'rev(5)'
1195 1203 5
1196 1204 $ log 'sort(limit(reverse(all()), 3))'
1197 1205 7
1198 1206 8
1199 1207 9
1200 1208 $ log 'sort(2 or 3 or 4 or 5, date)'
1201 1209 2
1202 1210 3
1203 1211 5
1204 1212 4
1205 1213 $ log 'tagged()'
1206 1214 6
1207 1215 $ log 'tag()'
1208 1216 6
1209 1217 $ log 'tag(1.0)'
1210 1218 6
1211 1219 $ log 'tag(tip)'
1212 1220 9
1213 1221
1214 1222 Test order of revisions in compound expression
1215 1223 ----------------------------------------------
1216 1224
1217 1225 The general rule is that only the outermost (= leftmost) predicate can
1218 1226 enforce its ordering requirement. The other predicates should take the
1219 1227 ordering defined by it.
1220 1228
1221 1229 'A & B' should follow the order of 'A':
1222 1230
1223 1231 $ log '2:0 & 0::2'
1224 1232 2
1225 1233 1
1226 1234 0
1227 1235
1228 1236 'head()' combines sets in right order:
1229 1237
1230 1238 $ log '2:0 & head()'
1231 1239 2
1232 1240 1
1233 1241 0
1234 1242
1235 1243 'a + b', which is optimized to '_list(a b)', should take the ordering of
1236 1244 the left expression:
1237 1245
1238 1246 $ try --optimize '2:0 & (0 + 1 + 2)'
1239 1247 (and
1240 1248 (range
1241 1249 ('symbol', '2')
1242 1250 ('symbol', '0'))
1243 1251 (group
1244 1252 (or
1245 ('symbol', '0')
1246 ('symbol', '1')
1247 ('symbol', '2'))))
1253 (list
1254 ('symbol', '0')
1255 ('symbol', '1')
1256 ('symbol', '2')))))
1248 1257 * optimized:
1249 1258 (and
1250 1259 (range
1251 1260 ('symbol', '2')
1252 1261 ('symbol', '0'))
1253 1262 (func
1254 1263 ('symbol', '_list')
1255 1264 ('string', '0\x001\x002')))
1256 1265 * set:
1257 1266 <baseset [0, 1, 2]>
1258 1267 0
1259 1268 1
1260 1269 2
1261 1270 BROKEN: should be '2 1 0'
1262 1271
1263 1272 'A + B' should take the ordering of the left expression:
1264 1273
1265 1274 $ try --optimize '2:0 & (0:1 + 2)'
1266 1275 (and
1267 1276 (range
1268 1277 ('symbol', '2')
1269 1278 ('symbol', '0'))
1270 1279 (group
1271 1280 (or
1272 (range
1273 ('symbol', '0')
1274 ('symbol', '1'))
1275 ('symbol', '2'))))
1281 (list
1282 (range
1283 ('symbol', '0')
1284 ('symbol', '1'))
1285 ('symbol', '2')))))
1276 1286 * optimized:
1277 1287 (and
1278 1288 (range
1279 1289 ('symbol', '2')
1280 1290 ('symbol', '0'))
1281 1291 (or
1282 (range
1283 ('symbol', '0')
1284 ('symbol', '1'))
1285 ('symbol', '2')))
1292 (list
1293 (range
1294 ('symbol', '0')
1295 ('symbol', '1'))
1296 ('symbol', '2'))))
1286 1297 * set:
1287 1298 <addset
1288 1299 <filteredset
1289 1300 <spanset+ 0:1>,
1290 1301 <spanset- 0:2>>,
1291 1302 <baseset [2]>>
1292 1303 0
1293 1304 1
1294 1305 2
1295 1306 BROKEN: should be '2 1 0'
1296 1307
1297 1308 '_intlist(a b)' should behave like 'a + b':
1298 1309
1299 1310 $ trylist --optimize '2:0 & %ld' 0 1 2
1300 1311 (and
1301 1312 (range
1302 1313 ('symbol', '2')
1303 1314 ('symbol', '0'))
1304 1315 (func
1305 1316 ('symbol', '_intlist')
1306 1317 ('string', '0\x001\x002')))
1307 1318 * optimized:
1308 1319 (and
1309 1320 (func
1310 1321 ('symbol', '_intlist')
1311 1322 ('string', '0\x001\x002'))
1312 1323 (range
1313 1324 ('symbol', '2')
1314 1325 ('symbol', '0')))
1315 1326 * set:
1316 1327 <filteredset
1317 1328 <spanset- 0:2>,
1318 1329 <baseset [0, 1, 2]>>
1319 1330 2
1320 1331 1
1321 1332 0
1322 1333
1323 1334 $ trylist --optimize '%ld & 2:0' 0 2 1
1324 1335 (and
1325 1336 (func
1326 1337 ('symbol', '_intlist')
1327 1338 ('string', '0\x002\x001'))
1328 1339 (range
1329 1340 ('symbol', '2')
1330 1341 ('symbol', '0')))
1331 1342 * optimized:
1332 1343 (and
1333 1344 (func
1334 1345 ('symbol', '_intlist')
1335 1346 ('string', '0\x002\x001'))
1336 1347 (range
1337 1348 ('symbol', '2')
1338 1349 ('symbol', '0')))
1339 1350 * set:
1340 1351 <filteredset
1341 1352 <spanset- 0:2>,
1342 1353 <baseset [0, 2, 1]>>
1343 1354 2
1344 1355 1
1345 1356 0
1346 1357 BROKEN: should be '0 2 1'
1347 1358
1348 1359 '_hexlist(a b)' should behave like 'a + b':
1349 1360
1350 1361 $ trylist --optimize --bin '2:0 & %ln' `hg log -T '{node} ' -r0:2`
1351 1362 (and
1352 1363 (range
1353 1364 ('symbol', '2')
1354 1365 ('symbol', '0'))
1355 1366 (func
1356 1367 ('symbol', '_hexlist')
1357 1368 ('string', '*'))) (glob)
1358 1369 * optimized:
1359 1370 (and
1360 1371 (range
1361 1372 ('symbol', '2')
1362 1373 ('symbol', '0'))
1363 1374 (func
1364 1375 ('symbol', '_hexlist')
1365 1376 ('string', '*'))) (glob)
1366 1377 * set:
1367 1378 <baseset [0, 1, 2]>
1368 1379 0
1369 1380 1
1370 1381 2
1371 1382 BROKEN: should be '2 1 0'
1372 1383
1373 1384 $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1`
1374 1385 (and
1375 1386 (func
1376 1387 ('symbol', '_hexlist')
1377 1388 ('string', '*')) (glob)
1378 1389 (range
1379 1390 ('symbol', '2')
1380 1391 ('symbol', '0')))
1381 1392 * optimized:
1382 1393 (and
1383 1394 (range
1384 1395 ('symbol', '2')
1385 1396 ('symbol', '0'))
1386 1397 (func
1387 1398 ('symbol', '_hexlist')
1388 1399 ('string', '*'))) (glob)
1389 1400 * set:
1390 1401 <baseset [0, 2, 1]>
1391 1402 0
1392 1403 2
1393 1404 1
1394 1405
1395 1406 'present()' should do nothing other than suppressing an error:
1396 1407
1397 1408 $ try --optimize '2:0 & present(0 + 1 + 2)'
1398 1409 (and
1399 1410 (range
1400 1411 ('symbol', '2')
1401 1412 ('symbol', '0'))
1402 1413 (func
1403 1414 ('symbol', 'present')
1404 1415 (or
1405 ('symbol', '0')
1406 ('symbol', '1')
1407 ('symbol', '2'))))
1416 (list
1417 ('symbol', '0')
1418 ('symbol', '1')
1419 ('symbol', '2')))))
1408 1420 * optimized:
1409 1421 (and
1410 1422 (range
1411 1423 ('symbol', '2')
1412 1424 ('symbol', '0'))
1413 1425 (func
1414 1426 ('symbol', 'present')
1415 1427 (func
1416 1428 ('symbol', '_list')
1417 1429 ('string', '0\x001\x002'))))
1418 1430 * set:
1419 1431 <baseset [0, 1, 2]>
1420 1432 0
1421 1433 1
1422 1434 2
1423 1435 BROKEN: should be '2 1 0'
1424 1436
1425 1437 'reverse()' should take effect only if it is the outermost expression:
1426 1438
1427 1439 $ try --optimize '0:2 & reverse(all())'
1428 1440 (and
1429 1441 (range
1430 1442 ('symbol', '0')
1431 1443 ('symbol', '2'))
1432 1444 (func
1433 1445 ('symbol', 'reverse')
1434 1446 (func
1435 1447 ('symbol', 'all')
1436 1448 None)))
1437 1449 * optimized:
1438 1450 (and
1439 1451 (range
1440 1452 ('symbol', '0')
1441 1453 ('symbol', '2'))
1442 1454 (func
1443 1455 ('symbol', 'reverse')
1444 1456 (func
1445 1457 ('symbol', 'all')
1446 1458 None)))
1447 1459 * set:
1448 1460 <filteredset
1449 1461 <spanset- 0:2>,
1450 1462 <spanset+ 0:9>>
1451 1463 2
1452 1464 1
1453 1465 0
1454 1466 BROKEN: should be '0 1 2'
1455 1467
1456 1468 'sort()' should take effect only if it is the outermost expression:
1457 1469
1458 1470 $ try --optimize '0:2 & sort(all(), -rev)'
1459 1471 (and
1460 1472 (range
1461 1473 ('symbol', '0')
1462 1474 ('symbol', '2'))
1463 1475 (func
1464 1476 ('symbol', 'sort')
1465 1477 (list
1466 1478 (func
1467 1479 ('symbol', 'all')
1468 1480 None)
1469 1481 (negate
1470 1482 ('symbol', 'rev')))))
1471 1483 * optimized:
1472 1484 (and
1473 1485 (range
1474 1486 ('symbol', '0')
1475 1487 ('symbol', '2'))
1476 1488 (func
1477 1489 ('symbol', 'sort')
1478 1490 (list
1479 1491 (func
1480 1492 ('symbol', 'all')
1481 1493 None)
1482 1494 ('string', '-rev'))))
1483 1495 * set:
1484 1496 <filteredset
1485 1497 <spanset- 0:2>,
1486 1498 <spanset+ 0:9>>
1487 1499 2
1488 1500 1
1489 1501 0
1490 1502 BROKEN: should be '0 1 2'
1491 1503
1492 1504 for 'A & f(B)', 'B' should not be affected by the order of 'A':
1493 1505
1494 1506 $ try --optimize '2:0 & first(1 + 0 + 2)'
1495 1507 (and
1496 1508 (range
1497 1509 ('symbol', '2')
1498 1510 ('symbol', '0'))
1499 1511 (func
1500 1512 ('symbol', 'first')
1501 1513 (or
1502 ('symbol', '1')
1503 ('symbol', '0')
1504 ('symbol', '2'))))
1514 (list
1515 ('symbol', '1')
1516 ('symbol', '0')
1517 ('symbol', '2')))))
1505 1518 * optimized:
1506 1519 (and
1507 1520 (range
1508 1521 ('symbol', '2')
1509 1522 ('symbol', '0'))
1510 1523 (func
1511 1524 ('symbol', 'first')
1512 1525 (func
1513 1526 ('symbol', '_list')
1514 1527 ('string', '1\x000\x002'))))
1515 1528 * set:
1516 1529 <baseset
1517 1530 <limit n=1, offset=0,
1518 1531 <spanset- 0:2>,
1519 1532 <baseset [1, 0, 2]>>>
1520 1533 1
1521 1534
1522 1535 $ try --optimize '2:0 & not last(0 + 2 + 1)'
1523 1536 (and
1524 1537 (range
1525 1538 ('symbol', '2')
1526 1539 ('symbol', '0'))
1527 1540 (not
1528 1541 (func
1529 1542 ('symbol', 'last')
1530 1543 (or
1531 ('symbol', '0')
1532 ('symbol', '2')
1533 ('symbol', '1')))))
1544 (list
1545 ('symbol', '0')
1546 ('symbol', '2')
1547 ('symbol', '1'))))))
1534 1548 * optimized:
1535 1549 (difference
1536 1550 (range
1537 1551 ('symbol', '2')
1538 1552 ('symbol', '0'))
1539 1553 (func
1540 1554 ('symbol', 'last')
1541 1555 (func
1542 1556 ('symbol', '_list')
1543 1557 ('string', '0\x002\x001'))))
1544 1558 * set:
1545 1559 <filteredset
1546 1560 <spanset- 0:2>,
1547 1561 <not
1548 1562 <baseset
1549 1563 <last n=1,
1550 1564 <fullreposet+ 0:9>,
1551 1565 <baseset [1, 2, 0]>>>>>
1552 1566 2
1553 1567 0
1554 1568
1555 1569 for 'A & (op)(B)', 'B' should not be affected by the order of 'A':
1556 1570
1557 1571 $ try --optimize '2:0 & (1 + 0 + 2):(0 + 2 + 1)'
1558 1572 (and
1559 1573 (range
1560 1574 ('symbol', '2')
1561 1575 ('symbol', '0'))
1562 1576 (range
1563 1577 (group
1564 1578 (or
1565 ('symbol', '1')
1566 ('symbol', '0')
1567 ('symbol', '2')))
1579 (list
1580 ('symbol', '1')
1581 ('symbol', '0')
1582 ('symbol', '2'))))
1568 1583 (group
1569 1584 (or
1570 ('symbol', '0')
1571 ('symbol', '2')
1572 ('symbol', '1')))))
1585 (list
1586 ('symbol', '0')
1587 ('symbol', '2')
1588 ('symbol', '1'))))))
1573 1589 * optimized:
1574 1590 (and
1575 1591 (range
1576 1592 ('symbol', '2')
1577 1593 ('symbol', '0'))
1578 1594 (range
1579 1595 (func
1580 1596 ('symbol', '_list')
1581 1597 ('string', '1\x000\x002'))
1582 1598 (func
1583 1599 ('symbol', '_list')
1584 1600 ('string', '0\x002\x001'))))
1585 1601 * set:
1586 1602 <filteredset
1587 1603 <baseset [1]>,
1588 1604 <spanset- 0:2>>
1589 1605 1
1590 1606
1591 1607 'A & B' can be rewritten as 'B & A' by weight, but the ordering rule should
1592 1608 be determined before the optimization (i.e. 'B' should take the ordering of
1593 1609 'A'):
1594 1610
1595 1611 $ try --optimize 'contains("glob:*") & (2 + 0 + 1)'
1596 1612 (and
1597 1613 (func
1598 1614 ('symbol', 'contains')
1599 1615 ('string', 'glob:*'))
1600 1616 (group
1601 1617 (or
1602 ('symbol', '2')
1603 ('symbol', '0')
1604 ('symbol', '1'))))
1618 (list
1619 ('symbol', '2')
1620 ('symbol', '0')
1621 ('symbol', '1')))))
1605 1622 * optimized:
1606 1623 (and
1607 1624 (func
1608 1625 ('symbol', '_list')
1609 1626 ('string', '2\x000\x001'))
1610 1627 (func
1611 1628 ('symbol', 'contains')
1612 1629 ('string', 'glob:*')))
1613 1630 * set:
1614 1631 <filteredset
1615 1632 <baseset [2, 0, 1]>,
1616 1633 <contains 'glob:*'>>
1617 1634 2
1618 1635 0
1619 1636 1
1620 1637 BROKEN: should be '0 1 2'
1621 1638
1622 1639 $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)'
1623 1640 (and
1624 1641 (func
1625 1642 ('symbol', 'reverse')
1626 1643 (func
1627 1644 ('symbol', 'contains')
1628 1645 ('string', 'glob:*')))
1629 1646 (group
1630 1647 (or
1631 ('symbol', '0')
1632 ('symbol', '2')
1633 ('symbol', '1'))))
1648 (list
1649 ('symbol', '0')
1650 ('symbol', '2')
1651 ('symbol', '1')))))
1634 1652 * optimized:
1635 1653 (and
1636 1654 (func
1637 1655 ('symbol', '_list')
1638 1656 ('string', '0\x002\x001'))
1639 1657 (func
1640 1658 ('symbol', 'reverse')
1641 1659 (func
1642 1660 ('symbol', 'contains')
1643 1661 ('string', 'glob:*'))))
1644 1662 * set:
1645 1663 <filteredset
1646 1664 <baseset [1, 2, 0]>,
1647 1665 <contains 'glob:*'>>
1648 1666 1
1649 1667 2
1650 1668 0
1651 1669 BROKEN: should be '2 1 0'
1652 1670
1653 1671 test sort revset
1654 1672 --------------------------------------------
1655 1673
1656 1674 test when adding two unordered revsets
1657 1675
1658 1676 $ log 'sort(keyword(issue) or modifies(b))'
1659 1677 4
1660 1678 6
1661 1679
1662 1680 test when sorting a reversed collection in the same way it is
1663 1681
1664 1682 $ log 'sort(reverse(all()), -rev)'
1665 1683 9
1666 1684 8
1667 1685 7
1668 1686 6
1669 1687 5
1670 1688 4
1671 1689 3
1672 1690 2
1673 1691 1
1674 1692 0
1675 1693
1676 1694 test when sorting a reversed collection
1677 1695
1678 1696 $ log 'sort(reverse(all()), rev)'
1679 1697 0
1680 1698 1
1681 1699 2
1682 1700 3
1683 1701 4
1684 1702 5
1685 1703 6
1686 1704 7
1687 1705 8
1688 1706 9
1689 1707
1690 1708
1691 1709 test sorting two sorted collections in different orders
1692 1710
1693 1711 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
1694 1712 2
1695 1713 6
1696 1714 8
1697 1715 9
1698 1716
1699 1717 test sorting two sorted collections in different orders backwards
1700 1718
1701 1719 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
1702 1720 9
1703 1721 8
1704 1722 6
1705 1723 2
1706 1724
1707 1725 test empty sort key which is noop
1708 1726
1709 1727 $ log 'sort(0 + 2 + 1, "")'
1710 1728 0
1711 1729 2
1712 1730 1
1713 1731
1714 1732 test invalid sort keys
1715 1733
1716 1734 $ log 'sort(all(), -invalid)'
1717 1735 hg: parse error: unknown sort key '-invalid'
1718 1736 [255]
1719 1737
1720 1738 $ cd ..
1721 1739
1722 1740 test sorting by multiple keys including variable-length strings
1723 1741
1724 1742 $ hg init sorting
1725 1743 $ cd sorting
1726 1744 $ cat <<EOF >> .hg/hgrc
1727 1745 > [ui]
1728 1746 > logtemplate = '{rev} {branch|p5}{desc|p5}{author|p5}{date|hgdate}\n'
1729 1747 > [templatealias]
1730 1748 > p5(s) = pad(s, 5)
1731 1749 > EOF
1732 1750 $ hg branch -qf b12
1733 1751 $ hg ci -m m111 -u u112 -d '111 10800'
1734 1752 $ hg branch -qf b11
1735 1753 $ hg ci -m m12 -u u111 -d '112 7200'
1736 1754 $ hg branch -qf b111
1737 1755 $ hg ci -m m11 -u u12 -d '111 3600'
1738 1756 $ hg branch -qf b112
1739 1757 $ hg ci -m m111 -u u11 -d '120 0'
1740 1758 $ hg branch -qf b111
1741 1759 $ hg ci -m m112 -u u111 -d '110 14400'
1742 1760 created new head
1743 1761
1744 1762 compare revisions (has fast path):
1745 1763
1746 1764 $ hg log -r 'sort(all(), rev)'
1747 1765 0 b12 m111 u112 111 10800
1748 1766 1 b11 m12 u111 112 7200
1749 1767 2 b111 m11 u12 111 3600
1750 1768 3 b112 m111 u11 120 0
1751 1769 4 b111 m112 u111 110 14400
1752 1770
1753 1771 $ hg log -r 'sort(all(), -rev)'
1754 1772 4 b111 m112 u111 110 14400
1755 1773 3 b112 m111 u11 120 0
1756 1774 2 b111 m11 u12 111 3600
1757 1775 1 b11 m12 u111 112 7200
1758 1776 0 b12 m111 u112 111 10800
1759 1777
1760 1778 compare variable-length strings (issue5218):
1761 1779
1762 1780 $ hg log -r 'sort(all(), branch)'
1763 1781 1 b11 m12 u111 112 7200
1764 1782 2 b111 m11 u12 111 3600
1765 1783 4 b111 m112 u111 110 14400
1766 1784 3 b112 m111 u11 120 0
1767 1785 0 b12 m111 u112 111 10800
1768 1786
1769 1787 $ hg log -r 'sort(all(), -branch)'
1770 1788 0 b12 m111 u112 111 10800
1771 1789 3 b112 m111 u11 120 0
1772 1790 2 b111 m11 u12 111 3600
1773 1791 4 b111 m112 u111 110 14400
1774 1792 1 b11 m12 u111 112 7200
1775 1793
1776 1794 $ hg log -r 'sort(all(), desc)'
1777 1795 2 b111 m11 u12 111 3600
1778 1796 0 b12 m111 u112 111 10800
1779 1797 3 b112 m111 u11 120 0
1780 1798 4 b111 m112 u111 110 14400
1781 1799 1 b11 m12 u111 112 7200
1782 1800
1783 1801 $ hg log -r 'sort(all(), -desc)'
1784 1802 1 b11 m12 u111 112 7200
1785 1803 4 b111 m112 u111 110 14400
1786 1804 0 b12 m111 u112 111 10800
1787 1805 3 b112 m111 u11 120 0
1788 1806 2 b111 m11 u12 111 3600
1789 1807
1790 1808 $ hg log -r 'sort(all(), user)'
1791 1809 3 b112 m111 u11 120 0
1792 1810 1 b11 m12 u111 112 7200
1793 1811 4 b111 m112 u111 110 14400
1794 1812 0 b12 m111 u112 111 10800
1795 1813 2 b111 m11 u12 111 3600
1796 1814
1797 1815 $ hg log -r 'sort(all(), -user)'
1798 1816 2 b111 m11 u12 111 3600
1799 1817 0 b12 m111 u112 111 10800
1800 1818 1 b11 m12 u111 112 7200
1801 1819 4 b111 m112 u111 110 14400
1802 1820 3 b112 m111 u11 120 0
1803 1821
1804 1822 compare dates (tz offset should have no effect):
1805 1823
1806 1824 $ hg log -r 'sort(all(), date)'
1807 1825 4 b111 m112 u111 110 14400
1808 1826 0 b12 m111 u112 111 10800
1809 1827 2 b111 m11 u12 111 3600
1810 1828 1 b11 m12 u111 112 7200
1811 1829 3 b112 m111 u11 120 0
1812 1830
1813 1831 $ hg log -r 'sort(all(), -date)'
1814 1832 3 b112 m111 u11 120 0
1815 1833 1 b11 m12 u111 112 7200
1816 1834 0 b12 m111 u112 111 10800
1817 1835 2 b111 m11 u12 111 3600
1818 1836 4 b111 m112 u111 110 14400
1819 1837
1820 1838 be aware that 'sort(x, -k)' is not exactly the same as 'reverse(sort(x, k))'
1821 1839 because '-k' reverses the comparison, not the list itself:
1822 1840
1823 1841 $ hg log -r 'sort(0 + 2, date)'
1824 1842 0 b12 m111 u112 111 10800
1825 1843 2 b111 m11 u12 111 3600
1826 1844
1827 1845 $ hg log -r 'sort(0 + 2, -date)'
1828 1846 0 b12 m111 u112 111 10800
1829 1847 2 b111 m11 u12 111 3600
1830 1848
1831 1849 $ hg log -r 'reverse(sort(0 + 2, date))'
1832 1850 2 b111 m11 u12 111 3600
1833 1851 0 b12 m111 u112 111 10800
1834 1852
1835 1853 sort by multiple keys:
1836 1854
1837 1855 $ hg log -r 'sort(all(), "branch -rev")'
1838 1856 1 b11 m12 u111 112 7200
1839 1857 4 b111 m112 u111 110 14400
1840 1858 2 b111 m11 u12 111 3600
1841 1859 3 b112 m111 u11 120 0
1842 1860 0 b12 m111 u112 111 10800
1843 1861
1844 1862 $ hg log -r 'sort(all(), "-desc -date")'
1845 1863 1 b11 m12 u111 112 7200
1846 1864 4 b111 m112 u111 110 14400
1847 1865 3 b112 m111 u11 120 0
1848 1866 0 b12 m111 u112 111 10800
1849 1867 2 b111 m11 u12 111 3600
1850 1868
1851 1869 $ hg log -r 'sort(all(), "user -branch date rev")'
1852 1870 3 b112 m111 u11 120 0
1853 1871 4 b111 m112 u111 110 14400
1854 1872 1 b11 m12 u111 112 7200
1855 1873 0 b12 m111 u112 111 10800
1856 1874 2 b111 m11 u12 111 3600
1857 1875
1858 1876 toposort prioritises graph branches
1859 1877
1860 1878 $ hg up 2
1861 1879 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1862 1880 $ touch a
1863 1881 $ hg addremove
1864 1882 adding a
1865 1883 $ hg ci -m 't1' -u 'tu' -d '130 0'
1866 1884 created new head
1867 1885 $ echo 'a' >> a
1868 1886 $ hg ci -m 't2' -u 'tu' -d '130 0'
1869 1887 $ hg book book1
1870 1888 $ hg up 4
1871 1889 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1872 1890 (leaving bookmark book1)
1873 1891 $ touch a
1874 1892 $ hg addremove
1875 1893 adding a
1876 1894 $ hg ci -m 't3' -u 'tu' -d '130 0'
1877 1895
1878 1896 $ hg log -r 'sort(all(), topo)'
1879 1897 7 b111 t3 tu 130 0
1880 1898 4 b111 m112 u111 110 14400
1881 1899 3 b112 m111 u11 120 0
1882 1900 6 b111 t2 tu 130 0
1883 1901 5 b111 t1 tu 130 0
1884 1902 2 b111 m11 u12 111 3600
1885 1903 1 b11 m12 u111 112 7200
1886 1904 0 b12 m111 u112 111 10800
1887 1905
1888 1906 $ hg log -r 'sort(all(), -topo)'
1889 1907 0 b12 m111 u112 111 10800
1890 1908 1 b11 m12 u111 112 7200
1891 1909 2 b111 m11 u12 111 3600
1892 1910 5 b111 t1 tu 130 0
1893 1911 6 b111 t2 tu 130 0
1894 1912 3 b112 m111 u11 120 0
1895 1913 4 b111 m112 u111 110 14400
1896 1914 7 b111 t3 tu 130 0
1897 1915
1898 1916 $ hg log -r 'sort(all(), topo, topo.firstbranch=book1)'
1899 1917 6 b111 t2 tu 130 0
1900 1918 5 b111 t1 tu 130 0
1901 1919 7 b111 t3 tu 130 0
1902 1920 4 b111 m112 u111 110 14400
1903 1921 3 b112 m111 u11 120 0
1904 1922 2 b111 m11 u12 111 3600
1905 1923 1 b11 m12 u111 112 7200
1906 1924 0 b12 m111 u112 111 10800
1907 1925
1908 1926 topographical sorting can't be combined with other sort keys, and you can't
1909 1927 use the topo.firstbranch option when topo sort is not active:
1910 1928
1911 1929 $ hg log -r 'sort(all(), "topo user")'
1912 1930 hg: parse error: topo sort order cannot be combined with other sort keys
1913 1931 [255]
1914 1932
1915 1933 $ hg log -r 'sort(all(), user, topo.firstbranch=book1)'
1916 1934 hg: parse error: topo.firstbranch can only be used when using the topo sort key
1917 1935 [255]
1918 1936
1919 1937 topo.firstbranch should accept any kind of expressions:
1920 1938
1921 1939 $ hg log -r 'sort(0, topo, topo.firstbranch=(book1))'
1922 1940 0 b12 m111 u112 111 10800
1923 1941
1924 1942 $ cd ..
1925 1943 $ cd repo
1926 1944
1927 1945 test subtracting something from an addset
1928 1946
1929 1947 $ log '(outgoing() or removes(a)) - removes(a)'
1930 1948 8
1931 1949 9
1932 1950
1933 1951 test intersecting something with an addset
1934 1952
1935 1953 $ log 'parents(outgoing() or removes(a))'
1936 1954 1
1937 1955 4
1938 1956 5
1939 1957 8
1940 1958
1941 1959 test that `or` operation combines elements in the right order:
1942 1960
1943 1961 $ log '3:4 or 2:5'
1944 1962 3
1945 1963 4
1946 1964 2
1947 1965 5
1948 1966 $ log '3:4 or 5:2'
1949 1967 3
1950 1968 4
1951 1969 5
1952 1970 2
1953 1971 $ log 'sort(3:4 or 2:5)'
1954 1972 2
1955 1973 3
1956 1974 4
1957 1975 5
1958 1976 $ log 'sort(3:4 or 5:2)'
1959 1977 2
1960 1978 3
1961 1979 4
1962 1980 5
1963 1981
1964 1982 test that more than one `-r`s are combined in the right order and deduplicated:
1965 1983
1966 1984 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
1967 1985 3
1968 1986 4
1969 1987 5
1970 1988 2
1971 1989 0
1972 1990 1
1973 1991
1974 1992 test that `or` operation skips duplicated revisions from right-hand side
1975 1993
1976 1994 $ try 'reverse(1::5) or ancestors(4)'
1977 1995 (or
1978 (func
1979 ('symbol', 'reverse')
1980 (dagrange
1981 ('symbol', '1')
1982 ('symbol', '5')))
1983 (func
1984 ('symbol', 'ancestors')
1985 ('symbol', '4')))
1996 (list
1997 (func
1998 ('symbol', 'reverse')
1999 (dagrange
2000 ('symbol', '1')
2001 ('symbol', '5')))
2002 (func
2003 ('symbol', 'ancestors')
2004 ('symbol', '4'))))
1986 2005 * set:
1987 2006 <addset
1988 2007 <baseset- [1, 3, 5]>,
1989 2008 <generatorset+>>
1990 2009 5
1991 2010 3
1992 2011 1
1993 2012 0
1994 2013 2
1995 2014 4
1996 2015 $ try 'sort(ancestors(4) or reverse(1::5))'
1997 2016 (func
1998 2017 ('symbol', 'sort')
1999 2018 (or
2000 (func
2001 ('symbol', 'ancestors')
2002 ('symbol', '4'))
2003 (func
2004 ('symbol', 'reverse')
2005 (dagrange
2006 ('symbol', '1')
2007 ('symbol', '5')))))
2019 (list
2020 (func
2021 ('symbol', 'ancestors')
2022 ('symbol', '4'))
2023 (func
2024 ('symbol', 'reverse')
2025 (dagrange
2026 ('symbol', '1')
2027 ('symbol', '5'))))))
2008 2028 * set:
2009 2029 <addset+
2010 2030 <generatorset+>,
2011 2031 <baseset- [1, 3, 5]>>
2012 2032 0
2013 2033 1
2014 2034 2
2015 2035 3
2016 2036 4
2017 2037 5
2018 2038
2019 2039 test optimization of trivial `or` operation
2020 2040
2021 2041 $ try --optimize '0|(1)|"2"|-2|tip|null'
2022 2042 (or
2023 ('symbol', '0')
2024 (group
2025 ('symbol', '1'))
2026 ('string', '2')
2027 (negate
2028 ('symbol', '2'))
2029 ('symbol', 'tip')
2030 ('symbol', 'null'))
2043 (list
2044 ('symbol', '0')
2045 (group
2046 ('symbol', '1'))
2047 ('string', '2')
2048 (negate
2049 ('symbol', '2'))
2050 ('symbol', 'tip')
2051 ('symbol', 'null')))
2031 2052 * optimized:
2032 2053 (func
2033 2054 ('symbol', '_list')
2034 2055 ('string', '0\x001\x002\x00-2\x00tip\x00null'))
2035 2056 * set:
2036 2057 <baseset [0, 1, 2, 8, 9, -1]>
2037 2058 0
2038 2059 1
2039 2060 2
2040 2061 8
2041 2062 9
2042 2063 -1
2043 2064
2044 2065 $ try --optimize '0|1|2:3'
2045 2066 (or
2046 ('symbol', '0')
2047 ('symbol', '1')
2048 (range
2049 ('symbol', '2')
2050 ('symbol', '3')))
2067 (list
2068 ('symbol', '0')
2069 ('symbol', '1')
2070 (range
2071 ('symbol', '2')
2072 ('symbol', '3'))))
2051 2073 * optimized:
2052 2074 (or
2053 (func
2054 ('symbol', '_list')
2055 ('string', '0\x001'))
2056 (range
2057 ('symbol', '2')
2058 ('symbol', '3')))
2075 (list
2076 (func
2077 ('symbol', '_list')
2078 ('string', '0\x001'))
2079 (range
2080 ('symbol', '2')
2081 ('symbol', '3'))))
2059 2082 * set:
2060 2083 <addset
2061 2084 <baseset [0, 1]>,
2062 2085 <spanset+ 2:3>>
2063 2086 0
2064 2087 1
2065 2088 2
2066 2089 3
2067 2090
2068 2091 $ try --optimize '0:1|2|3:4|5|6'
2069 2092 (or
2070 (range
2071 ('symbol', '0')
2072 ('symbol', '1'))
2073 ('symbol', '2')
2074 (range
2075 ('symbol', '3')
2076 ('symbol', '4'))
2077 ('symbol', '5')
2078 ('symbol', '6'))
2093 (list
2094 (range
2095 ('symbol', '0')
2096 ('symbol', '1'))
2097 ('symbol', '2')
2098 (range
2099 ('symbol', '3')
2100 ('symbol', '4'))
2101 ('symbol', '5')
2102 ('symbol', '6')))
2079 2103 * optimized:
2080 2104 (or
2081 (range
2082 ('symbol', '0')
2083 ('symbol', '1'))
2084 ('symbol', '2')
2085 (range
2086 ('symbol', '3')
2087 ('symbol', '4'))
2088 (func
2089 ('symbol', '_list')
2090 ('string', '5\x006')))
2105 (list
2106 (range
2107 ('symbol', '0')
2108 ('symbol', '1'))
2109 ('symbol', '2')
2110 (range
2111 ('symbol', '3')
2112 ('symbol', '4'))
2113 (func
2114 ('symbol', '_list')
2115 ('string', '5\x006'))))
2091 2116 * set:
2092 2117 <addset
2093 2118 <addset
2094 2119 <spanset+ 0:1>,
2095 2120 <baseset [2]>>,
2096 2121 <addset
2097 2122 <spanset+ 3:4>,
2098 2123 <baseset [5, 6]>>>
2099 2124 0
2100 2125 1
2101 2126 2
2102 2127 3
2103 2128 4
2104 2129 5
2105 2130 6
2106 2131
2107 2132 unoptimized `or` looks like this
2108 2133
2109 2134 $ try --no-optimized -p analyzed '0|1|2|3|4'
2110 2135 * analyzed:
2111 2136 (or
2112 ('symbol', '0')
2113 ('symbol', '1')
2114 ('symbol', '2')
2115 ('symbol', '3')
2116 ('symbol', '4'))
2137 (list
2138 ('symbol', '0')
2139 ('symbol', '1')
2140 ('symbol', '2')
2141 ('symbol', '3')
2142 ('symbol', '4')))
2117 2143 * set:
2118 2144 <addset
2119 2145 <addset
2120 2146 <baseset [0]>,
2121 2147 <baseset [1]>>,
2122 2148 <addset
2123 2149 <baseset [2]>,
2124 2150 <addset
2125 2151 <baseset [3]>,
2126 2152 <baseset [4]>>>>
2127 2153 0
2128 2154 1
2129 2155 2
2130 2156 3
2131 2157 4
2132 2158
2133 2159 test that `_list` should be narrowed by provided `subset`
2134 2160
2135 2161 $ log '0:2 and (null|1|2|3)'
2136 2162 1
2137 2163 2
2138 2164
2139 2165 test that `_list` should remove duplicates
2140 2166
2141 2167 $ log '0|1|2|1|2|-1|tip'
2142 2168 0
2143 2169 1
2144 2170 2
2145 2171 9
2146 2172
2147 2173 test unknown revision in `_list`
2148 2174
2149 2175 $ log '0|unknown'
2150 2176 abort: unknown revision 'unknown'!
2151 2177 [255]
2152 2178
2153 2179 test integer range in `_list`
2154 2180
2155 2181 $ log '-1|-10'
2156 2182 9
2157 2183 0
2158 2184
2159 2185 $ log '-10|-11'
2160 2186 abort: unknown revision '-11'!
2161 2187 [255]
2162 2188
2163 2189 $ log '9|10'
2164 2190 abort: unknown revision '10'!
2165 2191 [255]
2166 2192
2167 2193 test '0000' != '0' in `_list`
2168 2194
2169 2195 $ log '0|0000'
2170 2196 0
2171 2197 -1
2172 2198
2173 2199 test ',' in `_list`
2174 2200 $ log '0,1'
2175 2201 hg: parse error: can't use a list in this context
2176 2202 (see hg help "revsets.x or y")
2177 2203 [255]
2178 2204 $ try '0,1,2'
2179 2205 (list
2180 2206 ('symbol', '0')
2181 2207 ('symbol', '1')
2182 2208 ('symbol', '2'))
2183 2209 hg: parse error: can't use a list in this context
2184 2210 (see hg help "revsets.x or y")
2185 2211 [255]
2186 2212
2187 2213 test that chained `or` operations make balanced addsets
2188 2214
2189 2215 $ try '0:1|1:2|2:3|3:4|4:5'
2190 2216 (or
2191 (range
2192 ('symbol', '0')
2193 ('symbol', '1'))
2194 (range
2195 ('symbol', '1')
2196 ('symbol', '2'))
2197 (range
2198 ('symbol', '2')
2199 ('symbol', '3'))
2200 (range
2201 ('symbol', '3')
2202 ('symbol', '4'))
2203 (range
2204 ('symbol', '4')
2205 ('symbol', '5')))
2217 (list
2218 (range
2219 ('symbol', '0')
2220 ('symbol', '1'))
2221 (range
2222 ('symbol', '1')
2223 ('symbol', '2'))
2224 (range
2225 ('symbol', '2')
2226 ('symbol', '3'))
2227 (range
2228 ('symbol', '3')
2229 ('symbol', '4'))
2230 (range
2231 ('symbol', '4')
2232 ('symbol', '5'))))
2206 2233 * set:
2207 2234 <addset
2208 2235 <addset
2209 2236 <spanset+ 0:1>,
2210 2237 <spanset+ 1:2>>,
2211 2238 <addset
2212 2239 <spanset+ 2:3>,
2213 2240 <addset
2214 2241 <spanset+ 3:4>,
2215 2242 <spanset+ 4:5>>>>
2216 2243 0
2217 2244 1
2218 2245 2
2219 2246 3
2220 2247 4
2221 2248 5
2222 2249
2223 2250 no crash by empty group "()" while optimizing `or` operations
2224 2251
2225 2252 $ try --optimize '0|()'
2226 2253 (or
2227 ('symbol', '0')
2228 (group
2229 None))
2254 (list
2255 ('symbol', '0')
2256 (group
2257 None)))
2230 2258 * optimized:
2231 2259 (or
2232 ('symbol', '0')
2233 None)
2260 (list
2261 ('symbol', '0')
2262 None))
2234 2263 hg: parse error: missing argument
2235 2264 [255]
2236 2265
2237 2266 test that chained `or` operations never eat up stack (issue4624)
2238 2267 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
2239 2268
2240 2269 $ hg log -T '{rev}\n' -r `python -c "print '+'.join(['0:1'] * 500)"`
2241 2270 0
2242 2271 1
2243 2272
2244 2273 test that repeated `-r` options never eat up stack (issue4565)
2245 2274 (uses `-r 0::1` to avoid possible optimization at old-style parser)
2246 2275
2247 2276 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
2248 2277 0
2249 2278 1
2250 2279
2251 2280 check that conversion to only works
2252 2281 $ try --optimize '::3 - ::1'
2253 2282 (minus
2254 2283 (dagrangepre
2255 2284 ('symbol', '3'))
2256 2285 (dagrangepre
2257 2286 ('symbol', '1')))
2258 2287 * optimized:
2259 2288 (func
2260 2289 ('symbol', 'only')
2261 2290 (list
2262 2291 ('symbol', '3')
2263 2292 ('symbol', '1')))
2264 2293 * set:
2265 2294 <baseset+ [3]>
2266 2295 3
2267 2296 $ try --optimize 'ancestors(1) - ancestors(3)'
2268 2297 (minus
2269 2298 (func
2270 2299 ('symbol', 'ancestors')
2271 2300 ('symbol', '1'))
2272 2301 (func
2273 2302 ('symbol', 'ancestors')
2274 2303 ('symbol', '3')))
2275 2304 * optimized:
2276 2305 (func
2277 2306 ('symbol', 'only')
2278 2307 (list
2279 2308 ('symbol', '1')
2280 2309 ('symbol', '3')))
2281 2310 * set:
2282 2311 <baseset+ []>
2283 2312 $ try --optimize 'not ::2 and ::6'
2284 2313 (and
2285 2314 (not
2286 2315 (dagrangepre
2287 2316 ('symbol', '2')))
2288 2317 (dagrangepre
2289 2318 ('symbol', '6')))
2290 2319 * optimized:
2291 2320 (func
2292 2321 ('symbol', 'only')
2293 2322 (list
2294 2323 ('symbol', '6')
2295 2324 ('symbol', '2')))
2296 2325 * set:
2297 2326 <baseset+ [3, 4, 5, 6]>
2298 2327 3
2299 2328 4
2300 2329 5
2301 2330 6
2302 2331 $ try --optimize 'ancestors(6) and not ancestors(4)'
2303 2332 (and
2304 2333 (func
2305 2334 ('symbol', 'ancestors')
2306 2335 ('symbol', '6'))
2307 2336 (not
2308 2337 (func
2309 2338 ('symbol', 'ancestors')
2310 2339 ('symbol', '4'))))
2311 2340 * optimized:
2312 2341 (func
2313 2342 ('symbol', 'only')
2314 2343 (list
2315 2344 ('symbol', '6')
2316 2345 ('symbol', '4')))
2317 2346 * set:
2318 2347 <baseset+ [3, 5, 6]>
2319 2348 3
2320 2349 5
2321 2350 6
2322 2351
2323 2352 no crash by empty group "()" while optimizing to "only()"
2324 2353
2325 2354 $ try --optimize '::1 and ()'
2326 2355 (and
2327 2356 (dagrangepre
2328 2357 ('symbol', '1'))
2329 2358 (group
2330 2359 None))
2331 2360 * optimized:
2332 2361 (and
2333 2362 None
2334 2363 (func
2335 2364 ('symbol', 'ancestors')
2336 2365 ('symbol', '1')))
2337 2366 hg: parse error: missing argument
2338 2367 [255]
2339 2368
2340 2369 invalid function call should not be optimized to only()
2341 2370
2342 2371 $ log '"ancestors"(6) and not ancestors(4)'
2343 2372 hg: parse error: not a symbol
2344 2373 [255]
2345 2374
2346 2375 $ log 'ancestors(6) and not "ancestors"(4)'
2347 2376 hg: parse error: not a symbol
2348 2377 [255]
2349 2378
2350 2379 we can use patterns when searching for tags
2351 2380
2352 2381 $ log 'tag("1..*")'
2353 2382 abort: tag '1..*' does not exist!
2354 2383 [255]
2355 2384 $ log 'tag("re:1..*")'
2356 2385 6
2357 2386 $ log 'tag("re:[0-9].[0-9]")'
2358 2387 6
2359 2388 $ log 'tag("literal:1.0")'
2360 2389 6
2361 2390 $ log 'tag("re:0..*")'
2362 2391
2363 2392 $ log 'tag(unknown)'
2364 2393 abort: tag 'unknown' does not exist!
2365 2394 [255]
2366 2395 $ log 'tag("re:unknown")'
2367 2396 $ log 'present(tag("unknown"))'
2368 2397 $ log 'present(tag("re:unknown"))'
2369 2398 $ log 'branch(unknown)'
2370 2399 abort: unknown revision 'unknown'!
2371 2400 [255]
2372 2401 $ log 'branch("literal:unknown")'
2373 2402 abort: branch 'unknown' does not exist!
2374 2403 [255]
2375 2404 $ log 'branch("re:unknown")'
2376 2405 $ log 'present(branch("unknown"))'
2377 2406 $ log 'present(branch("re:unknown"))'
2378 2407 $ log 'user(bob)'
2379 2408 2
2380 2409
2381 2410 $ log '4::8'
2382 2411 4
2383 2412 8
2384 2413 $ log '4:8'
2385 2414 4
2386 2415 5
2387 2416 6
2388 2417 7
2389 2418 8
2390 2419
2391 2420 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
2392 2421 4
2393 2422 2
2394 2423 5
2395 2424
2396 2425 $ log 'not 0 and 0:2'
2397 2426 1
2398 2427 2
2399 2428 $ log 'not 1 and 0:2'
2400 2429 0
2401 2430 2
2402 2431 $ log 'not 2 and 0:2'
2403 2432 0
2404 2433 1
2405 2434 $ log '(1 and 2)::'
2406 2435 $ log '(1 and 2):'
2407 2436 $ log '(1 and 2):3'
2408 2437 $ log 'sort(head(), -rev)'
2409 2438 9
2410 2439 7
2411 2440 6
2412 2441 5
2413 2442 4
2414 2443 3
2415 2444 2
2416 2445 1
2417 2446 0
2418 2447 $ log '4::8 - 8'
2419 2448 4
2420 2449
2421 2450 matching() should preserve the order of the input set:
2422 2451
2423 2452 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
2424 2453 2
2425 2454 3
2426 2455 1
2427 2456
2428 2457 $ log 'named("unknown")'
2429 2458 abort: namespace 'unknown' does not exist!
2430 2459 [255]
2431 2460 $ log 'named("re:unknown")'
2432 2461 abort: no namespace exists that match 'unknown'!
2433 2462 [255]
2434 2463 $ log 'present(named("unknown"))'
2435 2464 $ log 'present(named("re:unknown"))'
2436 2465
2437 2466 $ log 'tag()'
2438 2467 6
2439 2468 $ log 'named("tags")'
2440 2469 6
2441 2470
2442 2471 issue2437
2443 2472
2444 2473 $ log '3 and p1(5)'
2445 2474 3
2446 2475 $ log '4 and p2(6)'
2447 2476 4
2448 2477 $ log '1 and parents(:2)'
2449 2478 1
2450 2479 $ log '2 and children(1:)'
2451 2480 2
2452 2481 $ log 'roots(all()) or roots(all())'
2453 2482 0
2454 2483 $ hg debugrevspec 'roots(all()) or roots(all())'
2455 2484 0
2456 2485 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
2457 2486 9
2458 2487 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
2459 2488 4
2460 2489
2461 2490 issue2654: report a parse error if the revset was not completely parsed
2462 2491
2463 2492 $ log '1 OR 2'
2464 2493 hg: parse error at 2: invalid token
2465 2494 [255]
2466 2495
2467 2496 or operator should preserve ordering:
2468 2497 $ log 'reverse(2::4) or tip'
2469 2498 4
2470 2499 2
2471 2500 9
2472 2501
2473 2502 parentrevspec
2474 2503
2475 2504 $ log 'merge()^0'
2476 2505 6
2477 2506 $ log 'merge()^'
2478 2507 5
2479 2508 $ log 'merge()^1'
2480 2509 5
2481 2510 $ log 'merge()^2'
2482 2511 4
2483 2512 $ log 'merge()^^'
2484 2513 3
2485 2514 $ log 'merge()^1^'
2486 2515 3
2487 2516 $ log 'merge()^^^'
2488 2517 1
2489 2518
2490 2519 $ log 'merge()~0'
2491 2520 6
2492 2521 $ log 'merge()~1'
2493 2522 5
2494 2523 $ log 'merge()~2'
2495 2524 3
2496 2525 $ log 'merge()~2^1'
2497 2526 1
2498 2527 $ log 'merge()~3'
2499 2528 1
2500 2529
2501 2530 $ log '(-3:tip)^'
2502 2531 4
2503 2532 6
2504 2533 8
2505 2534
2506 2535 $ log 'tip^foo'
2507 2536 hg: parse error: ^ expects a number 0, 1, or 2
2508 2537 [255]
2509 2538
2510 2539 Bogus function gets suggestions
2511 2540 $ log 'add()'
2512 2541 hg: parse error: unknown identifier: add
2513 2542 (did you mean adds?)
2514 2543 [255]
2515 2544 $ log 'added()'
2516 2545 hg: parse error: unknown identifier: added
2517 2546 (did you mean adds?)
2518 2547 [255]
2519 2548 $ log 'remo()'
2520 2549 hg: parse error: unknown identifier: remo
2521 2550 (did you mean one of remote, removes?)
2522 2551 [255]
2523 2552 $ log 'babar()'
2524 2553 hg: parse error: unknown identifier: babar
2525 2554 [255]
2526 2555
2527 2556 Bogus function with a similar internal name doesn't suggest the internal name
2528 2557 $ log 'matches()'
2529 2558 hg: parse error: unknown identifier: matches
2530 2559 (did you mean matching?)
2531 2560 [255]
2532 2561
2533 2562 Undocumented functions aren't suggested as similar either
2534 2563 $ log 'wdir2()'
2535 2564 hg: parse error: unknown identifier: wdir2
2536 2565 [255]
2537 2566
2538 2567 multiple revspecs
2539 2568
2540 2569 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
2541 2570 8
2542 2571 9
2543 2572 4
2544 2573 5
2545 2574 6
2546 2575 7
2547 2576
2548 2577 test usage in revpair (with "+")
2549 2578
2550 2579 (real pair)
2551 2580
2552 2581 $ hg diff -r 'tip^^' -r 'tip'
2553 2582 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2554 2583 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2555 2584 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2556 2585 @@ -0,0 +1,1 @@
2557 2586 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2558 2587 $ hg diff -r 'tip^^::tip'
2559 2588 diff -r 2326846efdab -r 24286f4ae135 .hgtags
2560 2589 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2561 2590 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2562 2591 @@ -0,0 +1,1 @@
2563 2592 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2564 2593
2565 2594 (single rev)
2566 2595
2567 2596 $ hg diff -r 'tip^' -r 'tip^'
2568 2597 $ hg diff -r 'tip^:tip^'
2569 2598
2570 2599 (single rev that does not looks like a range)
2571 2600
2572 2601 $ hg diff -r 'tip^::tip^ or tip^'
2573 2602 diff -r d5d0dcbdc4d9 .hgtags
2574 2603 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2575 2604 +++ b/.hgtags * (glob)
2576 2605 @@ -0,0 +1,1 @@
2577 2606 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2578 2607 $ hg diff -r 'tip^ or tip^'
2579 2608 diff -r d5d0dcbdc4d9 .hgtags
2580 2609 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2581 2610 +++ b/.hgtags * (glob)
2582 2611 @@ -0,0 +1,1 @@
2583 2612 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
2584 2613
2585 2614 (no rev)
2586 2615
2587 2616 $ hg diff -r 'author("babar") or author("celeste")'
2588 2617 abort: empty revision range
2589 2618 [255]
2590 2619
2591 2620 aliases:
2592 2621
2593 2622 $ echo '[revsetalias]' >> .hg/hgrc
2594 2623 $ echo 'm = merge()' >> .hg/hgrc
2595 2624 (revset aliases can override builtin revsets)
2596 2625 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
2597 2626 $ echo 'sincem = descendants(m)' >> .hg/hgrc
2598 2627 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
2599 2628 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2600 2629 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
2601 2630
2602 2631 $ try m
2603 2632 ('symbol', 'm')
2604 2633 * expanded:
2605 2634 (func
2606 2635 ('symbol', 'merge')
2607 2636 None)
2608 2637 * set:
2609 2638 <filteredset
2610 2639 <fullreposet+ 0:9>,
2611 2640 <merge>>
2612 2641 6
2613 2642
2614 2643 $ HGPLAIN=1
2615 2644 $ export HGPLAIN
2616 2645 $ try m
2617 2646 ('symbol', 'm')
2618 2647 abort: unknown revision 'm'!
2619 2648 [255]
2620 2649
2621 2650 $ HGPLAINEXCEPT=revsetalias
2622 2651 $ export HGPLAINEXCEPT
2623 2652 $ try m
2624 2653 ('symbol', 'm')
2625 2654 * expanded:
2626 2655 (func
2627 2656 ('symbol', 'merge')
2628 2657 None)
2629 2658 * set:
2630 2659 <filteredset
2631 2660 <fullreposet+ 0:9>,
2632 2661 <merge>>
2633 2662 6
2634 2663
2635 2664 $ unset HGPLAIN
2636 2665 $ unset HGPLAINEXCEPT
2637 2666
2638 2667 $ try 'p2(.)'
2639 2668 (func
2640 2669 ('symbol', 'p2')
2641 2670 ('symbol', '.'))
2642 2671 * expanded:
2643 2672 (func
2644 2673 ('symbol', 'p1')
2645 2674 ('symbol', '.'))
2646 2675 * set:
2647 2676 <baseset+ [8]>
2648 2677 8
2649 2678
2650 2679 $ HGPLAIN=1
2651 2680 $ export HGPLAIN
2652 2681 $ try 'p2(.)'
2653 2682 (func
2654 2683 ('symbol', 'p2')
2655 2684 ('symbol', '.'))
2656 2685 * set:
2657 2686 <baseset+ []>
2658 2687
2659 2688 $ HGPLAINEXCEPT=revsetalias
2660 2689 $ export HGPLAINEXCEPT
2661 2690 $ try 'p2(.)'
2662 2691 (func
2663 2692 ('symbol', 'p2')
2664 2693 ('symbol', '.'))
2665 2694 * expanded:
2666 2695 (func
2667 2696 ('symbol', 'p1')
2668 2697 ('symbol', '.'))
2669 2698 * set:
2670 2699 <baseset+ [8]>
2671 2700 8
2672 2701
2673 2702 $ unset HGPLAIN
2674 2703 $ unset HGPLAINEXCEPT
2675 2704
2676 2705 test alias recursion
2677 2706
2678 2707 $ try sincem
2679 2708 ('symbol', 'sincem')
2680 2709 * expanded:
2681 2710 (func
2682 2711 ('symbol', 'descendants')
2683 2712 (func
2684 2713 ('symbol', 'merge')
2685 2714 None))
2686 2715 * set:
2687 2716 <addset+
2688 2717 <filteredset
2689 2718 <fullreposet+ 0:9>,
2690 2719 <merge>>,
2691 2720 <generatorset+>>
2692 2721 6
2693 2722 7
2694 2723
2695 2724 test infinite recursion
2696 2725
2697 2726 $ echo 'recurse1 = recurse2' >> .hg/hgrc
2698 2727 $ echo 'recurse2 = recurse1' >> .hg/hgrc
2699 2728 $ try recurse1
2700 2729 ('symbol', 'recurse1')
2701 2730 hg: parse error: infinite expansion of revset alias "recurse1" detected
2702 2731 [255]
2703 2732
2704 2733 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
2705 2734 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
2706 2735 $ try "level2(level1(1, 2), 3)"
2707 2736 (func
2708 2737 ('symbol', 'level2')
2709 2738 (list
2710 2739 (func
2711 2740 ('symbol', 'level1')
2712 2741 (list
2713 2742 ('symbol', '1')
2714 2743 ('symbol', '2')))
2715 2744 ('symbol', '3')))
2716 2745 * expanded:
2717 2746 (or
2718 ('symbol', '3')
2719 (or
2720 ('symbol', '1')
2721 ('symbol', '2')))
2747 (list
2748 ('symbol', '3')
2749 (or
2750 (list
2751 ('symbol', '1')
2752 ('symbol', '2')))))
2722 2753 * set:
2723 2754 <addset
2724 2755 <baseset [3]>,
2725 2756 <baseset [1, 2]>>
2726 2757 3
2727 2758 1
2728 2759 2
2729 2760
2730 2761 test nesting and variable passing
2731 2762
2732 2763 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
2733 2764 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
2734 2765 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
2735 2766 $ try 'nested(2:5)'
2736 2767 (func
2737 2768 ('symbol', 'nested')
2738 2769 (range
2739 2770 ('symbol', '2')
2740 2771 ('symbol', '5')))
2741 2772 * expanded:
2742 2773 (func
2743 2774 ('symbol', 'max')
2744 2775 (range
2745 2776 ('symbol', '2')
2746 2777 ('symbol', '5')))
2747 2778 * set:
2748 2779 <baseset
2749 2780 <max
2750 2781 <fullreposet+ 0:9>,
2751 2782 <spanset+ 2:5>>>
2752 2783 5
2753 2784
2754 2785 test chained `or` operations are flattened at parsing phase
2755 2786
2756 2787 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
2757 2788 $ try 'chainedorops(0:1, 1:2, 2:3)'
2758 2789 (func
2759 2790 ('symbol', 'chainedorops')
2760 2791 (list
2761 2792 (range
2762 2793 ('symbol', '0')
2763 2794 ('symbol', '1'))
2764 2795 (range
2765 2796 ('symbol', '1')
2766 2797 ('symbol', '2'))
2767 2798 (range
2768 2799 ('symbol', '2')
2769 2800 ('symbol', '3'))))
2770 2801 * expanded:
2771 2802 (or
2772 (range
2773 ('symbol', '0')
2774 ('symbol', '1'))
2775 (range
2776 ('symbol', '1')
2777 ('symbol', '2'))
2778 (range
2779 ('symbol', '2')
2780 ('symbol', '3')))
2803 (list
2804 (range
2805 ('symbol', '0')
2806 ('symbol', '1'))
2807 (range
2808 ('symbol', '1')
2809 ('symbol', '2'))
2810 (range
2811 ('symbol', '2')
2812 ('symbol', '3'))))
2781 2813 * set:
2782 2814 <addset
2783 2815 <spanset+ 0:1>,
2784 2816 <addset
2785 2817 <spanset+ 1:2>,
2786 2818 <spanset+ 2:3>>>
2787 2819 0
2788 2820 1
2789 2821 2
2790 2822 3
2791 2823
2792 2824 test variable isolation, variable placeholders are rewritten as string
2793 2825 then parsed and matched again as string. Check they do not leak too
2794 2826 far away.
2795 2827
2796 2828 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
2797 2829 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
2798 2830 $ try 'callinjection(2:5)'
2799 2831 (func
2800 2832 ('symbol', 'callinjection')
2801 2833 (range
2802 2834 ('symbol', '2')
2803 2835 ('symbol', '5')))
2804 2836 * expanded:
2805 2837 (func
2806 2838 ('symbol', 'descendants')
2807 2839 (func
2808 2840 ('symbol', 'max')
2809 2841 ('string', '$1')))
2810 2842 abort: unknown revision '$1'!
2811 2843 [255]
2812 2844
2813 2845 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
2814 2846 but 'all()' should never be substituded to '0()'.
2815 2847
2816 2848 $ echo 'universe = all()' >> .hg/hgrc
2817 2849 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
2818 2850 $ try 'shadowall(0)'
2819 2851 (func
2820 2852 ('symbol', 'shadowall')
2821 2853 ('symbol', '0'))
2822 2854 * expanded:
2823 2855 (and
2824 2856 ('symbol', '0')
2825 2857 (func
2826 2858 ('symbol', 'all')
2827 2859 None))
2828 2860 * set:
2829 2861 <filteredset
2830 2862 <baseset [0]>,
2831 2863 <spanset+ 0:9>>
2832 2864 0
2833 2865
2834 2866 test unknown reference:
2835 2867
2836 2868 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
2837 2869 (func
2838 2870 ('symbol', 'unknownref')
2839 2871 ('symbol', '0'))
2840 2872 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
2841 2873 [255]
2842 2874
2843 2875 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
2844 2876 ('symbol', 'tip')
2845 2877 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
2846 2878 * set:
2847 2879 <baseset [9]>
2848 2880 9
2849 2881
2850 2882 $ try 'tip'
2851 2883 ('symbol', 'tip')
2852 2884 * set:
2853 2885 <baseset [9]>
2854 2886 9
2855 2887
2856 2888 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
2857 2889 ('symbol', 'tip')
2858 2890 warning: bad declaration of revset alias "bad name": at 4: invalid token
2859 2891 * set:
2860 2892 <baseset [9]>
2861 2893 9
2862 2894 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
2863 2895 $ try 'strictreplacing("foo", tip)'
2864 2896 (func
2865 2897 ('symbol', 'strictreplacing')
2866 2898 (list
2867 2899 ('string', 'foo')
2868 2900 ('symbol', 'tip')))
2869 2901 * expanded:
2870 2902 (or
2871 ('symbol', 'tip')
2872 (func
2873 ('symbol', 'desc')
2874 ('string', '$1')))
2903 (list
2904 ('symbol', 'tip')
2905 (func
2906 ('symbol', 'desc')
2907 ('string', '$1'))))
2875 2908 * set:
2876 2909 <addset
2877 2910 <baseset [9]>,
2878 2911 <filteredset
2879 2912 <fullreposet+ 0:9>,
2880 2913 <desc '$1'>>>
2881 2914 9
2882 2915
2883 2916 $ try 'd(2:5)'
2884 2917 (func
2885 2918 ('symbol', 'd')
2886 2919 (range
2887 2920 ('symbol', '2')
2888 2921 ('symbol', '5')))
2889 2922 * expanded:
2890 2923 (func
2891 2924 ('symbol', 'reverse')
2892 2925 (func
2893 2926 ('symbol', 'sort')
2894 2927 (list
2895 2928 (range
2896 2929 ('symbol', '2')
2897 2930 ('symbol', '5'))
2898 2931 ('symbol', 'date'))))
2899 2932 * set:
2900 2933 <baseset [4, 5, 3, 2]>
2901 2934 4
2902 2935 5
2903 2936 3
2904 2937 2
2905 2938 $ try 'rs(2 or 3, date)'
2906 2939 (func
2907 2940 ('symbol', 'rs')
2908 2941 (list
2909 2942 (or
2910 ('symbol', '2')
2911 ('symbol', '3'))
2943 (list
2944 ('symbol', '2')
2945 ('symbol', '3')))
2912 2946 ('symbol', 'date')))
2913 2947 * expanded:
2914 2948 (func
2915 2949 ('symbol', 'reverse')
2916 2950 (func
2917 2951 ('symbol', 'sort')
2918 2952 (list
2919 2953 (or
2920 ('symbol', '2')
2921 ('symbol', '3'))
2954 (list
2955 ('symbol', '2')
2956 ('symbol', '3')))
2922 2957 ('symbol', 'date'))))
2923 2958 * set:
2924 2959 <baseset [3, 2]>
2925 2960 3
2926 2961 2
2927 2962 $ try 'rs()'
2928 2963 (func
2929 2964 ('symbol', 'rs')
2930 2965 None)
2931 2966 hg: parse error: invalid number of arguments: 0
2932 2967 [255]
2933 2968 $ try 'rs(2)'
2934 2969 (func
2935 2970 ('symbol', 'rs')
2936 2971 ('symbol', '2'))
2937 2972 hg: parse error: invalid number of arguments: 1
2938 2973 [255]
2939 2974 $ try 'rs(2, data, 7)'
2940 2975 (func
2941 2976 ('symbol', 'rs')
2942 2977 (list
2943 2978 ('symbol', '2')
2944 2979 ('symbol', 'data')
2945 2980 ('symbol', '7')))
2946 2981 hg: parse error: invalid number of arguments: 3
2947 2982 [255]
2948 2983 $ try 'rs4(2 or 3, x, x, date)'
2949 2984 (func
2950 2985 ('symbol', 'rs4')
2951 2986 (list
2952 2987 (or
2953 ('symbol', '2')
2954 ('symbol', '3'))
2988 (list
2989 ('symbol', '2')
2990 ('symbol', '3')))
2955 2991 ('symbol', 'x')
2956 2992 ('symbol', 'x')
2957 2993 ('symbol', 'date')))
2958 2994 * expanded:
2959 2995 (func
2960 2996 ('symbol', 'reverse')
2961 2997 (func
2962 2998 ('symbol', 'sort')
2963 2999 (list
2964 3000 (or
2965 ('symbol', '2')
2966 ('symbol', '3'))
3001 (list
3002 ('symbol', '2')
3003 ('symbol', '3')))
2967 3004 ('symbol', 'date'))))
2968 3005 * set:
2969 3006 <baseset [3, 2]>
2970 3007 3
2971 3008 2
2972 3009
2973 3010 issue4553: check that revset aliases override existing hash prefix
2974 3011
2975 3012 $ hg log -qr e
2976 3013 6:e0cc66ef77e8
2977 3014
2978 3015 $ hg log -qr e --config revsetalias.e="all()"
2979 3016 0:2785f51eece5
2980 3017 1:d75937da8da0
2981 3018 2:5ed5505e9f1c
2982 3019 3:8528aa5637f2
2983 3020 4:2326846efdab
2984 3021 5:904fa392b941
2985 3022 6:e0cc66ef77e8
2986 3023 7:013af1973af4
2987 3024 8:d5d0dcbdc4d9
2988 3025 9:24286f4ae135
2989 3026
2990 3027 $ hg log -qr e: --config revsetalias.e="0"
2991 3028 0:2785f51eece5
2992 3029 1:d75937da8da0
2993 3030 2:5ed5505e9f1c
2994 3031 3:8528aa5637f2
2995 3032 4:2326846efdab
2996 3033 5:904fa392b941
2997 3034 6:e0cc66ef77e8
2998 3035 7:013af1973af4
2999 3036 8:d5d0dcbdc4d9
3000 3037 9:24286f4ae135
3001 3038
3002 3039 $ hg log -qr :e --config revsetalias.e="9"
3003 3040 0:2785f51eece5
3004 3041 1:d75937da8da0
3005 3042 2:5ed5505e9f1c
3006 3043 3:8528aa5637f2
3007 3044 4:2326846efdab
3008 3045 5:904fa392b941
3009 3046 6:e0cc66ef77e8
3010 3047 7:013af1973af4
3011 3048 8:d5d0dcbdc4d9
3012 3049 9:24286f4ae135
3013 3050
3014 3051 $ hg log -qr e:
3015 3052 6:e0cc66ef77e8
3016 3053 7:013af1973af4
3017 3054 8:d5d0dcbdc4d9
3018 3055 9:24286f4ae135
3019 3056
3020 3057 $ hg log -qr :e
3021 3058 0:2785f51eece5
3022 3059 1:d75937da8da0
3023 3060 2:5ed5505e9f1c
3024 3061 3:8528aa5637f2
3025 3062 4:2326846efdab
3026 3063 5:904fa392b941
3027 3064 6:e0cc66ef77e8
3028 3065
3029 3066 issue2549 - correct optimizations
3030 3067
3031 3068 $ try 'limit(1 or 2 or 3, 2) and not 2'
3032 3069 (and
3033 3070 (func
3034 3071 ('symbol', 'limit')
3035 3072 (list
3036 3073 (or
3037 ('symbol', '1')
3038 ('symbol', '2')
3039 ('symbol', '3'))
3074 (list
3075 ('symbol', '1')
3076 ('symbol', '2')
3077 ('symbol', '3')))
3040 3078 ('symbol', '2')))
3041 3079 (not
3042 3080 ('symbol', '2')))
3043 3081 * set:
3044 3082 <filteredset
3045 3083 <baseset
3046 3084 <limit n=2, offset=0,
3047 3085 <fullreposet+ 0:9>,
3048 3086 <baseset [1, 2, 3]>>>,
3049 3087 <not
3050 3088 <baseset [2]>>>
3051 3089 1
3052 3090 $ try 'max(1 or 2) and not 2'
3053 3091 (and
3054 3092 (func
3055 3093 ('symbol', 'max')
3056 3094 (or
3057 ('symbol', '1')
3058 ('symbol', '2')))
3095 (list
3096 ('symbol', '1')
3097 ('symbol', '2'))))
3059 3098 (not
3060 3099 ('symbol', '2')))
3061 3100 * set:
3062 3101 <filteredset
3063 3102 <baseset
3064 3103 <max
3065 3104 <fullreposet+ 0:9>,
3066 3105 <baseset [1, 2]>>>,
3067 3106 <not
3068 3107 <baseset [2]>>>
3069 3108 $ try 'min(1 or 2) and not 1'
3070 3109 (and
3071 3110 (func
3072 3111 ('symbol', 'min')
3073 3112 (or
3074 ('symbol', '1')
3075 ('symbol', '2')))
3113 (list
3114 ('symbol', '1')
3115 ('symbol', '2'))))
3076 3116 (not
3077 3117 ('symbol', '1')))
3078 3118 * set:
3079 3119 <filteredset
3080 3120 <baseset
3081 3121 <min
3082 3122 <fullreposet+ 0:9>,
3083 3123 <baseset [1, 2]>>>,
3084 3124 <not
3085 3125 <baseset [1]>>>
3086 3126 $ try 'last(1 or 2, 1) and not 2'
3087 3127 (and
3088 3128 (func
3089 3129 ('symbol', 'last')
3090 3130 (list
3091 3131 (or
3092 ('symbol', '1')
3093 ('symbol', '2'))
3132 (list
3133 ('symbol', '1')
3134 ('symbol', '2')))
3094 3135 ('symbol', '1')))
3095 3136 (not
3096 3137 ('symbol', '2')))
3097 3138 * set:
3098 3139 <filteredset
3099 3140 <baseset
3100 3141 <last n=1,
3101 3142 <fullreposet+ 0:9>,
3102 3143 <baseset [2, 1]>>>,
3103 3144 <not
3104 3145 <baseset [2]>>>
3105 3146
3106 3147 issue4289 - ordering of built-ins
3107 3148 $ hg log -M -q -r 3:2
3108 3149 3:8528aa5637f2
3109 3150 2:5ed5505e9f1c
3110 3151
3111 3152 test revsets started with 40-chars hash (issue3669)
3112 3153
3113 3154 $ ISSUE3669_TIP=`hg tip --template '{node}'`
3114 3155 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
3115 3156 9
3116 3157 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
3117 3158 8
3118 3159
3119 3160 test or-ed indirect predicates (issue3775)
3120 3161
3121 3162 $ log '6 or 6^1' | sort
3122 3163 5
3123 3164 6
3124 3165 $ log '6^1 or 6' | sort
3125 3166 5
3126 3167 6
3127 3168 $ log '4 or 4~1' | sort
3128 3169 2
3129 3170 4
3130 3171 $ log '4~1 or 4' | sort
3131 3172 2
3132 3173 4
3133 3174 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
3134 3175 0
3135 3176 1
3136 3177 2
3137 3178 3
3138 3179 4
3139 3180 5
3140 3181 6
3141 3182 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
3142 3183 0
3143 3184 1
3144 3185 2
3145 3186 3
3146 3187 4
3147 3188 5
3148 3189 6
3149 3190
3150 3191 tests for 'remote()' predicate:
3151 3192 #. (csets in remote) (id) (remote)
3152 3193 1. less than local current branch "default"
3153 3194 2. same with local specified "default"
3154 3195 3. more than local specified specified
3155 3196
3156 3197 $ hg clone --quiet -U . ../remote3
3157 3198 $ cd ../remote3
3158 3199 $ hg update -q 7
3159 3200 $ echo r > r
3160 3201 $ hg ci -Aqm 10
3161 3202 $ log 'remote()'
3162 3203 7
3163 3204 $ log 'remote("a-b-c-")'
3164 3205 2
3165 3206 $ cd ../repo
3166 3207 $ log 'remote(".a.b.c.", "../remote3")'
3167 3208
3168 3209 tests for concatenation of strings/symbols by "##"
3169 3210
3170 3211 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
3171 3212 (_concat
3172 3213 (_concat
3173 3214 (_concat
3174 3215 ('symbol', '278')
3175 3216 ('string', '5f5'))
3176 3217 ('symbol', '1ee'))
3177 3218 ('string', 'ce5'))
3178 3219 * concatenated:
3179 3220 ('string', '2785f51eece5')
3180 3221 * set:
3181 3222 <baseset [0]>
3182 3223 0
3183 3224
3184 3225 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
3185 3226 $ try "cat4(278, '5f5', 1ee, 'ce5')"
3186 3227 (func
3187 3228 ('symbol', 'cat4')
3188 3229 (list
3189 3230 ('symbol', '278')
3190 3231 ('string', '5f5')
3191 3232 ('symbol', '1ee')
3192 3233 ('string', 'ce5')))
3193 3234 * expanded:
3194 3235 (_concat
3195 3236 (_concat
3196 3237 (_concat
3197 3238 ('symbol', '278')
3198 3239 ('string', '5f5'))
3199 3240 ('symbol', '1ee'))
3200 3241 ('string', 'ce5'))
3201 3242 * concatenated:
3202 3243 ('string', '2785f51eece5')
3203 3244 * set:
3204 3245 <baseset [0]>
3205 3246 0
3206 3247
3207 3248 (check concatenation in alias nesting)
3208 3249
3209 3250 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
3210 3251 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
3211 3252 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
3212 3253 0
3213 3254
3214 3255 (check operator priority)
3215 3256
3216 3257 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
3217 3258 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
3218 3259 0
3219 3260 4
3220 3261
3221 3262 $ cd ..
3222 3263
3223 3264 prepare repository that has "default" branches of multiple roots
3224 3265
3225 3266 $ hg init namedbranch
3226 3267 $ cd namedbranch
3227 3268
3228 3269 $ echo default0 >> a
3229 3270 $ hg ci -Aqm0
3230 3271 $ echo default1 >> a
3231 3272 $ hg ci -m1
3232 3273
3233 3274 $ hg branch -q stable
3234 3275 $ echo stable2 >> a
3235 3276 $ hg ci -m2
3236 3277 $ echo stable3 >> a
3237 3278 $ hg ci -m3
3238 3279
3239 3280 $ hg update -q null
3240 3281 $ echo default4 >> a
3241 3282 $ hg ci -Aqm4
3242 3283 $ echo default5 >> a
3243 3284 $ hg ci -m5
3244 3285
3245 3286 "null" revision belongs to "default" branch (issue4683)
3246 3287
3247 3288 $ log 'branch(null)'
3248 3289 0
3249 3290 1
3250 3291 4
3251 3292 5
3252 3293
3253 3294 "null" revision belongs to "default" branch, but it shouldn't appear in set
3254 3295 unless explicitly specified (issue4682)
3255 3296
3256 3297 $ log 'children(branch(default))'
3257 3298 1
3258 3299 2
3259 3300 5
3260 3301
3261 3302 $ cd ..
3262 3303
3263 3304 test author/desc/keyword in problematic encoding
3264 3305 # unicode: cp932:
3265 3306 # u30A2 0x83 0x41(= 'A')
3266 3307 # u30C2 0x83 0x61(= 'a')
3267 3308
3268 3309 $ hg init problematicencoding
3269 3310 $ cd problematicencoding
3270 3311
3271 3312 $ python > setup.sh <<EOF
3272 3313 > print u'''
3273 3314 > echo a > text
3274 3315 > hg add text
3275 3316 > hg --encoding utf-8 commit -u '\u30A2' -m none
3276 3317 > echo b > text
3277 3318 > hg --encoding utf-8 commit -u '\u30C2' -m none
3278 3319 > echo c > text
3279 3320 > hg --encoding utf-8 commit -u none -m '\u30A2'
3280 3321 > echo d > text
3281 3322 > hg --encoding utf-8 commit -u none -m '\u30C2'
3282 3323 > '''.encode('utf-8')
3283 3324 > EOF
3284 3325 $ sh < setup.sh
3285 3326
3286 3327 test in problematic encoding
3287 3328 $ python > test.sh <<EOF
3288 3329 > print u'''
3289 3330 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
3290 3331 > echo ====
3291 3332 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
3292 3333 > echo ====
3293 3334 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
3294 3335 > echo ====
3295 3336 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
3296 3337 > echo ====
3297 3338 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
3298 3339 > echo ====
3299 3340 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
3300 3341 > '''.encode('cp932')
3301 3342 > EOF
3302 3343 $ sh < test.sh
3303 3344 0
3304 3345 ====
3305 3346 1
3306 3347 ====
3307 3348 2
3308 3349 ====
3309 3350 3
3310 3351 ====
3311 3352 0
3312 3353 2
3313 3354 ====
3314 3355 1
3315 3356 3
3316 3357
3317 3358 test error message of bad revset
3318 3359 $ hg log -r 'foo\\'
3319 3360 hg: parse error at 3: syntax error in revset 'foo\\'
3320 3361 [255]
3321 3362
3322 3363 $ cd ..
3323 3364
3324 3365 Test that revset predicate of extension isn't loaded at failure of
3325 3366 loading it
3326 3367
3327 3368 $ cd repo
3328 3369
3329 3370 $ cat <<EOF > $TESTTMP/custompredicate.py
3330 3371 > from mercurial import error, registrar, revset
3331 3372 >
3332 3373 > revsetpredicate = registrar.revsetpredicate()
3333 3374 >
3334 3375 > @revsetpredicate('custom1()')
3335 3376 > def custom1(repo, subset, x):
3336 3377 > return revset.baseset([1])
3337 3378 >
3338 3379 > raise error.Abort('intentional failure of loading extension')
3339 3380 > EOF
3340 3381 $ cat <<EOF > .hg/hgrc
3341 3382 > [extensions]
3342 3383 > custompredicate = $TESTTMP/custompredicate.py
3343 3384 > EOF
3344 3385
3345 3386 $ hg debugrevspec "custom1()"
3346 3387 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
3347 3388 hg: parse error: unknown identifier: custom1
3348 3389 [255]
3349 3390
3350 3391 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now