##// END OF EJS Templates
phases: keep internal state as rev-num instead of node-id...
marmoute -
r52299:f8bf1a8e default
parent child Browse files
Show More
@@ -4073,7 +4073,7 b' def mqphasedefaults(repo, roots):'
4073 else:
4073 else:
4074 mqphase = phases.draft
4074 mqphase = phases.draft
4075 qbase = repo[repo.mq.applied[0].node]
4075 qbase = repo[repo.mq.applied[0].node]
4076 roots[mqphase].add(qbase.node())
4076 roots[mqphase].add(qbase.rev())
4077 return roots
4077 return roots
4078
4078
4079
4079
@@ -58,7 +58,7 b' class index:'
58 def get_rev(self, value: bytes) -> Optional[int]: ...
58 def get_rev(self, value: bytes) -> Optional[int]: ...
59 def has_node(self, value: Union[int, bytes]) -> bool: ...
59 def has_node(self, value: Union[int, bytes]) -> bool: ...
60 def rev(self, node: bytes) -> int: ...
60 def rev(self, node: bytes) -> int: ...
61 def computephasesmapsets(self, root: Dict[int, Set[bytes]]) -> Tuple[int, Dict[int, Set[bytes]]]: ...
61 def computephasesmapsets(self, root: Dict[int, Set[int]]) -> Tuple[int, Dict[int, Set[bytes]]]: ...
62 def reachableroots2(self, minroot: int, heads: List[int], roots: List[int], includepath: bool) -> List[int]: ...
62 def reachableroots2(self, minroot: int, heads: List[int], roots: List[int], includepath: bool) -> List[int]: ...
63 def headrevs(self, filteredrevs: Optional[List[int]]) -> List[int]: ...
63 def headrevs(self, filteredrevs: Optional[List[int]]) -> List[int]: ...
64 def headrevsfiltered(self, filteredrevs: Optional[List[int]]) -> List[int]: ...
64 def headrevsfiltered(self, filteredrevs: Optional[List[int]]) -> List[int]: ...
@@ -1081,7 +1081,6 b' static int add_roots_get_min(indexObject'
1081 PyObject *item;
1081 PyObject *item;
1082 PyObject *iterator;
1082 PyObject *iterator;
1083 int rev, minrev = -1;
1083 int rev, minrev = -1;
1084 char *node;
1085
1084
1086 if (!PySet_Check(roots)) {
1085 if (!PySet_Check(roots)) {
1087 PyErr_SetString(PyExc_TypeError,
1086 PyErr_SetString(PyExc_TypeError,
@@ -1092,9 +1091,10 b' static int add_roots_get_min(indexObject'
1092 if (iterator == NULL)
1091 if (iterator == NULL)
1093 return -2;
1092 return -2;
1094 while ((item = PyIter_Next(iterator))) {
1093 while ((item = PyIter_Next(iterator))) {
1095 if (node_check(self->nodelen, item, &node) == -1)
1094 rev = (int)PyLong_AsLong(item);
1095 if (rev == -1 && PyErr_Occurred()) {
1096 goto failed;
1096 goto failed;
1097 rev = index_find_node(self, node);
1097 }
1098 /* null is implicitly public, so negative is invalid */
1098 /* null is implicitly public, so negative is invalid */
1099 if (rev < 0 || rev >= len)
1099 if (rev < 0 || rev >= len)
1100 goto failed;
1100 goto failed;
@@ -133,7 +133,7 b' from . import ('
133 util,
133 util,
134 )
134 )
135
135
136 Phaseroots = Dict[int, Set[bytes]]
136 Phaseroots = Dict[int, Set[int]]
137
137
138 if typing.TYPE_CHECKING:
138 if typing.TYPE_CHECKING:
139 from . import (
139 from . import (
@@ -210,7 +210,7 b' def _readroots('
210 repo = repo.unfiltered()
210 repo = repo.unfiltered()
211 dirty = False
211 dirty = False
212 roots = {i: set() for i in allphases}
212 roots = {i: set() for i in allphases}
213 has_node = repo.changelog.index.has_node
213 to_rev = repo.changelog.index.get_rev
214 unknown_msg = b'removing unknown node %s from %i-phase boundary\n'
214 unknown_msg = b'removing unknown node %s from %i-phase boundary\n'
215 try:
215 try:
216 f, pending = txnutil.trypending(repo.root, repo.svfs, b'phaseroots')
216 f, pending = txnutil.trypending(repo.root, repo.svfs, b'phaseroots')
@@ -219,11 +219,12 b' def _readroots('
219 str_phase, hex_node = line.split()
219 str_phase, hex_node = line.split()
220 phase = int(str_phase)
220 phase = int(str_phase)
221 node = bin(hex_node)
221 node = bin(hex_node)
222 if not has_node(node):
222 rev = to_rev(node)
223 if rev is None:
223 repo.ui.debug(unknown_msg % (short(hex_node), phase))
224 repo.ui.debug(unknown_msg % (short(hex_node), phase))
224 dirty = True
225 dirty = True
225 else:
226 else:
226 roots[phase].add(node)
227 roots[phase].add(rev)
227 finally:
228 finally:
228 f.close()
229 f.close()
229 except FileNotFoundError:
230 except FileNotFoundError:
@@ -391,7 +392,7 b' class phasecache:'
391
392
392 def nonpublicphaseroots(
393 def nonpublicphaseroots(
393 self, repo: "localrepo.localrepository"
394 self, repo: "localrepo.localrepository"
394 ) -> Set[bytes]:
395 ) -> Set[int]:
395 """returns the roots of all non-public phases
396 """returns the roots of all non-public phases
396
397
397 The roots are not minimized, so if the secret revisions are
398 The roots are not minimized, so if the secret revisions are
@@ -499,7 +500,7 b' class phasecache:'
499 self._phasesets = {phase: set() for phase in allphases}
500 self._phasesets = {phase: set() for phase in allphases}
500 lowerroots = set()
501 lowerroots = set()
501 for phase in reversed(trackedphases):
502 for phase in reversed(trackedphases):
502 roots = pycompat.maplist(cl.rev, self._phaseroots[phase])
503 roots = self._phaseroots[phase]
503 if roots:
504 if roots:
504 ps = set(cl.descendants(roots))
505 ps = set(cl.descendants(roots))
505 for root in roots:
506 for root in roots:
@@ -551,8 +552,10 b' class phasecache:'
551
552
552 def _write(self, repo, fp):
553 def _write(self, repo, fp):
553 assert repo.filtername is None
554 assert repo.filtername is None
555 to_node = repo.changelog.node
554 for phase, roots in self._phaseroots.items():
556 for phase, roots in self._phaseroots.items():
555 for h in sorted(roots):
557 for r in sorted(roots):
558 h = to_node(r)
556 fp.write(b'%i %s\n' % (phase, hex(h)))
559 fp.write(b'%i %s\n' % (phase, hex(h)))
557 self.dirty = False
560 self.dirty = False
558
561
@@ -584,7 +587,7 b' class phasecache:'
584 repo.invalidatevolatilesets()
587 repo.invalidatevolatilesets()
585
588
586 def advanceboundary(
589 def advanceboundary(
587 self, repo, tr, targetphase, nodes, revs=None, dryrun=None
590 self, repo, tr, targetphase, nodes=None, revs=None, dryrun=None
588 ):
591 ):
589 """Set all 'nodes' to phase 'targetphase'
592 """Set all 'nodes' to phase 'targetphase'
590
593
@@ -598,6 +601,8 b' class phasecache:'
598 # phaseroots values, replace them.
601 # phaseroots values, replace them.
599 if revs is None:
602 if revs is None:
600 revs = []
603 revs = []
604 if not revs and not nodes:
605 return set()
601 if tr is None:
606 if tr is None:
602 phasetracking = None
607 phasetracking = None
603 else:
608 else:
@@ -616,7 +621,7 b' class phasecache:'
616
621
617 olds = self._phaseroots[phase]
622 olds = self._phaseroots[phase]
618
623
619 affected = repo.revs(b'%ln::%ld', olds, revs)
624 affected = repo.revs(b'%ld::%ld', olds, revs)
620 changes.update(affected)
625 changes.update(affected)
621 if dryrun:
626 if dryrun:
622 continue
627 continue
@@ -625,10 +630,7 b' class phasecache:'
625 phasetracking, r, self.phase(repo, r), targetphase
630 phasetracking, r, self.phase(repo, r), targetphase
626 )
631 )
627
632
628 roots = {
633 roots = set(repo.revs(b'roots((%ld::) - %ld)', olds, affected))
629 ctx.node()
630 for ctx in repo.set(b'roots((%ln::) - %ld)', olds, affected)
631 }
632 if olds != roots:
634 if olds != roots:
633 self._updateroots(repo, phase, roots, tr)
635 self._updateroots(repo, phase, roots, tr)
634 # some roots may need to be declared for lower phases
636 # some roots may need to be declared for lower phases
@@ -636,7 +638,7 b' class phasecache:'
636 if not dryrun:
638 if not dryrun:
637 # declare deleted root in the target phase
639 # declare deleted root in the target phase
638 if targetphase != 0:
640 if targetphase != 0:
639 self._retractboundary(repo, tr, targetphase, delroots)
641 self._retractboundary(repo, tr, targetphase, revs=delroots)
640 repo.invalidatevolatilesets()
642 repo.invalidatevolatilesets()
641 return changes
643 return changes
642
644
@@ -651,21 +653,19 b' class phasecache:'
651 else:
653 else:
652 phasetracking = tr.changes.get(b'phases')
654 phasetracking = tr.changes.get(b'phases')
653 repo = repo.unfiltered()
655 repo = repo.unfiltered()
654 if (
656 retracted = self._retractboundary(repo, tr, targetphase, nodes)
655 self._retractboundary(repo, tr, targetphase, nodes)
657 if retracted and phasetracking is not None:
656 and phasetracking is not None
657 ):
658
658
659 # find the affected revisions
659 # find the affected revisions
660 new = self._phaseroots[targetphase]
660 new = self._phaseroots[targetphase]
661 old = oldroots[targetphase]
661 old = oldroots[targetphase]
662 affected = set(repo.revs(b'(%ln::) - (%ln::)', new, old))
662 affected = set(repo.revs(b'(%ld::) - (%ld::)', new, old))
663
663
664 # find the phase of the affected revision
664 # find the phase of the affected revision
665 for phase in range(targetphase, -1, -1):
665 for phase in range(targetphase, -1, -1):
666 if phase:
666 if phase:
667 roots = oldroots.get(phase, [])
667 roots = oldroots.get(phase, [])
668 revs = set(repo.revs(b'%ln::%ld', roots, affected))
668 revs = set(repo.revs(b'%ld::%ld', roots, affected))
669 affected -= revs
669 affected -= revs
670 else: # public phase
670 else: # public phase
671 revs = affected
671 revs = affected
@@ -673,11 +673,15 b' class phasecache:'
673 _trackphasechange(phasetracking, r, phase, targetphase)
673 _trackphasechange(phasetracking, r, phase, targetphase)
674 repo.invalidatevolatilesets()
674 repo.invalidatevolatilesets()
675
675
676 def _retractboundary(self, repo, tr, targetphase, nodes, revs=None):
676 def _retractboundary(self, repo, tr, targetphase, nodes=None, revs=None):
677 # Be careful to preserve shallow-copied values: do not update
677 # Be careful to preserve shallow-copied values: do not update
678 # phaseroots values, replace them.
678 # phaseroots values, replace them.
679 if revs is None:
679 if revs is None:
680 revs = []
680 revs = []
681 if nodes is None:
682 nodes = []
683 if not revs and not nodes:
684 return False
681 if (
685 if (
682 targetphase == internal
686 targetphase == internal
683 and not supportinternal(repo)
687 and not supportinternal(repo)
@@ -688,10 +692,8 b' class phasecache:'
688 msg = b'this repository does not support the %s phase' % name
692 msg = b'this repository does not support the %s phase' % name
689 raise error.ProgrammingError(msg)
693 raise error.ProgrammingError(msg)
690
694
691 repo = repo.unfiltered()
695 torev = repo.changelog.index.rev
692 torev = repo.changelog.rev
696 currentroots = self._phaseroots[targetphase]
693 tonode = repo.changelog.node
694 currentroots = {torev(node) for node in self._phaseroots[targetphase]}
695 finalroots = oldroots = set(currentroots)
697 finalroots = oldroots = set(currentroots)
696 newroots = [torev(node) for node in nodes] + [r for r in revs]
698 newroots = [torev(node) for node in nodes] + [r for r in revs]
697 newroots = [
699 newroots = [
@@ -701,6 +703,8 b' class phasecache:'
701 if newroots:
703 if newroots:
702 if nullrev in newroots:
704 if nullrev in newroots:
703 raise error.Abort(_(b'cannot change null revision phase'))
705 raise error.Abort(_(b'cannot change null revision phase'))
706 # do not break the CoW assumption of the shallow copy
707 currentroots = currentroots.copy()
704 currentroots.update(newroots)
708 currentroots.update(newroots)
705
709
706 # Only compute new roots for revs above the roots that are being
710 # Only compute new roots for revs above the roots that are being
@@ -712,18 +716,13 b' class phasecache:'
712 finalroots = {rev for rev in currentroots if rev < minnewroot}
716 finalroots = {rev for rev in currentroots if rev < minnewroot}
713 finalroots.update(updatedroots)
717 finalroots.update(updatedroots)
714 if finalroots != oldroots:
718 if finalroots != oldroots:
715 self._updateroots(
719 self._updateroots(repo, targetphase, finalroots, tr)
716 repo,
717 targetphase,
718 {tonode(rev) for rev in finalroots},
719 tr,
720 )
721 return True
720 return True
722 return False
721 return False
723
722
724 def register_strip(
723 def register_strip(
725 self,
724 self,
726 repo: "localrepo.localrepository",
725 repo,
727 tr,
726 tr,
728 strip_rev: int,
727 strip_rev: int,
729 ):
728 ):
@@ -731,12 +730,10 b' class phasecache:'
731
730
732 Any roots higher than the stripped revision should be dropped.
731 Any roots higher than the stripped revision should be dropped.
733 """
732 """
734 assert repo.filtername is None
733 for targetphase, roots in list(self._phaseroots.items()):
735 to_rev = repo.changelog.index.rev
734 filtered = {r for r in roots if r >= strip_rev}
736 for targetphase, nodes in list(self._phaseroots.items()):
737 filtered = {n for n in nodes if to_rev(n) >= strip_rev}
738 if filtered:
735 if filtered:
739 self._updateroots(repo, targetphase, nodes - filtered, tr)
736 self._updateroots(repo, targetphase, roots - filtered, tr)
740 self.invalidate()
737 self.invalidate()
741
738
742
739
@@ -793,9 +790,10 b' def listphases(repo: "localrepo.localrep'
793 keys = util.sortdict()
790 keys = util.sortdict()
794 value = b'%i' % draft
791 value = b'%i' % draft
795 cl = repo.unfiltered().changelog
792 cl = repo.unfiltered().changelog
793 to_node = cl.node
796 for root in repo._phasecache._phaseroots[draft]:
794 for root in repo._phasecache._phaseroots[draft]:
797 if repo._phasecache.phase(repo, cl.rev(root)) <= draft:
795 if repo._phasecache.phase(repo, root) <= draft:
798 keys[hex(root)] = value
796 keys[hex(to_node(root))] = value
799
797
800 if repo.publishing():
798 if repo.publishing():
801 # Add an extra data to let remote know we are a publishing
799 # Add an extra data to let remote know we are a publishing
@@ -160,7 +160,7 b' def computeimpactable(repo, visibilityex'
160 firstmutable = len(cl)
160 firstmutable = len(cl)
161 roots = repo._phasecache.nonpublicphaseroots(repo)
161 roots = repo._phasecache.nonpublicphaseroots(repo)
162 if roots:
162 if roots:
163 firstmutable = min(firstmutable, min(cl.rev(r) for r in roots))
163 firstmutable = min(firstmutable, min(roots))
164 # protect from nullrev root
164 # protect from nullrev root
165 firstmutable = max(0, firstmutable)
165 firstmutable = max(0, firstmutable)
166 return frozenset(range(firstmutable, len(cl)))
166 return frozenset(range(firstmutable, len(cl)))
@@ -970,31 +970,16 b' impl Index {'
970 py_roots: PyDict,
970 py_roots: PyDict,
971 ) -> PyResult<PyObject> {
971 ) -> PyResult<PyObject> {
972 let index = &*self.index(py).borrow();
972 let index = &*self.index(py).borrow();
973 let opt = self.get_nodetree(py)?.borrow();
974 let nt = opt.as_ref().unwrap();
975 let roots: Result<HashMap<Phase, Vec<Revision>>, PyErr> = py_roots
973 let roots: Result<HashMap<Phase, Vec<Revision>>, PyErr> = py_roots
976 .items_list(py)
974 .items_list(py)
977 .iter(py)
975 .iter(py)
978 .map(|r| {
976 .map(|r| {
979 let phase = r.get_item(py, 0)?;
977 let phase = r.get_item(py, 0)?;
980 let nodes = r.get_item(py, 1)?;
978 let revs: Vec<_> =
981 // Transform the nodes from Python to revs here since we
979 rev_pyiter_collect(py, &r.get_item(py, 1)?, index)?;
982 // have access to the nodemap
983 let revs: Result<_, _> = nodes
984 .iter(py)?
985 .map(|node| match node?.extract::<PyBytes>(py) {
986 Ok(py_bytes) => {
987 let node = node_from_py_bytes(py, &py_bytes)?;
988 nt.find_bin(index, node.into())
989 .map_err(|e| nodemap_error(py, e))?
990 .ok_or_else(|| revlog_error(py))
991 }
992 Err(e) => Err(e),
993 })
994 .collect();
995 let phase = Phase::try_from(phase.extract::<usize>(py)?)
980 let phase = Phase::try_from(phase.extract::<usize>(py)?)
996 .map_err(|_| revlog_error(py));
981 .map_err(|_| revlog_error(py));
997 Ok((phase?, revs?))
982 Ok((phase?, revs))
998 })
983 })
999 .collect();
984 .collect();
1000 let (len, phase_maps) = index
985 let (len, phase_maps) = index
@@ -197,7 +197,7 b' debugdelta chain basic output'
197 node trie depth: 1
197 node trie depth: 1
198 node trie last rev scanned: -1 (no-rust !)
198 node trie last rev scanned: -1 (no-rust !)
199 node trie last rev scanned: 3 (rust !)
199 node trie last rev scanned: 3 (rust !)
200 node trie lookups: 4 (no-rust !)
200 node trie lookups: 3 (no-rust !)
201 node trie lookups: 2 (rust !)
201 node trie lookups: 2 (rust !)
202 node trie misses: 1
202 node trie misses: 1
203 node trie splits: 1
203 node trie splits: 1
@@ -187,7 +187,7 b' Test corrupted p1/p2 fields that could c'
187 > ops = [
187 > ops = [
188 > ('reachableroots',
188 > ('reachableroots',
189 > lambda: cl.index.reachableroots2(0, [1], [0], False)),
189 > lambda: cl.index.reachableroots2(0, [1], [0], False)),
190 > ('compute_phases_map_sets', lambda: cl.computephases({1: {cl.node(0)}})),
190 > ('compute_phases_map_sets', lambda: cl.computephases({1: {0}})),
191 > ('index_headrevs', lambda: cl.headrevs()),
191 > ('index_headrevs', lambda: cl.headrevs()),
192 > ('find_gca_candidates', lambda: cl.commonancestorsheads(n0, n1)),
192 > ('find_gca_candidates', lambda: cl.commonancestorsheads(n0, n1)),
193 > ('find_deepest', lambda: cl.ancestor(n0, n1)),
193 > ('find_deepest', lambda: cl.ancestor(n0, n1)),
General Comments 0
You need to be logged in to leave comments. Login now