# HG changeset patch # User Bryan O'Sullivan # Date 1337481898 25200 # Node ID 2631cd5dd244492e6b8c0c10c6ed84b990456723 # Parent 1dc08dc63c09d13de7120408f382f692bc7e3d86 revlog: switch to a C version of headrevs The C implementation is more than 100 times faster than the Python version (which is still available as a fallback). In a repo with 330,000 revs and a stale .hg/cache/tags file, this patch improves the performance of "hg tip" from 2.2 to 1.6 seconds. diff -r 1dc08dc63c09 -r 2631cd5dd244 mercurial/parsers.c --- a/mercurial/parsers.c Sat May 19 19:44:23 2012 -0700 +++ b/mercurial/parsers.c Sat May 19 19:44:58 2012 -0700 @@ -534,6 +534,81 @@ return NULL; } +static PyObject *index_headrevs(indexObject *self) +{ + Py_ssize_t i, len, addlen; + char *nothead = NULL; + PyObject *heads; + + len = index_length(self) - 1; + heads = PyList_New(0); + if (heads == NULL) + goto bail; + if (len == 0) { + PyObject *nullid = PyInt_FromLong(-1); + if (nullid == NULL || PyList_Append(heads, nullid) == -1) { + Py_XDECREF(nullid); + goto bail; + } + goto done; + } + + nothead = calloc(len, 1); + if (nothead == NULL) + goto bail; + + for (i = 0; i < self->raw_length; i++) { + const char *data = index_deref(self, i); + int parent_1 = getbe32(data + 24); + int parent_2 = getbe32(data + 28); + if (parent_1 >= 0) + nothead[parent_1] = 1; + if (parent_2 >= 0) + nothead[parent_2] = 1; + } + + addlen = self->added ? PyList_GET_SIZE(self->added) : 0; + + for (i = 0; i < addlen; i++) { + PyObject *rev = PyList_GET_ITEM(self->added, i); + PyObject *p1 = PyTuple_GET_ITEM(rev, 5); + PyObject *p2 = PyTuple_GET_ITEM(rev, 6); + long parent_1, parent_2; + + if (!PyInt_Check(p1) || !PyInt_Check(p2)) { + PyErr_SetString(PyExc_TypeError, + "revlog parents are invalid"); + goto bail; + } + parent_1 = PyInt_AS_LONG(p1); + parent_2 = PyInt_AS_LONG(p2); + if (parent_1 >= 0) + nothead[parent_1] = 1; + if (parent_2 >= 0) + nothead[parent_2] = 1; + } + + for (i = 0; i < len; i++) { + PyObject *head; + + if (nothead[i]) + continue; + head = PyInt_FromLong(i); + if (head == NULL || PyList_Append(heads, head) == -1) { + Py_XDECREF(head); + goto bail; + } + } + +done: + free(nothead); + return heads; +bail: + Py_XDECREF(heads); + free(nothead); + return NULL; +} + static inline int nt_level(const char *node, Py_ssize_t level) { int v = node[level>>1]; @@ -1144,6 +1219,8 @@ "clear the index caches"}, {"get", (PyCFunction)index_m_get, METH_VARARGS, "get an index entry"}, + {"headrevs", (PyCFunction)index_headrevs, METH_NOARGS, + "get head revisions"}, {"insert", (PyCFunction)index_insert, METH_VARARGS, "insert an index entry"}, {"partialmatch", (PyCFunction)index_partialmatch, METH_VARARGS, diff -r 1dc08dc63c09 -r 2631cd5dd244 mercurial/revlog.py --- a/mercurial/revlog.py Sat May 19 19:44:23 2012 -0700 +++ b/mercurial/revlog.py Sat May 19 19:44:58 2012 -0700 @@ -635,6 +635,10 @@ return (orderedout, roots, heads) def headrevs(self): + try: + return self.index.headrevs() + except AttributeError: + pass count = len(self) if not count: return [nullrev]