##// END OF EJS Templates
revset: use integer representation of wdir() in revset...
Yuya Nishihara -
r25765:5e1b0739 default
parent child Browse files
Show More
@@ -1,3652 +1,3652 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 import re
9 9 import parser, util, error, hbisect, phases
10 10 import node
11 11 import heapq
12 12 import match as matchmod
13 13 from i18n import _
14 14 import encoding
15 15 import obsolete as obsmod
16 16 import pathutil
17 17 import repoview
18 18
19 19 def _revancestors(repo, revs, followfirst):
20 20 """Like revlog.ancestors(), but supports followfirst."""
21 21 if followfirst:
22 22 cut = 1
23 23 else:
24 24 cut = None
25 25 cl = repo.changelog
26 26
27 27 def iterate():
28 28 revs.sort(reverse=True)
29 29 irevs = iter(revs)
30 30 h = []
31 31
32 32 inputrev = next(irevs, None)
33 33 if inputrev is not None:
34 34 heapq.heappush(h, -inputrev)
35 35
36 36 seen = set()
37 37 while h:
38 38 current = -heapq.heappop(h)
39 39 if current == inputrev:
40 40 inputrev = next(irevs, None)
41 41 if inputrev is not None:
42 42 heapq.heappush(h, -inputrev)
43 43 if current not in seen:
44 44 seen.add(current)
45 45 yield current
46 46 for parent in cl.parentrevs(current)[:cut]:
47 47 if parent != node.nullrev:
48 48 heapq.heappush(h, -parent)
49 49
50 50 return generatorset(iterate(), iterasc=False)
51 51
52 52 def _revdescendants(repo, revs, followfirst):
53 53 """Like revlog.descendants() but supports followfirst."""
54 54 if followfirst:
55 55 cut = 1
56 56 else:
57 57 cut = None
58 58
59 59 def iterate():
60 60 cl = repo.changelog
61 61 # XXX this should be 'parentset.min()' assuming 'parentset' is a
62 62 # smartset (and if it is not, it should.)
63 63 first = min(revs)
64 64 nullrev = node.nullrev
65 65 if first == nullrev:
66 66 # Are there nodes with a null first parent and a non-null
67 67 # second one? Maybe. Do we care? Probably not.
68 68 for i in cl:
69 69 yield i
70 70 else:
71 71 seen = set(revs)
72 72 for i in cl.revs(first + 1):
73 73 for x in cl.parentrevs(i)[:cut]:
74 74 if x != nullrev and x in seen:
75 75 seen.add(i)
76 76 yield i
77 77 break
78 78
79 79 return generatorset(iterate(), iterasc=True)
80 80
81 81 def _revsbetween(repo, roots, heads):
82 82 """Return all paths between roots and heads, inclusive of both endpoint
83 83 sets."""
84 84 if not roots:
85 85 return baseset()
86 86 parentrevs = repo.changelog.parentrevs
87 87 visit = list(heads)
88 88 reachable = set()
89 89 seen = {}
90 90 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
91 91 # (and if it is not, it should.)
92 92 minroot = min(roots)
93 93 roots = set(roots)
94 94 # prefetch all the things! (because python is slow)
95 95 reached = reachable.add
96 96 dovisit = visit.append
97 97 nextvisit = visit.pop
98 98 # open-code the post-order traversal due to the tiny size of
99 99 # sys.getrecursionlimit()
100 100 while visit:
101 101 rev = nextvisit()
102 102 if rev in roots:
103 103 reached(rev)
104 104 parents = parentrevs(rev)
105 105 seen[rev] = parents
106 106 for parent in parents:
107 107 if parent >= minroot and parent not in seen:
108 108 dovisit(parent)
109 109 if not reachable:
110 110 return baseset()
111 111 for rev in sorted(seen):
112 112 for parent in seen[rev]:
113 113 if parent in reachable:
114 114 reached(rev)
115 115 return baseset(sorted(reachable))
116 116
117 117 elements = {
118 118 "(": (21, ("group", 1, ")"), ("func", 1, ")")),
119 119 "##": (20, None, ("_concat", 20)),
120 120 "~": (18, None, ("ancestor", 18)),
121 121 "^": (18, None, ("parent", 18), ("parentpost", 18)),
122 122 "-": (5, ("negate", 19), ("minus", 5)),
123 123 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
124 124 ("dagrangepost", 17)),
125 125 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
126 126 ("dagrangepost", 17)),
127 127 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
128 128 "not": (10, ("not", 10)),
129 129 "!": (10, ("not", 10)),
130 130 "and": (5, None, ("and", 5)),
131 131 "&": (5, None, ("and", 5)),
132 132 "%": (5, None, ("only", 5), ("onlypost", 5)),
133 133 "or": (4, None, ("or", 4)),
134 134 "|": (4, None, ("or", 4)),
135 135 "+": (4, None, ("or", 4)),
136 136 "=": (3, None, ("keyvalue", 3)),
137 137 ",": (2, None, ("list", 2)),
138 138 ")": (0, None, None),
139 139 "symbol": (0, ("symbol",), None),
140 140 "string": (0, ("string",), None),
141 141 "end": (0, None, None),
142 142 }
143 143
144 144 keywords = set(['and', 'or', 'not'])
145 145
146 146 # default set of valid characters for the initial letter of symbols
147 147 _syminitletters = set(c for c in [chr(i) for i in xrange(256)]
148 148 if c.isalnum() or c in '._@' or ord(c) > 127)
149 149
150 150 # default set of valid characters for non-initial letters of symbols
151 151 _symletters = set(c for c in [chr(i) for i in xrange(256)]
152 152 if c.isalnum() or c in '-._/@' or ord(c) > 127)
153 153
154 154 def tokenize(program, lookup=None, syminitletters=None, symletters=None):
155 155 '''
156 156 Parse a revset statement into a stream of tokens
157 157
158 158 ``syminitletters`` is the set of valid characters for the initial
159 159 letter of symbols.
160 160
161 161 By default, character ``c`` is recognized as valid for initial
162 162 letter of symbols, if ``c.isalnum() or c in '._@' or ord(c) > 127``.
163 163
164 164 ``symletters`` is the set of valid characters for non-initial
165 165 letters of symbols.
166 166
167 167 By default, character ``c`` is recognized as valid for non-initial
168 168 letters of symbols, if ``c.isalnum() or c in '-._/@' or ord(c) > 127``.
169 169
170 170 Check that @ is a valid unquoted token character (issue3686):
171 171 >>> list(tokenize("@::"))
172 172 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
173 173
174 174 '''
175 175 if syminitletters is None:
176 176 syminitletters = _syminitletters
177 177 if symletters is None:
178 178 symletters = _symletters
179 179
180 180 pos, l = 0, len(program)
181 181 while pos < l:
182 182 c = program[pos]
183 183 if c.isspace(): # skip inter-token whitespace
184 184 pass
185 185 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
186 186 yield ('::', None, pos)
187 187 pos += 1 # skip ahead
188 188 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
189 189 yield ('..', None, pos)
190 190 pos += 1 # skip ahead
191 191 elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully
192 192 yield ('##', None, pos)
193 193 pos += 1 # skip ahead
194 194 elif c in "():=,-|&+!~^%": # handle simple operators
195 195 yield (c, None, pos)
196 196 elif (c in '"\'' or c == 'r' and
197 197 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
198 198 if c == 'r':
199 199 pos += 1
200 200 c = program[pos]
201 201 decode = lambda x: x
202 202 else:
203 203 decode = lambda x: x.decode('string-escape')
204 204 pos += 1
205 205 s = pos
206 206 while pos < l: # find closing quote
207 207 d = program[pos]
208 208 if d == '\\': # skip over escaped characters
209 209 pos += 2
210 210 continue
211 211 if d == c:
212 212 yield ('string', decode(program[s:pos]), s)
213 213 break
214 214 pos += 1
215 215 else:
216 216 raise error.ParseError(_("unterminated string"), s)
217 217 # gather up a symbol/keyword
218 218 elif c in syminitletters:
219 219 s = pos
220 220 pos += 1
221 221 while pos < l: # find end of symbol
222 222 d = program[pos]
223 223 if d not in symletters:
224 224 break
225 225 if d == '.' and program[pos - 1] == '.': # special case for ..
226 226 pos -= 1
227 227 break
228 228 pos += 1
229 229 sym = program[s:pos]
230 230 if sym in keywords: # operator keywords
231 231 yield (sym, None, s)
232 232 elif '-' in sym:
233 233 # some jerk gave us foo-bar-baz, try to check if it's a symbol
234 234 if lookup and lookup(sym):
235 235 # looks like a real symbol
236 236 yield ('symbol', sym, s)
237 237 else:
238 238 # looks like an expression
239 239 parts = sym.split('-')
240 240 for p in parts[:-1]:
241 241 if p: # possible consecutive -
242 242 yield ('symbol', p, s)
243 243 s += len(p)
244 244 yield ('-', None, pos)
245 245 s += 1
246 246 if parts[-1]: # possible trailing -
247 247 yield ('symbol', parts[-1], s)
248 248 else:
249 249 yield ('symbol', sym, s)
250 250 pos -= 1
251 251 else:
252 252 raise error.ParseError(_("syntax error in revset '%s'") %
253 253 program, pos)
254 254 pos += 1
255 255 yield ('end', None, pos)
256 256
257 257 def parseerrordetail(inst):
258 258 """Compose error message from specified ParseError object
259 259 """
260 260 if len(inst.args) > 1:
261 261 return _('at %s: %s') % (inst.args[1], inst.args[0])
262 262 else:
263 263 return inst.args[0]
264 264
265 265 # helpers
266 266
267 267 def getstring(x, err):
268 268 if x and (x[0] == 'string' or x[0] == 'symbol'):
269 269 return x[1]
270 270 raise error.ParseError(err)
271 271
272 272 def getlist(x):
273 273 if not x:
274 274 return []
275 275 if x[0] == 'list':
276 276 return getlist(x[1]) + [x[2]]
277 277 return [x]
278 278
279 279 def getargs(x, min, max, err):
280 280 l = getlist(x)
281 281 if len(l) < min or (max >= 0 and len(l) > max):
282 282 raise error.ParseError(err)
283 283 return l
284 284
285 285 def getkwargs(x, funcname, keys):
286 286 return parser.buildargsdict(getlist(x), funcname, keys.split(),
287 287 keyvaluenode='keyvalue', keynode='symbol')
288 288
289 289 def isvalidsymbol(tree):
290 290 """Examine whether specified ``tree`` is valid ``symbol`` or not
291 291 """
292 292 return tree[0] == 'symbol' and len(tree) > 1
293 293
294 294 def getsymbol(tree):
295 295 """Get symbol name from valid ``symbol`` in ``tree``
296 296
297 297 This assumes that ``tree`` is already examined by ``isvalidsymbol``.
298 298 """
299 299 return tree[1]
300 300
301 301 def isvalidfunc(tree):
302 302 """Examine whether specified ``tree`` is valid ``func`` or not
303 303 """
304 304 return tree[0] == 'func' and len(tree) > 1 and isvalidsymbol(tree[1])
305 305
306 306 def getfuncname(tree):
307 307 """Get function name from valid ``func`` in ``tree``
308 308
309 309 This assumes that ``tree`` is already examined by ``isvalidfunc``.
310 310 """
311 311 return getsymbol(tree[1])
312 312
313 313 def getfuncargs(tree):
314 314 """Get list of function arguments from valid ``func`` in ``tree``
315 315
316 316 This assumes that ``tree`` is already examined by ``isvalidfunc``.
317 317 """
318 318 if len(tree) > 2:
319 319 return getlist(tree[2])
320 320 else:
321 321 return []
322 322
323 323 def getset(repo, subset, x):
324 324 if not x:
325 325 raise error.ParseError(_("missing argument"))
326 326 s = methods[x[0]](repo, subset, *x[1:])
327 327 if util.safehasattr(s, 'isascending'):
328 328 return s
329 329 if (repo.ui.configbool('devel', 'all-warnings')
330 330 or repo.ui.configbool('devel', 'old-revset')):
331 331 # else case should not happen, because all non-func are internal,
332 332 # ignoring for now.
333 333 if x[0] == 'func' and x[1][0] == 'symbol' and x[1][1] in symbols:
334 334 repo.ui.develwarn('revset "%s" use list instead of smartset, '
335 335 '(upgrade your code)' % x[1][1])
336 336 return baseset(s)
337 337
338 338 def _getrevsource(repo, r):
339 339 extra = repo[r].extra()
340 340 for label in ('source', 'transplant_source', 'rebase_source'):
341 341 if label in extra:
342 342 try:
343 343 return repo[extra[label]].rev()
344 344 except error.RepoLookupError:
345 345 pass
346 346 return None
347 347
348 348 # operator methods
349 349
350 350 def stringset(repo, subset, x):
351 351 x = repo[x].rev()
352 352 if (x in subset
353 353 or x == node.nullrev and isinstance(subset, fullreposet)):
354 354 return baseset([x])
355 355 return baseset()
356 356
357 357 def rangeset(repo, subset, x, y):
358 358 m = getset(repo, fullreposet(repo), x)
359 359 n = getset(repo, fullreposet(repo), y)
360 360
361 361 if not m or not n:
362 362 return baseset()
363 363 m, n = m.first(), n.last()
364 364
365 365 if m < n:
366 366 r = spanset(repo, m, n + 1)
367 367 else:
368 368 r = spanset(repo, m, n - 1)
369 369 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
370 370 # necessary to ensure we preserve the order in subset.
371 371 #
372 372 # This has performance implication, carrying the sorting over when possible
373 373 # would be more efficient.
374 374 return r & subset
375 375
376 376 def dagrange(repo, subset, x, y):
377 377 r = fullreposet(repo)
378 378 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
379 379 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
380 380 # necessary to ensure we preserve the order in subset.
381 381 return xs & subset
382 382
383 383 def andset(repo, subset, x, y):
384 384 return getset(repo, getset(repo, subset, x), y)
385 385
386 386 def orset(repo, subset, *xs):
387 387 rs = [getset(repo, subset, x) for x in xs]
388 388 return _combinesets(rs)
389 389
390 390 def notset(repo, subset, x):
391 391 return subset - getset(repo, subset, x)
392 392
393 393 def listset(repo, subset, a, b):
394 394 raise error.ParseError(_("can't use a list in this context"))
395 395
396 396 def keyvaluepair(repo, subset, k, v):
397 397 raise error.ParseError(_("can't use a key-value pair in this context"))
398 398
399 399 def func(repo, subset, a, b):
400 400 if a[0] == 'symbol' and a[1] in symbols:
401 401 return symbols[a[1]](repo, subset, b)
402 402
403 403 keep = lambda fn: getattr(fn, '__doc__', None) is not None
404 404
405 405 syms = [s for (s, fn) in symbols.items() if keep(fn)]
406 406 raise error.UnknownIdentifier(a[1], syms)
407 407
408 408 # functions
409 409
410 410 def adds(repo, subset, x):
411 411 """``adds(pattern)``
412 412 Changesets that add a file matching pattern.
413 413
414 414 The pattern without explicit kind like ``glob:`` is expected to be
415 415 relative to the current directory and match against a file or a
416 416 directory.
417 417 """
418 418 # i18n: "adds" is a keyword
419 419 pat = getstring(x, _("adds requires a pattern"))
420 420 return checkstatus(repo, subset, pat, 1)
421 421
422 422 def ancestor(repo, subset, x):
423 423 """``ancestor(*changeset)``
424 424 A greatest common ancestor of the changesets.
425 425
426 426 Accepts 0 or more changesets.
427 427 Will return empty list when passed no args.
428 428 Greatest common ancestor of a single changeset is that changeset.
429 429 """
430 430 # i18n: "ancestor" is a keyword
431 431 l = getlist(x)
432 432 rl = fullreposet(repo)
433 433 anc = None
434 434
435 435 # (getset(repo, rl, i) for i in l) generates a list of lists
436 436 for revs in (getset(repo, rl, i) for i in l):
437 437 for r in revs:
438 438 if anc is None:
439 439 anc = repo[r]
440 440 else:
441 441 anc = anc.ancestor(repo[r])
442 442
443 443 if anc is not None and anc.rev() in subset:
444 444 return baseset([anc.rev()])
445 445 return baseset()
446 446
447 447 def _ancestors(repo, subset, x, followfirst=False):
448 448 heads = getset(repo, fullreposet(repo), x)
449 449 if not heads:
450 450 return baseset()
451 451 s = _revancestors(repo, heads, followfirst)
452 452 return subset & s
453 453
454 454 def ancestors(repo, subset, x):
455 455 """``ancestors(set)``
456 456 Changesets that are ancestors of a changeset in set.
457 457 """
458 458 return _ancestors(repo, subset, x)
459 459
460 460 def _firstancestors(repo, subset, x):
461 461 # ``_firstancestors(set)``
462 462 # Like ``ancestors(set)`` but follows only the first parents.
463 463 return _ancestors(repo, subset, x, followfirst=True)
464 464
465 465 def ancestorspec(repo, subset, x, n):
466 466 """``set~n``
467 467 Changesets that are the Nth ancestor (first parents only) of a changeset
468 468 in set.
469 469 """
470 470 try:
471 471 n = int(n[1])
472 472 except (TypeError, ValueError):
473 473 raise error.ParseError(_("~ expects a number"))
474 474 ps = set()
475 475 cl = repo.changelog
476 476 for r in getset(repo, fullreposet(repo), x):
477 477 for i in range(n):
478 478 r = cl.parentrevs(r)[0]
479 479 ps.add(r)
480 480 return subset & ps
481 481
482 482 def author(repo, subset, x):
483 483 """``author(string)``
484 484 Alias for ``user(string)``.
485 485 """
486 486 # i18n: "author" is a keyword
487 487 n = encoding.lower(getstring(x, _("author requires a string")))
488 488 kind, pattern, matcher = _substringmatcher(n)
489 489 return subset.filter(lambda x: matcher(encoding.lower(repo[x].user())))
490 490
491 491 def bisect(repo, subset, x):
492 492 """``bisect(string)``
493 493 Changesets marked in the specified bisect status:
494 494
495 495 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
496 496 - ``goods``, ``bads`` : csets topologically good/bad
497 497 - ``range`` : csets taking part in the bisection
498 498 - ``pruned`` : csets that are goods, bads or skipped
499 499 - ``untested`` : csets whose fate is yet unknown
500 500 - ``ignored`` : csets ignored due to DAG topology
501 501 - ``current`` : the cset currently being bisected
502 502 """
503 503 # i18n: "bisect" is a keyword
504 504 status = getstring(x, _("bisect requires a string")).lower()
505 505 state = set(hbisect.get(repo, status))
506 506 return subset & state
507 507
508 508 # Backward-compatibility
509 509 # - no help entry so that we do not advertise it any more
510 510 def bisected(repo, subset, x):
511 511 return bisect(repo, subset, x)
512 512
513 513 def bookmark(repo, subset, x):
514 514 """``bookmark([name])``
515 515 The named bookmark or all bookmarks.
516 516
517 517 If `name` starts with `re:`, the remainder of the name is treated as
518 518 a regular expression. To match a bookmark that actually starts with `re:`,
519 519 use the prefix `literal:`.
520 520 """
521 521 # i18n: "bookmark" is a keyword
522 522 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
523 523 if args:
524 524 bm = getstring(args[0],
525 525 # i18n: "bookmark" is a keyword
526 526 _('the argument to bookmark must be a string'))
527 527 kind, pattern, matcher = _stringmatcher(bm)
528 528 bms = set()
529 529 if kind == 'literal':
530 530 bmrev = repo._bookmarks.get(pattern, None)
531 531 if not bmrev:
532 532 raise error.RepoLookupError(_("bookmark '%s' does not exist")
533 533 % bm)
534 534 bms.add(repo[bmrev].rev())
535 535 else:
536 536 matchrevs = set()
537 537 for name, bmrev in repo._bookmarks.iteritems():
538 538 if matcher(name):
539 539 matchrevs.add(bmrev)
540 540 if not matchrevs:
541 541 raise error.RepoLookupError(_("no bookmarks exist"
542 542 " that match '%s'") % pattern)
543 543 for bmrev in matchrevs:
544 544 bms.add(repo[bmrev].rev())
545 545 else:
546 546 bms = set([repo[r].rev()
547 547 for r in repo._bookmarks.values()])
548 548 bms -= set([node.nullrev])
549 549 return subset & bms
550 550
551 551 def branch(repo, subset, x):
552 552 """``branch(string or set)``
553 553 All changesets belonging to the given branch or the branches of the given
554 554 changesets.
555 555
556 556 If `string` starts with `re:`, the remainder of the name is treated as
557 557 a regular expression. To match a branch that actually starts with `re:`,
558 558 use the prefix `literal:`.
559 559 """
560 560 getbi = repo.revbranchcache().branchinfo
561 561
562 562 try:
563 563 b = getstring(x, '')
564 564 except error.ParseError:
565 565 # not a string, but another revspec, e.g. tip()
566 566 pass
567 567 else:
568 568 kind, pattern, matcher = _stringmatcher(b)
569 569 if kind == 'literal':
570 570 # note: falls through to the revspec case if no branch with
571 571 # this name exists
572 572 if pattern in repo.branchmap():
573 573 return subset.filter(lambda r: matcher(getbi(r)[0]))
574 574 else:
575 575 return subset.filter(lambda r: matcher(getbi(r)[0]))
576 576
577 577 s = getset(repo, fullreposet(repo), x)
578 578 b = set()
579 579 for r in s:
580 580 b.add(getbi(r)[0])
581 581 c = s.__contains__
582 582 return subset.filter(lambda r: c(r) or getbi(r)[0] in b)
583 583
584 584 def bumped(repo, subset, x):
585 585 """``bumped()``
586 586 Mutable changesets marked as successors of public changesets.
587 587
588 588 Only non-public and non-obsolete changesets can be `bumped`.
589 589 """
590 590 # i18n: "bumped" is a keyword
591 591 getargs(x, 0, 0, _("bumped takes no arguments"))
592 592 bumped = obsmod.getrevs(repo, 'bumped')
593 593 return subset & bumped
594 594
595 595 def bundle(repo, subset, x):
596 596 """``bundle()``
597 597 Changesets in the bundle.
598 598
599 599 Bundle must be specified by the -R option."""
600 600
601 601 try:
602 602 bundlerevs = repo.changelog.bundlerevs
603 603 except AttributeError:
604 604 raise util.Abort(_("no bundle provided - specify with -R"))
605 605 return subset & bundlerevs
606 606
607 607 def checkstatus(repo, subset, pat, field):
608 608 hasset = matchmod.patkind(pat) == 'set'
609 609
610 610 mcache = [None]
611 611 def matches(x):
612 612 c = repo[x]
613 613 if not mcache[0] or hasset:
614 614 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
615 615 m = mcache[0]
616 616 fname = None
617 617 if not m.anypats() and len(m.files()) == 1:
618 618 fname = m.files()[0]
619 619 if fname is not None:
620 620 if fname not in c.files():
621 621 return False
622 622 else:
623 623 for f in c.files():
624 624 if m(f):
625 625 break
626 626 else:
627 627 return False
628 628 files = repo.status(c.p1().node(), c.node())[field]
629 629 if fname is not None:
630 630 if fname in files:
631 631 return True
632 632 else:
633 633 for f in files:
634 634 if m(f):
635 635 return True
636 636
637 637 return subset.filter(matches)
638 638
639 639 def _children(repo, narrow, parentset):
640 640 if not parentset:
641 641 return baseset()
642 642 cs = set()
643 643 pr = repo.changelog.parentrevs
644 644 minrev = parentset.min()
645 645 for r in narrow:
646 646 if r <= minrev:
647 647 continue
648 648 for p in pr(r):
649 649 if p in parentset:
650 650 cs.add(r)
651 651 # XXX using a set to feed the baseset is wrong. Sets are not ordered.
652 652 # This does not break because of other fullreposet misbehavior.
653 653 return baseset(cs)
654 654
655 655 def children(repo, subset, x):
656 656 """``children(set)``
657 657 Child changesets of changesets in set.
658 658 """
659 659 s = getset(repo, fullreposet(repo), x)
660 660 cs = _children(repo, subset, s)
661 661 return subset & cs
662 662
663 663 def closed(repo, subset, x):
664 664 """``closed()``
665 665 Changeset is closed.
666 666 """
667 667 # i18n: "closed" is a keyword
668 668 getargs(x, 0, 0, _("closed takes no arguments"))
669 669 return subset.filter(lambda r: repo[r].closesbranch())
670 670
671 671 def contains(repo, subset, x):
672 672 """``contains(pattern)``
673 673 The revision's manifest contains a file matching pattern (but might not
674 674 modify it). See :hg:`help patterns` for information about file patterns.
675 675
676 676 The pattern without explicit kind like ``glob:`` is expected to be
677 677 relative to the current directory and match against a file exactly
678 678 for efficiency.
679 679 """
680 680 # i18n: "contains" is a keyword
681 681 pat = getstring(x, _("contains requires a pattern"))
682 682
683 683 def matches(x):
684 684 if not matchmod.patkind(pat):
685 685 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
686 686 if pats in repo[x]:
687 687 return True
688 688 else:
689 689 c = repo[x]
690 690 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
691 691 for f in c.manifest():
692 692 if m(f):
693 693 return True
694 694 return False
695 695
696 696 return subset.filter(matches)
697 697
698 698 def converted(repo, subset, x):
699 699 """``converted([id])``
700 700 Changesets converted from the given identifier in the old repository if
701 701 present, or all converted changesets if no identifier is specified.
702 702 """
703 703
704 704 # There is exactly no chance of resolving the revision, so do a simple
705 705 # string compare and hope for the best
706 706
707 707 rev = None
708 708 # i18n: "converted" is a keyword
709 709 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
710 710 if l:
711 711 # i18n: "converted" is a keyword
712 712 rev = getstring(l[0], _('converted requires a revision'))
713 713
714 714 def _matchvalue(r):
715 715 source = repo[r].extra().get('convert_revision', None)
716 716 return source is not None and (rev is None or source.startswith(rev))
717 717
718 718 return subset.filter(lambda r: _matchvalue(r))
719 719
720 720 def date(repo, subset, x):
721 721 """``date(interval)``
722 722 Changesets within the interval, see :hg:`help dates`.
723 723 """
724 724 # i18n: "date" is a keyword
725 725 ds = getstring(x, _("date requires a string"))
726 726 dm = util.matchdate(ds)
727 727 return subset.filter(lambda x: dm(repo[x].date()[0]))
728 728
729 729 def desc(repo, subset, x):
730 730 """``desc(string)``
731 731 Search commit message for string. The match is case-insensitive.
732 732 """
733 733 # i18n: "desc" is a keyword
734 734 ds = encoding.lower(getstring(x, _("desc requires a string")))
735 735
736 736 def matches(x):
737 737 c = repo[x]
738 738 return ds in encoding.lower(c.description())
739 739
740 740 return subset.filter(matches)
741 741
742 742 def _descendants(repo, subset, x, followfirst=False):
743 743 roots = getset(repo, fullreposet(repo), x)
744 744 if not roots:
745 745 return baseset()
746 746 s = _revdescendants(repo, roots, followfirst)
747 747
748 748 # Both sets need to be ascending in order to lazily return the union
749 749 # in the correct order.
750 750 base = subset & roots
751 751 desc = subset & s
752 752 result = base + desc
753 753 if subset.isascending():
754 754 result.sort()
755 755 elif subset.isdescending():
756 756 result.sort(reverse=True)
757 757 else:
758 758 result = subset & result
759 759 return result
760 760
761 761 def descendants(repo, subset, x):
762 762 """``descendants(set)``
763 763 Changesets which are descendants of changesets in set.
764 764 """
765 765 return _descendants(repo, subset, x)
766 766
767 767 def _firstdescendants(repo, subset, x):
768 768 # ``_firstdescendants(set)``
769 769 # Like ``descendants(set)`` but follows only the first parents.
770 770 return _descendants(repo, subset, x, followfirst=True)
771 771
772 772 def destination(repo, subset, x):
773 773 """``destination([set])``
774 774 Changesets that were created by a graft, transplant or rebase operation,
775 775 with the given revisions specified as the source. Omitting the optional set
776 776 is the same as passing all().
777 777 """
778 778 if x is not None:
779 779 sources = getset(repo, fullreposet(repo), x)
780 780 else:
781 781 sources = fullreposet(repo)
782 782
783 783 dests = set()
784 784
785 785 # subset contains all of the possible destinations that can be returned, so
786 786 # iterate over them and see if their source(s) were provided in the arg set.
787 787 # Even if the immediate src of r is not in the arg set, src's source (or
788 788 # further back) may be. Scanning back further than the immediate src allows
789 789 # transitive transplants and rebases to yield the same results as transitive
790 790 # grafts.
791 791 for r in subset:
792 792 src = _getrevsource(repo, r)
793 793 lineage = None
794 794
795 795 while src is not None:
796 796 if lineage is None:
797 797 lineage = list()
798 798
799 799 lineage.append(r)
800 800
801 801 # The visited lineage is a match if the current source is in the arg
802 802 # set. Since every candidate dest is visited by way of iterating
803 803 # subset, any dests further back in the lineage will be tested by a
804 804 # different iteration over subset. Likewise, if the src was already
805 805 # selected, the current lineage can be selected without going back
806 806 # further.
807 807 if src in sources or src in dests:
808 808 dests.update(lineage)
809 809 break
810 810
811 811 r = src
812 812 src = _getrevsource(repo, r)
813 813
814 814 return subset.filter(dests.__contains__)
815 815
816 816 def divergent(repo, subset, x):
817 817 """``divergent()``
818 818 Final successors of changesets with an alternative set of final successors.
819 819 """
820 820 # i18n: "divergent" is a keyword
821 821 getargs(x, 0, 0, _("divergent takes no arguments"))
822 822 divergent = obsmod.getrevs(repo, 'divergent')
823 823 return subset & divergent
824 824
825 825 def extinct(repo, subset, x):
826 826 """``extinct()``
827 827 Obsolete changesets with obsolete descendants only.
828 828 """
829 829 # i18n: "extinct" is a keyword
830 830 getargs(x, 0, 0, _("extinct takes no arguments"))
831 831 extincts = obsmod.getrevs(repo, 'extinct')
832 832 return subset & extincts
833 833
834 834 def extra(repo, subset, x):
835 835 """``extra(label, [value])``
836 836 Changesets with the given label in the extra metadata, with the given
837 837 optional value.
838 838
839 839 If `value` starts with `re:`, the remainder of the value is treated as
840 840 a regular expression. To match a value that actually starts with `re:`,
841 841 use the prefix `literal:`.
842 842 """
843 843 args = getkwargs(x, 'extra', 'label value')
844 844 if 'label' not in args:
845 845 # i18n: "extra" is a keyword
846 846 raise error.ParseError(_('extra takes at least 1 argument'))
847 847 # i18n: "extra" is a keyword
848 848 label = getstring(args['label'], _('first argument to extra must be '
849 849 'a string'))
850 850 value = None
851 851
852 852 if 'value' in args:
853 853 # i18n: "extra" is a keyword
854 854 value = getstring(args['value'], _('second argument to extra must be '
855 855 'a string'))
856 856 kind, value, matcher = _stringmatcher(value)
857 857
858 858 def _matchvalue(r):
859 859 extra = repo[r].extra()
860 860 return label in extra and (value is None or matcher(extra[label]))
861 861
862 862 return subset.filter(lambda r: _matchvalue(r))
863 863
864 864 def filelog(repo, subset, x):
865 865 """``filelog(pattern)``
866 866 Changesets connected to the specified filelog.
867 867
868 868 For performance reasons, visits only revisions mentioned in the file-level
869 869 filelog, rather than filtering through all changesets (much faster, but
870 870 doesn't include deletes or duplicate changes). For a slower, more accurate
871 871 result, use ``file()``.
872 872
873 873 The pattern without explicit kind like ``glob:`` is expected to be
874 874 relative to the current directory and match against a file exactly
875 875 for efficiency.
876 876
877 877 If some linkrev points to revisions filtered by the current repoview, we'll
878 878 work around it to return a non-filtered value.
879 879 """
880 880
881 881 # i18n: "filelog" is a keyword
882 882 pat = getstring(x, _("filelog requires a pattern"))
883 883 s = set()
884 884 cl = repo.changelog
885 885
886 886 if not matchmod.patkind(pat):
887 887 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
888 888 files = [f]
889 889 else:
890 890 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
891 891 files = (f for f in repo[None] if m(f))
892 892
893 893 for f in files:
894 894 backrevref = {} # final value for: filerev -> changerev
895 895 lowestchild = {} # lowest known filerev child of a filerev
896 896 delayed = [] # filerev with filtered linkrev, for post-processing
897 897 lowesthead = None # cache for manifest content of all head revisions
898 898 fl = repo.file(f)
899 899 for fr in list(fl):
900 900 rev = fl.linkrev(fr)
901 901 if rev not in cl:
902 902 # changerev pointed in linkrev is filtered
903 903 # record it for post processing.
904 904 delayed.append((fr, rev))
905 905 continue
906 906 for p in fl.parentrevs(fr):
907 907 if 0 <= p and p not in lowestchild:
908 908 lowestchild[p] = fr
909 909 backrevref[fr] = rev
910 910 s.add(rev)
911 911
912 912 # Post-processing of all filerevs we skipped because they were
913 913 # filtered. If such filerevs have known and unfiltered children, this
914 914 # means they have an unfiltered appearance out there. We'll use linkrev
915 915 # adjustment to find one of these appearances. The lowest known child
916 916 # will be used as a starting point because it is the best upper-bound we
917 917 # have.
918 918 #
919 919 # This approach will fail when an unfiltered but linkrev-shadowed
920 920 # appearance exists in a head changeset without unfiltered filerev
921 921 # children anywhere.
922 922 while delayed:
923 923 # must be a descending iteration. To slowly fill lowest child
924 924 # information that is of potential use by the next item.
925 925 fr, rev = delayed.pop()
926 926 lkr = rev
927 927
928 928 child = lowestchild.get(fr)
929 929
930 930 if child is None:
931 931 # search for existence of this file revision in a head revision.
932 932 # There are three possibilities:
933 933 # - the revision exists in a head and we can find an
934 934 # introduction from there,
935 935 # - the revision does not exist in a head because it has been
936 936 # changed since its introduction: we would have found a child
937 937 # and be in the other 'else' clause,
938 938 # - all versions of the revision are hidden.
939 939 if lowesthead is None:
940 940 lowesthead = {}
941 941 for h in repo.heads():
942 942 fnode = repo[h].manifest().get(f)
943 943 if fnode is not None:
944 944 lowesthead[fl.rev(fnode)] = h
945 945 headrev = lowesthead.get(fr)
946 946 if headrev is None:
947 947 # content is nowhere unfiltered
948 948 continue
949 949 rev = repo[headrev][f].introrev()
950 950 else:
951 951 # the lowest known child is a good upper bound
952 952 childcrev = backrevref[child]
953 953 # XXX this does not guarantee returning the lowest
954 954 # introduction of this revision, but this gives a
955 955 # result which is a good start and will fit in most
956 956 # cases. We probably need to fix the multiple
957 957 # introductions case properly (report each
958 958 # introduction, even for identical file revisions)
959 959 # once and for all at some point anyway.
960 960 for p in repo[childcrev][f].parents():
961 961 if p.filerev() == fr:
962 962 rev = p.rev()
963 963 break
964 964 if rev == lkr: # no shadowed entry found
965 965 # XXX This should never happen unless some manifest points
966 966 # to biggish file revisions (like a revision that uses a
967 967 # parent that never appears in the manifest ancestors)
968 968 continue
969 969
970 970 # Fill the data for the next iteration.
971 971 for p in fl.parentrevs(fr):
972 972 if 0 <= p and p not in lowestchild:
973 973 lowestchild[p] = fr
974 974 backrevref[fr] = rev
975 975 s.add(rev)
976 976
977 977 return subset & s
978 978
979 979 def first(repo, subset, x):
980 980 """``first(set, [n])``
981 981 An alias for limit().
982 982 """
983 983 return limit(repo, subset, x)
984 984
985 985 def _follow(repo, subset, x, name, followfirst=False):
986 986 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
987 987 c = repo['.']
988 988 if l:
989 989 x = getstring(l[0], _("%s expected a filename") % name)
990 990 if x in c:
991 991 cx = c[x]
992 992 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
993 993 # include the revision responsible for the most recent version
994 994 s.add(cx.introrev())
995 995 else:
996 996 return baseset()
997 997 else:
998 998 s = _revancestors(repo, baseset([c.rev()]), followfirst)
999 999
1000 1000 return subset & s
1001 1001
1002 1002 def follow(repo, subset, x):
1003 1003 """``follow([file])``
1004 1004 An alias for ``::.`` (ancestors of the working directory's first parent).
1005 1005 If a filename is specified, the history of the given file is followed,
1006 1006 including copies.
1007 1007 """
1008 1008 return _follow(repo, subset, x, 'follow')
1009 1009
1010 1010 def _followfirst(repo, subset, x):
1011 1011 # ``followfirst([file])``
1012 1012 # Like ``follow([file])`` but follows only the first parent of
1013 1013 # every revision or file revision.
1014 1014 return _follow(repo, subset, x, '_followfirst', followfirst=True)
1015 1015
1016 1016 def getall(repo, subset, x):
1017 1017 """``all()``
1018 1018 All changesets, the same as ``0:tip``.
1019 1019 """
1020 1020 # i18n: "all" is a keyword
1021 1021 getargs(x, 0, 0, _("all takes no arguments"))
1022 1022 return subset & spanset(repo) # drop "null" if any
1023 1023
1024 1024 def grep(repo, subset, x):
1025 1025 """``grep(regex)``
1026 1026 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1027 1027 to ensure special escape characters are handled correctly. Unlike
1028 1028 ``keyword(string)``, the match is case-sensitive.
1029 1029 """
1030 1030 try:
1031 1031 # i18n: "grep" is a keyword
1032 1032 gr = re.compile(getstring(x, _("grep requires a string")))
1033 1033 except re.error as e:
1034 1034 raise error.ParseError(_('invalid match pattern: %s') % e)
1035 1035
1036 1036 def matches(x):
1037 1037 c = repo[x]
1038 1038 for e in c.files() + [c.user(), c.description()]:
1039 1039 if gr.search(e):
1040 1040 return True
1041 1041 return False
1042 1042
1043 1043 return subset.filter(matches)
1044 1044
1045 1045 def _matchfiles(repo, subset, x):
1046 1046 # _matchfiles takes a revset list of prefixed arguments:
1047 1047 #
1048 1048 # [p:foo, i:bar, x:baz]
1049 1049 #
1050 1050 # builds a match object from them and filters subset. Allowed
1051 1051 # prefixes are 'p:' for regular patterns, 'i:' for include
1052 1052 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1053 1053 # a revision identifier, or the empty string to reference the
1054 1054 # working directory, from which the match object is
1055 1055 # initialized. Use 'd:' to set the default matching mode, default
1056 1056 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1057 1057
1058 1058 # i18n: "_matchfiles" is a keyword
1059 1059 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
1060 1060 pats, inc, exc = [], [], []
1061 1061 rev, default = None, None
1062 1062 for arg in l:
1063 1063 # i18n: "_matchfiles" is a keyword
1064 1064 s = getstring(arg, _("_matchfiles requires string arguments"))
1065 1065 prefix, value = s[:2], s[2:]
1066 1066 if prefix == 'p:':
1067 1067 pats.append(value)
1068 1068 elif prefix == 'i:':
1069 1069 inc.append(value)
1070 1070 elif prefix == 'x:':
1071 1071 exc.append(value)
1072 1072 elif prefix == 'r:':
1073 1073 if rev is not None:
1074 1074 # i18n: "_matchfiles" is a keyword
1075 1075 raise error.ParseError(_('_matchfiles expected at most one '
1076 1076 'revision'))
1077 1077 if value != '': # empty means working directory; leave rev as None
1078 1078 rev = value
1079 1079 elif prefix == 'd:':
1080 1080 if default is not None:
1081 1081 # i18n: "_matchfiles" is a keyword
1082 1082 raise error.ParseError(_('_matchfiles expected at most one '
1083 1083 'default mode'))
1084 1084 default = value
1085 1085 else:
1086 1086 # i18n: "_matchfiles" is a keyword
1087 1087 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
1088 1088 if not default:
1089 1089 default = 'glob'
1090 1090
1091 1091 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1092 1092 exclude=exc, ctx=repo[rev], default=default)
1093 1093
1094 1094 def matches(x):
1095 1095 for f in repo[x].files():
1096 1096 if m(f):
1097 1097 return True
1098 1098 return False
1099 1099
1100 1100 return subset.filter(matches)
1101 1101
1102 1102 def hasfile(repo, subset, x):
1103 1103 """``file(pattern)``
1104 1104 Changesets affecting files matched by pattern.
1105 1105
1106 1106 For a faster but less accurate result, consider using ``filelog()``
1107 1107 instead.
1108 1108
1109 1109 This predicate uses ``glob:`` as the default kind of pattern.
1110 1110 """
1111 1111 # i18n: "file" is a keyword
1112 1112 pat = getstring(x, _("file requires a pattern"))
1113 1113 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1114 1114
1115 1115 def head(repo, subset, x):
1116 1116 """``head()``
1117 1117 Changeset is a named branch head.
1118 1118 """
1119 1119 # i18n: "head" is a keyword
1120 1120 getargs(x, 0, 0, _("head takes no arguments"))
1121 1121 hs = set()
1122 1122 cl = repo.changelog
1123 1123 for b, ls in repo.branchmap().iteritems():
1124 1124 hs.update(cl.rev(h) for h in ls)
1125 1125 # XXX using a set to feed the baseset is wrong. Sets are not ordered.
1126 1126 # This does not break because of other fullreposet misbehavior.
1127 1127 # XXX We should combine with subset first: 'subset & baseset(...)'. This is
1128 1128 # necessary to ensure we preserve the order in subset.
1129 1129 return baseset(hs) & subset
1130 1130
1131 1131 def heads(repo, subset, x):
1132 1132 """``heads(set)``
1133 1133 Members of set with no children in set.
1134 1134 """
1135 1135 s = getset(repo, subset, x)
1136 1136 ps = parents(repo, subset, x)
1137 1137 return s - ps
1138 1138
1139 1139 def hidden(repo, subset, x):
1140 1140 """``hidden()``
1141 1141 Hidden changesets.
1142 1142 """
1143 1143 # i18n: "hidden" is a keyword
1144 1144 getargs(x, 0, 0, _("hidden takes no arguments"))
1145 1145 hiddenrevs = repoview.filterrevs(repo, 'visible')
1146 1146 return subset & hiddenrevs
1147 1147
1148 1148 def keyword(repo, subset, x):
1149 1149 """``keyword(string)``
1150 1150 Search commit message, user name, and names of changed files for
1151 1151 string. The match is case-insensitive.
1152 1152 """
1153 1153 # i18n: "keyword" is a keyword
1154 1154 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1155 1155
1156 1156 def matches(r):
1157 1157 c = repo[r]
1158 1158 return any(kw in encoding.lower(t)
1159 1159 for t in c.files() + [c.user(), c.description()])
1160 1160
1161 1161 return subset.filter(matches)
1162 1162
1163 1163 def limit(repo, subset, x):
1164 1164 """``limit(set, [n])``
1165 1165 First n members of set, defaulting to 1.
1166 1166 """
1167 1167 # i18n: "limit" is a keyword
1168 1168 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
1169 1169 try:
1170 1170 lim = 1
1171 1171 if len(l) == 2:
1172 1172 # i18n: "limit" is a keyword
1173 1173 lim = int(getstring(l[1], _("limit requires a number")))
1174 1174 except (TypeError, ValueError):
1175 1175 # i18n: "limit" is a keyword
1176 1176 raise error.ParseError(_("limit expects a number"))
1177 1177 ss = subset
1178 1178 os = getset(repo, fullreposet(repo), l[0])
1179 1179 result = []
1180 1180 it = iter(os)
1181 1181 for x in xrange(lim):
1182 1182 y = next(it, None)
1183 1183 if y is None:
1184 1184 break
1185 1185 elif y in ss:
1186 1186 result.append(y)
1187 1187 return baseset(result)
1188 1188
1189 1189 def last(repo, subset, x):
1190 1190 """``last(set, [n])``
1191 1191 Last n members of set, defaulting to 1.
1192 1192 """
1193 1193 # i18n: "last" is a keyword
1194 1194 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1195 1195 try:
1196 1196 lim = 1
1197 1197 if len(l) == 2:
1198 1198 # i18n: "last" is a keyword
1199 1199 lim = int(getstring(l[1], _("last requires a number")))
1200 1200 except (TypeError, ValueError):
1201 1201 # i18n: "last" is a keyword
1202 1202 raise error.ParseError(_("last expects a number"))
1203 1203 ss = subset
1204 1204 os = getset(repo, fullreposet(repo), l[0])
1205 1205 os.reverse()
1206 1206 result = []
1207 1207 it = iter(os)
1208 1208 for x in xrange(lim):
1209 1209 y = next(it, None)
1210 1210 if y is None:
1211 1211 break
1212 1212 elif y in ss:
1213 1213 result.append(y)
1214 1214 return baseset(result)
1215 1215
1216 1216 def maxrev(repo, subset, x):
1217 1217 """``max(set)``
1218 1218 Changeset with highest revision number in set.
1219 1219 """
1220 1220 os = getset(repo, fullreposet(repo), x)
1221 1221 if os:
1222 1222 m = os.max()
1223 1223 if m in subset:
1224 1224 return baseset([m])
1225 1225 return baseset()
1226 1226
1227 1227 def merge(repo, subset, x):
1228 1228 """``merge()``
1229 1229 Changeset is a merge changeset.
1230 1230 """
1231 1231 # i18n: "merge" is a keyword
1232 1232 getargs(x, 0, 0, _("merge takes no arguments"))
1233 1233 cl = repo.changelog
1234 1234 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1)
1235 1235
1236 1236 def branchpoint(repo, subset, x):
1237 1237 """``branchpoint()``
1238 1238 Changesets with more than one child.
1239 1239 """
1240 1240 # i18n: "branchpoint" is a keyword
1241 1241 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1242 1242 cl = repo.changelog
1243 1243 if not subset:
1244 1244 return baseset()
1245 1245 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1246 1246 # (and if it is not, it should.)
1247 1247 baserev = min(subset)
1248 1248 parentscount = [0]*(len(repo) - baserev)
1249 1249 for r in cl.revs(start=baserev + 1):
1250 1250 for p in cl.parentrevs(r):
1251 1251 if p >= baserev:
1252 1252 parentscount[p - baserev] += 1
1253 1253 return subset.filter(lambda r: parentscount[r - baserev] > 1)
1254 1254
1255 1255 def minrev(repo, subset, x):
1256 1256 """``min(set)``
1257 1257 Changeset with lowest revision number in set.
1258 1258 """
1259 1259 os = getset(repo, fullreposet(repo), x)
1260 1260 if os:
1261 1261 m = os.min()
1262 1262 if m in subset:
1263 1263 return baseset([m])
1264 1264 return baseset()
1265 1265
1266 1266 def modifies(repo, subset, x):
1267 1267 """``modifies(pattern)``
1268 1268 Changesets modifying files matched by pattern.
1269 1269
1270 1270 The pattern without explicit kind like ``glob:`` is expected to be
1271 1271 relative to the current directory and match against a file or a
1272 1272 directory.
1273 1273 """
1274 1274 # i18n: "modifies" is a keyword
1275 1275 pat = getstring(x, _("modifies requires a pattern"))
1276 1276 return checkstatus(repo, subset, pat, 0)
1277 1277
1278 1278 def named(repo, subset, x):
1279 1279 """``named(namespace)``
1280 1280 The changesets in a given namespace.
1281 1281
1282 1282 If `namespace` starts with `re:`, the remainder of the string is treated as
1283 1283 a regular expression. To match a namespace that actually starts with `re:`,
1284 1284 use the prefix `literal:`.
1285 1285 """
1286 1286 # i18n: "named" is a keyword
1287 1287 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1288 1288
1289 1289 ns = getstring(args[0],
1290 1290 # i18n: "named" is a keyword
1291 1291 _('the argument to named must be a string'))
1292 1292 kind, pattern, matcher = _stringmatcher(ns)
1293 1293 namespaces = set()
1294 1294 if kind == 'literal':
1295 1295 if pattern not in repo.names:
1296 1296 raise error.RepoLookupError(_("namespace '%s' does not exist")
1297 1297 % ns)
1298 1298 namespaces.add(repo.names[pattern])
1299 1299 else:
1300 1300 for name, ns in repo.names.iteritems():
1301 1301 if matcher(name):
1302 1302 namespaces.add(ns)
1303 1303 if not namespaces:
1304 1304 raise error.RepoLookupError(_("no namespace exists"
1305 1305 " that match '%s'") % pattern)
1306 1306
1307 1307 names = set()
1308 1308 for ns in namespaces:
1309 1309 for name in ns.listnames(repo):
1310 1310 if name not in ns.deprecated:
1311 1311 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1312 1312
1313 1313 names -= set([node.nullrev])
1314 1314 return subset & names
1315 1315
1316 1316 def node_(repo, subset, x):
1317 1317 """``id(string)``
1318 1318 Revision non-ambiguously specified by the given hex string prefix.
1319 1319 """
1320 1320 # i18n: "id" is a keyword
1321 1321 l = getargs(x, 1, 1, _("id requires one argument"))
1322 1322 # i18n: "id" is a keyword
1323 1323 n = getstring(l[0], _("id requires a string"))
1324 1324 if len(n) == 40:
1325 1325 try:
1326 1326 rn = repo.changelog.rev(node.bin(n))
1327 1327 except (LookupError, TypeError):
1328 1328 rn = None
1329 1329 else:
1330 1330 rn = None
1331 1331 pm = repo.changelog._partialmatch(n)
1332 1332 if pm is not None:
1333 1333 rn = repo.changelog.rev(pm)
1334 1334
1335 1335 if rn is None:
1336 1336 return baseset()
1337 1337 result = baseset([rn])
1338 1338 return result & subset
1339 1339
1340 1340 def obsolete(repo, subset, x):
1341 1341 """``obsolete()``
1342 1342 Mutable changeset with a newer version."""
1343 1343 # i18n: "obsolete" is a keyword
1344 1344 getargs(x, 0, 0, _("obsolete takes no arguments"))
1345 1345 obsoletes = obsmod.getrevs(repo, 'obsolete')
1346 1346 return subset & obsoletes
1347 1347
1348 1348 def only(repo, subset, x):
1349 1349 """``only(set, [set])``
1350 1350 Changesets that are ancestors of the first set that are not ancestors
1351 1351 of any other head in the repo. If a second set is specified, the result
1352 1352 is ancestors of the first set that are not ancestors of the second set
1353 1353 (i.e. ::<set1> - ::<set2>).
1354 1354 """
1355 1355 cl = repo.changelog
1356 1356 # i18n: "only" is a keyword
1357 1357 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1358 1358 include = getset(repo, fullreposet(repo), args[0])
1359 1359 if len(args) == 1:
1360 1360 if not include:
1361 1361 return baseset()
1362 1362
1363 1363 descendants = set(_revdescendants(repo, include, False))
1364 1364 exclude = [rev for rev in cl.headrevs()
1365 1365 if not rev in descendants and not rev in include]
1366 1366 else:
1367 1367 exclude = getset(repo, fullreposet(repo), args[1])
1368 1368
1369 1369 results = set(cl.findmissingrevs(common=exclude, heads=include))
1370 1370 # XXX we should turn this into a baseset instead of a set, smartset may do
1371 1371 # some optimisations from the fact this is a baseset.
1372 1372 return subset & results
1373 1373
1374 1374 def origin(repo, subset, x):
1375 1375 """``origin([set])``
1376 1376 Changesets that were specified as a source for the grafts, transplants or
1377 1377 rebases that created the given revisions. Omitting the optional set is the
1378 1378 same as passing all(). If a changeset created by these operations is itself
1379 1379 specified as a source for one of these operations, only the source changeset
1380 1380 for the first operation is selected.
1381 1381 """
1382 1382 if x is not None:
1383 1383 dests = getset(repo, fullreposet(repo), x)
1384 1384 else:
1385 1385 dests = fullreposet(repo)
1386 1386
1387 1387 def _firstsrc(rev):
1388 1388 src = _getrevsource(repo, rev)
1389 1389 if src is None:
1390 1390 return None
1391 1391
1392 1392 while True:
1393 1393 prev = _getrevsource(repo, src)
1394 1394
1395 1395 if prev is None:
1396 1396 return src
1397 1397 src = prev
1398 1398
1399 1399 o = set([_firstsrc(r) for r in dests])
1400 1400 o -= set([None])
1401 1401 # XXX we should turn this into a baseset instead of a set, smartset may do
1402 1402 # some optimisations from the fact this is a baseset.
1403 1403 return subset & o
1404 1404
1405 1405 def outgoing(repo, subset, x):
1406 1406 """``outgoing([path])``
1407 1407 Changesets not found in the specified destination repository, or the
1408 1408 default push location.
1409 1409 """
1410 1410 # Avoid cycles.
1411 1411 import discovery
1412 1412 import hg
1413 1413 # i18n: "outgoing" is a keyword
1414 1414 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1415 1415 # i18n: "outgoing" is a keyword
1416 1416 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1417 1417 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1418 1418 dest, branches = hg.parseurl(dest)
1419 1419 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1420 1420 if revs:
1421 1421 revs = [repo.lookup(rev) for rev in revs]
1422 1422 other = hg.peer(repo, {}, dest)
1423 1423 repo.ui.pushbuffer()
1424 1424 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1425 1425 repo.ui.popbuffer()
1426 1426 cl = repo.changelog
1427 1427 o = set([cl.rev(r) for r in outgoing.missing])
1428 1428 return subset & o
1429 1429
1430 1430 def p1(repo, subset, x):
1431 1431 """``p1([set])``
1432 1432 First parent of changesets in set, or the working directory.
1433 1433 """
1434 1434 if x is None:
1435 1435 p = repo[x].p1().rev()
1436 1436 if p >= 0:
1437 1437 return subset & baseset([p])
1438 1438 return baseset()
1439 1439
1440 1440 ps = set()
1441 1441 cl = repo.changelog
1442 1442 for r in getset(repo, fullreposet(repo), x):
1443 1443 ps.add(cl.parentrevs(r)[0])
1444 1444 ps -= set([node.nullrev])
1445 1445 # XXX we should turn this into a baseset instead of a set, smartset may do
1446 1446 # some optimisations from the fact this is a baseset.
1447 1447 return subset & ps
1448 1448
1449 1449 def p2(repo, subset, x):
1450 1450 """``p2([set])``
1451 1451 Second parent of changesets in set, or the working directory.
1452 1452 """
1453 1453 if x is None:
1454 1454 ps = repo[x].parents()
1455 1455 try:
1456 1456 p = ps[1].rev()
1457 1457 if p >= 0:
1458 1458 return subset & baseset([p])
1459 1459 return baseset()
1460 1460 except IndexError:
1461 1461 return baseset()
1462 1462
1463 1463 ps = set()
1464 1464 cl = repo.changelog
1465 1465 for r in getset(repo, fullreposet(repo), x):
1466 1466 ps.add(cl.parentrevs(r)[1])
1467 1467 ps -= set([node.nullrev])
1468 1468 # XXX we should turn this into a baseset instead of a set, smartset may do
1469 1469 # some optimisations from the fact this is a baseset.
1470 1470 return subset & ps
1471 1471
1472 1472 def parents(repo, subset, x):
1473 1473 """``parents([set])``
1474 1474 The set of all parents for all changesets in set, or the working directory.
1475 1475 """
1476 1476 if x is None:
1477 1477 ps = set(p.rev() for p in repo[x].parents())
1478 1478 else:
1479 1479 ps = set()
1480 1480 cl = repo.changelog
1481 1481 up = ps.update
1482 1482 parentrevs = cl.parentrevs
1483 1483 for r in getset(repo, fullreposet(repo), x):
1484 if r is None:
1484 if r == node.wdirrev:
1485 1485 up(p.rev() for p in repo[r].parents())
1486 1486 else:
1487 1487 up(parentrevs(r))
1488 1488 ps -= set([node.nullrev])
1489 1489 return subset & ps
1490 1490
1491 1491 def _phase(repo, subset, target):
1492 1492 """helper to select all rev in phase <target>"""
1493 1493 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1494 1494 if repo._phasecache._phasesets:
1495 1495 s = repo._phasecache._phasesets[target] - repo.changelog.filteredrevs
1496 1496 s = baseset(s)
1497 1497 s.sort() # set are non ordered, so we enforce ascending
1498 1498 return subset & s
1499 1499 else:
1500 1500 phase = repo._phasecache.phase
1501 1501 condition = lambda r: phase(repo, r) == target
1502 1502 return subset.filter(condition, cache=False)
1503 1503
1504 1504 def draft(repo, subset, x):
1505 1505 """``draft()``
1506 1506 Changeset in draft phase."""
1507 1507 # i18n: "draft" is a keyword
1508 1508 getargs(x, 0, 0, _("draft takes no arguments"))
1509 1509 target = phases.draft
1510 1510 return _phase(repo, subset, target)
1511 1511
1512 1512 def secret(repo, subset, x):
1513 1513 """``secret()``
1514 1514 Changeset in secret phase."""
1515 1515 # i18n: "secret" is a keyword
1516 1516 getargs(x, 0, 0, _("secret takes no arguments"))
1517 1517 target = phases.secret
1518 1518 return _phase(repo, subset, target)
1519 1519
1520 1520 def parentspec(repo, subset, x, n):
1521 1521 """``set^0``
1522 1522 The set.
1523 1523 ``set^1`` (or ``set^``), ``set^2``
1524 1524 First or second parent, respectively, of all changesets in set.
1525 1525 """
1526 1526 try:
1527 1527 n = int(n[1])
1528 1528 if n not in (0, 1, 2):
1529 1529 raise ValueError
1530 1530 except (TypeError, ValueError):
1531 1531 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1532 1532 ps = set()
1533 1533 cl = repo.changelog
1534 1534 for r in getset(repo, fullreposet(repo), x):
1535 1535 if n == 0:
1536 1536 ps.add(r)
1537 1537 elif n == 1:
1538 1538 ps.add(cl.parentrevs(r)[0])
1539 1539 elif n == 2:
1540 1540 parents = cl.parentrevs(r)
1541 1541 if len(parents) > 1:
1542 1542 ps.add(parents[1])
1543 1543 return subset & ps
1544 1544
1545 1545 def present(repo, subset, x):
1546 1546 """``present(set)``
1547 1547 An empty set, if any revision in set isn't found; otherwise,
1548 1548 all revisions in set.
1549 1549
1550 1550 If any of specified revisions is not present in the local repository,
1551 1551 the query is normally aborted. But this predicate allows the query
1552 1552 to continue even in such cases.
1553 1553 """
1554 1554 try:
1555 1555 return getset(repo, subset, x)
1556 1556 except error.RepoLookupError:
1557 1557 return baseset()
1558 1558
1559 1559 # for internal use
1560 1560 def _notpublic(repo, subset, x):
1561 1561 getargs(x, 0, 0, "_notpublic takes no arguments")
1562 1562 repo._phasecache.loadphaserevs(repo) # ensure phase's sets are loaded
1563 1563 if repo._phasecache._phasesets:
1564 1564 s = set()
1565 1565 for u in repo._phasecache._phasesets[1:]:
1566 1566 s.update(u)
1567 1567 s = baseset(s - repo.changelog.filteredrevs)
1568 1568 s.sort()
1569 1569 return subset & s
1570 1570 else:
1571 1571 phase = repo._phasecache.phase
1572 1572 target = phases.public
1573 1573 condition = lambda r: phase(repo, r) != target
1574 1574 return subset.filter(condition, cache=False)
1575 1575
1576 1576 def public(repo, subset, x):
1577 1577 """``public()``
1578 1578 Changeset in public phase."""
1579 1579 # i18n: "public" is a keyword
1580 1580 getargs(x, 0, 0, _("public takes no arguments"))
1581 1581 phase = repo._phasecache.phase
1582 1582 target = phases.public
1583 1583 condition = lambda r: phase(repo, r) == target
1584 1584 return subset.filter(condition, cache=False)
1585 1585
1586 1586 def remote(repo, subset, x):
1587 1587 """``remote([id [,path]])``
1588 1588 Local revision that corresponds to the given identifier in a
1589 1589 remote repository, if present. Here, the '.' identifier is a
1590 1590 synonym for the current local branch.
1591 1591 """
1592 1592
1593 1593 import hg # avoid start-up nasties
1594 1594 # i18n: "remote" is a keyword
1595 1595 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1596 1596
1597 1597 q = '.'
1598 1598 if len(l) > 0:
1599 1599 # i18n: "remote" is a keyword
1600 1600 q = getstring(l[0], _("remote requires a string id"))
1601 1601 if q == '.':
1602 1602 q = repo['.'].branch()
1603 1603
1604 1604 dest = ''
1605 1605 if len(l) > 1:
1606 1606 # i18n: "remote" is a keyword
1607 1607 dest = getstring(l[1], _("remote requires a repository path"))
1608 1608 dest = repo.ui.expandpath(dest or 'default')
1609 1609 dest, branches = hg.parseurl(dest)
1610 1610 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1611 1611 if revs:
1612 1612 revs = [repo.lookup(rev) for rev in revs]
1613 1613 other = hg.peer(repo, {}, dest)
1614 1614 n = other.lookup(q)
1615 1615 if n in repo:
1616 1616 r = repo[n].rev()
1617 1617 if r in subset:
1618 1618 return baseset([r])
1619 1619 return baseset()
1620 1620
1621 1621 def removes(repo, subset, x):
1622 1622 """``removes(pattern)``
1623 1623 Changesets which remove files matching pattern.
1624 1624
1625 1625 The pattern without explicit kind like ``glob:`` is expected to be
1626 1626 relative to the current directory and match against a file or a
1627 1627 directory.
1628 1628 """
1629 1629 # i18n: "removes" is a keyword
1630 1630 pat = getstring(x, _("removes requires a pattern"))
1631 1631 return checkstatus(repo, subset, pat, 2)
1632 1632
1633 1633 def rev(repo, subset, x):
1634 1634 """``rev(number)``
1635 1635 Revision with the given numeric identifier.
1636 1636 """
1637 1637 # i18n: "rev" is a keyword
1638 1638 l = getargs(x, 1, 1, _("rev requires one argument"))
1639 1639 try:
1640 1640 # i18n: "rev" is a keyword
1641 1641 l = int(getstring(l[0], _("rev requires a number")))
1642 1642 except (TypeError, ValueError):
1643 1643 # i18n: "rev" is a keyword
1644 1644 raise error.ParseError(_("rev expects a number"))
1645 1645 if l not in repo.changelog and l != node.nullrev:
1646 1646 return baseset()
1647 1647 return subset & baseset([l])
1648 1648
1649 1649 def matching(repo, subset, x):
1650 1650 """``matching(revision [, field])``
1651 1651 Changesets in which a given set of fields match the set of fields in the
1652 1652 selected revision or set.
1653 1653
1654 1654 To match more than one field pass the list of fields to match separated
1655 1655 by spaces (e.g. ``author description``).
1656 1656
1657 1657 Valid fields are most regular revision fields and some special fields.
1658 1658
1659 1659 Regular revision fields are ``description``, ``author``, ``branch``,
1660 1660 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1661 1661 and ``diff``.
1662 1662 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1663 1663 contents of the revision. Two revisions matching their ``diff`` will
1664 1664 also match their ``files``.
1665 1665
1666 1666 Special fields are ``summary`` and ``metadata``:
1667 1667 ``summary`` matches the first line of the description.
1668 1668 ``metadata`` is equivalent to matching ``description user date``
1669 1669 (i.e. it matches the main metadata fields).
1670 1670
1671 1671 ``metadata`` is the default field which is used when no fields are
1672 1672 specified. You can match more than one field at a time.
1673 1673 """
1674 1674 # i18n: "matching" is a keyword
1675 1675 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1676 1676
1677 1677 revs = getset(repo, fullreposet(repo), l[0])
1678 1678
1679 1679 fieldlist = ['metadata']
1680 1680 if len(l) > 1:
1681 1681 fieldlist = getstring(l[1],
1682 1682 # i18n: "matching" is a keyword
1683 1683 _("matching requires a string "
1684 1684 "as its second argument")).split()
1685 1685
1686 1686 # Make sure that there are no repeated fields,
1687 1687 # expand the 'special' 'metadata' field type
1688 1688 # and check the 'files' whenever we check the 'diff'
1689 1689 fields = []
1690 1690 for field in fieldlist:
1691 1691 if field == 'metadata':
1692 1692 fields += ['user', 'description', 'date']
1693 1693 elif field == 'diff':
1694 1694 # a revision matching the diff must also match the files
1695 1695 # since matching the diff is very costly, make sure to
1696 1696 # also match the files first
1697 1697 fields += ['files', 'diff']
1698 1698 else:
1699 1699 if field == 'author':
1700 1700 field = 'user'
1701 1701 fields.append(field)
1702 1702 fields = set(fields)
1703 1703 if 'summary' in fields and 'description' in fields:
1704 1704 # If a revision matches its description it also matches its summary
1705 1705 fields.discard('summary')
1706 1706
1707 1707 # We may want to match more than one field
1708 1708 # Not all fields take the same amount of time to be matched
1709 1709 # Sort the selected fields in order of increasing matching cost
1710 1710 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1711 1711 'files', 'description', 'substate', 'diff']
1712 1712 def fieldkeyfunc(f):
1713 1713 try:
1714 1714 return fieldorder.index(f)
1715 1715 except ValueError:
1716 1716 # assume an unknown field is very costly
1717 1717 return len(fieldorder)
1718 1718 fields = list(fields)
1719 1719 fields.sort(key=fieldkeyfunc)
1720 1720
1721 1721 # Each field will be matched with its own "getfield" function
1722 1722 # which will be added to the getfieldfuncs array of functions
1723 1723 getfieldfuncs = []
1724 1724 _funcs = {
1725 1725 'user': lambda r: repo[r].user(),
1726 1726 'branch': lambda r: repo[r].branch(),
1727 1727 'date': lambda r: repo[r].date(),
1728 1728 'description': lambda r: repo[r].description(),
1729 1729 'files': lambda r: repo[r].files(),
1730 1730 'parents': lambda r: repo[r].parents(),
1731 1731 'phase': lambda r: repo[r].phase(),
1732 1732 'substate': lambda r: repo[r].substate,
1733 1733 'summary': lambda r: repo[r].description().splitlines()[0],
1734 1734 'diff': lambda r: list(repo[r].diff(git=True),)
1735 1735 }
1736 1736 for info in fields:
1737 1737 getfield = _funcs.get(info, None)
1738 1738 if getfield is None:
1739 1739 raise error.ParseError(
1740 1740 # i18n: "matching" is a keyword
1741 1741 _("unexpected field name passed to matching: %s") % info)
1742 1742 getfieldfuncs.append(getfield)
1743 1743 # convert the getfield array of functions into a "getinfo" function
1744 1744 # which returns an array of field values (or a single value if there
1745 1745 # is only one field to match)
1746 1746 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1747 1747
1748 1748 def matches(x):
1749 1749 for rev in revs:
1750 1750 target = getinfo(rev)
1751 1751 match = True
1752 1752 for n, f in enumerate(getfieldfuncs):
1753 1753 if target[n] != f(x):
1754 1754 match = False
1755 1755 if match:
1756 1756 return True
1757 1757 return False
1758 1758
1759 1759 return subset.filter(matches)
1760 1760
1761 1761 def reverse(repo, subset, x):
1762 1762 """``reverse(set)``
1763 1763 Reverse order of set.
1764 1764 """
1765 1765 l = getset(repo, subset, x)
1766 1766 l.reverse()
1767 1767 return l
1768 1768
1769 1769 def roots(repo, subset, x):
1770 1770 """``roots(set)``
1771 1771 Changesets in set with no parent changeset in set.
1772 1772 """
1773 1773 s = getset(repo, fullreposet(repo), x)
1774 1774 parents = repo.changelog.parentrevs
1775 1775 def filter(r):
1776 1776 for p in parents(r):
1777 1777 if 0 <= p and p in s:
1778 1778 return False
1779 1779 return True
1780 1780 return subset & s.filter(filter)
1781 1781
1782 1782 def sort(repo, subset, x):
1783 1783 """``sort(set[, [-]key...])``
1784 1784 Sort set by keys. The default sort order is ascending, specify a key
1785 1785 as ``-key`` to sort in descending order.
1786 1786
1787 1787 The keys can be:
1788 1788
1789 1789 - ``rev`` for the revision number,
1790 1790 - ``branch`` for the branch name,
1791 1791 - ``desc`` for the commit message (description),
1792 1792 - ``user`` for user name (``author`` can be used as an alias),
1793 1793 - ``date`` for the commit date
1794 1794 """
1795 1795 # i18n: "sort" is a keyword
1796 1796 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1797 1797 keys = "rev"
1798 1798 if len(l) == 2:
1799 1799 # i18n: "sort" is a keyword
1800 1800 keys = getstring(l[1], _("sort spec must be a string"))
1801 1801
1802 1802 s = l[0]
1803 1803 keys = keys.split()
1804 1804 l = []
1805 1805 def invert(s):
1806 1806 return "".join(chr(255 - ord(c)) for c in s)
1807 1807 revs = getset(repo, subset, s)
1808 1808 if keys == ["rev"]:
1809 1809 revs.sort()
1810 1810 return revs
1811 1811 elif keys == ["-rev"]:
1812 1812 revs.sort(reverse=True)
1813 1813 return revs
1814 1814 for r in revs:
1815 1815 c = repo[r]
1816 1816 e = []
1817 1817 for k in keys:
1818 1818 if k == 'rev':
1819 1819 e.append(r)
1820 1820 elif k == '-rev':
1821 1821 e.append(-r)
1822 1822 elif k == 'branch':
1823 1823 e.append(c.branch())
1824 1824 elif k == '-branch':
1825 1825 e.append(invert(c.branch()))
1826 1826 elif k == 'desc':
1827 1827 e.append(c.description())
1828 1828 elif k == '-desc':
1829 1829 e.append(invert(c.description()))
1830 1830 elif k in 'user author':
1831 1831 e.append(c.user())
1832 1832 elif k in '-user -author':
1833 1833 e.append(invert(c.user()))
1834 1834 elif k == 'date':
1835 1835 e.append(c.date()[0])
1836 1836 elif k == '-date':
1837 1837 e.append(-c.date()[0])
1838 1838 else:
1839 1839 raise error.ParseError(_("unknown sort key %r") % k)
1840 1840 e.append(r)
1841 1841 l.append(e)
1842 1842 l.sort()
1843 1843 return baseset([e[-1] for e in l])
1844 1844
1845 1845 def subrepo(repo, subset, x):
1846 1846 """``subrepo([pattern])``
1847 1847 Changesets that add, modify or remove the given subrepo. If no subrepo
1848 1848 pattern is named, any subrepo changes are returned.
1849 1849 """
1850 1850 # i18n: "subrepo" is a keyword
1851 1851 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1852 1852 if len(args) != 0:
1853 1853 pat = getstring(args[0], _("subrepo requires a pattern"))
1854 1854
1855 1855 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
1856 1856
1857 1857 def submatches(names):
1858 1858 k, p, m = _stringmatcher(pat)
1859 1859 for name in names:
1860 1860 if m(name):
1861 1861 yield name
1862 1862
1863 1863 def matches(x):
1864 1864 c = repo[x]
1865 1865 s = repo.status(c.p1().node(), c.node(), match=m)
1866 1866
1867 1867 if len(args) == 0:
1868 1868 return s.added or s.modified or s.removed
1869 1869
1870 1870 if s.added:
1871 1871 return any(submatches(c.substate.keys()))
1872 1872
1873 1873 if s.modified:
1874 1874 subs = set(c.p1().substate.keys())
1875 1875 subs.update(c.substate.keys())
1876 1876
1877 1877 for path in submatches(subs):
1878 1878 if c.p1().substate.get(path) != c.substate.get(path):
1879 1879 return True
1880 1880
1881 1881 if s.removed:
1882 1882 return any(submatches(c.p1().substate.keys()))
1883 1883
1884 1884 return False
1885 1885
1886 1886 return subset.filter(matches)
1887 1887
1888 1888 def _stringmatcher(pattern):
1889 1889 """
1890 1890 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1891 1891 returns the matcher name, pattern, and matcher function.
1892 1892 missing or unknown prefixes are treated as literal matches.
1893 1893
1894 1894 helper for tests:
1895 1895 >>> def test(pattern, *tests):
1896 1896 ... kind, pattern, matcher = _stringmatcher(pattern)
1897 1897 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1898 1898
1899 1899 exact matching (no prefix):
1900 1900 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1901 1901 ('literal', 'abcdefg', [False, False, True])
1902 1902
1903 1903 regex matching ('re:' prefix)
1904 1904 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1905 1905 ('re', 'a.+b', [False, False, True])
1906 1906
1907 1907 force exact matches ('literal:' prefix)
1908 1908 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1909 1909 ('literal', 're:foobar', [False, True])
1910 1910
1911 1911 unknown prefixes are ignored and treated as literals
1912 1912 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1913 1913 ('literal', 'foo:bar', [False, False, True])
1914 1914 """
1915 1915 if pattern.startswith('re:'):
1916 1916 pattern = pattern[3:]
1917 1917 try:
1918 1918 regex = re.compile(pattern)
1919 1919 except re.error as e:
1920 1920 raise error.ParseError(_('invalid regular expression: %s')
1921 1921 % e)
1922 1922 return 're', pattern, regex.search
1923 1923 elif pattern.startswith('literal:'):
1924 1924 pattern = pattern[8:]
1925 1925 return 'literal', pattern, pattern.__eq__
1926 1926
1927 1927 def _substringmatcher(pattern):
1928 1928 kind, pattern, matcher = _stringmatcher(pattern)
1929 1929 if kind == 'literal':
1930 1930 matcher = lambda s: pattern in s
1931 1931 return kind, pattern, matcher
1932 1932
1933 1933 def tag(repo, subset, x):
1934 1934 """``tag([name])``
1935 1935 The specified tag by name, or all tagged revisions if no name is given.
1936 1936
1937 1937 If `name` starts with `re:`, the remainder of the name is treated as
1938 1938 a regular expression. To match a tag that actually starts with `re:`,
1939 1939 use the prefix `literal:`.
1940 1940 """
1941 1941 # i18n: "tag" is a keyword
1942 1942 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1943 1943 cl = repo.changelog
1944 1944 if args:
1945 1945 pattern = getstring(args[0],
1946 1946 # i18n: "tag" is a keyword
1947 1947 _('the argument to tag must be a string'))
1948 1948 kind, pattern, matcher = _stringmatcher(pattern)
1949 1949 if kind == 'literal':
1950 1950 # avoid resolving all tags
1951 1951 tn = repo._tagscache.tags.get(pattern, None)
1952 1952 if tn is None:
1953 1953 raise error.RepoLookupError(_("tag '%s' does not exist")
1954 1954 % pattern)
1955 1955 s = set([repo[tn].rev()])
1956 1956 else:
1957 1957 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1958 1958 else:
1959 1959 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1960 1960 return subset & s
1961 1961
1962 1962 def tagged(repo, subset, x):
1963 1963 return tag(repo, subset, x)
1964 1964
1965 1965 def unstable(repo, subset, x):
1966 1966 """``unstable()``
1967 1967 Non-obsolete changesets with obsolete ancestors.
1968 1968 """
1969 1969 # i18n: "unstable" is a keyword
1970 1970 getargs(x, 0, 0, _("unstable takes no arguments"))
1971 1971 unstables = obsmod.getrevs(repo, 'unstable')
1972 1972 return subset & unstables
1973 1973
1974 1974
1975 1975 def user(repo, subset, x):
1976 1976 """``user(string)``
1977 1977 User name contains string. The match is case-insensitive.
1978 1978
1979 1979 If `string` starts with `re:`, the remainder of the string is treated as
1980 1980 a regular expression. To match a user that actually contains `re:`, use
1981 1981 the prefix `literal:`.
1982 1982 """
1983 1983 return author(repo, subset, x)
1984 1984
1985 1985 # experimental
1986 1986 def wdir(repo, subset, x):
1987 1987 # i18n: "wdir" is a keyword
1988 1988 getargs(x, 0, 0, _("wdir takes no arguments"))
1989 if None in subset or isinstance(subset, fullreposet):
1990 return baseset([None])
1989 if node.wdirrev in subset or isinstance(subset, fullreposet):
1990 return baseset([node.wdirrev])
1991 1991 return baseset()
1992 1992
1993 1993 # for internal use
1994 1994 def _list(repo, subset, x):
1995 1995 s = getstring(x, "internal error")
1996 1996 if not s:
1997 1997 return baseset()
1998 1998 # remove duplicates here. it's difficult for caller to deduplicate sets
1999 1999 # because different symbols can point to the same rev.
2000 2000 cl = repo.changelog
2001 2001 ls = []
2002 2002 seen = set()
2003 2003 for t in s.split('\0'):
2004 2004 try:
2005 2005 # fast path for integer revision
2006 2006 r = int(t)
2007 2007 if str(r) != t or r not in cl:
2008 2008 raise ValueError
2009 2009 except ValueError:
2010 2010 r = repo[t].rev()
2011 2011 if r in seen:
2012 2012 continue
2013 2013 if (r in subset
2014 2014 or r == node.nullrev and isinstance(subset, fullreposet)):
2015 2015 ls.append(r)
2016 2016 seen.add(r)
2017 2017 return baseset(ls)
2018 2018
2019 2019 # for internal use
2020 2020 def _intlist(repo, subset, x):
2021 2021 s = getstring(x, "internal error")
2022 2022 if not s:
2023 2023 return baseset()
2024 2024 ls = [int(r) for r in s.split('\0')]
2025 2025 s = subset
2026 2026 return baseset([r for r in ls if r in s])
2027 2027
2028 2028 # for internal use
2029 2029 def _hexlist(repo, subset, x):
2030 2030 s = getstring(x, "internal error")
2031 2031 if not s:
2032 2032 return baseset()
2033 2033 cl = repo.changelog
2034 2034 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2035 2035 s = subset
2036 2036 return baseset([r for r in ls if r in s])
2037 2037
2038 2038 symbols = {
2039 2039 "adds": adds,
2040 2040 "all": getall,
2041 2041 "ancestor": ancestor,
2042 2042 "ancestors": ancestors,
2043 2043 "_firstancestors": _firstancestors,
2044 2044 "author": author,
2045 2045 "bisect": bisect,
2046 2046 "bisected": bisected,
2047 2047 "bookmark": bookmark,
2048 2048 "branch": branch,
2049 2049 "branchpoint": branchpoint,
2050 2050 "bumped": bumped,
2051 2051 "bundle": bundle,
2052 2052 "children": children,
2053 2053 "closed": closed,
2054 2054 "contains": contains,
2055 2055 "converted": converted,
2056 2056 "date": date,
2057 2057 "desc": desc,
2058 2058 "descendants": descendants,
2059 2059 "_firstdescendants": _firstdescendants,
2060 2060 "destination": destination,
2061 2061 "divergent": divergent,
2062 2062 "draft": draft,
2063 2063 "extinct": extinct,
2064 2064 "extra": extra,
2065 2065 "file": hasfile,
2066 2066 "filelog": filelog,
2067 2067 "first": first,
2068 2068 "follow": follow,
2069 2069 "_followfirst": _followfirst,
2070 2070 "grep": grep,
2071 2071 "head": head,
2072 2072 "heads": heads,
2073 2073 "hidden": hidden,
2074 2074 "id": node_,
2075 2075 "keyword": keyword,
2076 2076 "last": last,
2077 2077 "limit": limit,
2078 2078 "_matchfiles": _matchfiles,
2079 2079 "max": maxrev,
2080 2080 "merge": merge,
2081 2081 "min": minrev,
2082 2082 "modifies": modifies,
2083 2083 "named": named,
2084 2084 "obsolete": obsolete,
2085 2085 "only": only,
2086 2086 "origin": origin,
2087 2087 "outgoing": outgoing,
2088 2088 "p1": p1,
2089 2089 "p2": p2,
2090 2090 "parents": parents,
2091 2091 "present": present,
2092 2092 "public": public,
2093 2093 "_notpublic": _notpublic,
2094 2094 "remote": remote,
2095 2095 "removes": removes,
2096 2096 "rev": rev,
2097 2097 "reverse": reverse,
2098 2098 "roots": roots,
2099 2099 "sort": sort,
2100 2100 "secret": secret,
2101 2101 "subrepo": subrepo,
2102 2102 "matching": matching,
2103 2103 "tag": tag,
2104 2104 "tagged": tagged,
2105 2105 "user": user,
2106 2106 "unstable": unstable,
2107 2107 "wdir": wdir,
2108 2108 "_list": _list,
2109 2109 "_intlist": _intlist,
2110 2110 "_hexlist": _hexlist,
2111 2111 }
2112 2112
2113 2113 # symbols which can't be used for a DoS attack for any given input
2114 2114 # (e.g. those which accept regexes as plain strings shouldn't be included)
2115 2115 # functions that just return a lot of changesets (like all) don't count here
2116 2116 safesymbols = set([
2117 2117 "adds",
2118 2118 "all",
2119 2119 "ancestor",
2120 2120 "ancestors",
2121 2121 "_firstancestors",
2122 2122 "author",
2123 2123 "bisect",
2124 2124 "bisected",
2125 2125 "bookmark",
2126 2126 "branch",
2127 2127 "branchpoint",
2128 2128 "bumped",
2129 2129 "bundle",
2130 2130 "children",
2131 2131 "closed",
2132 2132 "converted",
2133 2133 "date",
2134 2134 "desc",
2135 2135 "descendants",
2136 2136 "_firstdescendants",
2137 2137 "destination",
2138 2138 "divergent",
2139 2139 "draft",
2140 2140 "extinct",
2141 2141 "extra",
2142 2142 "file",
2143 2143 "filelog",
2144 2144 "first",
2145 2145 "follow",
2146 2146 "_followfirst",
2147 2147 "head",
2148 2148 "heads",
2149 2149 "hidden",
2150 2150 "id",
2151 2151 "keyword",
2152 2152 "last",
2153 2153 "limit",
2154 2154 "_matchfiles",
2155 2155 "max",
2156 2156 "merge",
2157 2157 "min",
2158 2158 "modifies",
2159 2159 "obsolete",
2160 2160 "only",
2161 2161 "origin",
2162 2162 "outgoing",
2163 2163 "p1",
2164 2164 "p2",
2165 2165 "parents",
2166 2166 "present",
2167 2167 "public",
2168 2168 "_notpublic",
2169 2169 "remote",
2170 2170 "removes",
2171 2171 "rev",
2172 2172 "reverse",
2173 2173 "roots",
2174 2174 "sort",
2175 2175 "secret",
2176 2176 "matching",
2177 2177 "tag",
2178 2178 "tagged",
2179 2179 "user",
2180 2180 "unstable",
2181 2181 "wdir",
2182 2182 "_list",
2183 2183 "_intlist",
2184 2184 "_hexlist",
2185 2185 ])
2186 2186
2187 2187 methods = {
2188 2188 "range": rangeset,
2189 2189 "dagrange": dagrange,
2190 2190 "string": stringset,
2191 2191 "symbol": stringset,
2192 2192 "and": andset,
2193 2193 "or": orset,
2194 2194 "not": notset,
2195 2195 "list": listset,
2196 2196 "keyvalue": keyvaluepair,
2197 2197 "func": func,
2198 2198 "ancestor": ancestorspec,
2199 2199 "parent": parentspec,
2200 2200 "parentpost": p1,
2201 2201 }
2202 2202
2203 2203 def optimize(x, small):
2204 2204 if x is None:
2205 2205 return 0, x
2206 2206
2207 2207 smallbonus = 1
2208 2208 if small:
2209 2209 smallbonus = .5
2210 2210
2211 2211 op = x[0]
2212 2212 if op == 'minus':
2213 2213 return optimize(('and', x[1], ('not', x[2])), small)
2214 2214 elif op == 'only':
2215 2215 return optimize(('func', ('symbol', 'only'),
2216 2216 ('list', x[1], x[2])), small)
2217 2217 elif op == 'onlypost':
2218 2218 return optimize(('func', ('symbol', 'only'), x[1]), small)
2219 2219 elif op == 'dagrangepre':
2220 2220 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
2221 2221 elif op == 'dagrangepost':
2222 2222 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
2223 2223 elif op == 'rangepre':
2224 2224 return optimize(('range', ('string', '0'), x[1]), small)
2225 2225 elif op == 'rangepost':
2226 2226 return optimize(('range', x[1], ('string', 'tip')), small)
2227 2227 elif op == 'negate':
2228 2228 return optimize(('string',
2229 2229 '-' + getstring(x[1], _("can't negate that"))), small)
2230 2230 elif op in 'string symbol negate':
2231 2231 return smallbonus, x # single revisions are small
2232 2232 elif op == 'and':
2233 2233 wa, ta = optimize(x[1], True)
2234 2234 wb, tb = optimize(x[2], True)
2235 2235
2236 2236 # (::x and not ::y)/(not ::y and ::x) have a fast path
2237 2237 def isonly(revs, bases):
2238 2238 return (
2239 2239 revs[0] == 'func'
2240 2240 and getstring(revs[1], _('not a symbol')) == 'ancestors'
2241 2241 and bases[0] == 'not'
2242 2242 and bases[1][0] == 'func'
2243 2243 and getstring(bases[1][1], _('not a symbol')) == 'ancestors')
2244 2244
2245 2245 w = min(wa, wb)
2246 2246 if isonly(ta, tb):
2247 2247 return w, ('func', ('symbol', 'only'), ('list', ta[2], tb[1][2]))
2248 2248 if isonly(tb, ta):
2249 2249 return w, ('func', ('symbol', 'only'), ('list', tb[2], ta[1][2]))
2250 2250
2251 2251 if wa > wb:
2252 2252 return w, (op, tb, ta)
2253 2253 return w, (op, ta, tb)
2254 2254 elif op == 'or':
2255 2255 # fast path for machine-generated expression, that is likely to have
2256 2256 # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()'
2257 2257 ws, ts, ss = [], [], []
2258 2258 def flushss():
2259 2259 if not ss:
2260 2260 return
2261 2261 if len(ss) == 1:
2262 2262 w, t = ss[0]
2263 2263 else:
2264 2264 s = '\0'.join(t[1] for w, t in ss)
2265 2265 y = ('func', ('symbol', '_list'), ('string', s))
2266 2266 w, t = optimize(y, False)
2267 2267 ws.append(w)
2268 2268 ts.append(t)
2269 2269 del ss[:]
2270 2270 for y in x[1:]:
2271 2271 w, t = optimize(y, False)
2272 2272 if t[0] == 'string' or t[0] == 'symbol':
2273 2273 ss.append((w, t))
2274 2274 continue
2275 2275 flushss()
2276 2276 ws.append(w)
2277 2277 ts.append(t)
2278 2278 flushss()
2279 2279 if len(ts) == 1:
2280 2280 return ws[0], ts[0] # 'or' operation is fully optimized out
2281 2281 # we can't reorder trees by weight because it would change the order.
2282 2282 # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a")
2283 2283 # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0]))
2284 2284 return max(ws), (op,) + tuple(ts)
2285 2285 elif op == 'not':
2286 2286 # Optimize not public() to _notpublic() because we have a fast version
2287 2287 if x[1] == ('func', ('symbol', 'public'), None):
2288 2288 newsym = ('func', ('symbol', '_notpublic'), None)
2289 2289 o = optimize(newsym, not small)
2290 2290 return o[0], o[1]
2291 2291 else:
2292 2292 o = optimize(x[1], not small)
2293 2293 return o[0], (op, o[1])
2294 2294 elif op == 'parentpost':
2295 2295 o = optimize(x[1], small)
2296 2296 return o[0], (op, o[1])
2297 2297 elif op == 'group':
2298 2298 return optimize(x[1], small)
2299 2299 elif op in 'dagrange range list parent ancestorspec':
2300 2300 if op == 'parent':
2301 2301 # x^:y means (x^) : y, not x ^ (:y)
2302 2302 post = ('parentpost', x[1])
2303 2303 if x[2][0] == 'dagrangepre':
2304 2304 return optimize(('dagrange', post, x[2][1]), small)
2305 2305 elif x[2][0] == 'rangepre':
2306 2306 return optimize(('range', post, x[2][1]), small)
2307 2307
2308 2308 wa, ta = optimize(x[1], small)
2309 2309 wb, tb = optimize(x[2], small)
2310 2310 return wa + wb, (op, ta, tb)
2311 2311 elif op == 'func':
2312 2312 f = getstring(x[1], _("not a symbol"))
2313 2313 wa, ta = optimize(x[2], small)
2314 2314 if f in ("author branch closed date desc file grep keyword "
2315 2315 "outgoing user"):
2316 2316 w = 10 # slow
2317 2317 elif f in "modifies adds removes":
2318 2318 w = 30 # slower
2319 2319 elif f == "contains":
2320 2320 w = 100 # very slow
2321 2321 elif f == "ancestor":
2322 2322 w = 1 * smallbonus
2323 2323 elif f in "reverse limit first _intlist":
2324 2324 w = 0
2325 2325 elif f in "sort":
2326 2326 w = 10 # assume most sorts look at changelog
2327 2327 else:
2328 2328 w = 1
2329 2329 return w + wa, (op, x[1], ta)
2330 2330 return 1, x
2331 2331
2332 2332 _aliasarg = ('func', ('symbol', '_aliasarg'))
2333 2333 def _getaliasarg(tree):
2334 2334 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
2335 2335 return X, None otherwise.
2336 2336 """
2337 2337 if (len(tree) == 3 and tree[:2] == _aliasarg
2338 2338 and tree[2][0] == 'string'):
2339 2339 return tree[2][1]
2340 2340 return None
2341 2341
2342 2342 def _checkaliasarg(tree, known=None):
2343 2343 """Check tree contains no _aliasarg construct or only ones which
2344 2344 value is in known. Used to avoid alias placeholders injection.
2345 2345 """
2346 2346 if isinstance(tree, tuple):
2347 2347 arg = _getaliasarg(tree)
2348 2348 if arg is not None and (not known or arg not in known):
2349 2349 raise error.UnknownIdentifier('_aliasarg', [])
2350 2350 for t in tree:
2351 2351 _checkaliasarg(t, known)
2352 2352
2353 2353 # the set of valid characters for the initial letter of symbols in
2354 2354 # alias declarations and definitions
2355 2355 _aliassyminitletters = set(c for c in [chr(i) for i in xrange(256)]
2356 2356 if c.isalnum() or c in '._@$' or ord(c) > 127)
2357 2357
2358 2358 def _tokenizealias(program, lookup=None):
2359 2359 """Parse alias declaration/definition into a stream of tokens
2360 2360
2361 2361 This allows symbol names to use also ``$`` as an initial letter
2362 2362 (for backward compatibility), and callers of this function should
2363 2363 examine whether ``$`` is used also for unexpected symbols or not.
2364 2364 """
2365 2365 return tokenize(program, lookup=lookup,
2366 2366 syminitletters=_aliassyminitletters)
2367 2367
2368 2368 def _parsealiasdecl(decl):
2369 2369 """Parse alias declaration ``decl``
2370 2370
2371 2371 This returns ``(name, tree, args, errorstr)`` tuple:
2372 2372
2373 2373 - ``name``: of declared alias (may be ``decl`` itself at error)
2374 2374 - ``tree``: parse result (or ``None`` at error)
2375 2375 - ``args``: list of alias argument names (or None for symbol declaration)
2376 2376 - ``errorstr``: detail about detected error (or None)
2377 2377
2378 2378 >>> _parsealiasdecl('foo')
2379 2379 ('foo', ('symbol', 'foo'), None, None)
2380 2380 >>> _parsealiasdecl('$foo')
2381 2381 ('$foo', None, None, "'$' not for alias arguments")
2382 2382 >>> _parsealiasdecl('foo::bar')
2383 2383 ('foo::bar', None, None, 'invalid format')
2384 2384 >>> _parsealiasdecl('foo bar')
2385 2385 ('foo bar', None, None, 'at 4: invalid token')
2386 2386 >>> _parsealiasdecl('foo()')
2387 2387 ('foo', ('func', ('symbol', 'foo')), [], None)
2388 2388 >>> _parsealiasdecl('$foo()')
2389 2389 ('$foo()', None, None, "'$' not for alias arguments")
2390 2390 >>> _parsealiasdecl('foo($1, $2)')
2391 2391 ('foo', ('func', ('symbol', 'foo')), ['$1', '$2'], None)
2392 2392 >>> _parsealiasdecl('foo(bar_bar, baz.baz)')
2393 2393 ('foo', ('func', ('symbol', 'foo')), ['bar_bar', 'baz.baz'], None)
2394 2394 >>> _parsealiasdecl('foo($1, $2, nested($1, $2))')
2395 2395 ('foo($1, $2, nested($1, $2))', None, None, 'invalid argument list')
2396 2396 >>> _parsealiasdecl('foo(bar($1, $2))')
2397 2397 ('foo(bar($1, $2))', None, None, 'invalid argument list')
2398 2398 >>> _parsealiasdecl('foo("string")')
2399 2399 ('foo("string")', None, None, 'invalid argument list')
2400 2400 >>> _parsealiasdecl('foo($1, $2')
2401 2401 ('foo($1, $2', None, None, 'at 10: unexpected token: end')
2402 2402 >>> _parsealiasdecl('foo("string')
2403 2403 ('foo("string', None, None, 'at 5: unterminated string')
2404 2404 >>> _parsealiasdecl('foo($1, $2, $1)')
2405 2405 ('foo', None, None, 'argument names collide with each other')
2406 2406 """
2407 2407 p = parser.parser(elements)
2408 2408 try:
2409 2409 tree, pos = p.parse(_tokenizealias(decl))
2410 2410 if (pos != len(decl)):
2411 2411 raise error.ParseError(_('invalid token'), pos)
2412 2412
2413 2413 if isvalidsymbol(tree):
2414 2414 # "name = ...." style
2415 2415 name = getsymbol(tree)
2416 2416 if name.startswith('$'):
2417 2417 return (decl, None, None, _("'$' not for alias arguments"))
2418 2418 return (name, ('symbol', name), None, None)
2419 2419
2420 2420 if isvalidfunc(tree):
2421 2421 # "name(arg, ....) = ...." style
2422 2422 name = getfuncname(tree)
2423 2423 if name.startswith('$'):
2424 2424 return (decl, None, None, _("'$' not for alias arguments"))
2425 2425 args = []
2426 2426 for arg in getfuncargs(tree):
2427 2427 if not isvalidsymbol(arg):
2428 2428 return (decl, None, None, _("invalid argument list"))
2429 2429 args.append(getsymbol(arg))
2430 2430 if len(args) != len(set(args)):
2431 2431 return (name, None, None,
2432 2432 _("argument names collide with each other"))
2433 2433 return (name, ('func', ('symbol', name)), args, None)
2434 2434
2435 2435 return (decl, None, None, _("invalid format"))
2436 2436 except error.ParseError as inst:
2437 2437 return (decl, None, None, parseerrordetail(inst))
2438 2438
2439 2439 def _parsealiasdefn(defn, args):
2440 2440 """Parse alias definition ``defn``
2441 2441
2442 2442 This function also replaces alias argument references in the
2443 2443 specified definition by ``_aliasarg(ARGNAME)``.
2444 2444
2445 2445 ``args`` is a list of alias argument names, or None if the alias
2446 2446 is declared as a symbol.
2447 2447
2448 2448 This returns "tree" as parsing result.
2449 2449
2450 2450 >>> args = ['$1', '$2', 'foo']
2451 2451 >>> print prettyformat(_parsealiasdefn('$1 or foo', args))
2452 2452 (or
2453 2453 (func
2454 2454 ('symbol', '_aliasarg')
2455 2455 ('string', '$1'))
2456 2456 (func
2457 2457 ('symbol', '_aliasarg')
2458 2458 ('string', 'foo')))
2459 2459 >>> try:
2460 2460 ... _parsealiasdefn('$1 or $bar', args)
2461 2461 ... except error.ParseError, inst:
2462 2462 ... print parseerrordetail(inst)
2463 2463 at 6: '$' not for alias arguments
2464 2464 >>> args = ['$1', '$10', 'foo']
2465 2465 >>> print prettyformat(_parsealiasdefn('$10 or foobar', args))
2466 2466 (or
2467 2467 (func
2468 2468 ('symbol', '_aliasarg')
2469 2469 ('string', '$10'))
2470 2470 ('symbol', 'foobar'))
2471 2471 >>> print prettyformat(_parsealiasdefn('"$1" or "foo"', args))
2472 2472 (or
2473 2473 ('string', '$1')
2474 2474 ('string', 'foo'))
2475 2475 """
2476 2476 def tokenizedefn(program, lookup=None):
2477 2477 if args:
2478 2478 argset = set(args)
2479 2479 else:
2480 2480 argset = set()
2481 2481
2482 2482 for t, value, pos in _tokenizealias(program, lookup=lookup):
2483 2483 if t == 'symbol':
2484 2484 if value in argset:
2485 2485 # emulate tokenization of "_aliasarg('ARGNAME')":
2486 2486 # "_aliasarg()" is an unknown symbol only used separate
2487 2487 # alias argument placeholders from regular strings.
2488 2488 yield ('symbol', '_aliasarg', pos)
2489 2489 yield ('(', None, pos)
2490 2490 yield ('string', value, pos)
2491 2491 yield (')', None, pos)
2492 2492 continue
2493 2493 elif value.startswith('$'):
2494 2494 raise error.ParseError(_("'$' not for alias arguments"),
2495 2495 pos)
2496 2496 yield (t, value, pos)
2497 2497
2498 2498 p = parser.parser(elements)
2499 2499 tree, pos = p.parse(tokenizedefn(defn))
2500 2500 if pos != len(defn):
2501 2501 raise error.ParseError(_('invalid token'), pos)
2502 2502 return parser.simplifyinfixops(tree, ('or',))
2503 2503
2504 2504 class revsetalias(object):
2505 2505 # whether own `error` information is already shown or not.
2506 2506 # this avoids showing same warning multiple times at each `findaliases`.
2507 2507 warned = False
2508 2508
2509 2509 def __init__(self, name, value):
2510 2510 '''Aliases like:
2511 2511
2512 2512 h = heads(default)
2513 2513 b($1) = ancestors($1) - ancestors(default)
2514 2514 '''
2515 2515 self.name, self.tree, self.args, self.error = _parsealiasdecl(name)
2516 2516 if self.error:
2517 2517 self.error = _('failed to parse the declaration of revset alias'
2518 2518 ' "%s": %s') % (self.name, self.error)
2519 2519 return
2520 2520
2521 2521 try:
2522 2522 self.replacement = _parsealiasdefn(value, self.args)
2523 2523 # Check for placeholder injection
2524 2524 _checkaliasarg(self.replacement, self.args)
2525 2525 except error.ParseError as inst:
2526 2526 self.error = _('failed to parse the definition of revset alias'
2527 2527 ' "%s": %s') % (self.name, parseerrordetail(inst))
2528 2528
2529 2529 def _getalias(aliases, tree):
2530 2530 """If tree looks like an unexpanded alias, return it. Return None
2531 2531 otherwise.
2532 2532 """
2533 2533 if isinstance(tree, tuple) and tree:
2534 2534 if tree[0] == 'symbol' and len(tree) == 2:
2535 2535 name = tree[1]
2536 2536 alias = aliases.get(name)
2537 2537 if alias and alias.args is None and alias.tree == tree:
2538 2538 return alias
2539 2539 if tree[0] == 'func' and len(tree) > 1:
2540 2540 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
2541 2541 name = tree[1][1]
2542 2542 alias = aliases.get(name)
2543 2543 if alias and alias.args is not None and alias.tree == tree[:2]:
2544 2544 return alias
2545 2545 return None
2546 2546
2547 2547 def _expandargs(tree, args):
2548 2548 """Replace _aliasarg instances with the substitution value of the
2549 2549 same name in args, recursively.
2550 2550 """
2551 2551 if not tree or not isinstance(tree, tuple):
2552 2552 return tree
2553 2553 arg = _getaliasarg(tree)
2554 2554 if arg is not None:
2555 2555 return args[arg]
2556 2556 return tuple(_expandargs(t, args) for t in tree)
2557 2557
2558 2558 def _expandaliases(aliases, tree, expanding, cache):
2559 2559 """Expand aliases in tree, recursively.
2560 2560
2561 2561 'aliases' is a dictionary mapping user defined aliases to
2562 2562 revsetalias objects.
2563 2563 """
2564 2564 if not isinstance(tree, tuple):
2565 2565 # Do not expand raw strings
2566 2566 return tree
2567 2567 alias = _getalias(aliases, tree)
2568 2568 if alias is not None:
2569 2569 if alias.error:
2570 2570 raise util.Abort(alias.error)
2571 2571 if alias in expanding:
2572 2572 raise error.ParseError(_('infinite expansion of revset alias "%s" '
2573 2573 'detected') % alias.name)
2574 2574 expanding.append(alias)
2575 2575 if alias.name not in cache:
2576 2576 cache[alias.name] = _expandaliases(aliases, alias.replacement,
2577 2577 expanding, cache)
2578 2578 result = cache[alias.name]
2579 2579 expanding.pop()
2580 2580 if alias.args is not None:
2581 2581 l = getlist(tree[2])
2582 2582 if len(l) != len(alias.args):
2583 2583 raise error.ParseError(
2584 2584 _('invalid number of arguments: %s') % len(l))
2585 2585 l = [_expandaliases(aliases, a, [], cache) for a in l]
2586 2586 result = _expandargs(result, dict(zip(alias.args, l)))
2587 2587 else:
2588 2588 result = tuple(_expandaliases(aliases, t, expanding, cache)
2589 2589 for t in tree)
2590 2590 return result
2591 2591
2592 2592 def findaliases(ui, tree, showwarning=None):
2593 2593 _checkaliasarg(tree)
2594 2594 aliases = {}
2595 2595 for k, v in ui.configitems('revsetalias'):
2596 2596 alias = revsetalias(k, v)
2597 2597 aliases[alias.name] = alias
2598 2598 tree = _expandaliases(aliases, tree, [], {})
2599 2599 if showwarning:
2600 2600 # warn about problematic (but not referred) aliases
2601 2601 for name, alias in sorted(aliases.iteritems()):
2602 2602 if alias.error and not alias.warned:
2603 2603 showwarning(_('warning: %s\n') % (alias.error))
2604 2604 alias.warned = True
2605 2605 return tree
2606 2606
2607 2607 def foldconcat(tree):
2608 2608 """Fold elements to be concatenated by `##`
2609 2609 """
2610 2610 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2611 2611 return tree
2612 2612 if tree[0] == '_concat':
2613 2613 pending = [tree]
2614 2614 l = []
2615 2615 while pending:
2616 2616 e = pending.pop()
2617 2617 if e[0] == '_concat':
2618 2618 pending.extend(reversed(e[1:]))
2619 2619 elif e[0] in ('string', 'symbol'):
2620 2620 l.append(e[1])
2621 2621 else:
2622 2622 msg = _("\"##\" can't concatenate \"%s\" element") % (e[0])
2623 2623 raise error.ParseError(msg)
2624 2624 return ('string', ''.join(l))
2625 2625 else:
2626 2626 return tuple(foldconcat(t) for t in tree)
2627 2627
2628 2628 def parse(spec, lookup=None):
2629 2629 p = parser.parser(elements)
2630 2630 tree, pos = p.parse(tokenize(spec, lookup=lookup))
2631 2631 if pos != len(spec):
2632 2632 raise error.ParseError(_("invalid token"), pos)
2633 2633 return parser.simplifyinfixops(tree, ('or',))
2634 2634
2635 2635 def posttreebuilthook(tree, repo):
2636 2636 # hook for extensions to execute code on the optimized tree
2637 2637 pass
2638 2638
2639 2639 def match(ui, spec, repo=None):
2640 2640 if not spec:
2641 2641 raise error.ParseError(_("empty query"))
2642 2642 lookup = None
2643 2643 if repo:
2644 2644 lookup = repo.__contains__
2645 2645 tree = parse(spec, lookup)
2646 2646 if ui:
2647 2647 tree = findaliases(ui, tree, showwarning=ui.warn)
2648 2648 tree = foldconcat(tree)
2649 2649 weight, tree = optimize(tree, True)
2650 2650 posttreebuilthook(tree, repo)
2651 2651 def mfunc(repo, subset=None):
2652 2652 if subset is None:
2653 2653 subset = fullreposet(repo)
2654 2654 if util.safehasattr(subset, 'isascending'):
2655 2655 result = getset(repo, subset, tree)
2656 2656 else:
2657 2657 result = getset(repo, baseset(subset), tree)
2658 2658 return result
2659 2659 return mfunc
2660 2660
2661 2661 def formatspec(expr, *args):
2662 2662 '''
2663 2663 This is a convenience function for using revsets internally, and
2664 2664 escapes arguments appropriately. Aliases are intentionally ignored
2665 2665 so that intended expression behavior isn't accidentally subverted.
2666 2666
2667 2667 Supported arguments:
2668 2668
2669 2669 %r = revset expression, parenthesized
2670 2670 %d = int(arg), no quoting
2671 2671 %s = string(arg), escaped and single-quoted
2672 2672 %b = arg.branch(), escaped and single-quoted
2673 2673 %n = hex(arg), single-quoted
2674 2674 %% = a literal '%'
2675 2675
2676 2676 Prefixing the type with 'l' specifies a parenthesized list of that type.
2677 2677
2678 2678 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
2679 2679 '(10 or 11):: and ((this()) or (that()))'
2680 2680 >>> formatspec('%d:: and not %d::', 10, 20)
2681 2681 '10:: and not 20::'
2682 2682 >>> formatspec('%ld or %ld', [], [1])
2683 2683 "_list('') or 1"
2684 2684 >>> formatspec('keyword(%s)', 'foo\\xe9')
2685 2685 "keyword('foo\\\\xe9')"
2686 2686 >>> b = lambda: 'default'
2687 2687 >>> b.branch = b
2688 2688 >>> formatspec('branch(%b)', b)
2689 2689 "branch('default')"
2690 2690 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
2691 2691 "root(_list('a\\x00b\\x00c\\x00d'))"
2692 2692 '''
2693 2693
2694 2694 def quote(s):
2695 2695 return repr(str(s))
2696 2696
2697 2697 def argtype(c, arg):
2698 2698 if c == 'd':
2699 2699 return str(int(arg))
2700 2700 elif c == 's':
2701 2701 return quote(arg)
2702 2702 elif c == 'r':
2703 2703 parse(arg) # make sure syntax errors are confined
2704 2704 return '(%s)' % arg
2705 2705 elif c == 'n':
2706 2706 return quote(node.hex(arg))
2707 2707 elif c == 'b':
2708 2708 return quote(arg.branch())
2709 2709
2710 2710 def listexp(s, t):
2711 2711 l = len(s)
2712 2712 if l == 0:
2713 2713 return "_list('')"
2714 2714 elif l == 1:
2715 2715 return argtype(t, s[0])
2716 2716 elif t == 'd':
2717 2717 return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
2718 2718 elif t == 's':
2719 2719 return "_list('%s')" % "\0".join(s)
2720 2720 elif t == 'n':
2721 2721 return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
2722 2722 elif t == 'b':
2723 2723 return "_list('%s')" % "\0".join(a.branch() for a in s)
2724 2724
2725 2725 m = l // 2
2726 2726 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
2727 2727
2728 2728 ret = ''
2729 2729 pos = 0
2730 2730 arg = 0
2731 2731 while pos < len(expr):
2732 2732 c = expr[pos]
2733 2733 if c == '%':
2734 2734 pos += 1
2735 2735 d = expr[pos]
2736 2736 if d == '%':
2737 2737 ret += d
2738 2738 elif d in 'dsnbr':
2739 2739 ret += argtype(d, args[arg])
2740 2740 arg += 1
2741 2741 elif d == 'l':
2742 2742 # a list of some type
2743 2743 pos += 1
2744 2744 d = expr[pos]
2745 2745 ret += listexp(list(args[arg]), d)
2746 2746 arg += 1
2747 2747 else:
2748 2748 raise util.Abort('unexpected revspec format character %s' % d)
2749 2749 else:
2750 2750 ret += c
2751 2751 pos += 1
2752 2752
2753 2753 return ret
2754 2754
2755 2755 def prettyformat(tree):
2756 2756 return parser.prettyformat(tree, ('string', 'symbol'))
2757 2757
2758 2758 def depth(tree):
2759 2759 if isinstance(tree, tuple):
2760 2760 return max(map(depth, tree)) + 1
2761 2761 else:
2762 2762 return 0
2763 2763
2764 2764 def funcsused(tree):
2765 2765 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
2766 2766 return set()
2767 2767 else:
2768 2768 funcs = set()
2769 2769 for s in tree[1:]:
2770 2770 funcs |= funcsused(s)
2771 2771 if tree[0] == 'func':
2772 2772 funcs.add(tree[1][1])
2773 2773 return funcs
2774 2774
2775 2775 class abstractsmartset(object):
2776 2776
2777 2777 def __nonzero__(self):
2778 2778 """True if the smartset is not empty"""
2779 2779 raise NotImplementedError()
2780 2780
2781 2781 def __contains__(self, rev):
2782 2782 """provide fast membership testing"""
2783 2783 raise NotImplementedError()
2784 2784
2785 2785 def __iter__(self):
2786 2786 """iterate the set in the order it is supposed to be iterated"""
2787 2787 raise NotImplementedError()
2788 2788
2789 2789 # Attributes containing a function to perform a fast iteration in a given
2790 2790 # direction. A smartset can have none, one, or both defined.
2791 2791 #
2792 2792 # Default value is None instead of a function returning None to avoid
2793 2793 # initializing an iterator just for testing if a fast method exists.
2794 2794 fastasc = None
2795 2795 fastdesc = None
2796 2796
2797 2797 def isascending(self):
2798 2798 """True if the set will iterate in ascending order"""
2799 2799 raise NotImplementedError()
2800 2800
2801 2801 def isdescending(self):
2802 2802 """True if the set will iterate in descending order"""
2803 2803 raise NotImplementedError()
2804 2804
2805 2805 def min(self):
2806 2806 """return the minimum element in the set"""
2807 2807 if self.fastasc is not None:
2808 2808 for r in self.fastasc():
2809 2809 return r
2810 2810 raise ValueError('arg is an empty sequence')
2811 2811 return min(self)
2812 2812
2813 2813 def max(self):
2814 2814 """return the maximum element in the set"""
2815 2815 if self.fastdesc is not None:
2816 2816 for r in self.fastdesc():
2817 2817 return r
2818 2818 raise ValueError('arg is an empty sequence')
2819 2819 return max(self)
2820 2820
2821 2821 def first(self):
2822 2822 """return the first element in the set (user iteration perspective)
2823 2823
2824 2824 Return None if the set is empty"""
2825 2825 raise NotImplementedError()
2826 2826
2827 2827 def last(self):
2828 2828 """return the last element in the set (user iteration perspective)
2829 2829
2830 2830 Return None if the set is empty"""
2831 2831 raise NotImplementedError()
2832 2832
2833 2833 def __len__(self):
2834 2834 """return the length of the smartsets
2835 2835
2836 2836 This can be expensive on smartset that could be lazy otherwise."""
2837 2837 raise NotImplementedError()
2838 2838
2839 2839 def reverse(self):
2840 2840 """reverse the expected iteration order"""
2841 2841 raise NotImplementedError()
2842 2842
2843 2843 def sort(self, reverse=True):
2844 2844 """get the set to iterate in an ascending or descending order"""
2845 2845 raise NotImplementedError()
2846 2846
2847 2847 def __and__(self, other):
2848 2848 """Returns a new object with the intersection of the two collections.
2849 2849
2850 2850 This is part of the mandatory API for smartset."""
2851 2851 if isinstance(other, fullreposet):
2852 2852 return self
2853 2853 return self.filter(other.__contains__, cache=False)
2854 2854
2855 2855 def __add__(self, other):
2856 2856 """Returns a new object with the union of the two collections.
2857 2857
2858 2858 This is part of the mandatory API for smartset."""
2859 2859 return addset(self, other)
2860 2860
2861 2861 def __sub__(self, other):
2862 2862 """Returns a new object with the substraction of the two collections.
2863 2863
2864 2864 This is part of the mandatory API for smartset."""
2865 2865 c = other.__contains__
2866 2866 return self.filter(lambda r: not c(r), cache=False)
2867 2867
2868 2868 def filter(self, condition, cache=True):
2869 2869 """Returns this smartset filtered by condition as a new smartset.
2870 2870
2871 2871 `condition` is a callable which takes a revision number and returns a
2872 2872 boolean.
2873 2873
2874 2874 This is part of the mandatory API for smartset."""
2875 2875 # builtin cannot be cached. but do not needs to
2876 2876 if cache and util.safehasattr(condition, 'func_code'):
2877 2877 condition = util.cachefunc(condition)
2878 2878 return filteredset(self, condition)
2879 2879
2880 2880 class baseset(abstractsmartset):
2881 2881 """Basic data structure that represents a revset and contains the basic
2882 2882 operation that it should be able to perform.
2883 2883
2884 2884 Every method in this class should be implemented by any smartset class.
2885 2885 """
2886 2886 def __init__(self, data=()):
2887 2887 if not isinstance(data, list):
2888 2888 data = list(data)
2889 2889 self._list = data
2890 2890 self._ascending = None
2891 2891
2892 2892 @util.propertycache
2893 2893 def _set(self):
2894 2894 return set(self._list)
2895 2895
2896 2896 @util.propertycache
2897 2897 def _asclist(self):
2898 2898 asclist = self._list[:]
2899 2899 asclist.sort()
2900 2900 return asclist
2901 2901
2902 2902 def __iter__(self):
2903 2903 if self._ascending is None:
2904 2904 return iter(self._list)
2905 2905 elif self._ascending:
2906 2906 return iter(self._asclist)
2907 2907 else:
2908 2908 return reversed(self._asclist)
2909 2909
2910 2910 def fastasc(self):
2911 2911 return iter(self._asclist)
2912 2912
2913 2913 def fastdesc(self):
2914 2914 return reversed(self._asclist)
2915 2915
2916 2916 @util.propertycache
2917 2917 def __contains__(self):
2918 2918 return self._set.__contains__
2919 2919
2920 2920 def __nonzero__(self):
2921 2921 return bool(self._list)
2922 2922
2923 2923 def sort(self, reverse=False):
2924 2924 self._ascending = not bool(reverse)
2925 2925
2926 2926 def reverse(self):
2927 2927 if self._ascending is None:
2928 2928 self._list.reverse()
2929 2929 else:
2930 2930 self._ascending = not self._ascending
2931 2931
2932 2932 def __len__(self):
2933 2933 return len(self._list)
2934 2934
2935 2935 def isascending(self):
2936 2936 """Returns True if the collection is ascending order, False if not.
2937 2937
2938 2938 This is part of the mandatory API for smartset."""
2939 2939 if len(self) <= 1:
2940 2940 return True
2941 2941 return self._ascending is not None and self._ascending
2942 2942
2943 2943 def isdescending(self):
2944 2944 """Returns True if the collection is descending order, False if not.
2945 2945
2946 2946 This is part of the mandatory API for smartset."""
2947 2947 if len(self) <= 1:
2948 2948 return True
2949 2949 return self._ascending is not None and not self._ascending
2950 2950
2951 2951 def first(self):
2952 2952 if self:
2953 2953 if self._ascending is None:
2954 2954 return self._list[0]
2955 2955 elif self._ascending:
2956 2956 return self._asclist[0]
2957 2957 else:
2958 2958 return self._asclist[-1]
2959 2959 return None
2960 2960
2961 2961 def last(self):
2962 2962 if self:
2963 2963 if self._ascending is None:
2964 2964 return self._list[-1]
2965 2965 elif self._ascending:
2966 2966 return self._asclist[-1]
2967 2967 else:
2968 2968 return self._asclist[0]
2969 2969 return None
2970 2970
2971 2971 def __repr__(self):
2972 2972 d = {None: '', False: '-', True: '+'}[self._ascending]
2973 2973 return '<%s%s %r>' % (type(self).__name__, d, self._list)
2974 2974
2975 2975 class filteredset(abstractsmartset):
2976 2976 """Duck type for baseset class which iterates lazily over the revisions in
2977 2977 the subset and contains a function which tests for membership in the
2978 2978 revset
2979 2979 """
2980 2980 def __init__(self, subset, condition=lambda x: True):
2981 2981 """
2982 2982 condition: a function that decide whether a revision in the subset
2983 2983 belongs to the revset or not.
2984 2984 """
2985 2985 self._subset = subset
2986 2986 self._condition = condition
2987 2987 self._cache = {}
2988 2988
2989 2989 def __contains__(self, x):
2990 2990 c = self._cache
2991 2991 if x not in c:
2992 2992 v = c[x] = x in self._subset and self._condition(x)
2993 2993 return v
2994 2994 return c[x]
2995 2995
2996 2996 def __iter__(self):
2997 2997 return self._iterfilter(self._subset)
2998 2998
2999 2999 def _iterfilter(self, it):
3000 3000 cond = self._condition
3001 3001 for x in it:
3002 3002 if cond(x):
3003 3003 yield x
3004 3004
3005 3005 @property
3006 3006 def fastasc(self):
3007 3007 it = self._subset.fastasc
3008 3008 if it is None:
3009 3009 return None
3010 3010 return lambda: self._iterfilter(it())
3011 3011
3012 3012 @property
3013 3013 def fastdesc(self):
3014 3014 it = self._subset.fastdesc
3015 3015 if it is None:
3016 3016 return None
3017 3017 return lambda: self._iterfilter(it())
3018 3018
3019 3019 def __nonzero__(self):
3020 3020 for r in self:
3021 3021 return True
3022 3022 return False
3023 3023
3024 3024 def __len__(self):
3025 3025 # Basic implementation to be changed in future patches.
3026 3026 l = baseset([r for r in self])
3027 3027 return len(l)
3028 3028
3029 3029 def sort(self, reverse=False):
3030 3030 self._subset.sort(reverse=reverse)
3031 3031
3032 3032 def reverse(self):
3033 3033 self._subset.reverse()
3034 3034
3035 3035 def isascending(self):
3036 3036 return self._subset.isascending()
3037 3037
3038 3038 def isdescending(self):
3039 3039 return self._subset.isdescending()
3040 3040
3041 3041 def first(self):
3042 3042 for x in self:
3043 3043 return x
3044 3044 return None
3045 3045
3046 3046 def last(self):
3047 3047 it = None
3048 3048 if self.isascending():
3049 3049 it = self.fastdesc
3050 3050 elif self.isdescending():
3051 3051 it = self.fastasc
3052 3052 if it is not None:
3053 3053 for x in it():
3054 3054 return x
3055 3055 return None #empty case
3056 3056 else:
3057 3057 x = None
3058 3058 for x in self:
3059 3059 pass
3060 3060 return x
3061 3061
3062 3062 def __repr__(self):
3063 3063 return '<%s %r>' % (type(self).__name__, self._subset)
3064 3064
3065 3065 # this function will be removed, or merged to addset or orset, when
3066 3066 # - scmutil.revrange() can be rewritten to not combine calculated smartsets
3067 3067 # - or addset can handle more than two sets without balanced tree
3068 3068 def _combinesets(subsets):
3069 3069 """Create balanced tree of addsets representing union of given sets"""
3070 3070 if not subsets:
3071 3071 return baseset()
3072 3072 if len(subsets) == 1:
3073 3073 return subsets[0]
3074 3074 p = len(subsets) // 2
3075 3075 xs = _combinesets(subsets[:p])
3076 3076 ys = _combinesets(subsets[p:])
3077 3077 return addset(xs, ys)
3078 3078
3079 3079 def _iterordered(ascending, iter1, iter2):
3080 3080 """produce an ordered iteration from two iterators with the same order
3081 3081
3082 3082 The ascending is used to indicated the iteration direction.
3083 3083 """
3084 3084 choice = max
3085 3085 if ascending:
3086 3086 choice = min
3087 3087
3088 3088 val1 = None
3089 3089 val2 = None
3090 3090 try:
3091 3091 # Consume both iterators in an ordered way until one is empty
3092 3092 while True:
3093 3093 if val1 is None:
3094 3094 val1 = iter1.next()
3095 3095 if val2 is None:
3096 3096 val2 = iter2.next()
3097 3097 next = choice(val1, val2)
3098 3098 yield next
3099 3099 if val1 == next:
3100 3100 val1 = None
3101 3101 if val2 == next:
3102 3102 val2 = None
3103 3103 except StopIteration:
3104 3104 # Flush any remaining values and consume the other one
3105 3105 it = iter2
3106 3106 if val1 is not None:
3107 3107 yield val1
3108 3108 it = iter1
3109 3109 elif val2 is not None:
3110 3110 # might have been equality and both are empty
3111 3111 yield val2
3112 3112 for val in it:
3113 3113 yield val
3114 3114
3115 3115 class addset(abstractsmartset):
3116 3116 """Represent the addition of two sets
3117 3117
3118 3118 Wrapper structure for lazily adding two structures without losing much
3119 3119 performance on the __contains__ method
3120 3120
3121 3121 If the ascending attribute is set, that means the two structures are
3122 3122 ordered in either an ascending or descending way. Therefore, we can add
3123 3123 them maintaining the order by iterating over both at the same time
3124 3124
3125 3125 >>> xs = baseset([0, 3, 2])
3126 3126 >>> ys = baseset([5, 2, 4])
3127 3127
3128 3128 >>> rs = addset(xs, ys)
3129 3129 >>> bool(rs), 0 in rs, 1 in rs, 5 in rs, rs.first(), rs.last()
3130 3130 (True, True, False, True, 0, 4)
3131 3131 >>> rs = addset(xs, baseset([]))
3132 3132 >>> bool(rs), 0 in rs, 1 in rs, rs.first(), rs.last()
3133 3133 (True, True, False, 0, 2)
3134 3134 >>> rs = addset(baseset([]), baseset([]))
3135 3135 >>> bool(rs), 0 in rs, rs.first(), rs.last()
3136 3136 (False, False, None, None)
3137 3137
3138 3138 iterate unsorted:
3139 3139 >>> rs = addset(xs, ys)
3140 3140 >>> [x for x in rs] # without _genlist
3141 3141 [0, 3, 2, 5, 4]
3142 3142 >>> assert not rs._genlist
3143 3143 >>> len(rs)
3144 3144 5
3145 3145 >>> [x for x in rs] # with _genlist
3146 3146 [0, 3, 2, 5, 4]
3147 3147 >>> assert rs._genlist
3148 3148
3149 3149 iterate ascending:
3150 3150 >>> rs = addset(xs, ys, ascending=True)
3151 3151 >>> [x for x in rs], [x for x in rs.fastasc()] # without _asclist
3152 3152 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3153 3153 >>> assert not rs._asclist
3154 3154 >>> len(rs)
3155 3155 5
3156 3156 >>> [x for x in rs], [x for x in rs.fastasc()]
3157 3157 ([0, 2, 3, 4, 5], [0, 2, 3, 4, 5])
3158 3158 >>> assert rs._asclist
3159 3159
3160 3160 iterate descending:
3161 3161 >>> rs = addset(xs, ys, ascending=False)
3162 3162 >>> [x for x in rs], [x for x in rs.fastdesc()] # without _asclist
3163 3163 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3164 3164 >>> assert not rs._asclist
3165 3165 >>> len(rs)
3166 3166 5
3167 3167 >>> [x for x in rs], [x for x in rs.fastdesc()]
3168 3168 ([5, 4, 3, 2, 0], [5, 4, 3, 2, 0])
3169 3169 >>> assert rs._asclist
3170 3170
3171 3171 iterate ascending without fastasc:
3172 3172 >>> rs = addset(xs, generatorset(ys), ascending=True)
3173 3173 >>> assert rs.fastasc is None
3174 3174 >>> [x for x in rs]
3175 3175 [0, 2, 3, 4, 5]
3176 3176
3177 3177 iterate descending without fastdesc:
3178 3178 >>> rs = addset(generatorset(xs), ys, ascending=False)
3179 3179 >>> assert rs.fastdesc is None
3180 3180 >>> [x for x in rs]
3181 3181 [5, 4, 3, 2, 0]
3182 3182 """
3183 3183 def __init__(self, revs1, revs2, ascending=None):
3184 3184 self._r1 = revs1
3185 3185 self._r2 = revs2
3186 3186 self._iter = None
3187 3187 self._ascending = ascending
3188 3188 self._genlist = None
3189 3189 self._asclist = None
3190 3190
3191 3191 def __len__(self):
3192 3192 return len(self._list)
3193 3193
3194 3194 def __nonzero__(self):
3195 3195 return bool(self._r1) or bool(self._r2)
3196 3196
3197 3197 @util.propertycache
3198 3198 def _list(self):
3199 3199 if not self._genlist:
3200 3200 self._genlist = baseset(iter(self))
3201 3201 return self._genlist
3202 3202
3203 3203 def __iter__(self):
3204 3204 """Iterate over both collections without repeating elements
3205 3205
3206 3206 If the ascending attribute is not set, iterate over the first one and
3207 3207 then over the second one checking for membership on the first one so we
3208 3208 dont yield any duplicates.
3209 3209
3210 3210 If the ascending attribute is set, iterate over both collections at the
3211 3211 same time, yielding only one value at a time in the given order.
3212 3212 """
3213 3213 if self._ascending is None:
3214 3214 if self._genlist:
3215 3215 return iter(self._genlist)
3216 3216 def arbitraryordergen():
3217 3217 for r in self._r1:
3218 3218 yield r
3219 3219 inr1 = self._r1.__contains__
3220 3220 for r in self._r2:
3221 3221 if not inr1(r):
3222 3222 yield r
3223 3223 return arbitraryordergen()
3224 3224 # try to use our own fast iterator if it exists
3225 3225 self._trysetasclist()
3226 3226 if self._ascending:
3227 3227 attr = 'fastasc'
3228 3228 else:
3229 3229 attr = 'fastdesc'
3230 3230 it = getattr(self, attr)
3231 3231 if it is not None:
3232 3232 return it()
3233 3233 # maybe half of the component supports fast
3234 3234 # get iterator for _r1
3235 3235 iter1 = getattr(self._r1, attr)
3236 3236 if iter1 is None:
3237 3237 # let's avoid side effect (not sure it matters)
3238 3238 iter1 = iter(sorted(self._r1, reverse=not self._ascending))
3239 3239 else:
3240 3240 iter1 = iter1()
3241 3241 # get iterator for _r2
3242 3242 iter2 = getattr(self._r2, attr)
3243 3243 if iter2 is None:
3244 3244 # let's avoid side effect (not sure it matters)
3245 3245 iter2 = iter(sorted(self._r2, reverse=not self._ascending))
3246 3246 else:
3247 3247 iter2 = iter2()
3248 3248 return _iterordered(self._ascending, iter1, iter2)
3249 3249
3250 3250 def _trysetasclist(self):
3251 3251 """populate the _asclist attribute if possible and necessary"""
3252 3252 if self._genlist is not None and self._asclist is None:
3253 3253 self._asclist = sorted(self._genlist)
3254 3254
3255 3255 @property
3256 3256 def fastasc(self):
3257 3257 self._trysetasclist()
3258 3258 if self._asclist is not None:
3259 3259 return self._asclist.__iter__
3260 3260 iter1 = self._r1.fastasc
3261 3261 iter2 = self._r2.fastasc
3262 3262 if None in (iter1, iter2):
3263 3263 return None
3264 3264 return lambda: _iterordered(True, iter1(), iter2())
3265 3265
3266 3266 @property
3267 3267 def fastdesc(self):
3268 3268 self._trysetasclist()
3269 3269 if self._asclist is not None:
3270 3270 return self._asclist.__reversed__
3271 3271 iter1 = self._r1.fastdesc
3272 3272 iter2 = self._r2.fastdesc
3273 3273 if None in (iter1, iter2):
3274 3274 return None
3275 3275 return lambda: _iterordered(False, iter1(), iter2())
3276 3276
3277 3277 def __contains__(self, x):
3278 3278 return x in self._r1 or x in self._r2
3279 3279
3280 3280 def sort(self, reverse=False):
3281 3281 """Sort the added set
3282 3282
3283 3283 For this we use the cached list with all the generated values and if we
3284 3284 know they are ascending or descending we can sort them in a smart way.
3285 3285 """
3286 3286 self._ascending = not reverse
3287 3287
3288 3288 def isascending(self):
3289 3289 return self._ascending is not None and self._ascending
3290 3290
3291 3291 def isdescending(self):
3292 3292 return self._ascending is not None and not self._ascending
3293 3293
3294 3294 def reverse(self):
3295 3295 if self._ascending is None:
3296 3296 self._list.reverse()
3297 3297 else:
3298 3298 self._ascending = not self._ascending
3299 3299
3300 3300 def first(self):
3301 3301 for x in self:
3302 3302 return x
3303 3303 return None
3304 3304
3305 3305 def last(self):
3306 3306 self.reverse()
3307 3307 val = self.first()
3308 3308 self.reverse()
3309 3309 return val
3310 3310
3311 3311 def __repr__(self):
3312 3312 d = {None: '', False: '-', True: '+'}[self._ascending]
3313 3313 return '<%s%s %r, %r>' % (type(self).__name__, d, self._r1, self._r2)
3314 3314
3315 3315 class generatorset(abstractsmartset):
3316 3316 """Wrap a generator for lazy iteration
3317 3317
3318 3318 Wrapper structure for generators that provides lazy membership and can
3319 3319 be iterated more than once.
3320 3320 When asked for membership it generates values until either it finds the
3321 3321 requested one or has gone through all the elements in the generator
3322 3322 """
3323 3323 def __init__(self, gen, iterasc=None):
3324 3324 """
3325 3325 gen: a generator producing the values for the generatorset.
3326 3326 """
3327 3327 self._gen = gen
3328 3328 self._asclist = None
3329 3329 self._cache = {}
3330 3330 self._genlist = []
3331 3331 self._finished = False
3332 3332 self._ascending = True
3333 3333 if iterasc is not None:
3334 3334 if iterasc:
3335 3335 self.fastasc = self._iterator
3336 3336 self.__contains__ = self._asccontains
3337 3337 else:
3338 3338 self.fastdesc = self._iterator
3339 3339 self.__contains__ = self._desccontains
3340 3340
3341 3341 def __nonzero__(self):
3342 3342 # Do not use 'for r in self' because it will enforce the iteration
3343 3343 # order (default ascending), possibly unrolling a whole descending
3344 3344 # iterator.
3345 3345 if self._genlist:
3346 3346 return True
3347 3347 for r in self._consumegen():
3348 3348 return True
3349 3349 return False
3350 3350
3351 3351 def __contains__(self, x):
3352 3352 if x in self._cache:
3353 3353 return self._cache[x]
3354 3354
3355 3355 # Use new values only, as existing values would be cached.
3356 3356 for l in self._consumegen():
3357 3357 if l == x:
3358 3358 return True
3359 3359
3360 3360 self._cache[x] = False
3361 3361 return False
3362 3362
3363 3363 def _asccontains(self, x):
3364 3364 """version of contains optimised for ascending generator"""
3365 3365 if x in self._cache:
3366 3366 return self._cache[x]
3367 3367
3368 3368 # Use new values only, as existing values would be cached.
3369 3369 for l in self._consumegen():
3370 3370 if l == x:
3371 3371 return True
3372 3372 if l > x:
3373 3373 break
3374 3374
3375 3375 self._cache[x] = False
3376 3376 return False
3377 3377
3378 3378 def _desccontains(self, x):
3379 3379 """version of contains optimised for descending generator"""
3380 3380 if x in self._cache:
3381 3381 return self._cache[x]
3382 3382
3383 3383 # Use new values only, as existing values would be cached.
3384 3384 for l in self._consumegen():
3385 3385 if l == x:
3386 3386 return True
3387 3387 if l < x:
3388 3388 break
3389 3389
3390 3390 self._cache[x] = False
3391 3391 return False
3392 3392
3393 3393 def __iter__(self):
3394 3394 if self._ascending:
3395 3395 it = self.fastasc
3396 3396 else:
3397 3397 it = self.fastdesc
3398 3398 if it is not None:
3399 3399 return it()
3400 3400 # we need to consume the iterator
3401 3401 for x in self._consumegen():
3402 3402 pass
3403 3403 # recall the same code
3404 3404 return iter(self)
3405 3405
3406 3406 def _iterator(self):
3407 3407 if self._finished:
3408 3408 return iter(self._genlist)
3409 3409
3410 3410 # We have to use this complex iteration strategy to allow multiple
3411 3411 # iterations at the same time. We need to be able to catch revision
3412 3412 # removed from _consumegen and added to genlist in another instance.
3413 3413 #
3414 3414 # Getting rid of it would provide an about 15% speed up on this
3415 3415 # iteration.
3416 3416 genlist = self._genlist
3417 3417 nextrev = self._consumegen().next
3418 3418 _len = len # cache global lookup
3419 3419 def gen():
3420 3420 i = 0
3421 3421 while True:
3422 3422 if i < _len(genlist):
3423 3423 yield genlist[i]
3424 3424 else:
3425 3425 yield nextrev()
3426 3426 i += 1
3427 3427 return gen()
3428 3428
3429 3429 def _consumegen(self):
3430 3430 cache = self._cache
3431 3431 genlist = self._genlist.append
3432 3432 for item in self._gen:
3433 3433 cache[item] = True
3434 3434 genlist(item)
3435 3435 yield item
3436 3436 if not self._finished:
3437 3437 self._finished = True
3438 3438 asc = self._genlist[:]
3439 3439 asc.sort()
3440 3440 self._asclist = asc
3441 3441 self.fastasc = asc.__iter__
3442 3442 self.fastdesc = asc.__reversed__
3443 3443
3444 3444 def __len__(self):
3445 3445 for x in self._consumegen():
3446 3446 pass
3447 3447 return len(self._genlist)
3448 3448
3449 3449 def sort(self, reverse=False):
3450 3450 self._ascending = not reverse
3451 3451
3452 3452 def reverse(self):
3453 3453 self._ascending = not self._ascending
3454 3454
3455 3455 def isascending(self):
3456 3456 return self._ascending
3457 3457
3458 3458 def isdescending(self):
3459 3459 return not self._ascending
3460 3460
3461 3461 def first(self):
3462 3462 if self._ascending:
3463 3463 it = self.fastasc
3464 3464 else:
3465 3465 it = self.fastdesc
3466 3466 if it is None:
3467 3467 # we need to consume all and try again
3468 3468 for x in self._consumegen():
3469 3469 pass
3470 3470 return self.first()
3471 3471 return next(it(), None)
3472 3472
3473 3473 def last(self):
3474 3474 if self._ascending:
3475 3475 it = self.fastdesc
3476 3476 else:
3477 3477 it = self.fastasc
3478 3478 if it is None:
3479 3479 # we need to consume all and try again
3480 3480 for x in self._consumegen():
3481 3481 pass
3482 3482 return self.first()
3483 3483 return next(it(), None)
3484 3484
3485 3485 def __repr__(self):
3486 3486 d = {False: '-', True: '+'}[self._ascending]
3487 3487 return '<%s%s>' % (type(self).__name__, d)
3488 3488
3489 3489 class spanset(abstractsmartset):
3490 3490 """Duck type for baseset class which represents a range of revisions and
3491 3491 can work lazily and without having all the range in memory
3492 3492
3493 3493 Note that spanset(x, y) behave almost like xrange(x, y) except for two
3494 3494 notable points:
3495 3495 - when x < y it will be automatically descending,
3496 3496 - revision filtered with this repoview will be skipped.
3497 3497
3498 3498 """
3499 3499 def __init__(self, repo, start=0, end=None):
3500 3500 """
3501 3501 start: first revision included the set
3502 3502 (default to 0)
3503 3503 end: first revision excluded (last+1)
3504 3504 (default to len(repo)
3505 3505
3506 3506 Spanset will be descending if `end` < `start`.
3507 3507 """
3508 3508 if end is None:
3509 3509 end = len(repo)
3510 3510 self._ascending = start <= end
3511 3511 if not self._ascending:
3512 3512 start, end = end + 1, start +1
3513 3513 self._start = start
3514 3514 self._end = end
3515 3515 self._hiddenrevs = repo.changelog.filteredrevs
3516 3516
3517 3517 def sort(self, reverse=False):
3518 3518 self._ascending = not reverse
3519 3519
3520 3520 def reverse(self):
3521 3521 self._ascending = not self._ascending
3522 3522
3523 3523 def _iterfilter(self, iterrange):
3524 3524 s = self._hiddenrevs
3525 3525 for r in iterrange:
3526 3526 if r not in s:
3527 3527 yield r
3528 3528
3529 3529 def __iter__(self):
3530 3530 if self._ascending:
3531 3531 return self.fastasc()
3532 3532 else:
3533 3533 return self.fastdesc()
3534 3534
3535 3535 def fastasc(self):
3536 3536 iterrange = xrange(self._start, self._end)
3537 3537 if self._hiddenrevs:
3538 3538 return self._iterfilter(iterrange)
3539 3539 return iter(iterrange)
3540 3540
3541 3541 def fastdesc(self):
3542 3542 iterrange = xrange(self._end - 1, self._start - 1, -1)
3543 3543 if self._hiddenrevs:
3544 3544 return self._iterfilter(iterrange)
3545 3545 return iter(iterrange)
3546 3546
3547 3547 def __contains__(self, rev):
3548 3548 hidden = self._hiddenrevs
3549 3549 return ((self._start <= rev < self._end)
3550 3550 and not (hidden and rev in hidden))
3551 3551
3552 3552 def __nonzero__(self):
3553 3553 for r in self:
3554 3554 return True
3555 3555 return False
3556 3556
3557 3557 def __len__(self):
3558 3558 if not self._hiddenrevs:
3559 3559 return abs(self._end - self._start)
3560 3560 else:
3561 3561 count = 0
3562 3562 start = self._start
3563 3563 end = self._end
3564 3564 for rev in self._hiddenrevs:
3565 3565 if (end < rev <= start) or (start <= rev < end):
3566 3566 count += 1
3567 3567 return abs(self._end - self._start) - count
3568 3568
3569 3569 def isascending(self):
3570 3570 return self._ascending
3571 3571
3572 3572 def isdescending(self):
3573 3573 return not self._ascending
3574 3574
3575 3575 def first(self):
3576 3576 if self._ascending:
3577 3577 it = self.fastasc
3578 3578 else:
3579 3579 it = self.fastdesc
3580 3580 for x in it():
3581 3581 return x
3582 3582 return None
3583 3583
3584 3584 def last(self):
3585 3585 if self._ascending:
3586 3586 it = self.fastdesc
3587 3587 else:
3588 3588 it = self.fastasc
3589 3589 for x in it():
3590 3590 return x
3591 3591 return None
3592 3592
3593 3593 def __repr__(self):
3594 3594 d = {False: '-', True: '+'}[self._ascending]
3595 3595 return '<%s%s %d:%d>' % (type(self).__name__, d,
3596 3596 self._start, self._end - 1)
3597 3597
3598 3598 class fullreposet(spanset):
3599 3599 """a set containing all revisions in the repo
3600 3600
3601 3601 This class exists to host special optimization and magic to handle virtual
3602 3602 revisions such as "null".
3603 3603 """
3604 3604
3605 3605 def __init__(self, repo):
3606 3606 super(fullreposet, self).__init__(repo)
3607 3607
3608 3608 def __and__(self, other):
3609 3609 """As self contains the whole repo, all of the other set should also be
3610 3610 in self. Therefore `self & other = other`.
3611 3611
3612 3612 This boldly assumes the other contains valid revs only.
3613 3613 """
3614 3614 # other not a smartset, make is so
3615 3615 if not util.safehasattr(other, 'isascending'):
3616 3616 # filter out hidden revision
3617 3617 # (this boldly assumes all smartset are pure)
3618 3618 #
3619 3619 # `other` was used with "&", let's assume this is a set like
3620 3620 # object.
3621 3621 other = baseset(other - self._hiddenrevs)
3622 3622
3623 3623 # XXX As fullreposet is also used as bootstrap, this is wrong.
3624 3624 #
3625 3625 # With a giveme312() revset returning [3,1,2], this makes
3626 3626 # 'hg log -r "giveme312()"' -> 1, 2, 3 (wrong)
3627 3627 # We cannot just drop it because other usage still need to sort it:
3628 3628 # 'hg log -r "all() and giveme312()"' -> 1, 2, 3 (right)
3629 3629 #
3630 3630 # There is also some faulty revset implementations that rely on it
3631 3631 # (eg: children as of its state in e8075329c5fb)
3632 3632 #
3633 3633 # When we fix the two points above we can move this into the if clause
3634 3634 other.sort(reverse=self.isdescending())
3635 3635 return other
3636 3636
3637 3637 def prettyformatset(revs):
3638 3638 lines = []
3639 3639 rs = repr(revs)
3640 3640 p = 0
3641 3641 while p < len(rs):
3642 3642 q = rs.find('<', p + 1)
3643 3643 if q < 0:
3644 3644 q = len(rs)
3645 3645 l = rs.count('<', 0, p) - rs.count('>', 0, p)
3646 3646 assert l >= 0
3647 3647 lines.append((l, rs[p:q].rstrip()))
3648 3648 p = q
3649 3649 return '\n'.join(' ' * l + s for l, s in lines)
3650 3650
3651 3651 # tell hggettext to extract docstrings from these functions:
3652 3652 i18nfunctions = symbols.values()
@@ -1,2395 +1,2403 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 > diff -u log.nodes glog.nodes | grep '^[-+@ ]' || :
1433 1433 > }
1434 1434
1435 1435 glog always reorders nodes which explains the difference with log
1436 1436
1437 1437 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1438 1438 ['27', '25', '21', '34', '32', '31']
1439 1439 []
1440 1440 --- log.nodes * (glob)
1441 1441 +++ glog.nodes * (glob)
1442 1442 @@ -1,6 +1,6 @@
1443 1443 -nodetag 27
1444 1444 -nodetag 25
1445 1445 -nodetag 21
1446 1446 nodetag 34
1447 1447 nodetag 32
1448 1448 nodetag 31
1449 1449 +nodetag 27
1450 1450 +nodetag 25
1451 1451 +nodetag 21
1452 1452 $ testlog -u test -u not-a-user
1453 1453 []
1454 1454 (group
1455 1455 (group
1456 1456 (or
1457 1457 (func
1458 1458 ('symbol', 'user')
1459 1459 ('string', 'test'))
1460 1460 (func
1461 1461 ('symbol', 'user')
1462 1462 ('string', 'not-a-user')))))
1463 1463 $ testlog -b not-a-branch
1464 1464 abort: unknown revision 'not-a-branch'!
1465 1465 abort: unknown revision 'not-a-branch'!
1466 1466 abort: unknown revision 'not-a-branch'!
1467 1467 $ testlog -b 35 -b 36 --only-branch branch
1468 1468 []
1469 1469 (group
1470 1470 (group
1471 1471 (or
1472 1472 (func
1473 1473 ('symbol', 'branch')
1474 1474 ('string', 'default'))
1475 1475 (func
1476 1476 ('symbol', 'branch')
1477 1477 ('string', 'branch'))
1478 1478 (func
1479 1479 ('symbol', 'branch')
1480 1480 ('string', 'branch')))))
1481 1481 $ testlog -k expand -k merge
1482 1482 []
1483 1483 (group
1484 1484 (group
1485 1485 (or
1486 1486 (func
1487 1487 ('symbol', 'keyword')
1488 1488 ('string', 'expand'))
1489 1489 (func
1490 1490 ('symbol', 'keyword')
1491 1491 ('string', 'merge')))))
1492 1492 $ testlog --only-merges
1493 1493 []
1494 1494 (group
1495 1495 (func
1496 1496 ('symbol', 'merge')
1497 1497 None))
1498 1498 $ testlog --no-merges
1499 1499 []
1500 1500 (group
1501 1501 (not
1502 1502 (func
1503 1503 ('symbol', 'merge')
1504 1504 None)))
1505 1505 $ testlog --date '2 0 to 4 0'
1506 1506 []
1507 1507 (group
1508 1508 (func
1509 1509 ('symbol', 'date')
1510 1510 ('string', '2 0 to 4 0')))
1511 1511 $ hg log -G -d 'brace ) in a date'
1512 1512 abort: invalid date: 'brace ) in a date'
1513 1513 [255]
1514 1514 $ testlog --prune 31 --prune 32
1515 1515 []
1516 1516 (group
1517 1517 (group
1518 1518 (and
1519 1519 (not
1520 1520 (group
1521 1521 (or
1522 1522 ('string', '31')
1523 1523 (func
1524 1524 ('symbol', 'ancestors')
1525 1525 ('string', '31')))))
1526 1526 (not
1527 1527 (group
1528 1528 (or
1529 1529 ('string', '32')
1530 1530 (func
1531 1531 ('symbol', 'ancestors')
1532 1532 ('string', '32'))))))))
1533 1533
1534 1534 Dedicated repo for --follow and paths filtering. The g is crafted to
1535 1535 have 2 filelog topological heads in a linear changeset graph.
1536 1536
1537 1537 $ cd ..
1538 1538 $ hg init follow
1539 1539 $ cd follow
1540 1540 $ testlog --follow
1541 1541 []
1542 1542 []
1543 1543 $ testlog -rnull
1544 1544 ['null']
1545 1545 []
1546 1546 $ echo a > a
1547 1547 $ echo aa > aa
1548 1548 $ echo f > f
1549 1549 $ hg ci -Am "add a" a aa f
1550 1550 $ hg cp a b
1551 1551 $ hg cp f g
1552 1552 $ hg ci -m "copy a b"
1553 1553 $ mkdir dir
1554 1554 $ hg mv b dir
1555 1555 $ echo g >> g
1556 1556 $ echo f >> f
1557 1557 $ hg ci -m "mv b dir/b"
1558 1558 $ hg mv a b
1559 1559 $ hg cp -f f g
1560 1560 $ echo a > d
1561 1561 $ hg add d
1562 1562 $ hg ci -m "mv a b; add d"
1563 1563 $ hg mv dir/b e
1564 1564 $ hg ci -m "mv dir/b e"
1565 1565 $ hg log -G --template '({rev}) {desc|firstline}\n'
1566 1566 @ (4) mv dir/b e
1567 1567 |
1568 1568 o (3) mv a b; add d
1569 1569 |
1570 1570 o (2) mv b dir/b
1571 1571 |
1572 1572 o (1) copy a b
1573 1573 |
1574 1574 o (0) add a
1575 1575
1576 1576
1577 1577 $ testlog a
1578 1578 []
1579 1579 (group
1580 1580 (group
1581 1581 (func
1582 1582 ('symbol', 'filelog')
1583 1583 ('string', 'a'))))
1584 1584 $ testlog a b
1585 1585 []
1586 1586 (group
1587 1587 (group
1588 1588 (or
1589 1589 (func
1590 1590 ('symbol', 'filelog')
1591 1591 ('string', 'a'))
1592 1592 (func
1593 1593 ('symbol', 'filelog')
1594 1594 ('string', 'b')))))
1595 1595
1596 1596 Test falling back to slow path for non-existing files
1597 1597
1598 1598 $ testlog a c
1599 1599 []
1600 1600 (group
1601 1601 (func
1602 1602 ('symbol', '_matchfiles')
1603 1603 (list
1604 1604 (list
1605 1605 (list
1606 1606 ('string', 'r:')
1607 1607 ('string', 'd:relpath'))
1608 1608 ('string', 'p:a'))
1609 1609 ('string', 'p:c'))))
1610 1610
1611 1611 Test multiple --include/--exclude/paths
1612 1612
1613 1613 $ testlog --include a --include e --exclude b --exclude e a e
1614 1614 []
1615 1615 (group
1616 1616 (func
1617 1617 ('symbol', '_matchfiles')
1618 1618 (list
1619 1619 (list
1620 1620 (list
1621 1621 (list
1622 1622 (list
1623 1623 (list
1624 1624 (list
1625 1625 ('string', 'r:')
1626 1626 ('string', 'd:relpath'))
1627 1627 ('string', 'p:a'))
1628 1628 ('string', 'p:e'))
1629 1629 ('string', 'i:a'))
1630 1630 ('string', 'i:e'))
1631 1631 ('string', 'x:b'))
1632 1632 ('string', 'x:e'))))
1633 1633
1634 1634 Test glob expansion of pats
1635 1635
1636 1636 $ expandglobs=`$PYTHON -c "import mercurial.util; \
1637 1637 > print mercurial.util.expandglobs and 'true' or 'false'"`
1638 1638 $ if [ $expandglobs = "true" ]; then
1639 1639 > testlog 'a*';
1640 1640 > else
1641 1641 > testlog a*;
1642 1642 > fi;
1643 1643 []
1644 1644 (group
1645 1645 (group
1646 1646 (func
1647 1647 ('symbol', 'filelog')
1648 1648 ('string', 'aa'))))
1649 1649
1650 1650 Test --follow on a non-existent directory
1651 1651
1652 1652 $ testlog -f dir
1653 1653 abort: cannot follow file not in parent revision: "dir"
1654 1654 abort: cannot follow file not in parent revision: "dir"
1655 1655 abort: cannot follow file not in parent revision: "dir"
1656 1656
1657 1657 Test --follow on a directory
1658 1658
1659 1659 $ hg up -q '.^'
1660 1660 $ testlog -f dir
1661 1661 []
1662 1662 (group
1663 1663 (and
1664 1664 (func
1665 1665 ('symbol', 'ancestors')
1666 1666 ('symbol', '.'))
1667 1667 (func
1668 1668 ('symbol', '_matchfiles')
1669 1669 (list
1670 1670 (list
1671 1671 ('string', 'r:')
1672 1672 ('string', 'd:relpath'))
1673 1673 ('string', 'p:dir')))))
1674 1674 $ hg up -q tip
1675 1675
1676 1676 Test --follow on file not in parent revision
1677 1677
1678 1678 $ testlog -f a
1679 1679 abort: cannot follow file not in parent revision: "a"
1680 1680 abort: cannot follow file not in parent revision: "a"
1681 1681 abort: cannot follow file not in parent revision: "a"
1682 1682
1683 1683 Test --follow and patterns
1684 1684
1685 1685 $ testlog -f 'glob:*'
1686 1686 []
1687 1687 (group
1688 1688 (and
1689 1689 (func
1690 1690 ('symbol', 'ancestors')
1691 1691 ('symbol', '.'))
1692 1692 (func
1693 1693 ('symbol', '_matchfiles')
1694 1694 (list
1695 1695 (list
1696 1696 ('string', 'r:')
1697 1697 ('string', 'd:relpath'))
1698 1698 ('string', 'p:glob:*')))))
1699 1699
1700 1700 Test --follow on a single rename
1701 1701
1702 1702 $ hg up -q 2
1703 1703 $ testlog -f a
1704 1704 []
1705 1705 (group
1706 1706 (group
1707 1707 (func
1708 1708 ('symbol', 'follow')
1709 1709 ('string', 'a'))))
1710 1710
1711 1711 Test --follow and multiple renames
1712 1712
1713 1713 $ hg up -q tip
1714 1714 $ testlog -f e
1715 1715 []
1716 1716 (group
1717 1717 (group
1718 1718 (func
1719 1719 ('symbol', 'follow')
1720 1720 ('string', 'e'))))
1721 1721
1722 1722 Test --follow and multiple filelog heads
1723 1723
1724 1724 $ hg up -q 2
1725 1725 $ testlog -f g
1726 1726 []
1727 1727 (group
1728 1728 (group
1729 1729 (func
1730 1730 ('symbol', 'follow')
1731 1731 ('string', 'g'))))
1732 1732 $ cat log.nodes
1733 1733 nodetag 2
1734 1734 nodetag 1
1735 1735 nodetag 0
1736 1736 $ hg up -q tip
1737 1737 $ testlog -f g
1738 1738 []
1739 1739 (group
1740 1740 (group
1741 1741 (func
1742 1742 ('symbol', 'follow')
1743 1743 ('string', 'g'))))
1744 1744 $ cat log.nodes
1745 1745 nodetag 3
1746 1746 nodetag 2
1747 1747 nodetag 0
1748 1748
1749 1749 Test --follow and multiple files
1750 1750
1751 1751 $ testlog -f g e
1752 1752 []
1753 1753 (group
1754 1754 (group
1755 1755 (or
1756 1756 (func
1757 1757 ('symbol', 'follow')
1758 1758 ('string', 'g'))
1759 1759 (func
1760 1760 ('symbol', 'follow')
1761 1761 ('string', 'e')))))
1762 1762 $ cat log.nodes
1763 1763 nodetag 4
1764 1764 nodetag 3
1765 1765 nodetag 2
1766 1766 nodetag 1
1767 1767 nodetag 0
1768 1768
1769 1769 Test --follow null parent
1770 1770
1771 1771 $ hg up -q null
1772 1772 $ testlog -f
1773 1773 []
1774 1774 []
1775 1775
1776 1776 Test --follow-first
1777 1777
1778 1778 $ hg up -q 3
1779 1779 $ echo ee > e
1780 1780 $ hg ci -Am "add another e" e
1781 1781 created new head
1782 1782 $ hg merge --tool internal:other 4
1783 1783 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1784 1784 (branch merge, don't forget to commit)
1785 1785 $ echo merge > e
1786 1786 $ hg ci -m "merge 5 and 4"
1787 1787 $ testlog --follow-first
1788 1788 []
1789 1789 (group
1790 1790 (func
1791 1791 ('symbol', '_firstancestors')
1792 1792 (func
1793 1793 ('symbol', 'rev')
1794 1794 ('symbol', '6'))))
1795 1795
1796 1796 Cannot compare with log --follow-first FILE as it never worked
1797 1797
1798 1798 $ hg log -G --print-revset --follow-first e
1799 1799 []
1800 1800 (group
1801 1801 (group
1802 1802 (func
1803 1803 ('symbol', '_followfirst')
1804 1804 ('string', 'e'))))
1805 1805 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1806 1806 @ 6 merge 5 and 4
1807 1807 |\
1808 1808 o | 5 add another e
1809 1809 | |
1810 1810
1811 1811 Test --copies
1812 1812
1813 1813 $ hg log -G --copies --template "{rev} {desc|firstline} \
1814 1814 > copies: {file_copies_switch}\n"
1815 1815 @ 6 merge 5 and 4 copies:
1816 1816 |\
1817 1817 | o 5 add another e copies:
1818 1818 | |
1819 1819 o | 4 mv dir/b e copies: e (dir/b)
1820 1820 |/
1821 1821 o 3 mv a b; add d copies: b (a)g (f)
1822 1822 |
1823 1823 o 2 mv b dir/b copies: dir/b (b)
1824 1824 |
1825 1825 o 1 copy a b copies: b (a)g (f)
1826 1826 |
1827 1827 o 0 add a copies:
1828 1828
1829 1829 Test "set:..." and parent revision
1830 1830
1831 1831 $ hg up -q 4
1832 1832 $ testlog "set:copied()"
1833 1833 []
1834 1834 (group
1835 1835 (func
1836 1836 ('symbol', '_matchfiles')
1837 1837 (list
1838 1838 (list
1839 1839 ('string', 'r:')
1840 1840 ('string', 'd:relpath'))
1841 1841 ('string', 'p:set:copied()'))))
1842 1842 $ testlog --include "set:copied()"
1843 1843 []
1844 1844 (group
1845 1845 (func
1846 1846 ('symbol', '_matchfiles')
1847 1847 (list
1848 1848 (list
1849 1849 ('string', 'r:')
1850 1850 ('string', 'd:relpath'))
1851 1851 ('string', 'i:set:copied()'))))
1852 1852 $ testlog -r "sort(file('set:copied()'), -rev)"
1853 1853 ["sort(file('set:copied()'), -rev)"]
1854 1854 []
1855 1855
1856 1856 Test --removed
1857 1857
1858 1858 $ testlog --removed
1859 1859 []
1860 1860 []
1861 1861 $ testlog --removed a
1862 1862 []
1863 1863 (group
1864 1864 (func
1865 1865 ('symbol', '_matchfiles')
1866 1866 (list
1867 1867 (list
1868 1868 ('string', 'r:')
1869 1869 ('string', 'd:relpath'))
1870 1870 ('string', 'p:a'))))
1871 1871 $ testlog --removed --follow a
1872 1872 []
1873 1873 (group
1874 1874 (and
1875 1875 (func
1876 1876 ('symbol', 'ancestors')
1877 1877 ('symbol', '.'))
1878 1878 (func
1879 1879 ('symbol', '_matchfiles')
1880 1880 (list
1881 1881 (list
1882 1882 ('string', 'r:')
1883 1883 ('string', 'd:relpath'))
1884 1884 ('string', 'p:a')))))
1885 1885
1886 1886 Test --patch and --stat with --follow and --follow-first
1887 1887
1888 1888 $ hg up -q 3
1889 1889 $ hg log -G --git --patch b
1890 1890 o changeset: 1:216d4c92cf98
1891 1891 | user: test
1892 1892 | date: Thu Jan 01 00:00:00 1970 +0000
1893 1893 | summary: copy a b
1894 1894 |
1895 1895 | diff --git a/a b/b
1896 1896 | copy from a
1897 1897 | copy to b
1898 1898 |
1899 1899
1900 1900 $ hg log -G --git --stat b
1901 1901 o changeset: 1:216d4c92cf98
1902 1902 | user: test
1903 1903 | date: Thu Jan 01 00:00:00 1970 +0000
1904 1904 | summary: copy a b
1905 1905 |
1906 1906 | b | 0
1907 1907 | 1 files changed, 0 insertions(+), 0 deletions(-)
1908 1908 |
1909 1909
1910 1910 $ hg log -G --git --patch --follow b
1911 1911 o changeset: 1:216d4c92cf98
1912 1912 | user: test
1913 1913 | date: Thu Jan 01 00:00:00 1970 +0000
1914 1914 | summary: copy a b
1915 1915 |
1916 1916 | diff --git a/a b/b
1917 1917 | copy from a
1918 1918 | copy to b
1919 1919 |
1920 1920 o changeset: 0:f8035bb17114
1921 1921 user: test
1922 1922 date: Thu Jan 01 00:00:00 1970 +0000
1923 1923 summary: add a
1924 1924
1925 1925 diff --git a/a b/a
1926 1926 new file mode 100644
1927 1927 --- /dev/null
1928 1928 +++ b/a
1929 1929 @@ -0,0 +1,1 @@
1930 1930 +a
1931 1931
1932 1932
1933 1933 $ hg log -G --git --stat --follow b
1934 1934 o changeset: 1:216d4c92cf98
1935 1935 | user: test
1936 1936 | date: Thu Jan 01 00:00:00 1970 +0000
1937 1937 | summary: copy a b
1938 1938 |
1939 1939 | b | 0
1940 1940 | 1 files changed, 0 insertions(+), 0 deletions(-)
1941 1941 |
1942 1942 o changeset: 0:f8035bb17114
1943 1943 user: test
1944 1944 date: Thu Jan 01 00:00:00 1970 +0000
1945 1945 summary: add a
1946 1946
1947 1947 a | 1 +
1948 1948 1 files changed, 1 insertions(+), 0 deletions(-)
1949 1949
1950 1950
1951 1951 $ hg up -q 6
1952 1952 $ hg log -G --git --patch --follow-first e
1953 1953 @ changeset: 6:fc281d8ff18d
1954 1954 |\ tag: tip
1955 1955 | | parent: 5:99b31f1c2782
1956 1956 | | parent: 4:17d952250a9d
1957 1957 | | user: test
1958 1958 | | date: Thu Jan 01 00:00:00 1970 +0000
1959 1959 | | summary: merge 5 and 4
1960 1960 | |
1961 1961 | | diff --git a/e b/e
1962 1962 | | --- a/e
1963 1963 | | +++ b/e
1964 1964 | | @@ -1,1 +1,1 @@
1965 1965 | | -ee
1966 1966 | | +merge
1967 1967 | |
1968 1968 o | changeset: 5:99b31f1c2782
1969 1969 | | parent: 3:5918b8d165d1
1970 1970 | | user: test
1971 1971 | | date: Thu Jan 01 00:00:00 1970 +0000
1972 1972 | | summary: add another e
1973 1973 | |
1974 1974 | | diff --git a/e b/e
1975 1975 | | new file mode 100644
1976 1976 | | --- /dev/null
1977 1977 | | +++ b/e
1978 1978 | | @@ -0,0 +1,1 @@
1979 1979 | | +ee
1980 1980 | |
1981 1981
1982 1982 Test old-style --rev
1983 1983
1984 1984 $ hg tag 'foo-bar'
1985 1985 $ testlog -r 'foo-bar'
1986 1986 ['foo-bar']
1987 1987 []
1988 1988
1989 1989 Test --follow and forward --rev
1990 1990
1991 1991 $ hg up -q 6
1992 1992 $ echo g > g
1993 1993 $ hg ci -Am 'add g' g
1994 1994 created new head
1995 1995 $ hg up -q 2
1996 1996 $ hg log -G --template "{rev} {desc|firstline}\n"
1997 1997 o 8 add g
1998 1998 |
1999 1999 | o 7 Added tag foo-bar for changeset fc281d8ff18d
2000 2000 |/
2001 2001 o 6 merge 5 and 4
2002 2002 |\
2003 2003 | o 5 add another e
2004 2004 | |
2005 2005 o | 4 mv dir/b e
2006 2006 |/
2007 2007 o 3 mv a b; add d
2008 2008 |
2009 2009 @ 2 mv b dir/b
2010 2010 |
2011 2011 o 1 copy a b
2012 2012 |
2013 2013 o 0 add a
2014 2014
2015 2015 $ hg archive -r 7 archive
2016 2016 $ grep changessincelatesttag archive/.hg_archival.txt
2017 2017 changessincelatesttag: 1
2018 2018 $ rm -r archive
2019 2019
2020 2020 changessincelatesttag with no prior tag
2021 2021 $ hg archive -r 4 archive
2022 2022 $ grep changessincelatesttag archive/.hg_archival.txt
2023 2023 changessincelatesttag: 5
2024 2024
2025 2025 $ hg export 'all()'
2026 2026 # HG changeset patch
2027 2027 # User test
2028 2028 # Date 0 0
2029 2029 # Thu Jan 01 00:00:00 1970 +0000
2030 2030 # Node ID f8035bb17114da16215af3436ec5222428ace8ee
2031 2031 # Parent 0000000000000000000000000000000000000000
2032 2032 add a
2033 2033
2034 2034 diff -r 000000000000 -r f8035bb17114 a
2035 2035 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2036 2036 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2037 2037 @@ -0,0 +1,1 @@
2038 2038 +a
2039 2039 diff -r 000000000000 -r f8035bb17114 aa
2040 2040 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2041 2041 +++ b/aa Thu Jan 01 00:00:00 1970 +0000
2042 2042 @@ -0,0 +1,1 @@
2043 2043 +aa
2044 2044 diff -r 000000000000 -r f8035bb17114 f
2045 2045 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2046 2046 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2047 2047 @@ -0,0 +1,1 @@
2048 2048 +f
2049 2049 # HG changeset patch
2050 2050 # User test
2051 2051 # Date 0 0
2052 2052 # Thu Jan 01 00:00:00 1970 +0000
2053 2053 # Node ID 216d4c92cf98ff2b4641d508b76b529f3d424c92
2054 2054 # Parent f8035bb17114da16215af3436ec5222428ace8ee
2055 2055 copy a b
2056 2056
2057 2057 diff -r f8035bb17114 -r 216d4c92cf98 b
2058 2058 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2059 2059 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2060 2060 @@ -0,0 +1,1 @@
2061 2061 +a
2062 2062 diff -r f8035bb17114 -r 216d4c92cf98 g
2063 2063 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2064 2064 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2065 2065 @@ -0,0 +1,1 @@
2066 2066 +f
2067 2067 # HG changeset patch
2068 2068 # User test
2069 2069 # Date 0 0
2070 2070 # Thu Jan 01 00:00:00 1970 +0000
2071 2071 # Node ID bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2072 2072 # Parent 216d4c92cf98ff2b4641d508b76b529f3d424c92
2073 2073 mv b dir/b
2074 2074
2075 2075 diff -r 216d4c92cf98 -r bb573313a9e8 b
2076 2076 --- a/b Thu Jan 01 00:00:00 1970 +0000
2077 2077 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2078 2078 @@ -1,1 +0,0 @@
2079 2079 -a
2080 2080 diff -r 216d4c92cf98 -r bb573313a9e8 dir/b
2081 2081 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2082 2082 +++ b/dir/b Thu Jan 01 00:00:00 1970 +0000
2083 2083 @@ -0,0 +1,1 @@
2084 2084 +a
2085 2085 diff -r 216d4c92cf98 -r bb573313a9e8 f
2086 2086 --- a/f Thu Jan 01 00:00:00 1970 +0000
2087 2087 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2088 2088 @@ -1,1 +1,2 @@
2089 2089 f
2090 2090 +f
2091 2091 diff -r 216d4c92cf98 -r bb573313a9e8 g
2092 2092 --- a/g Thu Jan 01 00:00:00 1970 +0000
2093 2093 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2094 2094 @@ -1,1 +1,2 @@
2095 2095 f
2096 2096 +g
2097 2097 # HG changeset patch
2098 2098 # User test
2099 2099 # Date 0 0
2100 2100 # Thu Jan 01 00:00:00 1970 +0000
2101 2101 # Node ID 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2102 2102 # Parent bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2103 2103 mv a b; add d
2104 2104
2105 2105 diff -r bb573313a9e8 -r 5918b8d165d1 a
2106 2106 --- a/a Thu Jan 01 00:00:00 1970 +0000
2107 2107 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2108 2108 @@ -1,1 +0,0 @@
2109 2109 -a
2110 2110 diff -r bb573313a9e8 -r 5918b8d165d1 b
2111 2111 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2112 2112 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2113 2113 @@ -0,0 +1,1 @@
2114 2114 +a
2115 2115 diff -r bb573313a9e8 -r 5918b8d165d1 d
2116 2116 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2117 2117 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2118 2118 @@ -0,0 +1,1 @@
2119 2119 +a
2120 2120 diff -r bb573313a9e8 -r 5918b8d165d1 g
2121 2121 --- a/g Thu Jan 01 00:00:00 1970 +0000
2122 2122 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2123 2123 @@ -1,2 +1,2 @@
2124 2124 f
2125 2125 -g
2126 2126 +f
2127 2127 # HG changeset patch
2128 2128 # User test
2129 2129 # Date 0 0
2130 2130 # Thu Jan 01 00:00:00 1970 +0000
2131 2131 # Node ID 17d952250a9d03cc3dc77b199ab60e959b9b0260
2132 2132 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2133 2133 mv dir/b e
2134 2134
2135 2135 diff -r 5918b8d165d1 -r 17d952250a9d dir/b
2136 2136 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2137 2137 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2138 2138 @@ -1,1 +0,0 @@
2139 2139 -a
2140 2140 diff -r 5918b8d165d1 -r 17d952250a9d e
2141 2141 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2142 2142 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2143 2143 @@ -0,0 +1,1 @@
2144 2144 +a
2145 2145 # HG changeset patch
2146 2146 # User test
2147 2147 # Date 0 0
2148 2148 # Thu Jan 01 00:00:00 1970 +0000
2149 2149 # Node ID 99b31f1c2782e2deb1723cef08930f70fc84b37b
2150 2150 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2151 2151 add another e
2152 2152
2153 2153 diff -r 5918b8d165d1 -r 99b31f1c2782 e
2154 2154 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2155 2155 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2156 2156 @@ -0,0 +1,1 @@
2157 2157 +ee
2158 2158 # HG changeset patch
2159 2159 # User test
2160 2160 # Date 0 0
2161 2161 # Thu Jan 01 00:00:00 1970 +0000
2162 2162 # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
2163 2163 # Parent 99b31f1c2782e2deb1723cef08930f70fc84b37b
2164 2164 # Parent 17d952250a9d03cc3dc77b199ab60e959b9b0260
2165 2165 merge 5 and 4
2166 2166
2167 2167 diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
2168 2168 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2169 2169 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2170 2170 @@ -1,1 +0,0 @@
2171 2171 -a
2172 2172 diff -r 99b31f1c2782 -r fc281d8ff18d e
2173 2173 --- a/e Thu Jan 01 00:00:00 1970 +0000
2174 2174 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2175 2175 @@ -1,1 +1,1 @@
2176 2176 -ee
2177 2177 +merge
2178 2178 # HG changeset patch
2179 2179 # User test
2180 2180 # Date 0 0
2181 2181 # Thu Jan 01 00:00:00 1970 +0000
2182 2182 # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
2183 2183 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2184 2184 Added tag foo-bar for changeset fc281d8ff18d
2185 2185
2186 2186 diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
2187 2187 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2188 2188 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2189 2189 @@ -0,0 +1,1 @@
2190 2190 +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
2191 2191 # HG changeset patch
2192 2192 # User test
2193 2193 # Date 0 0
2194 2194 # Thu Jan 01 00:00:00 1970 +0000
2195 2195 # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
2196 2196 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2197 2197 add g
2198 2198
2199 2199 diff -r fc281d8ff18d -r 24c2e826ddeb g
2200 2200 --- a/g Thu Jan 01 00:00:00 1970 +0000
2201 2201 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2202 2202 @@ -1,2 +1,1 @@
2203 2203 -f
2204 2204 -f
2205 2205 +g
2206 2206 $ testlog --follow -r6 -r8 -r5 -r7 -r4
2207 2207 ['6', '8', '5', '7', '4']
2208 2208 (group
2209 2209 (func
2210 2210 ('symbol', 'descendants')
2211 2211 (func
2212 2212 ('symbol', 'rev')
2213 2213 ('symbol', '6'))))
2214 2214
2215 2215 Test --follow-first and forward --rev
2216 2216
2217 2217 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
2218 2218 ['6', '8', '5', '7', '4']
2219 2219 (group
2220 2220 (func
2221 2221 ('symbol', '_firstdescendants')
2222 2222 (func
2223 2223 ('symbol', 'rev')
2224 2224 ('symbol', '6'))))
2225 2225 --- log.nodes * (glob)
2226 2226 +++ glog.nodes * (glob)
2227 2227 @@ -1,3 +1,3 @@
2228 2228 -nodetag 6
2229 2229 nodetag 8
2230 2230 nodetag 7
2231 2231 +nodetag 6
2232 2232
2233 2233 Test --follow and backward --rev
2234 2234
2235 2235 $ testlog --follow -r6 -r5 -r7 -r8 -r4
2236 2236 ['6', '5', '7', '8', '4']
2237 2237 (group
2238 2238 (func
2239 2239 ('symbol', 'ancestors')
2240 2240 (func
2241 2241 ('symbol', 'rev')
2242 2242 ('symbol', '6'))))
2243 2243
2244 2244 Test --follow-first and backward --rev
2245 2245
2246 2246 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2247 2247 ['6', '5', '7', '8', '4']
2248 2248 (group
2249 2249 (func
2250 2250 ('symbol', '_firstancestors')
2251 2251 (func
2252 2252 ('symbol', 'rev')
2253 2253 ('symbol', '6'))))
2254 2254
2255 2255 Test --follow with --rev of graphlog extension
2256 2256
2257 2257 $ hg --config extensions.graphlog= glog -qfr1
2258 2258 o 1:216d4c92cf98
2259 2259 |
2260 2260 o 0:f8035bb17114
2261 2261
2262 2262
2263 2263 Test subdir
2264 2264
2265 2265 $ hg up -q 3
2266 2266 $ cd dir
2267 2267 $ testlog .
2268 2268 []
2269 2269 (group
2270 2270 (func
2271 2271 ('symbol', '_matchfiles')
2272 2272 (list
2273 2273 (list
2274 2274 ('string', 'r:')
2275 2275 ('string', 'd:relpath'))
2276 2276 ('string', 'p:.'))))
2277 2277 $ testlog ../b
2278 2278 []
2279 2279 (group
2280 2280 (group
2281 2281 (func
2282 2282 ('symbol', 'filelog')
2283 2283 ('string', '../b'))))
2284 2284 $ testlog -f ../b
2285 2285 []
2286 2286 (group
2287 2287 (group
2288 2288 (func
2289 2289 ('symbol', 'follow')
2290 2290 ('string', 'b'))))
2291 2291 $ cd ..
2292 2292
2293 2293 Test --hidden
2294 2294 (enable obsolete)
2295 2295
2296 2296 $ cat >> $HGRCPATH << EOF
2297 2297 > [experimental]
2298 2298 > evolution=createmarkers
2299 2299 > EOF
2300 2300
2301 2301 $ hg debugobsolete `hg id --debug -i -r 8`
2302 2302 $ testlog
2303 2303 []
2304 2304 []
2305 2305 $ testlog --hidden
2306 2306 []
2307 2307 []
2308 2308 $ hg log -G --template '{rev} {desc}\n'
2309 2309 o 7 Added tag foo-bar for changeset fc281d8ff18d
2310 2310 |
2311 2311 o 6 merge 5 and 4
2312 2312 |\
2313 2313 | o 5 add another e
2314 2314 | |
2315 2315 o | 4 mv dir/b e
2316 2316 |/
2317 2317 @ 3 mv a b; add d
2318 2318 |
2319 2319 o 2 mv b dir/b
2320 2320 |
2321 2321 o 1 copy a b
2322 2322 |
2323 2323 o 0 add a
2324 2324
2325 2325
2326 2326 A template without trailing newline should do something sane
2327 2327
2328 2328 $ hg log -G -r ::2 --template '{rev} {desc}'
2329 2329 o 2 mv b dir/b
2330 2330 |
2331 2331 o 1 copy a b
2332 2332 |
2333 2333 o 0 add a
2334 2334
2335 2335
2336 2336 Extra newlines must be preserved
2337 2337
2338 2338 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2339 2339 o
2340 2340 | 2 mv b dir/b
2341 2341 |
2342 2342 o
2343 2343 | 1 copy a b
2344 2344 |
2345 2345 o
2346 2346 0 add a
2347 2347
2348 2348
2349 2349 The almost-empty template should do something sane too ...
2350 2350
2351 2351 $ hg log -G -r ::2 --template '\n'
2352 2352 o
2353 2353 |
2354 2354 o
2355 2355 |
2356 2356 o
2357 2357
2358 2358
2359 2359 issue3772
2360 2360
2361 2361 $ hg log -G -r :null
2362 2362 o changeset: 0:f8035bb17114
2363 2363 | user: test
2364 2364 | date: Thu Jan 01 00:00:00 1970 +0000
2365 2365 | summary: add a
2366 2366 |
2367 2367 o changeset: -1:000000000000
2368 2368 user:
2369 2369 date: Thu Jan 01 00:00:00 1970 +0000
2370 2370
2371 2371 $ hg log -G -r null:null
2372 2372 o changeset: -1:000000000000
2373 2373 user:
2374 2374 date: Thu Jan 01 00:00:00 1970 +0000
2375 2375
2376 2376
2377 2377 should not draw line down to null due to the magic of fullreposet
2378 2378
2379 2379 $ hg log -G -r 'all()' | tail -6
2380 2380 |
2381 2381 o changeset: 0:f8035bb17114
2382 2382 user: test
2383 2383 date: Thu Jan 01 00:00:00 1970 +0000
2384 2384 summary: add a
2385 2385
2386 2386
2387 2387 $ hg log -G -r 'branch(default)' | tail -6
2388 2388 |
2389 2389 o changeset: 0:f8035bb17114
2390 2390 user: test
2391 2391 date: Thu Jan 01 00:00:00 1970 +0000
2392 2392 summary: add a
2393 2393
2394 2394
2395 working-directory revision
2396
2397 $ hg log -G -qr '. + wdir()'
2398 o 2147483647:ffffffffffff
2399 |
2400 @ 3:5918b8d165d1
2401 |
2402
2395 2403 $ cd ..
@@ -1,2053 +1,2061 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 $ hg init repo
35 35 $ cd repo
36 36
37 37 $ echo a > a
38 38 $ hg branch a
39 39 marked working directory as branch a
40 40 (branches are permanent and global, did you want a bookmark?)
41 41 $ hg ci -Aqm0
42 42
43 43 $ echo b > b
44 44 $ hg branch b
45 45 marked working directory as branch b
46 46 $ hg ci -Aqm1
47 47
48 48 $ rm a
49 49 $ hg branch a-b-c-
50 50 marked working directory as branch a-b-c-
51 51 $ hg ci -Aqm2 -u Bob
52 52
53 53 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
54 54 2
55 55 $ hg log -r "extra('branch')" --template '{rev}\n'
56 56 0
57 57 1
58 58 2
59 59 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
60 60 0 a
61 61 2 a-b-c-
62 62
63 63 $ hg co 1
64 64 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
65 65 $ hg branch +a+b+c+
66 66 marked working directory as branch +a+b+c+
67 67 $ hg ci -Aqm3
68 68
69 69 $ hg co 2 # interleave
70 70 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
71 71 $ echo bb > b
72 72 $ hg branch -- -a-b-c-
73 73 marked working directory as branch -a-b-c-
74 74 $ hg ci -Aqm4 -d "May 12 2005"
75 75
76 76 $ hg co 3
77 77 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 78 $ hg branch !a/b/c/
79 79 marked working directory as branch !a/b/c/
80 80 $ hg ci -Aqm"5 bug"
81 81
82 82 $ hg merge 4
83 83 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
84 84 (branch merge, don't forget to commit)
85 85 $ hg branch _a_b_c_
86 86 marked working directory as branch _a_b_c_
87 87 $ hg ci -Aqm"6 issue619"
88 88
89 89 $ hg branch .a.b.c.
90 90 marked working directory as branch .a.b.c.
91 91 $ hg ci -Aqm7
92 92
93 93 $ hg branch all
94 94 marked working directory as branch all
95 95
96 96 $ hg co 4
97 97 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 98 $ hg branch Γ©
99 99 marked working directory as branch \xc3\xa9 (esc)
100 100 $ hg ci -Aqm9
101 101
102 102 $ hg tag -r6 1.0
103 103 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
104 104
105 105 $ hg clone --quiet -U -r 7 . ../remote1
106 106 $ hg clone --quiet -U -r 8 . ../remote2
107 107 $ echo "[paths]" >> .hg/hgrc
108 108 $ echo "default = ../remote1" >> .hg/hgrc
109 109
110 110 trivial
111 111
112 112 $ try 0:1
113 113 (range
114 114 ('symbol', '0')
115 115 ('symbol', '1'))
116 116 * set:
117 117 <spanset+ 0:1>
118 118 0
119 119 1
120 120 $ try 3::6
121 121 (dagrange
122 122 ('symbol', '3')
123 123 ('symbol', '6'))
124 124 * set:
125 125 <baseset [3, 5, 6]>
126 126 3
127 127 5
128 128 6
129 129 $ try '0|1|2'
130 130 (or
131 131 ('symbol', '0')
132 132 ('symbol', '1')
133 133 ('symbol', '2'))
134 134 * set:
135 135 <baseset [0, 1, 2]>
136 136 0
137 137 1
138 138 2
139 139
140 140 names that should work without quoting
141 141
142 142 $ try a
143 143 ('symbol', 'a')
144 144 * set:
145 145 <baseset [0]>
146 146 0
147 147 $ try b-a
148 148 (minus
149 149 ('symbol', 'b')
150 150 ('symbol', 'a'))
151 151 * set:
152 152 <filteredset
153 153 <baseset [1]>>
154 154 1
155 155 $ try _a_b_c_
156 156 ('symbol', '_a_b_c_')
157 157 * set:
158 158 <baseset [6]>
159 159 6
160 160 $ try _a_b_c_-a
161 161 (minus
162 162 ('symbol', '_a_b_c_')
163 163 ('symbol', 'a'))
164 164 * set:
165 165 <filteredset
166 166 <baseset [6]>>
167 167 6
168 168 $ try .a.b.c.
169 169 ('symbol', '.a.b.c.')
170 170 * set:
171 171 <baseset [7]>
172 172 7
173 173 $ try .a.b.c.-a
174 174 (minus
175 175 ('symbol', '.a.b.c.')
176 176 ('symbol', 'a'))
177 177 * set:
178 178 <filteredset
179 179 <baseset [7]>>
180 180 7
181 181 $ try -- '-a-b-c-' # complains
182 182 hg: parse error at 7: not a prefix: end
183 183 [255]
184 184 $ log -a-b-c- # succeeds with fallback
185 185 4
186 186
187 187 $ try -- -a-b-c--a # complains
188 188 (minus
189 189 (minus
190 190 (minus
191 191 (negate
192 192 ('symbol', 'a'))
193 193 ('symbol', 'b'))
194 194 ('symbol', 'c'))
195 195 (negate
196 196 ('symbol', 'a')))
197 197 abort: unknown revision '-a'!
198 198 [255]
199 199 $ try Γ©
200 200 ('symbol', '\xc3\xa9')
201 201 * set:
202 202 <baseset [9]>
203 203 9
204 204
205 205 no quoting needed
206 206
207 207 $ log ::a-b-c-
208 208 0
209 209 1
210 210 2
211 211
212 212 quoting needed
213 213
214 214 $ try '"-a-b-c-"-a'
215 215 (minus
216 216 ('string', '-a-b-c-')
217 217 ('symbol', 'a'))
218 218 * set:
219 219 <filteredset
220 220 <baseset [4]>>
221 221 4
222 222
223 223 $ log '1 or 2'
224 224 1
225 225 2
226 226 $ log '1|2'
227 227 1
228 228 2
229 229 $ log '1 and 2'
230 230 $ log '1&2'
231 231 $ try '1&2|3' # precedence - and is higher
232 232 (or
233 233 (and
234 234 ('symbol', '1')
235 235 ('symbol', '2'))
236 236 ('symbol', '3'))
237 237 * set:
238 238 <addset
239 239 <baseset []>,
240 240 <baseset [3]>>
241 241 3
242 242 $ try '1|2&3'
243 243 (or
244 244 ('symbol', '1')
245 245 (and
246 246 ('symbol', '2')
247 247 ('symbol', '3')))
248 248 * set:
249 249 <addset
250 250 <baseset [1]>,
251 251 <baseset []>>
252 252 1
253 253 $ try '1&2&3' # associativity
254 254 (and
255 255 (and
256 256 ('symbol', '1')
257 257 ('symbol', '2'))
258 258 ('symbol', '3'))
259 259 * set:
260 260 <baseset []>
261 261 $ try '1|(2|3)'
262 262 (or
263 263 ('symbol', '1')
264 264 (group
265 265 (or
266 266 ('symbol', '2')
267 267 ('symbol', '3'))))
268 268 * set:
269 269 <addset
270 270 <baseset [1]>,
271 271 <baseset [2, 3]>>
272 272 1
273 273 2
274 274 3
275 275 $ log '1.0' # tag
276 276 6
277 277 $ log 'a' # branch
278 278 0
279 279 $ log '2785f51ee'
280 280 0
281 281 $ log 'date(2005)'
282 282 4
283 283 $ log 'date(this is a test)'
284 284 hg: parse error at 10: unexpected token: symbol
285 285 [255]
286 286 $ log 'date()'
287 287 hg: parse error: date requires a string
288 288 [255]
289 289 $ log 'date'
290 290 abort: unknown revision 'date'!
291 291 [255]
292 292 $ log 'date('
293 293 hg: parse error at 5: not a prefix: end
294 294 [255]
295 295 $ log 'date(tip)'
296 296 abort: invalid date: 'tip'
297 297 [255]
298 298 $ log '0:date'
299 299 abort: unknown revision 'date'!
300 300 [255]
301 301 $ log '::"date"'
302 302 abort: unknown revision 'date'!
303 303 [255]
304 304 $ hg book date -r 4
305 305 $ log '0:date'
306 306 0
307 307 1
308 308 2
309 309 3
310 310 4
311 311 $ log '::date'
312 312 0
313 313 1
314 314 2
315 315 4
316 316 $ log '::"date"'
317 317 0
318 318 1
319 319 2
320 320 4
321 321 $ log 'date(2005) and 1::'
322 322 4
323 323 $ hg book -d date
324 324
325 325 keyword arguments
326 326
327 327 $ log 'extra(branch, value=a)'
328 328 0
329 329
330 330 $ log 'extra(branch, a, b)'
331 331 hg: parse error: extra takes at most 2 arguments
332 332 [255]
333 333 $ log 'extra(a, label=b)'
334 334 hg: parse error: extra got multiple values for keyword argument 'label'
335 335 [255]
336 336 $ log 'extra(label=branch, default)'
337 337 hg: parse error: extra got an invalid argument
338 338 [255]
339 339 $ log 'extra(branch, foo+bar=baz)'
340 340 hg: parse error: extra got an invalid argument
341 341 [255]
342 342 $ log 'extra(unknown=branch)'
343 343 hg: parse error: extra got an unexpected keyword argument 'unknown'
344 344 [255]
345 345
346 346 $ try 'foo=bar|baz'
347 347 (keyvalue
348 348 ('symbol', 'foo')
349 349 (or
350 350 ('symbol', 'bar')
351 351 ('symbol', 'baz')))
352 352 hg: parse error: can't use a key-value pair in this context
353 353 [255]
354 354
355 355 Test that symbols only get parsed as functions if there's an opening
356 356 parenthesis.
357 357
358 358 $ hg book only -r 9
359 359 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
360 360 8
361 361 9
362 362
363 363 ancestor can accept 0 or more arguments
364 364
365 365 $ log 'ancestor()'
366 366 $ log 'ancestor(1)'
367 367 1
368 368 $ log 'ancestor(4,5)'
369 369 1
370 370 $ log 'ancestor(4,5) and 4'
371 371 $ log 'ancestor(0,0,1,3)'
372 372 0
373 373 $ log 'ancestor(3,1,5,3,5,1)'
374 374 1
375 375 $ log 'ancestor(0,1,3,5)'
376 376 0
377 377 $ log 'ancestor(1,2,3,4,5)'
378 378 1
379 379
380 380 test ancestors
381 381
382 382 $ log 'ancestors(5)'
383 383 0
384 384 1
385 385 3
386 386 5
387 387 $ log 'ancestor(ancestors(5))'
388 388 0
389 389 $ log '::r3232()'
390 390 0
391 391 1
392 392 2
393 393 3
394 394
395 395 $ log 'author(bob)'
396 396 2
397 397 $ log 'author("re:bob|test")'
398 398 0
399 399 1
400 400 2
401 401 3
402 402 4
403 403 5
404 404 6
405 405 7
406 406 8
407 407 9
408 408 $ log 'branch(Γ©)'
409 409 8
410 410 9
411 411 $ log 'branch(a)'
412 412 0
413 413 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
414 414 0 a
415 415 2 a-b-c-
416 416 3 +a+b+c+
417 417 4 -a-b-c-
418 418 5 !a/b/c/
419 419 6 _a_b_c_
420 420 7 .a.b.c.
421 421 $ log 'children(ancestor(4,5))'
422 422 2
423 423 3
424 424 $ log 'closed()'
425 425 $ log 'contains(a)'
426 426 0
427 427 1
428 428 3
429 429 5
430 430 $ log 'contains("../repo/a")'
431 431 0
432 432 1
433 433 3
434 434 5
435 435 $ log 'desc(B)'
436 436 5
437 437 $ log 'descendants(2 or 3)'
438 438 2
439 439 3
440 440 4
441 441 5
442 442 6
443 443 7
444 444 8
445 445 9
446 446 $ log 'file("b*")'
447 447 1
448 448 4
449 449 $ log 'filelog("b")'
450 450 1
451 451 4
452 452 $ log 'filelog("../repo/b")'
453 453 1
454 454 4
455 455 $ log 'follow()'
456 456 0
457 457 1
458 458 2
459 459 4
460 460 8
461 461 9
462 462 $ log 'grep("issue\d+")'
463 463 6
464 464 $ try 'grep("(")' # invalid regular expression
465 465 (func
466 466 ('symbol', 'grep')
467 467 ('string', '('))
468 468 hg: parse error: invalid match pattern: unbalanced parenthesis
469 469 [255]
470 470 $ try 'grep("\bissue\d+")'
471 471 (func
472 472 ('symbol', 'grep')
473 473 ('string', '\x08issue\\d+'))
474 474 * set:
475 475 <filteredset
476 476 <fullreposet+ 0:9>>
477 477 $ try 'grep(r"\bissue\d+")'
478 478 (func
479 479 ('symbol', 'grep')
480 480 ('string', '\\bissue\\d+'))
481 481 * set:
482 482 <filteredset
483 483 <fullreposet+ 0:9>>
484 484 6
485 485 $ try 'grep(r"\")'
486 486 hg: parse error at 7: unterminated string
487 487 [255]
488 488 $ log 'head()'
489 489 0
490 490 1
491 491 2
492 492 3
493 493 4
494 494 5
495 495 6
496 496 7
497 497 9
498 498 $ log 'heads(6::)'
499 499 7
500 500 $ log 'keyword(issue)'
501 501 6
502 502 $ log 'keyword("test a")'
503 503 $ log 'limit(head(), 1)'
504 504 0
505 505 $ log 'matching(6)'
506 506 6
507 507 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
508 508 6
509 509 7
510 510
511 511 Testing min and max
512 512
513 513 max: simple
514 514
515 515 $ log 'max(contains(a))'
516 516 5
517 517
518 518 max: simple on unordered set)
519 519
520 520 $ log 'max((4+0+2+5+7) and contains(a))'
521 521 5
522 522
523 523 max: no result
524 524
525 525 $ log 'max(contains(stringthatdoesnotappearanywhere))'
526 526
527 527 max: no result on unordered set
528 528
529 529 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
530 530
531 531 min: simple
532 532
533 533 $ log 'min(contains(a))'
534 534 0
535 535
536 536 min: simple on unordered set
537 537
538 538 $ log 'min((4+0+2+5+7) and contains(a))'
539 539 0
540 540
541 541 min: empty
542 542
543 543 $ log 'min(contains(stringthatdoesnotappearanywhere))'
544 544
545 545 min: empty on unordered set
546 546
547 547 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
548 548
549 549
550 550 $ log 'merge()'
551 551 6
552 552 $ log 'branchpoint()'
553 553 1
554 554 4
555 555 $ log 'modifies(b)'
556 556 4
557 557 $ log 'modifies("path:b")'
558 558 4
559 559 $ log 'modifies("*")'
560 560 4
561 561 6
562 562 $ log 'modifies("set:modified()")'
563 563 4
564 564 $ log 'id(5)'
565 565 2
566 566 $ log 'only(9)'
567 567 8
568 568 9
569 569 $ log 'only(8)'
570 570 8
571 571 $ log 'only(9, 5)'
572 572 2
573 573 4
574 574 8
575 575 9
576 576 $ log 'only(7 + 9, 5 + 2)'
577 577 4
578 578 6
579 579 7
580 580 8
581 581 9
582 582
583 583 Test empty set input
584 584 $ log 'only(p2())'
585 585 $ log 'only(p1(), p2())'
586 586 0
587 587 1
588 588 2
589 589 4
590 590 8
591 591 9
592 592
593 593 Test '%' operator
594 594
595 595 $ log '9%'
596 596 8
597 597 9
598 598 $ log '9%5'
599 599 2
600 600 4
601 601 8
602 602 9
603 603 $ log '(7 + 9)%(5 + 2)'
604 604 4
605 605 6
606 606 7
607 607 8
608 608 9
609 609
610 610 Test opreand of '%' is optimized recursively (issue4670)
611 611
612 612 $ try --optimize '8:9-8%'
613 613 (onlypost
614 614 (minus
615 615 (range
616 616 ('symbol', '8')
617 617 ('symbol', '9'))
618 618 ('symbol', '8')))
619 619 * optimized:
620 620 (func
621 621 ('symbol', 'only')
622 622 (and
623 623 (range
624 624 ('symbol', '8')
625 625 ('symbol', '9'))
626 626 (not
627 627 ('symbol', '8'))))
628 628 * set:
629 629 <baseset+ [8, 9]>
630 630 8
631 631 9
632 632 $ try --optimize '(9)%(5)'
633 633 (only
634 634 (group
635 635 ('symbol', '9'))
636 636 (group
637 637 ('symbol', '5')))
638 638 * optimized:
639 639 (func
640 640 ('symbol', 'only')
641 641 (list
642 642 ('symbol', '9')
643 643 ('symbol', '5')))
644 644 * set:
645 645 <baseset+ [8, 9, 2, 4]>
646 646 2
647 647 4
648 648 8
649 649 9
650 650
651 651 Test the order of operations
652 652
653 653 $ log '7 + 9%5 + 2'
654 654 7
655 655 2
656 656 4
657 657 8
658 658 9
659 659
660 660 Test explicit numeric revision
661 661 $ log 'rev(-2)'
662 662 $ log 'rev(-1)'
663 663 -1
664 664 $ log 'rev(0)'
665 665 0
666 666 $ log 'rev(9)'
667 667 9
668 668 $ log 'rev(10)'
669 669 $ log 'rev(tip)'
670 670 hg: parse error: rev expects a number
671 671 [255]
672 672
673 673 Test hexadecimal revision
674 674 $ log 'id(2)'
675 675 abort: 00changelog.i@2: ambiguous identifier!
676 676 [255]
677 677 $ log 'id(23268)'
678 678 4
679 679 $ log 'id(2785f51eece)'
680 680 0
681 681 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
682 682 8
683 683 $ log 'id(d5d0dcbdc4a)'
684 684 $ log 'id(d5d0dcbdc4w)'
685 685 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
686 686 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
687 687 $ log 'id(1.0)'
688 688 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
689 689
690 690 Test null revision
691 691 $ log '(null)'
692 692 -1
693 693 $ log '(null:0)'
694 694 -1
695 695 0
696 696 $ log '(0:null)'
697 697 0
698 698 -1
699 699 $ log 'null::0'
700 700 -1
701 701 0
702 702 $ log 'null:tip - 0:'
703 703 -1
704 704 $ log 'null: and null::' | head -1
705 705 -1
706 706 $ log 'null: or 0:' | head -2
707 707 -1
708 708 0
709 709 $ log 'ancestors(null)'
710 710 -1
711 711 $ log 'reverse(null:)' | tail -2
712 712 0
713 713 -1
714 714 BROKEN: should be '-1'
715 715 $ log 'first(null:)'
716 716 BROKEN: should be '-1'
717 717 $ log 'min(null:)'
718 718 $ log 'tip:null and all()' | tail -2
719 719 1
720 720 0
721 721
722 722 Test working-directory revision
723 723 $ hg debugrevspec 'wdir()'
724 None
724 2147483647
725 725 $ hg debugrevspec 'tip or wdir()'
726 726 9
727 None
727 2147483647
728 728 $ hg debugrevspec '0:tip and wdir()'
729 $ log '(all() + wdir()) & min(. + wdir())'
730 9
731 $ log '(all() + wdir()) & max(. + wdir())'
732 2147483647
733 $ log '(all() + wdir()) & first(wdir() + .)'
734 2147483647
735 $ log '(all() + wdir()) & last(. + wdir())'
736 2147483647
729 737
730 738 $ log 'outgoing()'
731 739 8
732 740 9
733 741 $ log 'outgoing("../remote1")'
734 742 8
735 743 9
736 744 $ log 'outgoing("../remote2")'
737 745 3
738 746 5
739 747 6
740 748 7
741 749 9
742 750 $ log 'p1(merge())'
743 751 5
744 752 $ log 'p2(merge())'
745 753 4
746 754 $ log 'parents(merge())'
747 755 4
748 756 5
749 757 $ log 'p1(branchpoint())'
750 758 0
751 759 2
752 760 $ log 'p2(branchpoint())'
753 761 $ log 'parents(branchpoint())'
754 762 0
755 763 2
756 764 $ log 'removes(a)'
757 765 2
758 766 6
759 767 $ log 'roots(all())'
760 768 0
761 769 $ log 'reverse(2 or 3 or 4 or 5)'
762 770 5
763 771 4
764 772 3
765 773 2
766 774 $ log 'reverse(all())'
767 775 9
768 776 8
769 777 7
770 778 6
771 779 5
772 780 4
773 781 3
774 782 2
775 783 1
776 784 0
777 785 $ log 'reverse(all()) & filelog(b)'
778 786 4
779 787 1
780 788 $ log 'rev(5)'
781 789 5
782 790 $ log 'sort(limit(reverse(all()), 3))'
783 791 7
784 792 8
785 793 9
786 794 $ log 'sort(2 or 3 or 4 or 5, date)'
787 795 2
788 796 3
789 797 5
790 798 4
791 799 $ log 'tagged()'
792 800 6
793 801 $ log 'tag()'
794 802 6
795 803 $ log 'tag(1.0)'
796 804 6
797 805 $ log 'tag(tip)'
798 806 9
799 807
800 808 test sort revset
801 809 --------------------------------------------
802 810
803 811 test when adding two unordered revsets
804 812
805 813 $ log 'sort(keyword(issue) or modifies(b))'
806 814 4
807 815 6
808 816
809 817 test when sorting a reversed collection in the same way it is
810 818
811 819 $ log 'sort(reverse(all()), -rev)'
812 820 9
813 821 8
814 822 7
815 823 6
816 824 5
817 825 4
818 826 3
819 827 2
820 828 1
821 829 0
822 830
823 831 test when sorting a reversed collection
824 832
825 833 $ log 'sort(reverse(all()), rev)'
826 834 0
827 835 1
828 836 2
829 837 3
830 838 4
831 839 5
832 840 6
833 841 7
834 842 8
835 843 9
836 844
837 845
838 846 test sorting two sorted collections in different orders
839 847
840 848 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
841 849 2
842 850 6
843 851 8
844 852 9
845 853
846 854 test sorting two sorted collections in different orders backwards
847 855
848 856 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
849 857 9
850 858 8
851 859 6
852 860 2
853 861
854 862 test subtracting something from an addset
855 863
856 864 $ log '(outgoing() or removes(a)) - removes(a)'
857 865 8
858 866 9
859 867
860 868 test intersecting something with an addset
861 869
862 870 $ log 'parents(outgoing() or removes(a))'
863 871 1
864 872 4
865 873 5
866 874 8
867 875
868 876 test that `or` operation combines elements in the right order:
869 877
870 878 $ log '3:4 or 2:5'
871 879 3
872 880 4
873 881 2
874 882 5
875 883 $ log '3:4 or 5:2'
876 884 3
877 885 4
878 886 5
879 887 2
880 888 $ log 'sort(3:4 or 2:5)'
881 889 2
882 890 3
883 891 4
884 892 5
885 893 $ log 'sort(3:4 or 5:2)'
886 894 2
887 895 3
888 896 4
889 897 5
890 898
891 899 test that more than one `-r`s are combined in the right order and deduplicated:
892 900
893 901 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
894 902 3
895 903 4
896 904 5
897 905 2
898 906 0
899 907 1
900 908
901 909 test that `or` operation skips duplicated revisions from right-hand side
902 910
903 911 $ try 'reverse(1::5) or ancestors(4)'
904 912 (or
905 913 (func
906 914 ('symbol', 'reverse')
907 915 (dagrange
908 916 ('symbol', '1')
909 917 ('symbol', '5')))
910 918 (func
911 919 ('symbol', 'ancestors')
912 920 ('symbol', '4')))
913 921 * set:
914 922 <addset
915 923 <baseset [5, 3, 1]>,
916 924 <generatorset+>>
917 925 5
918 926 3
919 927 1
920 928 0
921 929 2
922 930 4
923 931 $ try 'sort(ancestors(4) or reverse(1::5))'
924 932 (func
925 933 ('symbol', 'sort')
926 934 (or
927 935 (func
928 936 ('symbol', 'ancestors')
929 937 ('symbol', '4'))
930 938 (func
931 939 ('symbol', 'reverse')
932 940 (dagrange
933 941 ('symbol', '1')
934 942 ('symbol', '5')))))
935 943 * set:
936 944 <addset+
937 945 <generatorset+>,
938 946 <baseset [5, 3, 1]>>
939 947 0
940 948 1
941 949 2
942 950 3
943 951 4
944 952 5
945 953
946 954 test optimization of trivial `or` operation
947 955
948 956 $ try --optimize '0|(1)|"2"|-2|tip|null'
949 957 (or
950 958 ('symbol', '0')
951 959 (group
952 960 ('symbol', '1'))
953 961 ('string', '2')
954 962 (negate
955 963 ('symbol', '2'))
956 964 ('symbol', 'tip')
957 965 ('symbol', 'null'))
958 966 * optimized:
959 967 (func
960 968 ('symbol', '_list')
961 969 ('string', '0\x001\x002\x00-2\x00tip\x00null'))
962 970 * set:
963 971 <baseset [0, 1, 2, 8, 9, -1]>
964 972 0
965 973 1
966 974 2
967 975 8
968 976 9
969 977 -1
970 978
971 979 $ try --optimize '0|1|2:3'
972 980 (or
973 981 ('symbol', '0')
974 982 ('symbol', '1')
975 983 (range
976 984 ('symbol', '2')
977 985 ('symbol', '3')))
978 986 * optimized:
979 987 (or
980 988 (func
981 989 ('symbol', '_list')
982 990 ('string', '0\x001'))
983 991 (range
984 992 ('symbol', '2')
985 993 ('symbol', '3')))
986 994 * set:
987 995 <addset
988 996 <baseset [0, 1]>,
989 997 <spanset+ 2:3>>
990 998 0
991 999 1
992 1000 2
993 1001 3
994 1002
995 1003 $ try --optimize '0:1|2|3:4|5|6'
996 1004 (or
997 1005 (range
998 1006 ('symbol', '0')
999 1007 ('symbol', '1'))
1000 1008 ('symbol', '2')
1001 1009 (range
1002 1010 ('symbol', '3')
1003 1011 ('symbol', '4'))
1004 1012 ('symbol', '5')
1005 1013 ('symbol', '6'))
1006 1014 * optimized:
1007 1015 (or
1008 1016 (range
1009 1017 ('symbol', '0')
1010 1018 ('symbol', '1'))
1011 1019 ('symbol', '2')
1012 1020 (range
1013 1021 ('symbol', '3')
1014 1022 ('symbol', '4'))
1015 1023 (func
1016 1024 ('symbol', '_list')
1017 1025 ('string', '5\x006')))
1018 1026 * set:
1019 1027 <addset
1020 1028 <addset
1021 1029 <spanset+ 0:1>,
1022 1030 <baseset [2]>>,
1023 1031 <addset
1024 1032 <spanset+ 3:4>,
1025 1033 <baseset [5, 6]>>>
1026 1034 0
1027 1035 1
1028 1036 2
1029 1037 3
1030 1038 4
1031 1039 5
1032 1040 6
1033 1041
1034 1042 test that `_list` should be narrowed by provided `subset`
1035 1043
1036 1044 $ log '0:2 and (null|1|2|3)'
1037 1045 1
1038 1046 2
1039 1047
1040 1048 test that `_list` should remove duplicates
1041 1049
1042 1050 $ log '0|1|2|1|2|-1|tip'
1043 1051 0
1044 1052 1
1045 1053 2
1046 1054 9
1047 1055
1048 1056 test unknown revision in `_list`
1049 1057
1050 1058 $ log '0|unknown'
1051 1059 abort: unknown revision 'unknown'!
1052 1060 [255]
1053 1061
1054 1062 test integer range in `_list`
1055 1063
1056 1064 $ log '-1|-10'
1057 1065 9
1058 1066 0
1059 1067
1060 1068 $ log '-10|-11'
1061 1069 abort: unknown revision '-11'!
1062 1070 [255]
1063 1071
1064 1072 $ log '9|10'
1065 1073 abort: unknown revision '10'!
1066 1074 [255]
1067 1075
1068 1076 test '0000' != '0' in `_list`
1069 1077
1070 1078 $ log '0|0000'
1071 1079 0
1072 1080 -1
1073 1081
1074 1082 test that chained `or` operations make balanced addsets
1075 1083
1076 1084 $ try '0:1|1:2|2:3|3:4|4:5'
1077 1085 (or
1078 1086 (range
1079 1087 ('symbol', '0')
1080 1088 ('symbol', '1'))
1081 1089 (range
1082 1090 ('symbol', '1')
1083 1091 ('symbol', '2'))
1084 1092 (range
1085 1093 ('symbol', '2')
1086 1094 ('symbol', '3'))
1087 1095 (range
1088 1096 ('symbol', '3')
1089 1097 ('symbol', '4'))
1090 1098 (range
1091 1099 ('symbol', '4')
1092 1100 ('symbol', '5')))
1093 1101 * set:
1094 1102 <addset
1095 1103 <addset
1096 1104 <spanset+ 0:1>,
1097 1105 <spanset+ 1:2>>,
1098 1106 <addset
1099 1107 <spanset+ 2:3>,
1100 1108 <addset
1101 1109 <spanset+ 3:4>,
1102 1110 <spanset+ 4:5>>>>
1103 1111 0
1104 1112 1
1105 1113 2
1106 1114 3
1107 1115 4
1108 1116 5
1109 1117
1110 1118 test that chained `or` operations never eat up stack (issue4624)
1111 1119 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
1112 1120
1113 1121 $ hg log -T '{rev}\n' -r "`python -c "print '|'.join(['0:1'] * 500)"`"
1114 1122 0
1115 1123 1
1116 1124
1117 1125 test that repeated `-r` options never eat up stack (issue4565)
1118 1126 (uses `-r 0::1` to avoid possible optimization at old-style parser)
1119 1127
1120 1128 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
1121 1129 0
1122 1130 1
1123 1131
1124 1132 check that conversion to only works
1125 1133 $ try --optimize '::3 - ::1'
1126 1134 (minus
1127 1135 (dagrangepre
1128 1136 ('symbol', '3'))
1129 1137 (dagrangepre
1130 1138 ('symbol', '1')))
1131 1139 * optimized:
1132 1140 (func
1133 1141 ('symbol', 'only')
1134 1142 (list
1135 1143 ('symbol', '3')
1136 1144 ('symbol', '1')))
1137 1145 * set:
1138 1146 <baseset+ [3]>
1139 1147 3
1140 1148 $ try --optimize 'ancestors(1) - ancestors(3)'
1141 1149 (minus
1142 1150 (func
1143 1151 ('symbol', 'ancestors')
1144 1152 ('symbol', '1'))
1145 1153 (func
1146 1154 ('symbol', 'ancestors')
1147 1155 ('symbol', '3')))
1148 1156 * optimized:
1149 1157 (func
1150 1158 ('symbol', 'only')
1151 1159 (list
1152 1160 ('symbol', '1')
1153 1161 ('symbol', '3')))
1154 1162 * set:
1155 1163 <baseset+ []>
1156 1164 $ try --optimize 'not ::2 and ::6'
1157 1165 (and
1158 1166 (not
1159 1167 (dagrangepre
1160 1168 ('symbol', '2')))
1161 1169 (dagrangepre
1162 1170 ('symbol', '6')))
1163 1171 * optimized:
1164 1172 (func
1165 1173 ('symbol', 'only')
1166 1174 (list
1167 1175 ('symbol', '6')
1168 1176 ('symbol', '2')))
1169 1177 * set:
1170 1178 <baseset+ [3, 4, 5, 6]>
1171 1179 3
1172 1180 4
1173 1181 5
1174 1182 6
1175 1183 $ try --optimize 'ancestors(6) and not ancestors(4)'
1176 1184 (and
1177 1185 (func
1178 1186 ('symbol', 'ancestors')
1179 1187 ('symbol', '6'))
1180 1188 (not
1181 1189 (func
1182 1190 ('symbol', 'ancestors')
1183 1191 ('symbol', '4'))))
1184 1192 * optimized:
1185 1193 (func
1186 1194 ('symbol', 'only')
1187 1195 (list
1188 1196 ('symbol', '6')
1189 1197 ('symbol', '4')))
1190 1198 * set:
1191 1199 <baseset+ [3, 5, 6]>
1192 1200 3
1193 1201 5
1194 1202 6
1195 1203
1196 1204 we can use patterns when searching for tags
1197 1205
1198 1206 $ log 'tag("1..*")'
1199 1207 abort: tag '1..*' does not exist!
1200 1208 [255]
1201 1209 $ log 'tag("re:1..*")'
1202 1210 6
1203 1211 $ log 'tag("re:[0-9].[0-9]")'
1204 1212 6
1205 1213 $ log 'tag("literal:1.0")'
1206 1214 6
1207 1215 $ log 'tag("re:0..*")'
1208 1216
1209 1217 $ log 'tag(unknown)'
1210 1218 abort: tag 'unknown' does not exist!
1211 1219 [255]
1212 1220 $ log 'tag("re:unknown")'
1213 1221 $ log 'present(tag("unknown"))'
1214 1222 $ log 'present(tag("re:unknown"))'
1215 1223 $ log 'branch(unknown)'
1216 1224 abort: unknown revision 'unknown'!
1217 1225 [255]
1218 1226 $ log 'branch("re:unknown")'
1219 1227 $ log 'present(branch("unknown"))'
1220 1228 $ log 'present(branch("re:unknown"))'
1221 1229 $ log 'user(bob)'
1222 1230 2
1223 1231
1224 1232 $ log '4::8'
1225 1233 4
1226 1234 8
1227 1235 $ log '4:8'
1228 1236 4
1229 1237 5
1230 1238 6
1231 1239 7
1232 1240 8
1233 1241
1234 1242 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
1235 1243 4
1236 1244 2
1237 1245 5
1238 1246
1239 1247 $ log 'not 0 and 0:2'
1240 1248 1
1241 1249 2
1242 1250 $ log 'not 1 and 0:2'
1243 1251 0
1244 1252 2
1245 1253 $ log 'not 2 and 0:2'
1246 1254 0
1247 1255 1
1248 1256 $ log '(1 and 2)::'
1249 1257 $ log '(1 and 2):'
1250 1258 $ log '(1 and 2):3'
1251 1259 $ log 'sort(head(), -rev)'
1252 1260 9
1253 1261 7
1254 1262 6
1255 1263 5
1256 1264 4
1257 1265 3
1258 1266 2
1259 1267 1
1260 1268 0
1261 1269 $ log '4::8 - 8'
1262 1270 4
1263 1271 $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)'
1264 1272 2
1265 1273 3
1266 1274 1
1267 1275
1268 1276 $ log 'named("unknown")'
1269 1277 abort: namespace 'unknown' does not exist!
1270 1278 [255]
1271 1279 $ log 'named("re:unknown")'
1272 1280 abort: no namespace exists that match 'unknown'!
1273 1281 [255]
1274 1282 $ log 'present(named("unknown"))'
1275 1283 $ log 'present(named("re:unknown"))'
1276 1284
1277 1285 $ log 'tag()'
1278 1286 6
1279 1287 $ log 'named("tags")'
1280 1288 6
1281 1289
1282 1290 issue2437
1283 1291
1284 1292 $ log '3 and p1(5)'
1285 1293 3
1286 1294 $ log '4 and p2(6)'
1287 1295 4
1288 1296 $ log '1 and parents(:2)'
1289 1297 1
1290 1298 $ log '2 and children(1:)'
1291 1299 2
1292 1300 $ log 'roots(all()) or roots(all())'
1293 1301 0
1294 1302 $ hg debugrevspec 'roots(all()) or roots(all())'
1295 1303 0
1296 1304 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
1297 1305 9
1298 1306 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
1299 1307 4
1300 1308
1301 1309 issue2654: report a parse error if the revset was not completely parsed
1302 1310
1303 1311 $ log '1 OR 2'
1304 1312 hg: parse error at 2: invalid token
1305 1313 [255]
1306 1314
1307 1315 or operator should preserve ordering:
1308 1316 $ log 'reverse(2::4) or tip'
1309 1317 4
1310 1318 2
1311 1319 9
1312 1320
1313 1321 parentrevspec
1314 1322
1315 1323 $ log 'merge()^0'
1316 1324 6
1317 1325 $ log 'merge()^'
1318 1326 5
1319 1327 $ log 'merge()^1'
1320 1328 5
1321 1329 $ log 'merge()^2'
1322 1330 4
1323 1331 $ log 'merge()^^'
1324 1332 3
1325 1333 $ log 'merge()^1^'
1326 1334 3
1327 1335 $ log 'merge()^^^'
1328 1336 1
1329 1337
1330 1338 $ log 'merge()~0'
1331 1339 6
1332 1340 $ log 'merge()~1'
1333 1341 5
1334 1342 $ log 'merge()~2'
1335 1343 3
1336 1344 $ log 'merge()~2^1'
1337 1345 1
1338 1346 $ log 'merge()~3'
1339 1347 1
1340 1348
1341 1349 $ log '(-3:tip)^'
1342 1350 4
1343 1351 6
1344 1352 8
1345 1353
1346 1354 $ log 'tip^foo'
1347 1355 hg: parse error: ^ expects a number 0, 1, or 2
1348 1356 [255]
1349 1357
1350 1358 Bogus function gets suggestions
1351 1359 $ log 'add()'
1352 1360 hg: parse error: unknown identifier: add
1353 1361 (did you mean 'adds'?)
1354 1362 [255]
1355 1363 $ log 'added()'
1356 1364 hg: parse error: unknown identifier: added
1357 1365 (did you mean 'adds'?)
1358 1366 [255]
1359 1367 $ log 'remo()'
1360 1368 hg: parse error: unknown identifier: remo
1361 1369 (did you mean one of remote, removes?)
1362 1370 [255]
1363 1371 $ log 'babar()'
1364 1372 hg: parse error: unknown identifier: babar
1365 1373 [255]
1366 1374
1367 1375 Bogus function with a similar internal name doesn't suggest the internal name
1368 1376 $ log 'matches()'
1369 1377 hg: parse error: unknown identifier: matches
1370 1378 (did you mean 'matching'?)
1371 1379 [255]
1372 1380
1373 1381 Undocumented functions aren't suggested as similar either
1374 1382 $ log 'wdir2()'
1375 1383 hg: parse error: unknown identifier: wdir2
1376 1384 [255]
1377 1385
1378 1386 multiple revspecs
1379 1387
1380 1388 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
1381 1389 8
1382 1390 9
1383 1391 4
1384 1392 5
1385 1393 6
1386 1394 7
1387 1395
1388 1396 test usage in revpair (with "+")
1389 1397
1390 1398 (real pair)
1391 1399
1392 1400 $ hg diff -r 'tip^^' -r 'tip'
1393 1401 diff -r 2326846efdab -r 24286f4ae135 .hgtags
1394 1402 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1395 1403 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1396 1404 @@ -0,0 +1,1 @@
1397 1405 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1398 1406 $ hg diff -r 'tip^^::tip'
1399 1407 diff -r 2326846efdab -r 24286f4ae135 .hgtags
1400 1408 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1401 1409 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1402 1410 @@ -0,0 +1,1 @@
1403 1411 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1404 1412
1405 1413 (single rev)
1406 1414
1407 1415 $ hg diff -r 'tip^' -r 'tip^'
1408 1416 $ hg diff -r 'tip^::tip^ or tip^'
1409 1417
1410 1418 (single rev that does not looks like a range)
1411 1419
1412 1420 $ hg diff -r 'tip^ or tip^'
1413 1421 diff -r d5d0dcbdc4d9 .hgtags
1414 1422 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1415 1423 +++ b/.hgtags * (glob)
1416 1424 @@ -0,0 +1,1 @@
1417 1425 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1418 1426
1419 1427 (no rev)
1420 1428
1421 1429 $ hg diff -r 'author("babar") or author("celeste")'
1422 1430 abort: empty revision range
1423 1431 [255]
1424 1432
1425 1433 aliases:
1426 1434
1427 1435 $ echo '[revsetalias]' >> .hg/hgrc
1428 1436 $ echo 'm = merge()' >> .hg/hgrc
1429 1437 (revset aliases can override builtin revsets)
1430 1438 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
1431 1439 $ echo 'sincem = descendants(m)' >> .hg/hgrc
1432 1440 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
1433 1441 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
1434 1442 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
1435 1443
1436 1444 $ try m
1437 1445 ('symbol', 'm')
1438 1446 (func
1439 1447 ('symbol', 'merge')
1440 1448 None)
1441 1449 * set:
1442 1450 <filteredset
1443 1451 <fullreposet+ 0:9>>
1444 1452 6
1445 1453
1446 1454 $ HGPLAIN=1
1447 1455 $ export HGPLAIN
1448 1456 $ try m
1449 1457 ('symbol', 'm')
1450 1458 abort: unknown revision 'm'!
1451 1459 [255]
1452 1460
1453 1461 $ HGPLAINEXCEPT=revsetalias
1454 1462 $ export HGPLAINEXCEPT
1455 1463 $ try m
1456 1464 ('symbol', 'm')
1457 1465 (func
1458 1466 ('symbol', 'merge')
1459 1467 None)
1460 1468 * set:
1461 1469 <filteredset
1462 1470 <fullreposet+ 0:9>>
1463 1471 6
1464 1472
1465 1473 $ unset HGPLAIN
1466 1474 $ unset HGPLAINEXCEPT
1467 1475
1468 1476 $ try 'p2(.)'
1469 1477 (func
1470 1478 ('symbol', 'p2')
1471 1479 ('symbol', '.'))
1472 1480 (func
1473 1481 ('symbol', 'p1')
1474 1482 ('symbol', '.'))
1475 1483 * set:
1476 1484 <baseset+ [8]>
1477 1485 8
1478 1486
1479 1487 $ HGPLAIN=1
1480 1488 $ export HGPLAIN
1481 1489 $ try 'p2(.)'
1482 1490 (func
1483 1491 ('symbol', 'p2')
1484 1492 ('symbol', '.'))
1485 1493 * set:
1486 1494 <baseset+ []>
1487 1495
1488 1496 $ HGPLAINEXCEPT=revsetalias
1489 1497 $ export HGPLAINEXCEPT
1490 1498 $ try 'p2(.)'
1491 1499 (func
1492 1500 ('symbol', 'p2')
1493 1501 ('symbol', '.'))
1494 1502 (func
1495 1503 ('symbol', 'p1')
1496 1504 ('symbol', '.'))
1497 1505 * set:
1498 1506 <baseset+ [8]>
1499 1507 8
1500 1508
1501 1509 $ unset HGPLAIN
1502 1510 $ unset HGPLAINEXCEPT
1503 1511
1504 1512 test alias recursion
1505 1513
1506 1514 $ try sincem
1507 1515 ('symbol', 'sincem')
1508 1516 (func
1509 1517 ('symbol', 'descendants')
1510 1518 (func
1511 1519 ('symbol', 'merge')
1512 1520 None))
1513 1521 * set:
1514 1522 <addset+
1515 1523 <filteredset
1516 1524 <fullreposet+ 0:9>>,
1517 1525 <generatorset+>>
1518 1526 6
1519 1527 7
1520 1528
1521 1529 test infinite recursion
1522 1530
1523 1531 $ echo 'recurse1 = recurse2' >> .hg/hgrc
1524 1532 $ echo 'recurse2 = recurse1' >> .hg/hgrc
1525 1533 $ try recurse1
1526 1534 ('symbol', 'recurse1')
1527 1535 hg: parse error: infinite expansion of revset alias "recurse1" detected
1528 1536 [255]
1529 1537
1530 1538 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
1531 1539 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
1532 1540 $ try "level2(level1(1, 2), 3)"
1533 1541 (func
1534 1542 ('symbol', 'level2')
1535 1543 (list
1536 1544 (func
1537 1545 ('symbol', 'level1')
1538 1546 (list
1539 1547 ('symbol', '1')
1540 1548 ('symbol', '2')))
1541 1549 ('symbol', '3')))
1542 1550 (or
1543 1551 ('symbol', '3')
1544 1552 (or
1545 1553 ('symbol', '1')
1546 1554 ('symbol', '2')))
1547 1555 * set:
1548 1556 <addset
1549 1557 <baseset [3]>,
1550 1558 <baseset [1, 2]>>
1551 1559 3
1552 1560 1
1553 1561 2
1554 1562
1555 1563 test nesting and variable passing
1556 1564
1557 1565 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
1558 1566 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
1559 1567 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
1560 1568 $ try 'nested(2:5)'
1561 1569 (func
1562 1570 ('symbol', 'nested')
1563 1571 (range
1564 1572 ('symbol', '2')
1565 1573 ('symbol', '5')))
1566 1574 (func
1567 1575 ('symbol', 'max')
1568 1576 (range
1569 1577 ('symbol', '2')
1570 1578 ('symbol', '5')))
1571 1579 * set:
1572 1580 <baseset [5]>
1573 1581 5
1574 1582
1575 1583 test chained `or` operations are flattened at parsing phase
1576 1584
1577 1585 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
1578 1586 $ try 'chainedorops(0:1, 1:2, 2:3)'
1579 1587 (func
1580 1588 ('symbol', 'chainedorops')
1581 1589 (list
1582 1590 (list
1583 1591 (range
1584 1592 ('symbol', '0')
1585 1593 ('symbol', '1'))
1586 1594 (range
1587 1595 ('symbol', '1')
1588 1596 ('symbol', '2')))
1589 1597 (range
1590 1598 ('symbol', '2')
1591 1599 ('symbol', '3'))))
1592 1600 (or
1593 1601 (range
1594 1602 ('symbol', '0')
1595 1603 ('symbol', '1'))
1596 1604 (range
1597 1605 ('symbol', '1')
1598 1606 ('symbol', '2'))
1599 1607 (range
1600 1608 ('symbol', '2')
1601 1609 ('symbol', '3')))
1602 1610 * set:
1603 1611 <addset
1604 1612 <spanset+ 0:1>,
1605 1613 <addset
1606 1614 <spanset+ 1:2>,
1607 1615 <spanset+ 2:3>>>
1608 1616 0
1609 1617 1
1610 1618 2
1611 1619 3
1612 1620
1613 1621 test variable isolation, variable placeholders are rewritten as string
1614 1622 then parsed and matched again as string. Check they do not leak too
1615 1623 far away.
1616 1624
1617 1625 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
1618 1626 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
1619 1627 $ try 'callinjection(2:5)'
1620 1628 (func
1621 1629 ('symbol', 'callinjection')
1622 1630 (range
1623 1631 ('symbol', '2')
1624 1632 ('symbol', '5')))
1625 1633 (func
1626 1634 ('symbol', 'descendants')
1627 1635 (func
1628 1636 ('symbol', 'max')
1629 1637 ('string', '$1')))
1630 1638 abort: unknown revision '$1'!
1631 1639 [255]
1632 1640
1633 1641 $ echo 'injectparamasstring2 = max(_aliasarg("$1"))' >> .hg/hgrc
1634 1642 $ echo 'callinjection2($1) = descendants(injectparamasstring2)' >> .hg/hgrc
1635 1643 $ try 'callinjection2(2:5)'
1636 1644 (func
1637 1645 ('symbol', 'callinjection2')
1638 1646 (range
1639 1647 ('symbol', '2')
1640 1648 ('symbol', '5')))
1641 1649 abort: failed to parse the definition of revset alias "injectparamasstring2": unknown identifier: _aliasarg
1642 1650 [255]
1643 1651 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1644 1652 ('symbol', 'tip')
1645 1653 warning: failed to parse the definition of revset alias "anotherbadone": at 7: not a prefix: end
1646 1654 warning: failed to parse the definition of revset alias "injectparamasstring2": unknown identifier: _aliasarg
1647 1655 * set:
1648 1656 <baseset [9]>
1649 1657 9
1650 1658 >>> data = file('.hg/hgrc', 'rb').read()
1651 1659 >>> file('.hg/hgrc', 'wb').write(data.replace('_aliasarg', ''))
1652 1660
1653 1661 $ try 'tip'
1654 1662 ('symbol', 'tip')
1655 1663 * set:
1656 1664 <baseset [9]>
1657 1665 9
1658 1666
1659 1667 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1660 1668 ('symbol', 'tip')
1661 1669 warning: failed to parse the declaration of revset alias "bad name": at 4: invalid token
1662 1670 * set:
1663 1671 <baseset [9]>
1664 1672 9
1665 1673 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1666 1674 $ try 'strictreplacing("foo", tip)'
1667 1675 (func
1668 1676 ('symbol', 'strictreplacing')
1669 1677 (list
1670 1678 ('string', 'foo')
1671 1679 ('symbol', 'tip')))
1672 1680 (or
1673 1681 ('symbol', 'tip')
1674 1682 (func
1675 1683 ('symbol', 'desc')
1676 1684 ('string', '$1')))
1677 1685 * set:
1678 1686 <addset
1679 1687 <baseset [9]>,
1680 1688 <filteredset
1681 1689 <fullreposet+ 0:9>>>
1682 1690 9
1683 1691
1684 1692 $ try 'd(2:5)'
1685 1693 (func
1686 1694 ('symbol', 'd')
1687 1695 (range
1688 1696 ('symbol', '2')
1689 1697 ('symbol', '5')))
1690 1698 (func
1691 1699 ('symbol', 'reverse')
1692 1700 (func
1693 1701 ('symbol', 'sort')
1694 1702 (list
1695 1703 (range
1696 1704 ('symbol', '2')
1697 1705 ('symbol', '5'))
1698 1706 ('symbol', 'date'))))
1699 1707 * set:
1700 1708 <baseset [4, 5, 3, 2]>
1701 1709 4
1702 1710 5
1703 1711 3
1704 1712 2
1705 1713 $ try 'rs(2 or 3, date)'
1706 1714 (func
1707 1715 ('symbol', 'rs')
1708 1716 (list
1709 1717 (or
1710 1718 ('symbol', '2')
1711 1719 ('symbol', '3'))
1712 1720 ('symbol', 'date')))
1713 1721 (func
1714 1722 ('symbol', 'reverse')
1715 1723 (func
1716 1724 ('symbol', 'sort')
1717 1725 (list
1718 1726 (or
1719 1727 ('symbol', '2')
1720 1728 ('symbol', '3'))
1721 1729 ('symbol', 'date'))))
1722 1730 * set:
1723 1731 <baseset [3, 2]>
1724 1732 3
1725 1733 2
1726 1734 $ try 'rs()'
1727 1735 (func
1728 1736 ('symbol', 'rs')
1729 1737 None)
1730 1738 hg: parse error: invalid number of arguments: 0
1731 1739 [255]
1732 1740 $ try 'rs(2)'
1733 1741 (func
1734 1742 ('symbol', 'rs')
1735 1743 ('symbol', '2'))
1736 1744 hg: parse error: invalid number of arguments: 1
1737 1745 [255]
1738 1746 $ try 'rs(2, data, 7)'
1739 1747 (func
1740 1748 ('symbol', 'rs')
1741 1749 (list
1742 1750 (list
1743 1751 ('symbol', '2')
1744 1752 ('symbol', 'data'))
1745 1753 ('symbol', '7')))
1746 1754 hg: parse error: invalid number of arguments: 3
1747 1755 [255]
1748 1756 $ try 'rs4(2 or 3, x, x, date)'
1749 1757 (func
1750 1758 ('symbol', 'rs4')
1751 1759 (list
1752 1760 (list
1753 1761 (list
1754 1762 (or
1755 1763 ('symbol', '2')
1756 1764 ('symbol', '3'))
1757 1765 ('symbol', 'x'))
1758 1766 ('symbol', 'x'))
1759 1767 ('symbol', 'date')))
1760 1768 (func
1761 1769 ('symbol', 'reverse')
1762 1770 (func
1763 1771 ('symbol', 'sort')
1764 1772 (list
1765 1773 (or
1766 1774 ('symbol', '2')
1767 1775 ('symbol', '3'))
1768 1776 ('symbol', 'date'))))
1769 1777 * set:
1770 1778 <baseset [3, 2]>
1771 1779 3
1772 1780 2
1773 1781
1774 1782 issue4553: check that revset aliases override existing hash prefix
1775 1783
1776 1784 $ hg log -qr e
1777 1785 6:e0cc66ef77e8
1778 1786
1779 1787 $ hg log -qr e --config revsetalias.e="all()"
1780 1788 0:2785f51eece5
1781 1789 1:d75937da8da0
1782 1790 2:5ed5505e9f1c
1783 1791 3:8528aa5637f2
1784 1792 4:2326846efdab
1785 1793 5:904fa392b941
1786 1794 6:e0cc66ef77e8
1787 1795 7:013af1973af4
1788 1796 8:d5d0dcbdc4d9
1789 1797 9:24286f4ae135
1790 1798
1791 1799 $ hg log -qr e: --config revsetalias.e="0"
1792 1800 0:2785f51eece5
1793 1801 1:d75937da8da0
1794 1802 2:5ed5505e9f1c
1795 1803 3:8528aa5637f2
1796 1804 4:2326846efdab
1797 1805 5:904fa392b941
1798 1806 6:e0cc66ef77e8
1799 1807 7:013af1973af4
1800 1808 8:d5d0dcbdc4d9
1801 1809 9:24286f4ae135
1802 1810
1803 1811 $ hg log -qr :e --config revsetalias.e="9"
1804 1812 0:2785f51eece5
1805 1813 1:d75937da8da0
1806 1814 2:5ed5505e9f1c
1807 1815 3:8528aa5637f2
1808 1816 4:2326846efdab
1809 1817 5:904fa392b941
1810 1818 6:e0cc66ef77e8
1811 1819 7:013af1973af4
1812 1820 8:d5d0dcbdc4d9
1813 1821 9:24286f4ae135
1814 1822
1815 1823 $ hg log -qr e:
1816 1824 6:e0cc66ef77e8
1817 1825 7:013af1973af4
1818 1826 8:d5d0dcbdc4d9
1819 1827 9:24286f4ae135
1820 1828
1821 1829 $ hg log -qr :e
1822 1830 0:2785f51eece5
1823 1831 1:d75937da8da0
1824 1832 2:5ed5505e9f1c
1825 1833 3:8528aa5637f2
1826 1834 4:2326846efdab
1827 1835 5:904fa392b941
1828 1836 6:e0cc66ef77e8
1829 1837
1830 1838 issue2549 - correct optimizations
1831 1839
1832 1840 $ log 'limit(1 or 2 or 3, 2) and not 2'
1833 1841 1
1834 1842 $ log 'max(1 or 2) and not 2'
1835 1843 $ log 'min(1 or 2) and not 1'
1836 1844 $ log 'last(1 or 2, 1) and not 2'
1837 1845
1838 1846 issue4289 - ordering of built-ins
1839 1847 $ hg log -M -q -r 3:2
1840 1848 3:8528aa5637f2
1841 1849 2:5ed5505e9f1c
1842 1850
1843 1851 test revsets started with 40-chars hash (issue3669)
1844 1852
1845 1853 $ ISSUE3669_TIP=`hg tip --template '{node}'`
1846 1854 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
1847 1855 9
1848 1856 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
1849 1857 8
1850 1858
1851 1859 test or-ed indirect predicates (issue3775)
1852 1860
1853 1861 $ log '6 or 6^1' | sort
1854 1862 5
1855 1863 6
1856 1864 $ log '6^1 or 6' | sort
1857 1865 5
1858 1866 6
1859 1867 $ log '4 or 4~1' | sort
1860 1868 2
1861 1869 4
1862 1870 $ log '4~1 or 4' | sort
1863 1871 2
1864 1872 4
1865 1873 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
1866 1874 0
1867 1875 1
1868 1876 2
1869 1877 3
1870 1878 4
1871 1879 5
1872 1880 6
1873 1881 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
1874 1882 0
1875 1883 1
1876 1884 2
1877 1885 3
1878 1886 4
1879 1887 5
1880 1888 6
1881 1889
1882 1890 tests for 'remote()' predicate:
1883 1891 #. (csets in remote) (id) (remote)
1884 1892 1. less than local current branch "default"
1885 1893 2. same with local specified "default"
1886 1894 3. more than local specified specified
1887 1895
1888 1896 $ hg clone --quiet -U . ../remote3
1889 1897 $ cd ../remote3
1890 1898 $ hg update -q 7
1891 1899 $ echo r > r
1892 1900 $ hg ci -Aqm 10
1893 1901 $ log 'remote()'
1894 1902 7
1895 1903 $ log 'remote("a-b-c-")'
1896 1904 2
1897 1905 $ cd ../repo
1898 1906 $ log 'remote(".a.b.c.", "../remote3")'
1899 1907
1900 1908 tests for concatenation of strings/symbols by "##"
1901 1909
1902 1910 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
1903 1911 (_concat
1904 1912 (_concat
1905 1913 (_concat
1906 1914 ('symbol', '278')
1907 1915 ('string', '5f5'))
1908 1916 ('symbol', '1ee'))
1909 1917 ('string', 'ce5'))
1910 1918 ('string', '2785f51eece5')
1911 1919 * set:
1912 1920 <baseset [0]>
1913 1921 0
1914 1922
1915 1923 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
1916 1924 $ try "cat4(278, '5f5', 1ee, 'ce5')"
1917 1925 (func
1918 1926 ('symbol', 'cat4')
1919 1927 (list
1920 1928 (list
1921 1929 (list
1922 1930 ('symbol', '278')
1923 1931 ('string', '5f5'))
1924 1932 ('symbol', '1ee'))
1925 1933 ('string', 'ce5')))
1926 1934 (_concat
1927 1935 (_concat
1928 1936 (_concat
1929 1937 ('symbol', '278')
1930 1938 ('string', '5f5'))
1931 1939 ('symbol', '1ee'))
1932 1940 ('string', 'ce5'))
1933 1941 ('string', '2785f51eece5')
1934 1942 * set:
1935 1943 <baseset [0]>
1936 1944 0
1937 1945
1938 1946 (check concatenation in alias nesting)
1939 1947
1940 1948 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
1941 1949 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
1942 1950 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
1943 1951 0
1944 1952
1945 1953 (check operator priority)
1946 1954
1947 1955 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
1948 1956 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
1949 1957 0
1950 1958 4
1951 1959
1952 1960 $ cd ..
1953 1961
1954 1962 prepare repository that has "default" branches of multiple roots
1955 1963
1956 1964 $ hg init namedbranch
1957 1965 $ cd namedbranch
1958 1966
1959 1967 $ echo default0 >> a
1960 1968 $ hg ci -Aqm0
1961 1969 $ echo default1 >> a
1962 1970 $ hg ci -m1
1963 1971
1964 1972 $ hg branch -q stable
1965 1973 $ echo stable2 >> a
1966 1974 $ hg ci -m2
1967 1975 $ echo stable3 >> a
1968 1976 $ hg ci -m3
1969 1977
1970 1978 $ hg update -q null
1971 1979 $ echo default4 >> a
1972 1980 $ hg ci -Aqm4
1973 1981 $ echo default5 >> a
1974 1982 $ hg ci -m5
1975 1983
1976 1984 "null" revision belongs to "default" branch (issue4683)
1977 1985
1978 1986 $ log 'branch(null)'
1979 1987 0
1980 1988 1
1981 1989 4
1982 1990 5
1983 1991
1984 1992 "null" revision belongs to "default" branch, but it shouldn't appear in set
1985 1993 unless explicitly specified (issue4682)
1986 1994
1987 1995 $ log 'children(branch(default))'
1988 1996 1
1989 1997 2
1990 1998 5
1991 1999
1992 2000 $ cd ..
1993 2001
1994 2002 test author/desc/keyword in problematic encoding
1995 2003 # unicode: cp932:
1996 2004 # u30A2 0x83 0x41(= 'A')
1997 2005 # u30C2 0x83 0x61(= 'a')
1998 2006
1999 2007 $ hg init problematicencoding
2000 2008 $ cd problematicencoding
2001 2009
2002 2010 $ python > setup.sh <<EOF
2003 2011 > print u'''
2004 2012 > echo a > text
2005 2013 > hg add text
2006 2014 > hg --encoding utf-8 commit -u '\u30A2' -m none
2007 2015 > echo b > text
2008 2016 > hg --encoding utf-8 commit -u '\u30C2' -m none
2009 2017 > echo c > text
2010 2018 > hg --encoding utf-8 commit -u none -m '\u30A2'
2011 2019 > echo d > text
2012 2020 > hg --encoding utf-8 commit -u none -m '\u30C2'
2013 2021 > '''.encode('utf-8')
2014 2022 > EOF
2015 2023 $ sh < setup.sh
2016 2024
2017 2025 test in problematic encoding
2018 2026 $ python > test.sh <<EOF
2019 2027 > print u'''
2020 2028 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
2021 2029 > echo ====
2022 2030 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
2023 2031 > echo ====
2024 2032 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
2025 2033 > echo ====
2026 2034 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
2027 2035 > echo ====
2028 2036 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
2029 2037 > echo ====
2030 2038 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
2031 2039 > '''.encode('cp932')
2032 2040 > EOF
2033 2041 $ sh < test.sh
2034 2042 0
2035 2043 ====
2036 2044 1
2037 2045 ====
2038 2046 2
2039 2047 ====
2040 2048 3
2041 2049 ====
2042 2050 0
2043 2051 2
2044 2052 ====
2045 2053 1
2046 2054 3
2047 2055
2048 2056 test error message of bad revset
2049 2057 $ hg log -r 'foo\\'
2050 2058 hg: parse error at 3: syntax error in revset 'foo\\'
2051 2059 [255]
2052 2060
2053 2061 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now