diff --git a/mercurial/cext/parsers.c b/mercurial/cext/parsers.c --- a/mercurial/cext/parsers.c +++ b/mercurial/cext/parsers.c @@ -710,7 +710,7 @@ void manifest_module_init(PyObject *mod); void revlog_module_init(PyObject *mod); -static const int version = 3; +static const int version = 4; static void module_init(PyObject *mod) { diff --git a/mercurial/cext/revlog.c b/mercurial/cext/revlog.c --- a/mercurial/cext/revlog.c +++ b/mercurial/cext/revlog.c @@ -628,7 +628,7 @@ { PyObject *roots = Py_None; PyObject *ret = NULL; - PyObject *phaseslist = NULL; + PyObject *phasessize = NULL; PyObject *phaseroots = NULL; PyObject *phaseset = NULL; PyObject *phasessetlist = NULL; @@ -685,12 +685,10 @@ } } /* Transform phase list to a python list */ - phaseslist = PyList_New(len); - if (phaseslist == NULL) + phasessize = PyInt_FromLong(len); + if (phasessize == NULL) goto release; for (i = 0; i < len; i++) { - PyObject *phaseval; - phase = phases[i]; /* We only store the sets of phase for non public phase, the public phase * is computed as a difference */ @@ -702,15 +700,11 @@ PySet_Add(phaseset, rev); Py_XDECREF(rev); } - phaseval = PyInt_FromLong(phase); - if (phaseval == NULL) - goto release; - PyList_SET_ITEM(phaseslist, i, phaseval); } - ret = PyTuple_Pack(2, phaseslist, phasessetlist); + ret = PyTuple_Pack(2, phasessize, phasessetlist); release: - Py_XDECREF(phaseslist); + Py_XDECREF(phasessize); Py_XDECREF(phasessetlist); done: free(phases); diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -688,8 +688,8 @@ def _activebookmark(self): return self._bookmarks.active - # _phaserevs and _phasesets depend on changelog. what we need is to - # call _phasecache.invalidate() if '00changelog.i' was changed, but it + # _phasesets depend on changelog. what we need is to call + # _phasecache.invalidate() if '00changelog.i' was changed, but it # can't be easily expressed in filecache mechanism. @storecache('phaseroots', '00changelog.i') def _phasecache(self): diff --git a/mercurial/phases.py b/mercurial/phases.py --- a/mercurial/phases.py +++ b/mercurial/phases.py @@ -115,6 +115,7 @@ ) from . import ( error, + pycompat, smartset, txnutil, util, @@ -202,7 +203,7 @@ if _load: # Cheap trick to allow shallow-copy without copy module self.phaseroots, self.dirty = _readroots(repo, phasedefaults) - self._phaserevs = None + self._phasemaxrev = nullrev self._phasesets = None self.filterunknown(repo) self.opener = repo.svfs @@ -210,23 +211,30 @@ def getrevset(self, repo, phases): """return a smartset for the given phases""" self.loadphaserevs(repo) # ensure phase's sets are loaded - - if self._phasesets and all(self._phasesets[p] is not None - for p in phases): - # fast path - use _phasesets - revs = self._phasesets[phases[0]] - if len(phases) > 1: - revs = revs.copy() # only copy when needed - for p in phases[1:]: - revs.update(self._phasesets[p]) + phases = set(phases) + if public not in phases: + # fast path: _phasesets contains the interesting sets, + # might only need a union and post-filtering. + if len(phases) == 1: + [p] = phases + revs = self._phasesets[p] + else: + revs = set.union(*[self._phasesets[p] for p in phases]) if repo.changelog.filteredrevs: revs = revs - repo.changelog.filteredrevs return smartset.baseset(revs) else: - # slow path - enumerate all revisions - phase = self.phase - revs = (r for r in repo if phase(repo, r) in phases) - return smartset.generatorset(revs, iterasc=True) + phases = set(allphases).difference(phases) + if not phases: + return smartset.fullreposet(repo) + if len(phases) == 1: + [p] = phases + revs = self._phasesets[p] + else: + revs = set.union(*[self._phasesets[p] for p in phases]) + if not revs: + return smartset.fullreposet(repo) + return smartset.fullreposet(repo).filter(lambda r: r not in revs) def copy(self): # Shallow copy meant to ensure isolation in @@ -235,13 +243,14 @@ ph.phaseroots = self.phaseroots[:] ph.dirty = self.dirty ph.opener = self.opener - ph._phaserevs = self._phaserevs + ph._phasemaxrev = self._phasemaxrev ph._phasesets = self._phasesets return ph def replace(self, phcache): """replace all values in 'self' with content of phcache""" - for a in ('phaseroots', 'dirty', 'opener', '_phaserevs', '_phasesets'): + for a in ('phaseroots', 'dirty', 'opener', '_phasemaxrev', + '_phasesets'): setattr(self, a, getattr(phcache, a)) def _getphaserevsnative(self, repo): @@ -253,42 +262,38 @@ def _computephaserevspure(self, repo): repo = repo.unfiltered() - revs = [public] * len(repo.changelog) - self._phaserevs = revs - self._populatephaseroots(repo) - for phase in trackedphases: - roots = list(map(repo.changelog.rev, self.phaseroots[phase])) - if roots: - for rev in roots: - revs[rev] = phase - for rev in repo.changelog.descendants(roots): - revs[rev] = phase + cl = repo.changelog + self._phasesets = [set() for phase in allphases] + roots = pycompat.maplist(cl.rev, self.phaseroots[secret]) + if roots: + ps = set(cl.descendants(roots)) + for root in roots: + ps.add(root) + self._phasesets[secret] = ps + roots = pycompat.maplist(cl.rev, self.phaseroots[draft]) + if roots: + ps = set(cl.descendants(roots)) + for root in roots: + ps.add(root) + ps.difference_update(self._phasesets[secret]) + self._phasesets[draft] = ps + self._phasemaxrev = len(cl) def loadphaserevs(self, repo): """ensure phase information is loaded in the object""" - if self._phaserevs is None: + if self._phasesets is None: try: res = self._getphaserevsnative(repo) - self._phaserevs, self._phasesets = res + self._phasemaxrev, self._phasesets = res except AttributeError: self._computephaserevspure(repo) def invalidate(self): - self._phaserevs = None + self._phasemaxrev = nullrev self._phasesets = None - def _populatephaseroots(self, repo): - """Fills the _phaserevs cache with phases for the roots. - """ - cl = repo.changelog - phaserevs = self._phaserevs - for phase in trackedphases: - roots = map(cl.rev, self.phaseroots[phase]) - for root in roots: - phaserevs[root] = phase - def phase(self, repo, rev): - # We need a repo argument here to be able to build _phaserevs + # We need a repo argument here to be able to build _phasesets # if necessary. The repository instance is not stored in # phasecache to avoid reference cycles. The changelog instance # is not stored because it is a filecache() property and can @@ -297,10 +302,13 @@ return public if rev < nullrev: raise ValueError(_('cannot lookup negative revision')) - if self._phaserevs is None or rev >= len(self._phaserevs): + if rev >= self._phasemaxrev: self.invalidate() self.loadphaserevs(repo) - return self._phaserevs[rev] + for phase in trackedphases: + if rev in self._phasesets[phase]: + return phase + return public def write(self): if not self.dirty: @@ -455,10 +463,10 @@ if filtered: self.dirty = True # filterunknown is called by repo.destroyed, we may have no changes in - # root but phaserevs contents is certainly invalid (or at least we + # root but _phasesets contents is certainly invalid (or at least we # have not proper way to check that). related to issue 3858. # - # The other caller is __init__ that have no _phaserevs initialized + # The other caller is __init__ that have no _phasesets initialized # anyway. If this change we should consider adding a dedicated # "destroyed" function to phasecache or a proper cache key mechanism # (see branchmap one) diff --git a/mercurial/policy.py b/mercurial/policy.py --- a/mercurial/policy.py +++ b/mercurial/policy.py @@ -75,7 +75,7 @@ (r'cext', r'diffhelpers'): 1, (r'cext', r'mpatch'): 1, (r'cext', r'osutil'): 1, - (r'cext', r'parsers'): 3, + (r'cext', r'parsers'): 4, } # map import request to other package or module