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