phases: add set per phase in C phase computation
To speed up the computation of draft(), secret(), divergent(), obsolete() and
unstable() we need to have a fast way of getting the list of revisions that
are in draft(), secret() or the union of both: not public().
This patch extends the work on phase computation in C and make the phase
computation code also return a list of set for each non public phase.
Using these sets we can quickly obtain all the revisions of a given phase.
We do not return a set for the public phase as we expect it to be roughly the
size of the repo. Also, it can be computed easily by substracting the entries in the
non public phases from all the revs in the repo.
--- a/mercurial/parsers.c Fri May 08 12:30:51 2015 -0700
+++ b/mercurial/parsers.c Wed Apr 01 11:17:17 2015 -0700
@@ -1071,14 +1071,17 @@
phases[i] = phases[parent_2];
}
-static PyObject *compute_phases(indexObject *self, PyObject *args)
+static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args)
{
PyObject *roots = Py_None;
+ PyObject *ret = NULL;
PyObject *phaseslist = NULL;
PyObject *phaseroots = NULL;
PyObject *rev = NULL;
PyObject *p1 = NULL;
PyObject *p2 = NULL;
+ PyObject *phaseset = NULL;
+ PyObject *phasessetlist = NULL;
Py_ssize_t addlen = self->added ? PyList_GET_SIZE(self->added) : 0;
Py_ssize_t len = index_length(self) - 1;
Py_ssize_t numphase = 0;
@@ -1088,6 +1091,7 @@
int parent_1, parent_2;
char *phases = NULL;
const char *data;
+ long phase;
if (!PyArg_ParseTuple(args, "O", &roots))
goto release_none;
@@ -1100,13 +1104,24 @@
/* Put the phase information of all the roots in phases */
numphase = PyList_GET_SIZE(roots)+1;
minrevallphases = len + 1;
+ phasessetlist = PyList_New(numphase);
+ if (phasessetlist == NULL)
+ goto release_none;
+
+ PyList_SET_ITEM(phasessetlist, 0, Py_None);
+ Py_INCREF(Py_None);
+
for (i = 0; i < numphase-1; i++) {
phaseroots = PyList_GET_ITEM(roots, i);
+ phaseset = PySet_New(NULL);
+ if (phaseset == NULL)
+ goto release_phasesetlist;
+ PyList_SET_ITEM(phasessetlist, i+1, phaseset);
if (!PyList_Check(phaseroots))
- goto release_phases;
+ goto release_phasesetlist;
minrevphase = add_roots_get_min(self, phaseroots, i+1, phases);
if (minrevphase == -2) /* Error from add_roots_get_min */
- goto release_phases;
+ goto release_phasesetlist;
minrevallphases = MIN(minrevallphases, minrevphase);
}
/* Propagate the phase information from the roots to the revs */
@@ -1121,7 +1136,7 @@
p2 = PyTuple_GET_ITEM(rev, 6);
if (!PyInt_Check(p1) || !PyInt_Check(p2)) {
PyErr_SetString(PyExc_TypeError, "revlog parents are invalid");
- goto release_phases;
+ goto release_phasesetlist;
}
parent_1 = (int)PyInt_AS_LONG(p1);
parent_2 = (int)PyInt_AS_LONG(p2);
@@ -1131,14 +1146,35 @@
/* Transform phase list to a python list */
phaseslist = PyList_New(len);
if (phaseslist == NULL)
- goto release_phases;
- for (i = 0; i < len; i++)
- PyList_SET_ITEM(phaseslist, i, PyInt_FromLong(phases[i]));
+ goto release_phasesetlist;
+ for (i = 0; i < len; i++) {
+ phase = phases[i];
+ /* We only store the sets of phase for non public phase, the public phase
+ * is computed as a difference */
+ if (phase != 0) {
+ phaseset = PyList_GET_ITEM(phasessetlist, phase);
+ PySet_Add(phaseset, PyInt_FromLong(i));
+ }
+ PyList_SET_ITEM(phaseslist, i, PyInt_FromLong(phase));
+ }
+ ret = PyList_New(2);
+ if (ret == NULL)
+ goto release_phaseslist;
+ PyList_SET_ITEM(ret, 0, phaseslist);
+ PyList_SET_ITEM(ret, 1, phasessetlist);
+ /* We don't release phaseslist and phasessetlist as we return them to
+ * python */
+ goto release_phases;
+
+release_phaseslist:
+ Py_XDECREF(phaseslist);
+release_phasesetlist:
+ Py_XDECREF(phasessetlist);
release_phases:
free(phases);
release_none:
- return phaseslist;
+ return ret;
}
static PyObject *index_headrevs(indexObject *self, PyObject *args)
@@ -2278,8 +2314,8 @@
"clear the index caches"},
{"get", (PyCFunction)index_m_get, METH_VARARGS,
"get an index entry"},
- {"computephases", (PyCFunction)compute_phases, METH_VARARGS,
- "compute phases"},
+ {"computephasesmapsets", (PyCFunction)compute_phases_map_sets,
+ METH_VARARGS, "compute phases"},
{"headrevs", (PyCFunction)index_headrevs, METH_VARARGS,
"get head revisions"}, /* Can do filtering since 3.2 */
{"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS,
--- a/mercurial/phases.py Fri May 08 12:30:51 2015 -0700
+++ b/mercurial/phases.py Wed Apr 01 11:17:17 2015 -0700
@@ -155,6 +155,7 @@
# Cheap trick to allow shallow-copy without copy module
self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
self._phaserevs = None
+ self._phasesets = None
self.filterunknown(repo)
self.opener = repo.svfs
@@ -177,7 +178,7 @@
nativeroots = []
for phase in trackedphases:
nativeroots.append(map(repo.changelog.rev, self.phaseroots[phase]))
- return repo.changelog.computephases(nativeroots)
+ return repo.changelog.computephasesmapsets(nativeroots)
def _computephaserevspure(self, repo):
repo = repo.unfiltered()
@@ -199,7 +200,8 @@
'nativephaseskillswitch'):
self._computephaserevspure(repo)
else:
- self._phaserevs = self._getphaserevsnative(repo)
+ res = self._getphaserevsnative(repo)
+ self._phaserevs, self._phasesets = res
except AttributeError:
self._computephaserevspure(repo)
return self._phaserevs