diff mercurial/cext/revlog.c @ 46730:502e795b55ac

revlog-index: add `replace_sidedata_info` method During a `pull` operation where the server does not provide sidedata, the client that requires it should generate them on-the-fly. In the generic case, we need to wait for the changelog + manifests + filelogs to be added, since we don't know what the sidedata computers might need: this means rewriting the sidedata of index entries from within the pull transaction (and no further back) right after we've added them. Both Python and C implementations only allow for rewriting the sidedata offset and length for revs within the transaction where they were created. Differential Revision: https://phab.mercurial-scm.org/D10031
author Raphaël Gomès <rgomes@octobus.net>
date Mon, 15 Feb 2021 11:08:28 +0100
parents 358737abeeef
children d4ba4d51f85f
line wrap: on
line diff
--- a/mercurial/cext/revlog.c	Fri Feb 19 11:04:17 2021 +0100
+++ b/mercurial/cext/revlog.c	Mon Feb 15 11:08:28 2021 +0100
@@ -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,