Mercurial > public > mercurial-scm > hg
comparison mercurial/phases.py @ 45789:09735cde6275
phases: allow registration and boundary advancement with revision sets
The core internals either use revision sets already or can trivially use
them. Use the new interface in cg1unpacker.apply to avoid materializing
the list of all new nodes as it is normally just a revision range. This
avoids about 67 Bytes / changeset on AMD64 in peak RSS.
Differential Revision: https://phab.mercurial-scm.org/D9232
author | Joerg Sonnenberger <joerg@bec.de> |
---|---|
date | Mon, 19 Oct 2020 02:54:12 +0200 |
parents | 29a259be6424 |
children | 5d65e04b6a80 |
comparison
equal
deleted
inserted
replaced
45788:a5206e71c536 | 45789:09735cde6275 |
---|---|
508 self.dirty = True | 508 self.dirty = True |
509 | 509 |
510 tr.addfilegenerator(b'phase', (b'phaseroots',), self._write) | 510 tr.addfilegenerator(b'phase', (b'phaseroots',), self._write) |
511 tr.hookargs[b'phases_moved'] = b'1' | 511 tr.hookargs[b'phases_moved'] = b'1' |
512 | 512 |
513 def registernew(self, repo, tr, targetphase, nodes): | 513 def registernew(self, repo, tr, targetphase, nodes, revs=None): |
514 if revs is None: | |
515 revs = [] | |
514 repo = repo.unfiltered() | 516 repo = repo.unfiltered() |
515 self._retractboundary(repo, tr, targetphase, nodes) | 517 self._retractboundary(repo, tr, targetphase, nodes, revs=revs) |
516 if tr is not None and b'phases' in tr.changes: | 518 if tr is not None and b'phases' in tr.changes: |
517 phasetracking = tr.changes[b'phases'] | 519 phasetracking = tr.changes[b'phases'] |
518 torev = repo.changelog.rev | 520 torev = repo.changelog.rev |
519 phase = self.phase | 521 phase = self.phase |
520 revs = [torev(node) for node in nodes] | 522 revs = [torev(node) for node in nodes] + sorted(revs) |
521 revs.sort() | 523 revs.sort() |
522 for rev in revs: | 524 for rev in revs: |
523 revphase = phase(repo, rev) | 525 revphase = phase(repo, rev) |
524 _trackphasechange(phasetracking, rev, None, revphase) | 526 _trackphasechange(phasetracking, rev, None, revphase) |
525 repo.invalidatevolatilesets() | 527 repo.invalidatevolatilesets() |
526 | 528 |
527 def advanceboundary(self, repo, tr, targetphase, nodes, dryrun=None): | 529 def advanceboundary( |
530 self, repo, tr, targetphase, nodes, revs=None, dryrun=None | |
531 ): | |
528 """Set all 'nodes' to phase 'targetphase' | 532 """Set all 'nodes' to phase 'targetphase' |
529 | 533 |
530 Nodes with a phase lower than 'targetphase' are not affected. | 534 Nodes with a phase lower than 'targetphase' are not affected. |
531 | 535 |
532 If dryrun is True, no actions will be performed | 536 If dryrun is True, no actions will be performed |
533 | 537 |
534 Returns a set of revs whose phase is changed or should be changed | 538 Returns a set of revs whose phase is changed or should be changed |
535 """ | 539 """ |
536 # Be careful to preserve shallow-copied values: do not update | 540 # Be careful to preserve shallow-copied values: do not update |
537 # phaseroots values, replace them. | 541 # phaseroots values, replace them. |
542 if revs is None: | |
543 revs = [] | |
538 if tr is None: | 544 if tr is None: |
539 phasetracking = None | 545 phasetracking = None |
540 else: | 546 else: |
541 phasetracking = tr.changes.get(b'phases') | 547 phasetracking = tr.changes.get(b'phases') |
542 | 548 |
543 repo = repo.unfiltered() | 549 repo = repo.unfiltered() |
550 revs = [repo[n].rev() for n in nodes] + [r for r in revs] | |
544 | 551 |
545 changes = set() # set of revisions to be changed | 552 changes = set() # set of revisions to be changed |
546 delroots = [] # set of root deleted by this path | 553 delroots = [] # set of root deleted by this path |
547 for phase in (phase for phase in allphases if phase > targetphase): | 554 for phase in (phase for phase in allphases if phase > targetphase): |
548 # filter nodes that are not in a compatible phase already | 555 # filter nodes that are not in a compatible phase already |
549 nodes = [ | 556 revs = [rev for rev in revs if self.phase(repo, rev) >= phase] |
550 n for n in nodes if self.phase(repo, repo[n].rev()) >= phase | 557 if not revs: |
551 ] | |
552 if not nodes: | |
553 break # no roots to move anymore | 558 break # no roots to move anymore |
554 | 559 |
555 olds = self.phaseroots[phase] | 560 olds = self.phaseroots[phase] |
556 | 561 |
557 affected = repo.revs(b'%ln::%ln', olds, nodes) | 562 affected = repo.revs(b'%ln::%ld', olds, revs) |
558 changes.update(affected) | 563 changes.update(affected) |
559 if dryrun: | 564 if dryrun: |
560 continue | 565 continue |
561 for r in affected: | 566 for r in affected: |
562 _trackphasechange( | 567 _trackphasechange( |
609 revs = affected | 614 revs = affected |
610 for r in sorted(revs): | 615 for r in sorted(revs): |
611 _trackphasechange(phasetracking, r, phase, targetphase) | 616 _trackphasechange(phasetracking, r, phase, targetphase) |
612 repo.invalidatevolatilesets() | 617 repo.invalidatevolatilesets() |
613 | 618 |
614 def _retractboundary(self, repo, tr, targetphase, nodes): | 619 def _retractboundary(self, repo, tr, targetphase, nodes, revs=None): |
615 # Be careful to preserve shallow-copied values: do not update | 620 # Be careful to preserve shallow-copied values: do not update |
616 # phaseroots values, replace them. | 621 # phaseroots values, replace them. |
622 if revs is None: | |
623 revs = [] | |
617 if targetphase in (archived, internal) and not supportinternal(repo): | 624 if targetphase in (archived, internal) and not supportinternal(repo): |
618 name = phasenames[targetphase] | 625 name = phasenames[targetphase] |
619 msg = b'this repository does not support the %s phase' % name | 626 msg = b'this repository does not support the %s phase' % name |
620 raise error.ProgrammingError(msg) | 627 raise error.ProgrammingError(msg) |
621 | 628 |
622 repo = repo.unfiltered() | 629 repo = repo.unfiltered() |
623 torev = repo.changelog.rev | 630 torev = repo.changelog.rev |
624 tonode = repo.changelog.node | 631 tonode = repo.changelog.node |
625 currentroots = {torev(node) for node in self.phaseroots[targetphase]} | 632 currentroots = {torev(node) for node in self.phaseroots[targetphase]} |
626 finalroots = oldroots = set(currentroots) | 633 finalroots = oldroots = set(currentroots) |
627 newroots = [torev(node) for node in nodes] | 634 newroots = [torev(node) for node in nodes] + [r for r in revs] |
628 newroots = [ | 635 newroots = [ |
629 rev for rev in newroots if self.phase(repo, rev) < targetphase | 636 rev for rev in newroots if self.phase(repo, rev) < targetphase |
630 ] | 637 ] |
631 | 638 |
632 if newroots: | 639 if newroots: |
677 # "destroyed" function to phasecache or a proper cache key mechanism | 684 # "destroyed" function to phasecache or a proper cache key mechanism |
678 # (see branchmap one) | 685 # (see branchmap one) |
679 self.invalidate() | 686 self.invalidate() |
680 | 687 |
681 | 688 |
682 def advanceboundary(repo, tr, targetphase, nodes, dryrun=None): | 689 def advanceboundary(repo, tr, targetphase, nodes, revs=None, dryrun=None): |
683 """Add nodes to a phase changing other nodes phases if necessary. | 690 """Add nodes to a phase changing other nodes phases if necessary. |
684 | 691 |
685 This function move boundary *forward* this means that all nodes | 692 This function move boundary *forward* this means that all nodes |
686 are set in the target phase or kept in a *lower* phase. | 693 are set in the target phase or kept in a *lower* phase. |
687 | 694 |
689 | 696 |
690 If dryrun is True, no actions will be performed | 697 If dryrun is True, no actions will be performed |
691 | 698 |
692 Returns a set of revs whose phase is changed or should be changed | 699 Returns a set of revs whose phase is changed or should be changed |
693 """ | 700 """ |
701 if revs is None: | |
702 revs = [] | |
694 phcache = repo._phasecache.copy() | 703 phcache = repo._phasecache.copy() |
695 changes = phcache.advanceboundary( | 704 changes = phcache.advanceboundary( |
696 repo, tr, targetphase, nodes, dryrun=dryrun | 705 repo, tr, targetphase, nodes, revs=revs, dryrun=dryrun |
697 ) | 706 ) |
698 if not dryrun: | 707 if not dryrun: |
699 repo._phasecache.replace(phcache) | 708 repo._phasecache.replace(phcache) |
700 return changes | 709 return changes |
701 | 710 |
711 phcache = repo._phasecache.copy() | 720 phcache = repo._phasecache.copy() |
712 phcache.retractboundary(repo, tr, targetphase, nodes) | 721 phcache.retractboundary(repo, tr, targetphase, nodes) |
713 repo._phasecache.replace(phcache) | 722 repo._phasecache.replace(phcache) |
714 | 723 |
715 | 724 |
716 def registernew(repo, tr, targetphase, nodes): | 725 def registernew(repo, tr, targetphase, nodes, revs=None): |
717 """register a new revision and its phase | 726 """register a new revision and its phase |
718 | 727 |
719 Code adding revisions to the repository should use this function to | 728 Code adding revisions to the repository should use this function to |
720 set new changeset in their target phase (or higher). | 729 set new changeset in their target phase (or higher). |
721 """ | 730 """ |
731 if revs is None: | |
732 revs = [] | |
722 phcache = repo._phasecache.copy() | 733 phcache = repo._phasecache.copy() |
723 phcache.registernew(repo, tr, targetphase, nodes) | 734 phcache.registernew(repo, tr, targetphase, nodes, revs=revs) |
724 repo._phasecache.replace(phcache) | 735 repo._phasecache.replace(phcache) |
725 | 736 |
726 | 737 |
727 def listphases(repo): | 738 def listphases(repo): |
728 """List phases root for serialization over pushkey""" | 739 """List phases root for serialization over pushkey""" |