[PATCH 1 of 2 V3] phases: add set per phase in C phase computation

Laurent Charignon lcharignon at fb.com
Mon May 18 22:19:12 UTC 2015


# HG changeset patch
# User Laurent Charignon <lcharignon at fb.com>
# Date 1427912237 25200
#      Wed Apr 01 11:17:17 2015 -0700
# Node ID 9f875b3f3bcbbe52d884cefd8ebad23a4a6c068e
# Parent  819cd397e306e36c48d977150502413da29f088f
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.

diff --git a/mercurial/parsers.c b/mercurial/parsers.c
--- a/mercurial/parsers.c
+++ b/mercurial/parsers.c
@@ -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,
diff --git a/mercurial/phases.py b/mercurial/phases.py
--- a/mercurial/phases.py
+++ b/mercurial/phases.py
@@ -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


More information about the Mercurial-devel mailing list