diff --git a/mercurial/cext/revlog.c b/mercurial/cext/revlog.c --- a/mercurial/cext/revlog.c +++ b/mercurial/cext/revlog.c @@ -464,6 +464,80 @@ Py_RETURN_NONE; } +/* Replace an existing index entry with a new value. This should not be used + outside of the context of sidedata rewriting, inside the transaction that + creates the given revision. */ +static PyObject *index_replace(indexObject *self, PyObject *args) +{ + uint64_t offset_flags, unified_revlog_id, sidedata_offset; + int rev, comp_len, uncomp_len, base_rev, link_rev, parent_1, parent_2; + Py_ssize_t c_node_id_len, rank, sidedata_comp_len; + const char *c_node_id; + char *data; + PyObject *obj; + + 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, "nO", &rev, &obj)) + 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; + } + + if (!PyArg_ParseTuple(obj, v2_tuple_format, &offset_flags, &comp_len, + &uncomp_len, &base_rev, &link_rev, &parent_1, + &parent_2, &c_node_id, &c_node_id_len, + &unified_revlog_id, &rank, &sidedata_offset, + &sidedata_comp_len)) { + PyErr_SetString(PyExc_TypeError, "12-tuple required"); + return NULL; + } + + if (c_node_id_len != self->nodelen) { + PyErr_SetString(PyExc_TypeError, "invalid node"); + return NULL; + } + + /* Rewrite the newly added node, offset from the "already on-disk" + * length */ + data = self->added + self->hdrsize * (rev - self->length); + putbe32(offset_flags >> 32, data); + putbe32(offset_flags & 0xffffffffU, data + 4); + putbe32(comp_len, data + 8); + putbe32(uncomp_len, data + 12); + putbe32(base_rev, data + 16); + putbe32(link_rev, data + 20); + putbe32(parent_1, data + 24); + putbe32(parent_2, data + 28); + memcpy(data + 32, c_node_id, c_node_id_len); + /* Padding since SHA-1 is only 20 bytes for now */ + memset(data + 32 + c_node_id_len, 0, 32 - c_node_id_len); + putbe64(unified_revlog_id, data + 64); + putbe32(rank, data + 72); + putbe64(sidedata_offset, data + 76); + putbe32(sidedata_comp_len, data + 84); + /* Padding for 96 bytes alignment */ + memset(data + 88, 0, self->hdrsize - 88); + + Py_RETURN_NONE; +} + static PyObject *index_stats(indexObject *self) { PyObject *obj = PyDict_New(); @@ -2795,6 +2869,8 @@ "compute phases"}, {"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS, "reachableroots"}, + {"replace", (PyCFunction)index_replace, 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 @@ -116,6 +116,23 @@ r = (offset_type(0, gettype(r[0])),) + r[1:] return r + def replace(self, i, tup): + """ + Replace an existing index entry with a new value. This should + not 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) + if i >= self._lgt: + self._extra[i - self._lgt] = _pack(self.index_format, *tup) + else: + index = self._calculate_index(i) + self._data[index : index + self.index_size] = _pack( + self.index_format, *tup + ) + class IndexObject(BaseIndexObject): def __init__(self, data):