Show More
@@ -75,27 +75,49 b' def _walkrevtree(pfunc, revs, startdepth' | |||||
75 | if prev != node.nullrev: |
|
75 | if prev != node.nullrev: | |
76 | heapq.heappush(pendingheap, (heapsign * prev, pdepth)) |
|
76 | heapq.heappush(pendingheap, (heapsign * prev, pdepth)) | |
77 |
|
77 | |||
78 | def _genrevancestors(repo, revs, followfirst, startdepth, stopdepth): |
|
78 | def _genrevancestors(repo, revs, followfirst, startdepth, stopdepth, cutfunc): | |
79 | if followfirst: |
|
79 | if followfirst: | |
80 | cut = 1 |
|
80 | cut = 1 | |
81 | else: |
|
81 | else: | |
82 | cut = None |
|
82 | cut = None | |
83 | cl = repo.changelog |
|
83 | cl = repo.changelog | |
84 | def pfunc(rev): |
|
84 | def plainpfunc(rev): | |
85 | try: |
|
85 | try: | |
86 | return cl.parentrevs(rev)[:cut] |
|
86 | return cl.parentrevs(rev)[:cut] | |
87 | except error.WdirUnsupported: |
|
87 | except error.WdirUnsupported: | |
88 | return (pctx.rev() for pctx in repo[rev].parents()[:cut]) |
|
88 | return (pctx.rev() for pctx in repo[rev].parents()[:cut]) | |
|
89 | if cutfunc is None: | |||
|
90 | pfunc = plainpfunc | |||
|
91 | else: | |||
|
92 | pfunc = lambda rev: [r for r in plainpfunc(rev) if not cutfunc(r)] | |||
|
93 | revs = revs.filter(lambda rev: not cutfunc(rev)) | |||
89 | return _walkrevtree(pfunc, revs, startdepth, stopdepth, reverse=True) |
|
94 | return _walkrevtree(pfunc, revs, startdepth, stopdepth, reverse=True) | |
90 |
|
95 | |||
91 |
def revancestors(repo, revs, followfirst, startdepth=None, |
|
96 | def revancestors(repo, revs, followfirst=False, startdepth=None, | |
|
97 | stopdepth=None, cutfunc=None): | |||
92 | """Like revlog.ancestors(), but supports additional options, includes |
|
98 | """Like revlog.ancestors(), but supports additional options, includes | |
93 | the given revs themselves, and returns a smartset |
|
99 | the given revs themselves, and returns a smartset | |
94 |
|
100 | |||
95 | Scan ends at the stopdepth (exlusive) if specified. Revisions found |
|
101 | Scan ends at the stopdepth (exlusive) if specified. Revisions found | |
96 | earlier than the startdepth are omitted. |
|
102 | earlier than the startdepth are omitted. | |
|
103 | ||||
|
104 | If cutfunc is provided, it will be used to cut the traversal of the DAG. | |||
|
105 | When cutfunc(X) returns True, the DAG traversal stops - revision X and | |||
|
106 | X's ancestors in the traversal path will be skipped. This could be an | |||
|
107 | optimization sometimes. | |||
|
108 | ||||
|
109 | Note: if Y is an ancestor of X, cutfunc(X) returning True does not | |||
|
110 | necessarily mean Y will also be cut. Usually cutfunc(Y) also wants to | |||
|
111 | return True in this case. For example, | |||
|
112 | ||||
|
113 | D # revancestors(repo, D, cutfunc=lambda rev: rev == B) | |||
|
114 | |\ # will include "A", because the path D -> C -> A was not cut. | |||
|
115 | B C # If "B" gets cut, "A" might want to be cut too. | |||
|
116 | |/ | |||
|
117 | A | |||
97 | """ |
|
118 | """ | |
98 |
gen = _genrevancestors(repo, revs, followfirst, startdepth, stopdepth |
|
119 | gen = _genrevancestors(repo, revs, followfirst, startdepth, stopdepth, | |
|
120 | cutfunc) | |||
99 | return generatorset(gen, iterasc=False) |
|
121 | return generatorset(gen, iterasc=False) | |
100 |
|
122 | |||
101 | def _genrevdescendants(repo, revs, followfirst): |
|
123 | def _genrevdescendants(repo, revs, followfirst): |
@@ -1577,6 +1577,37 b' def _notpublic(repo, subset, x):' | |||||
1577 | getargs(x, 0, 0, "_notpublic takes no arguments") |
|
1577 | getargs(x, 0, 0, "_notpublic takes no arguments") | |
1578 | return _phase(repo, subset, phases.draft, phases.secret) |
|
1578 | return _phase(repo, subset, phases.draft, phases.secret) | |
1579 |
|
1579 | |||
|
1580 | # for internal use | |||
|
1581 | @predicate('_phaseandancestors(phasename, set)', safe=True) | |||
|
1582 | def _phaseandancestors(repo, subset, x): | |||
|
1583 | # equivalent to (phasename() & ancestors(set)) but more efficient | |||
|
1584 | # phasename could be one of 'draft', 'secret', or '_notpublic' | |||
|
1585 | args = getargs(x, 2, 2, "_phaseandancestors requires two arguments") | |||
|
1586 | phasename = getsymbol(args[0]) | |||
|
1587 | s = getset(repo, fullreposet(repo), args[1]) | |||
|
1588 | ||||
|
1589 | draft = phases.draft | |||
|
1590 | secret = phases.secret | |||
|
1591 | phasenamemap = { | |||
|
1592 | '_notpublic': draft, | |||
|
1593 | 'draft': draft, # follow secret's ancestors | |||
|
1594 | 'secret': secret, | |||
|
1595 | } | |||
|
1596 | if phasename not in phasenamemap: | |||
|
1597 | raise error.ParseError('%r is not a valid phasename' % phasename) | |||
|
1598 | ||||
|
1599 | minimalphase = phasenamemap[phasename] | |||
|
1600 | getphase = repo._phasecache.phase | |||
|
1601 | ||||
|
1602 | def cutfunc(rev): | |||
|
1603 | return getphase(repo, rev) < minimalphase | |||
|
1604 | ||||
|
1605 | revs = dagop.revancestors(repo, s, cutfunc=cutfunc) | |||
|
1606 | ||||
|
1607 | if phasename == 'draft': # need to remove secret changesets | |||
|
1608 | revs = revs.filter(lambda r: getphase(repo, r) == draft) | |||
|
1609 | return subset & revs | |||
|
1610 | ||||
1580 | @predicate('public()', safe=True) |
|
1611 | @predicate('public()', safe=True) | |
1581 | def public(repo, subset, x): |
|
1612 | def public(repo, subset, x): | |
1582 | """Changeset in public phase.""" |
|
1613 | """Changeset in public phase.""" |
@@ -369,6 +369,11 b' def _optimize(x, small):' | |||||
369 | wb, tb = _optimize(x[2], True) |
|
369 | wb, tb = _optimize(x[2], True) | |
370 | w = min(wa, wb) |
|
370 | w = min(wa, wb) | |
371 |
|
371 | |||
|
372 | # (draft/secret/_notpublic() & ::x) have a fast path | |||
|
373 | m = _match('_() & ancestors(_)', ('and', ta, tb)) | |||
|
374 | if m and getsymbol(m[1]) in {'draft', 'secret', '_notpublic'}: | |||
|
375 | return w, _build('_phaseandancestors(_, _)', m[1], m[2]) | |||
|
376 | ||||
372 | # (::x and not ::y)/(not ::y and ::x) have a fast path |
|
377 | # (::x and not ::y)/(not ::y and ::x) have a fast path | |
373 | m = _matchonly(ta, tb) or _matchonly(tb, ta) |
|
378 | m = _matchonly(ta, tb) or _matchonly(tb, ta) | |
374 | if m: |
|
379 | if m: |
@@ -4321,3 +4321,147 b' Test obsstore related revsets' | |||||
4321 |
|
4321 | |||
4322 | $ hg log -r 'successors(B+A)-contentdivergent()-obsolete()' -T '{desc}\n' |
|
4322 | $ hg log -r 'successors(B+A)-contentdivergent()-obsolete()' -T '{desc}\n' | |
4323 | Z |
|
4323 | Z | |
|
4324 | ||||
|
4325 | Test `draft() & ::x` optimization | |||
|
4326 | ||||
|
4327 | $ hg init $TESTTMP/repo2 | |||
|
4328 | $ cd $TESTTMP/repo2 | |||
|
4329 | $ hg debugdrawdag <<'EOS' | |||
|
4330 | > P5 S1 | |||
|
4331 | > | | | |||
|
4332 | > S2 | D3 | |||
|
4333 | > \|/ | |||
|
4334 | > P4 | |||
|
4335 | > | | |||
|
4336 | > P3 D2 | |||
|
4337 | > | | | |||
|
4338 | > P2 D1 | |||
|
4339 | > |/ | |||
|
4340 | > P1 | |||
|
4341 | > | | |||
|
4342 | > P0 | |||
|
4343 | > EOS | |||
|
4344 | $ hg phase --public -r P5 | |||
|
4345 | $ hg phase --force --secret -r S1+S2 | |||
|
4346 | $ hg log -G -T '{rev} {desc} {phase}' -r 'sort(all(), topo, topo.firstbranch=P5)' | |||
|
4347 | o 8 P5 public | |||
|
4348 | | | |||
|
4349 | | o 10 S1 secret | |||
|
4350 | | | | |||
|
4351 | | o 7 D3 draft | |||
|
4352 | |/ | |||
|
4353 | | o 9 S2 secret | |||
|
4354 | |/ | |||
|
4355 | o 6 P4 public | |||
|
4356 | | | |||
|
4357 | o 5 P3 public | |||
|
4358 | | | |||
|
4359 | o 3 P2 public | |||
|
4360 | | | |||
|
4361 | | o 4 D2 draft | |||
|
4362 | | | | |||
|
4363 | | o 2 D1 draft | |||
|
4364 | |/ | |||
|
4365 | o 1 P1 public | |||
|
4366 | | | |||
|
4367 | o 0 P0 public | |||
|
4368 | ||||
|
4369 | $ hg debugrevspec --verify -p analyzed -p optimized 'draft() & ::(((S1+D1+P5)-D3)+S2)' | |||
|
4370 | * analyzed: | |||
|
4371 | (and | |||
|
4372 | (func | |||
|
4373 | ('symbol', 'draft') | |||
|
4374 | None) | |||
|
4375 | (func | |||
|
4376 | ('symbol', 'ancestors') | |||
|
4377 | (or | |||
|
4378 | (list | |||
|
4379 | (and | |||
|
4380 | (or | |||
|
4381 | (list | |||
|
4382 | ('symbol', 'S1') | |||
|
4383 | ('symbol', 'D1') | |||
|
4384 | ('symbol', 'P5'))) | |||
|
4385 | (not | |||
|
4386 | ('symbol', 'D3'))) | |||
|
4387 | ('symbol', 'S2'))))) | |||
|
4388 | * optimized: | |||
|
4389 | (func | |||
|
4390 | ('symbol', '_phaseandancestors') | |||
|
4391 | (list | |||
|
4392 | ('symbol', 'draft') | |||
|
4393 | (or | |||
|
4394 | (list | |||
|
4395 | (difference | |||
|
4396 | (func | |||
|
4397 | ('symbol', '_list') | |||
|
4398 | ('string', 'S1\x00D1\x00P5')) | |||
|
4399 | ('symbol', 'D3')) | |||
|
4400 | ('symbol', 'S2'))))) | |||
|
4401 | $ hg debugrevspec --verify -p analyzed -p optimized 'secret() & ::9' | |||
|
4402 | * analyzed: | |||
|
4403 | (and | |||
|
4404 | (func | |||
|
4405 | ('symbol', 'secret') | |||
|
4406 | None) | |||
|
4407 | (func | |||
|
4408 | ('symbol', 'ancestors') | |||
|
4409 | ('symbol', '9'))) | |||
|
4410 | * optimized: | |||
|
4411 | (func | |||
|
4412 | ('symbol', '_phaseandancestors') | |||
|
4413 | (list | |||
|
4414 | ('symbol', 'secret') | |||
|
4415 | ('symbol', '9'))) | |||
|
4416 | $ hg debugrevspec --verify -p analyzed -p optimized '7 & ( (not public()) & ::(tag()) )' | |||
|
4417 | * analyzed: | |||
|
4418 | (and | |||
|
4419 | ('symbol', '7') | |||
|
4420 | (and | |||
|
4421 | (not | |||
|
4422 | (func | |||
|
4423 | ('symbol', 'public') | |||
|
4424 | None)) | |||
|
4425 | (func | |||
|
4426 | ('symbol', 'ancestors') | |||
|
4427 | (func | |||
|
4428 | ('symbol', 'tag') | |||
|
4429 | None)))) | |||
|
4430 | * optimized: | |||
|
4431 | (and | |||
|
4432 | ('symbol', '7') | |||
|
4433 | (func | |||
|
4434 | ('symbol', '_phaseandancestors') | |||
|
4435 | (list | |||
|
4436 | ('symbol', '_notpublic') | |||
|
4437 | (func | |||
|
4438 | ('symbol', 'tag') | |||
|
4439 | None)))) | |||
|
4440 | $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, 1)' | |||
|
4441 | * optimized: | |||
|
4442 | (and | |||
|
4443 | (func | |||
|
4444 | ('symbol', '_notpublic') | |||
|
4445 | None) | |||
|
4446 | (func | |||
|
4447 | ('symbol', 'ancestors') | |||
|
4448 | (list | |||
|
4449 | (func | |||
|
4450 | ('symbol', '_list') | |||
|
4451 | ('string', 'S1\x00D2\x00P5')) | |||
|
4452 | ('symbol', '1')))) | |||
|
4453 | $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, depth=1)' | |||
|
4454 | * optimized: | |||
|
4455 | (and | |||
|
4456 | (func | |||
|
4457 | ('symbol', '_notpublic') | |||
|
4458 | None) | |||
|
4459 | (func | |||
|
4460 | ('symbol', 'ancestors') | |||
|
4461 | (list | |||
|
4462 | (func | |||
|
4463 | ('symbol', '_list') | |||
|
4464 | ('string', 'S1\x00D2\x00P5')) | |||
|
4465 | (keyvalue | |||
|
4466 | ('symbol', 'depth') | |||
|
4467 | ('symbol', '1'))))) |
General Comments 0
You need to be logged in to leave comments.
Login now