D1606: phases: drop the list with phase of each rev, always comput phase sets
joerg.sonnenberger (Joerg Sonnenberger)
phabricator at mercurial-scm.org
Thu Dec 7 19:01:05 EST 2017
joerg.sonnenberger updated this revision to Diff 4228.
REPOSITORY
rHG Mercurial
CHANGES SINCE LAST UPDATE
https://phab.mercurial-scm.org/D1606?vs=4155&id=4228
REVISION DETAIL
https://phab.mercurial-scm.org/D1606
AFFECTED FILES
mercurial/cext/parsers.c
mercurial/cext/revlog.c
mercurial/localrepo.py
mercurial/phases.py
mercurial/policy.py
CHANGE DETAILS
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
diff --git a/mercurial/phases.py b/mercurial/phases.py
--- a/mercurial/phases.py
+++ b/mercurial/phases.py
@@ -202,46 +202,54 @@
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
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
# advance/retractboundary(), nothing more.
ph = self.__class__(None, None, _load=False)
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,40 +261,36 @@
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 = map(cl.rev, self.phaseroots[secret])
+ if roots:
+ ps = set(cl.descendants(roots))
+ for root in roots:
+ ps.add(root)
+ self._phasesets[secret] = ps
+ roots = map(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
# if necessary. The repository instance is not stored in
@@ -297,10 +301,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 +462,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/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -704,8 +704,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/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,8 +685,8 @@
}
}
/* 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;
@@ -702,15 +702,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/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)
{
To: joerg.sonnenberger, #hg-reviewers, quark
Cc: durin42, quark, mercurial-devel
More information about the Mercurial-devel
mailing list