##// END OF EJS Templates
revset: disable subset optimization for parents() and children() (issue2437)...
Wagner Bruna -
r12786:9aae04f4 default
parent child Browse files
Show More
@@ -1,626 +1,626 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, discovery
10 10 import match as matchmod
11 11 from i18n import _
12 12
13 13 elements = {
14 14 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
15 15 "-": (5, ("negate", 19), ("minus", 5)),
16 16 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
17 17 ("dagrangepost", 17)),
18 18 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
19 19 ("dagrangepost", 17)),
20 20 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
21 21 "not": (10, ("not", 10)),
22 22 "!": (10, ("not", 10)),
23 23 "and": (5, None, ("and", 5)),
24 24 "&": (5, None, ("and", 5)),
25 25 "or": (4, None, ("or", 4)),
26 26 "|": (4, None, ("or", 4)),
27 27 "+": (4, None, ("or", 4)),
28 28 ",": (2, None, ("list", 2)),
29 29 ")": (0, None, None),
30 30 "symbol": (0, ("symbol",), None),
31 31 "string": (0, ("string",), None),
32 32 "end": (0, None, None),
33 33 }
34 34
35 35 keywords = set(['and', 'or', 'not'])
36 36
37 37 def tokenize(program):
38 38 pos, l = 0, len(program)
39 39 while pos < l:
40 40 c = program[pos]
41 41 if c.isspace(): # skip inter-token whitespace
42 42 pass
43 43 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
44 44 yield ('::', None, pos)
45 45 pos += 1 # skip ahead
46 46 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
47 47 yield ('..', None, pos)
48 48 pos += 1 # skip ahead
49 49 elif c in "():,-|&+!": # handle simple operators
50 50 yield (c, None, pos)
51 51 elif (c in '"\'' or c == 'r' and
52 52 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
53 53 if c == 'r':
54 54 pos += 1
55 55 c = program[pos]
56 56 decode = lambda x: x
57 57 else:
58 58 decode = lambda x: x.decode('string-escape')
59 59 pos += 1
60 60 s = pos
61 61 while pos < l: # find closing quote
62 62 d = program[pos]
63 63 if d == '\\': # skip over escaped characters
64 64 pos += 2
65 65 continue
66 66 if d == c:
67 67 yield ('string', decode(program[s:pos]), s)
68 68 break
69 69 pos += 1
70 70 else:
71 71 raise error.ParseError(_("unterminated string"), s)
72 72 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
73 73 s = pos
74 74 pos += 1
75 75 while pos < l: # find end of symbol
76 76 d = program[pos]
77 77 if not (d.isalnum() or d in "._" or ord(d) > 127):
78 78 break
79 79 if d == '.' and program[pos - 1] == '.': # special case for ..
80 80 pos -= 1
81 81 break
82 82 pos += 1
83 83 sym = program[s:pos]
84 84 if sym in keywords: # operator keywords
85 85 yield (sym, None, s)
86 86 else:
87 87 yield ('symbol', sym, s)
88 88 pos -= 1
89 89 else:
90 90 raise error.ParseError(_("syntax error"), pos)
91 91 pos += 1
92 92 yield ('end', None, pos)
93 93
94 94 # helpers
95 95
96 96 def getstring(x, err):
97 97 if x and (x[0] == 'string' or x[0] == 'symbol'):
98 98 return x[1]
99 99 raise error.ParseError(err)
100 100
101 101 def getlist(x):
102 102 if not x:
103 103 return []
104 104 if x[0] == 'list':
105 105 return getlist(x[1]) + [x[2]]
106 106 return [x]
107 107
108 108 def getargs(x, min, max, err):
109 109 l = getlist(x)
110 110 if len(l) < min or len(l) > max:
111 111 raise error.ParseError(err)
112 112 return l
113 113
114 114 def getset(repo, subset, x):
115 115 if not x:
116 116 raise error.ParseError(_("missing argument"))
117 117 return methods[x[0]](repo, subset, *x[1:])
118 118
119 119 # operator methods
120 120
121 121 def stringset(repo, subset, x):
122 122 x = repo[x].rev()
123 123 if x == -1 and len(subset) == len(repo):
124 124 return [-1]
125 125 if x in subset:
126 126 return [x]
127 127 return []
128 128
129 129 def symbolset(repo, subset, x):
130 130 if x in symbols:
131 131 raise error.ParseError(_("can't use %s here") % x)
132 132 return stringset(repo, subset, x)
133 133
134 134 def rangeset(repo, subset, x, y):
135 135 m = getset(repo, subset, x)
136 136 if not m:
137 137 m = getset(repo, range(len(repo)), x)
138 138
139 139 n = getset(repo, subset, y)
140 140 if not n:
141 141 n = getset(repo, range(len(repo)), y)
142 142
143 143 if not m or not n:
144 144 return []
145 145 m, n = m[0], n[-1]
146 146
147 147 if m < n:
148 148 r = range(m, n + 1)
149 149 else:
150 150 r = range(m, n - 1, -1)
151 151 s = set(subset)
152 152 return [x for x in r if x in s]
153 153
154 154 def andset(repo, subset, x, y):
155 155 return getset(repo, getset(repo, subset, x), y)
156 156
157 157 def orset(repo, subset, x, y):
158 158 s = set(getset(repo, subset, x))
159 159 s |= set(getset(repo, [r for r in subset if r not in s], y))
160 160 return [r for r in subset if r in s]
161 161
162 162 def notset(repo, subset, x):
163 163 s = set(getset(repo, subset, x))
164 164 return [r for r in subset if r not in s]
165 165
166 166 def listset(repo, subset, a, b):
167 167 raise error.ParseError(_("can't use a list in this context"))
168 168
169 169 def func(repo, subset, a, b):
170 170 if a[0] == 'symbol' and a[1] in symbols:
171 171 return symbols[a[1]](repo, subset, b)
172 172 raise error.ParseError(_("not a function: %s") % a[1])
173 173
174 174 # functions
175 175
176 176 def node(repo, subset, x):
177 177 l = getargs(x, 1, 1, _("id requires one argument"))
178 178 n = getstring(l[0], _("id requires a string"))
179 179 if len(n) == 40:
180 180 rn = repo[n].rev()
181 181 else:
182 182 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
183 183 return [r for r in subset if r == rn]
184 184
185 185 def rev(repo, subset, x):
186 186 l = getargs(x, 1, 1, _("rev requires one argument"))
187 187 try:
188 188 l = int(getstring(l[0], _("rev requires a number")))
189 189 except ValueError:
190 190 raise error.ParseError(_("rev expects a number"))
191 191 return [r for r in subset if r == l]
192 192
193 193 def p1(repo, subset, x):
194 194 ps = set()
195 195 cl = repo.changelog
196 for r in getset(repo, subset, x):
196 for r in getset(repo, range(len(repo)), x):
197 197 ps.add(cl.parentrevs(r)[0])
198 198 return [r for r in subset if r in ps]
199 199
200 200 def p2(repo, subset, x):
201 201 ps = set()
202 202 cl = repo.changelog
203 for r in getset(repo, subset, x):
203 for r in getset(repo, range(len(repo)), x):
204 204 ps.add(cl.parentrevs(r)[1])
205 205 return [r for r in subset if r in ps]
206 206
207 207 def parents(repo, subset, x):
208 208 ps = set()
209 209 cl = repo.changelog
210 for r in getset(repo, subset, x):
210 for r in getset(repo, range(len(repo)), x):
211 211 ps.update(cl.parentrevs(r))
212 212 return [r for r in subset if r in ps]
213 213
214 214 def maxrev(repo, subset, x):
215 215 s = getset(repo, subset, x)
216 216 if s:
217 217 m = max(s)
218 218 if m in subset:
219 219 return [m]
220 220 return []
221 221
222 222 def minrev(repo, subset, x):
223 223 s = getset(repo, subset, x)
224 224 if s:
225 225 m = min(s)
226 226 if m in subset:
227 227 return [m]
228 228 return []
229 229
230 230 def limit(repo, subset, x):
231 231 l = getargs(x, 2, 2, _("limit requires two arguments"))
232 232 try:
233 233 lim = int(getstring(l[1], _("limit requires a number")))
234 234 except ValueError:
235 235 raise error.ParseError(_("limit expects a number"))
236 236 return getset(repo, subset, l[0])[:lim]
237 237
238 238 def children(repo, subset, x):
239 239 cs = set()
240 240 cl = repo.changelog
241 s = set(getset(repo, subset, x))
241 s = set(getset(repo, range(len(repo)), x))
242 242 for r in xrange(0, len(repo)):
243 243 for p in cl.parentrevs(r):
244 244 if p in s:
245 245 cs.add(r)
246 246 return [r for r in subset if r in cs]
247 247
248 248 def branch(repo, subset, x):
249 249 s = getset(repo, range(len(repo)), x)
250 250 b = set()
251 251 for r in s:
252 252 b.add(repo[r].branch())
253 253 s = set(s)
254 254 return [r for r in subset if r in s or repo[r].branch() in b]
255 255
256 256 def ancestor(repo, subset, x):
257 257 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
258 258 r = range(len(repo))
259 259 a = getset(repo, r, l[0])
260 260 b = getset(repo, r, l[1])
261 261 if len(a) != 1 or len(b) != 1:
262 262 raise error.ParseError(_("ancestor arguments must be single revisions"))
263 263 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
264 264
265 265 return [r for r in an if r in subset]
266 266
267 267 def ancestors(repo, subset, x):
268 268 args = getset(repo, range(len(repo)), x)
269 269 if not args:
270 270 return []
271 271 s = set(repo.changelog.ancestors(*args)) | set(args)
272 272 return [r for r in subset if r in s]
273 273
274 274 def descendants(repo, subset, x):
275 275 args = getset(repo, range(len(repo)), x)
276 276 if not args:
277 277 return []
278 278 s = set(repo.changelog.descendants(*args)) | set(args)
279 279 return [r for r in subset if r in s]
280 280
281 281 def follow(repo, subset, x):
282 282 getargs(x, 0, 0, _("follow takes no arguments"))
283 283 p = repo['.'].rev()
284 284 s = set(repo.changelog.ancestors(p)) | set([p])
285 285 return [r for r in subset if r in s]
286 286
287 287 def date(repo, subset, x):
288 288 ds = getstring(x, _("date requires a string"))
289 289 dm = util.matchdate(ds)
290 290 return [r for r in subset if dm(repo[r].date()[0])]
291 291
292 292 def keyword(repo, subset, x):
293 293 kw = getstring(x, _("keyword requires a string")).lower()
294 294 l = []
295 295 for r in subset:
296 296 c = repo[r]
297 297 t = " ".join(c.files() + [c.user(), c.description()])
298 298 if kw in t.lower():
299 299 l.append(r)
300 300 return l
301 301
302 302 def grep(repo, subset, x):
303 303 try:
304 304 gr = re.compile(getstring(x, _("grep requires a string")))
305 305 except re.error, e:
306 306 raise error.ParseError(_('invalid match pattern: %s') % e)
307 307 l = []
308 308 for r in subset:
309 309 c = repo[r]
310 310 for e in c.files() + [c.user(), c.description()]:
311 311 if gr.search(e):
312 312 l.append(r)
313 313 continue
314 314 return l
315 315
316 316 def author(repo, subset, x):
317 317 n = getstring(x, _("author requires a string")).lower()
318 318 return [r for r in subset if n in repo[r].user().lower()]
319 319
320 320 def hasfile(repo, subset, x):
321 321 pat = getstring(x, _("file requires a pattern"))
322 322 m = matchmod.match(repo.root, repo.getcwd(), [pat])
323 323 s = []
324 324 for r in subset:
325 325 for f in repo[r].files():
326 326 if m(f):
327 327 s.append(r)
328 328 continue
329 329 return s
330 330
331 331 def contains(repo, subset, x):
332 332 pat = getstring(x, _("contains requires a pattern"))
333 333 m = matchmod.match(repo.root, repo.getcwd(), [pat])
334 334 s = []
335 335 if m.files() == [pat]:
336 336 for r in subset:
337 337 if pat in repo[r]:
338 338 s.append(r)
339 339 continue
340 340 else:
341 341 for r in subset:
342 342 for f in repo[r].manifest():
343 343 if m(f):
344 344 s.append(r)
345 345 continue
346 346 return s
347 347
348 348 def checkstatus(repo, subset, pat, field):
349 349 m = matchmod.match(repo.root, repo.getcwd(), [pat])
350 350 s = []
351 351 fast = (m.files() == [pat])
352 352 for r in subset:
353 353 c = repo[r]
354 354 if fast:
355 355 if pat not in c.files():
356 356 continue
357 357 else:
358 358 for f in c.files():
359 359 if m(f):
360 360 break
361 361 else:
362 362 continue
363 363 files = repo.status(c.p1().node(), c.node())[field]
364 364 if fast:
365 365 if pat in files:
366 366 s.append(r)
367 367 continue
368 368 else:
369 369 for f in files:
370 370 if m(f):
371 371 s.append(r)
372 372 continue
373 373 return s
374 374
375 375 def modifies(repo, subset, x):
376 376 pat = getstring(x, _("modifies requires a pattern"))
377 377 return checkstatus(repo, subset, pat, 0)
378 378
379 379 def adds(repo, subset, x):
380 380 pat = getstring(x, _("adds requires a pattern"))
381 381 return checkstatus(repo, subset, pat, 1)
382 382
383 383 def removes(repo, subset, x):
384 384 pat = getstring(x, _("removes requires a pattern"))
385 385 return checkstatus(repo, subset, pat, 2)
386 386
387 387 def merge(repo, subset, x):
388 388 getargs(x, 0, 0, _("merge takes no arguments"))
389 389 cl = repo.changelog
390 390 return [r for r in subset if cl.parentrevs(r)[1] != -1]
391 391
392 392 def closed(repo, subset, x):
393 393 getargs(x, 0, 0, _("closed takes no arguments"))
394 394 return [r for r in subset if repo[r].extra().get('close')]
395 395
396 396 def head(repo, subset, x):
397 397 getargs(x, 0, 0, _("head takes no arguments"))
398 398 hs = set()
399 399 for b, ls in repo.branchmap().iteritems():
400 400 hs.update(repo[h].rev() for h in ls)
401 401 return [r for r in subset if r in hs]
402 402
403 403 def reverse(repo, subset, x):
404 404 l = getset(repo, subset, x)
405 405 l.reverse()
406 406 return l
407 407
408 408 def present(repo, subset, x):
409 409 try:
410 410 return getset(repo, subset, x)
411 411 except error.RepoLookupError:
412 412 return []
413 413
414 414 def sort(repo, subset, x):
415 415 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
416 416 keys = "rev"
417 417 if len(l) == 2:
418 418 keys = getstring(l[1], _("sort spec must be a string"))
419 419
420 420 s = l[0]
421 421 keys = keys.split()
422 422 l = []
423 423 def invert(s):
424 424 return "".join(chr(255 - ord(c)) for c in s)
425 425 for r in getset(repo, subset, s):
426 426 c = repo[r]
427 427 e = []
428 428 for k in keys:
429 429 if k == 'rev':
430 430 e.append(r)
431 431 elif k == '-rev':
432 432 e.append(-r)
433 433 elif k == 'branch':
434 434 e.append(c.branch())
435 435 elif k == '-branch':
436 436 e.append(invert(c.branch()))
437 437 elif k == 'desc':
438 438 e.append(c.description())
439 439 elif k == '-desc':
440 440 e.append(invert(c.description()))
441 441 elif k in 'user author':
442 442 e.append(c.user())
443 443 elif k in '-user -author':
444 444 e.append(invert(c.user()))
445 445 elif k == 'date':
446 446 e.append(c.date()[0])
447 447 elif k == '-date':
448 448 e.append(-c.date()[0])
449 449 else:
450 450 raise error.ParseError(_("unknown sort key %r") % k)
451 451 e.append(r)
452 452 l.append(e)
453 453 l.sort()
454 454 return [e[-1] for e in l]
455 455
456 456 def getall(repo, subset, x):
457 457 getargs(x, 0, 0, _("all takes no arguments"))
458 458 return subset
459 459
460 460 def heads(repo, subset, x):
461 461 s = getset(repo, subset, x)
462 462 ps = set(parents(repo, subset, x))
463 463 return [r for r in s if r not in ps]
464 464
465 465 def roots(repo, subset, x):
466 466 s = getset(repo, subset, x)
467 467 cs = set(children(repo, subset, x))
468 468 return [r for r in s if r not in cs]
469 469
470 470 def outgoing(repo, subset, x):
471 471 import hg # avoid start-up nasties
472 472 l = getargs(x, 0, 1, _("outgoing requires a repository path"))
473 473 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
474 474 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
475 475 dest, branches = hg.parseurl(dest)
476 476 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
477 477 if revs:
478 478 revs = [repo.lookup(rev) for rev in revs]
479 479 other = hg.repository(hg.remoteui(repo, {}), dest)
480 480 repo.ui.pushbuffer()
481 481 o = discovery.findoutgoing(repo, other)
482 482 repo.ui.popbuffer()
483 483 cl = repo.changelog
484 484 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
485 485 return [r for r in subset if r in o]
486 486
487 487 def tag(repo, subset, x):
488 488 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
489 489 cl = repo.changelog
490 490 if args:
491 491 tn = getstring(args[0],
492 492 _('the argument to tag must be a string'))
493 493 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
494 494 else:
495 495 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
496 496 return [r for r in subset if r in s]
497 497
498 498 symbols = {
499 499 "adds": adds,
500 500 "all": getall,
501 501 "ancestor": ancestor,
502 502 "ancestors": ancestors,
503 503 "author": author,
504 504 "branch": branch,
505 505 "children": children,
506 506 "closed": closed,
507 507 "contains": contains,
508 508 "date": date,
509 509 "descendants": descendants,
510 510 "file": hasfile,
511 511 "follow": follow,
512 512 "grep": grep,
513 513 "head": head,
514 514 "heads": heads,
515 515 "keyword": keyword,
516 516 "limit": limit,
517 517 "max": maxrev,
518 518 "min": minrev,
519 519 "merge": merge,
520 520 "modifies": modifies,
521 521 "id": node,
522 522 "outgoing": outgoing,
523 523 "p1": p1,
524 524 "p2": p2,
525 525 "parents": parents,
526 526 "present": present,
527 527 "removes": removes,
528 528 "reverse": reverse,
529 529 "rev": rev,
530 530 "roots": roots,
531 531 "sort": sort,
532 532 "tag": tag,
533 533 "tagged": tag,
534 534 "user": author,
535 535 }
536 536
537 537 methods = {
538 538 "range": rangeset,
539 539 "string": stringset,
540 540 "symbol": symbolset,
541 541 "and": andset,
542 542 "or": orset,
543 543 "not": notset,
544 544 "list": listset,
545 545 "func": func,
546 546 }
547 547
548 548 def optimize(x, small):
549 549 if x == None:
550 550 return 0, x
551 551
552 552 smallbonus = 1
553 553 if small:
554 554 smallbonus = .5
555 555
556 556 op = x[0]
557 557 if op == 'minus':
558 558 return optimize(('and', x[1], ('not', x[2])), small)
559 559 elif op == 'dagrange':
560 560 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
561 561 ('func', ('symbol', 'ancestors'), x[2])), small)
562 562 elif op == 'dagrangepre':
563 563 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
564 564 elif op == 'dagrangepost':
565 565 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
566 566 elif op == 'rangepre':
567 567 return optimize(('range', ('string', '0'), x[1]), small)
568 568 elif op == 'rangepost':
569 569 return optimize(('range', x[1], ('string', 'tip')), small)
570 570 elif op == 'negate':
571 571 return optimize(('string',
572 572 '-' + getstring(x[1], _("can't negate that"))), small)
573 573 elif op in 'string symbol negate':
574 574 return smallbonus, x # single revisions are small
575 575 elif op == 'and' or op == 'dagrange':
576 576 wa, ta = optimize(x[1], True)
577 577 wb, tb = optimize(x[2], True)
578 578 w = min(wa, wb)
579 579 if wa > wb:
580 580 return w, (op, tb, ta)
581 581 return w, (op, ta, tb)
582 582 elif op == 'or':
583 583 wa, ta = optimize(x[1], False)
584 584 wb, tb = optimize(x[2], False)
585 585 if wb < wa:
586 586 wb, wa = wa, wb
587 587 return max(wa, wb), (op, ta, tb)
588 588 elif op == 'not':
589 589 o = optimize(x[1], not small)
590 590 return o[0], (op, o[1])
591 591 elif op == 'group':
592 592 return optimize(x[1], small)
593 593 elif op in 'range list':
594 594 wa, ta = optimize(x[1], small)
595 595 wb, tb = optimize(x[2], small)
596 596 return wa + wb, (op, ta, tb)
597 597 elif op == 'func':
598 598 f = getstring(x[1], _("not a symbol"))
599 599 wa, ta = optimize(x[2], small)
600 600 if f in "grep date user author keyword branch file outgoing":
601 601 w = 10 # slow
602 602 elif f in "modifies adds removes":
603 603 w = 30 # slower
604 604 elif f == "contains":
605 605 w = 100 # very slow
606 606 elif f == "ancestor":
607 607 w = 1 * smallbonus
608 608 elif f == "reverse limit":
609 609 w = 0
610 610 elif f in "sort":
611 611 w = 10 # assume most sorts look at changelog
612 612 else:
613 613 w = 1
614 614 return w + wa, (op, x[1], ta)
615 615 return 1, x
616 616
617 617 parse = parser.parser(tokenize, elements).parse
618 618
619 619 def match(spec):
620 620 if not spec:
621 621 raise error.ParseError(_("empty query"))
622 622 tree = parse(spec)
623 623 weight, tree = optimize(tree, True)
624 624 def mfunc(repo, subset):
625 625 return getset(repo, subset, tree)
626 626 return mfunc
@@ -1,341 +1,358 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 $ hg ci -Aqm0
19 19
20 20 $ echo b > b
21 21 $ hg branch b
22 22 marked working directory as branch b
23 23 $ hg ci -Aqm1
24 24
25 25 $ rm a
26 26 $ hg branch a-b-c-
27 27 marked working directory as branch a-b-c-
28 28 $ hg ci -Aqm2 -u Bob
29 29
30 30 $ hg co 1
31 31 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
32 32 $ hg branch +a+b+c+
33 33 marked working directory as branch +a+b+c+
34 34 $ hg ci -Aqm3
35 35
36 36 $ hg co 2 # interleave
37 37 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
38 38 $ echo bb > b
39 39 $ hg branch -- -a-b-c-
40 40 marked working directory as branch -a-b-c-
41 41 $ hg ci -Aqm4 -d "May 12 2005"
42 42
43 43 $ hg co 3
44 44 2 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 $ hg ci -Aqm"5 bug"
48 48
49 49 $ hg merge 4
50 50 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
51 51 (branch merge, don't forget to commit)
52 52 $ hg branch _a_b_c_
53 53 marked working directory as branch _a_b_c_
54 54 $ hg ci -Aqm"6 issue619"
55 55
56 56 $ hg branch .a.b.c.
57 57 marked working directory as branch .a.b.c.
58 58 $ hg ci -Aqm7
59 59
60 60 $ hg branch all
61 61 marked working directory as branch all
62 62 $ hg ci --close-branch -Aqm8
63 63 abort: can only close branch heads
64 64 [255]
65 65
66 66 $ hg co 4
67 67 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
68 68 $ hg branch é
69 69 marked working directory as branch é
70 70 $ hg ci -Aqm9
71 71
72 72 $ hg tag -r6 1.0
73 73
74 74 $ hg clone --quiet -U -r 7 . ../remote1
75 75 $ hg clone --quiet -U -r 8 . ../remote2
76 76 $ echo "[paths]" >> .hg/hgrc
77 77 $ echo "default = ../remote1" >> .hg/hgrc
78 78
79 79 names that should work without quoting
80 80
81 81 $ try a
82 82 ('symbol', 'a')
83 83 0
84 84 $ try b-a
85 85 ('minus', ('symbol', 'b'), ('symbol', 'a'))
86 86 1
87 87 $ try _a_b_c_
88 88 ('symbol', '_a_b_c_')
89 89 6
90 90 $ try _a_b_c_-a
91 91 ('minus', ('symbol', '_a_b_c_'), ('symbol', 'a'))
92 92 6
93 93 $ try .a.b.c.
94 94 ('symbol', '.a.b.c.')
95 95 7
96 96 $ try .a.b.c.-a
97 97 ('minus', ('symbol', '.a.b.c.'), ('symbol', 'a'))
98 98 7
99 99 $ try -- '-a-b-c-' # complains
100 100 hg: parse error at 7: not a prefix: end
101 101 [255]
102 102 $ log -a-b-c- # succeeds with fallback
103 103 4
104 104 $ try -- -a-b-c--a # complains
105 105 ('minus', ('minus', ('minus', ('negate', ('symbol', 'a')), ('symbol', 'b')), ('symbol', 'c')), ('negate', ('symbol', 'a')))
106 106 abort: unknown revision '-a'!
107 107 [255]
108 108 $ try é
109 109 ('symbol', '\xc3\xa9')
110 110 9
111 111
112 112 quoting needed
113 113
114 114 $ try '"-a-b-c-"-a'
115 115 ('minus', ('string', '-a-b-c-'), ('symbol', 'a'))
116 116 4
117 117
118 118 $ log '1 or 2'
119 119 1
120 120 2
121 121 $ log '1|2'
122 122 1
123 123 2
124 124 $ log '1 and 2'
125 125 $ log '1&2'
126 126 $ try '1&2|3' # precedence - and is higher
127 127 ('or', ('and', ('symbol', '1'), ('symbol', '2')), ('symbol', '3'))
128 128 3
129 129 $ try '1|2&3'
130 130 ('or', ('symbol', '1'), ('and', ('symbol', '2'), ('symbol', '3')))
131 131 1
132 132 $ try '1&2&3' # associativity
133 133 ('and', ('and', ('symbol', '1'), ('symbol', '2')), ('symbol', '3'))
134 134 $ try '1|(2|3)'
135 135 ('or', ('symbol', '1'), ('group', ('or', ('symbol', '2'), ('symbol', '3'))))
136 136 1
137 137 2
138 138 3
139 139 $ log '1.0' # tag
140 140 6
141 141 $ log 'a' # branch
142 142 0
143 143 $ log '2785f51ee'
144 144 0
145 145 $ log 'date(2005)'
146 146 4
147 147 $ log 'date(this is a test)'
148 148 hg: parse error at 10: unexpected token: symbol
149 149 [255]
150 150 $ log 'date()'
151 151 hg: parse error: date requires a string
152 152 [255]
153 153 $ log 'date'
154 154 hg: parse error: can't use date here
155 155 [255]
156 156 $ log 'date('
157 157 hg: parse error at 5: not a prefix: end
158 158 [255]
159 159 $ log 'date(tip)'
160 160 abort: invalid date: 'tip'
161 161 [255]
162 162 $ log '"date"'
163 163 abort: unknown revision 'date'!
164 164 [255]
165 165 $ log 'date(2005) and 1::'
166 166 4
167 167
168 168 $ log 'ancestor(1)'
169 169 hg: parse error: ancestor requires two arguments
170 170 [255]
171 171 $ log 'ancestor(4,5)'
172 172 1
173 173 $ log 'ancestor(4,5) and 4'
174 174 $ log 'ancestors(5)'
175 175 0
176 176 1
177 177 3
178 178 5
179 179 $ log 'author(bob)'
180 180 2
181 181 $ log 'branch(é)'
182 182 8
183 183 9
184 184 $ log 'children(ancestor(4,5))'
185 185 2
186 186 3
187 187 $ log 'closed()'
188 188 $ log 'contains(a)'
189 189 0
190 190 1
191 191 3
192 192 5
193 193 $ log 'descendants(2 or 3)'
194 194 2
195 195 3
196 196 4
197 197 5
198 198 6
199 199 7
200 200 8
201 201 9
202 202 $ log 'file(b)'
203 203 1
204 204 4
205 205 $ log 'follow()'
206 206 0
207 207 1
208 208 2
209 209 4
210 210 8
211 211 9
212 212 $ log 'grep("issue\d+")'
213 213 6
214 214 $ try 'grep("(")' # invalid regular expression
215 215 ('func', ('symbol', 'grep'), ('string', '('))
216 216 hg: parse error: invalid match pattern: unbalanced parenthesis
217 217 [255]
218 218 $ try 'grep("\bissue\d+")'
219 219 ('func', ('symbol', 'grep'), ('string', '\x08issue\\d+'))
220 220 $ try 'grep(r"\bissue\d+")'
221 221 ('func', ('symbol', 'grep'), ('string', '\\bissue\\d+'))
222 222 6
223 223 $ try 'grep(r"\")'
224 224 hg: parse error at 7: unterminated string
225 225 [255]
226 226 $ log 'head()'
227 227 0
228 228 1
229 229 2
230 230 3
231 231 4
232 232 5
233 233 6
234 234 7
235 235 9
236 236 $ log 'heads(6::)'
237 237 7
238 238 $ log 'keyword(issue)'
239 239 6
240 240 $ log 'limit(head(), 1)'
241 241 0
242 242 $ log 'max(contains(a))'
243 243 5
244 244 $ log 'min(contains(a))'
245 245 0
246 246 $ log 'merge()'
247 247 6
248 248 $ log 'modifies(b)'
249 249 4
250 250 $ log 'id(5)'
251 251 2
252 252 $ log 'outgoing()'
253 253 8
254 254 9
255 255 $ log 'outgoing("../remote1")'
256 256 8
257 257 9
258 258 $ log 'outgoing("../remote2")'
259 259 3
260 260 5
261 261 6
262 262 7
263 263 9
264 264 $ log 'p1(merge())'
265 265 5
266 266 $ log 'p2(merge())'
267 267 4
268 268 $ log 'parents(merge())'
269 269 4
270 270 5
271 271 $ log 'removes(a)'
272 272 2
273 273 6
274 274 $ log 'roots(all())'
275 275 0
276 276 $ log 'reverse(2 or 3 or 4 or 5)'
277 277 5
278 278 4
279 279 3
280 280 2
281 281 $ log 'rev(5)'
282 282 5
283 283 $ log 'sort(limit(reverse(all()), 3))'
284 284 7
285 285 8
286 286 9
287 287 $ log 'sort(2 or 3 or 4 or 5, date)'
288 288 2
289 289 3
290 290 5
291 291 4
292 292 $ log 'tagged()'
293 293 6
294 294 $ log 'tag()'
295 295 6
296 296 $ log 'tag(1.0)'
297 297 6
298 298 $ log 'tag(tip)'
299 299 9
300 300 $ log 'user(bob)'
301 301 2
302 302
303 303 $ log '4::8'
304 304 4
305 305 8
306 306 $ log '4:8'
307 307 4
308 308 5
309 309 6
310 310 7
311 311 8
312 312
313 313 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
314 314 4
315 315 2
316 316 5
317 317
318 318 $ log 'not 0 and 0:2'
319 319 1
320 320 2
321 321 $ log 'not 1 and 0:2'
322 322 0
323 323 2
324 324 $ log 'not 2 and 0:2'
325 325 0
326 326 1
327 327 $ log '(1 and 2)::'
328 328 $ log '(1 and 2):'
329 329 $ log '(1 and 2):3'
330 330 $ log 'sort(head(), -rev)'
331 331 9
332 332 7
333 333 6
334 334 5
335 335 4
336 336 3
337 337 2
338 338 1
339 339 0
340 340 $ log '4::8 - 8'
341 341 4
342
343 issue2437
344
345 $ log '3 and p1(5)'
346 3
347 $ log '4 and p2(6)'
348 4
349 $ log '1 and parents(:2)'
350 1
351 $ log '2 and children(1:)'
352 2
353 $ log 'roots(all()) or roots(all())'
354 0
355 $ log 'heads(branch(é)) or heads(branch(é))'
356 9
357 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(é)))'
358 4
General Comments 0
You need to be logged in to leave comments. Login now