diff --git a/mercurial/cext/revlog.c b/mercurial/cext/revlog.c --- a/mercurial/cext/revlog.c +++ b/mercurial/cext/revlog.c @@ -458,6 +458,56 @@ Py_RETURN_NONE; } +/* Replace an existing index entry's sidedata offset and length with new ones. + This cannot be used outside of the context of sidedata rewriting, + inside the transaction that creates the given revision. */ +static PyObject *index_replace_sidedata_info(indexObject *self, PyObject *args) +{ + uint64_t sidedata_offset; + int rev; + Py_ssize_t sidedata_comp_len; + char *data; + #if LONG_MAX == 0x7fffffffL + const char *const sidedata_format = PY23("nKi", "nKi"); + #else + const char *const sidedata_format = PY23("nki", "nki"); + #endif + + if (self->hdrsize == v1_hdrsize || self->inlined) { + /* + There is a bug in the transaction handling when going from an + inline revlog to a separate index and data file. Turn it off until + it's fixed, since v2 revlogs sometimes get rewritten on exchange. + See issue6485. + */ + raise_revlog_error(); + return NULL; + } + + if (!PyArg_ParseTuple(args, sidedata_format, &rev, &sidedata_offset, + &sidedata_comp_len)) + return NULL; + + if (rev < 0 || rev >= index_length(self)) { + PyErr_SetString(PyExc_IndexError, "revision outside index"); + return NULL; + } + if (rev < self->length) { + PyErr_SetString( + PyExc_IndexError, + "cannot rewrite entries outside of this transaction"); + return NULL; + } + + /* Find the newly added node, offset from the "already on-disk" length */ + data = self->added + self->hdrsize * (rev - self->length); + putbe64(sidedata_offset, data + 64); + putbe32(sidedata_comp_len, data + 72); + + + Py_RETURN_NONE; +} + static PyObject *index_stats(indexObject *self) { PyObject *obj = PyDict_New(); @@ -2789,6 +2839,8 @@ "compute phases"}, {"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS, "reachableroots"}, + {"replace_sidedata_info", (PyCFunction)index_replace_sidedata_info, + METH_VARARGS, "replace an existing index entry with a new value"}, {"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/pure/parsers.py b/mercurial/pure/parsers.py --- a/mercurial/pure/parsers.py +++ b/mercurial/pure/parsers.py @@ -259,6 +259,27 @@ assert index_size == 96, index_size null_item = (0, 0, 0, -1, -1, -1, -1, nullid, 0, 0) + def replace_sidedata_info(self, i, sidedata_offset, sidedata_length): + """ + Replace an existing index entry's sidedata offset and length with new + ones. + This cannot be used outside of the context of sidedata rewriting, + inside the transaction that creates the revision `i`. + """ + if i < 0: + raise KeyError + self._check_index(i) + sidedata_format = b">Qi" + packed_size = struct.calcsize(sidedata_format) + if i >= self._lgt: + packed = _pack(sidedata_format, sidedata_offset, sidedata_length) + old = self._extra[i - self._lgt] + new = old[:64] + packed + old[64 + packed_size :] + self._extra[i - self._lgt] = new + else: + msg = b"cannot rewrite entries outside of this transaction" + raise KeyError(msg) + class IndexObject2(Index2Mixin, IndexObject): pass