rust-index: add support for delta-chain computation
authorRaphaël Gomès <rgomes@octobus.net>
Thu, 03 Aug 2023 15:50:14 +0200
changeset 51213 62e39bef36ca
parent 51212 9b06e7f32bc5
child 51214 050098d60c30
rust-index: add support for delta-chain computation
rust/hg-core/src/revlog/index.rs
rust/hg-cpython/src/revlog.rs
--- a/rust/hg-core/src/revlog/index.rs	Thu Aug 03 15:01:34 2023 +0200
+++ b/rust/hg-core/src/revlog/index.rs	Thu Aug 03 15:50:14 2023 +0200
@@ -512,6 +512,50 @@
         }
     }
 
+    /// Obtain the delta chain for a revision.
+    ///
+    /// `stop_rev` specifies a revision to stop at. If not specified, we
+    /// stop at the base of the chain.
+    ///
+    /// Returns a 2-tuple of (chain, stopped) where `chain` is a vec of
+    /// revs in ascending order and `stopped` is a bool indicating whether
+    /// `stoprev` was hit.
+    pub fn delta_chain(
+        &self,
+        rev: Revision,
+        stop_rev: Option<Revision>,
+    ) -> Result<(Vec<Revision>, bool), HgError> {
+        let mut current_rev = rev;
+        let mut entry = self.get_entry(rev).unwrap();
+        let mut chain = vec![];
+        while current_rev.0 != entry.base_revision_or_base_of_delta_chain().0
+            && stop_rev.map(|r| r != current_rev).unwrap_or(true)
+        {
+            chain.push(current_rev);
+            let new_rev = if self.uses_generaldelta() {
+                entry.base_revision_or_base_of_delta_chain()
+            } else {
+                UncheckedRevision(current_rev.0 - 1)
+            };
+            if new_rev.0 == NULL_REVISION.0 {
+                break;
+            }
+            current_rev = self.check_revision(new_rev).ok_or_else(|| {
+                HgError::corrupted(format!("Revision {new_rev} out of range"))
+            })?;
+            entry = self.get_entry(current_rev).unwrap()
+        }
+
+        let stopped = if stop_rev.map(|r| current_rev == r).unwrap_or(false) {
+            true
+        } else {
+            chain.push(current_rev);
+            false
+        };
+        chain.reverse();
+        Ok((chain, stopped))
+    }
+
     pub fn find_snapshots(
         &self,
         start_rev: UncheckedRevision,
--- a/rust/hg-cpython/src/revlog.rs	Thu Aug 03 15:01:34 2023 +0200
+++ b/rust/hg-cpython/src/revlog.rs	Thu Aug 03 15:50:14 2023 +0200
@@ -310,7 +310,40 @@
 
     /// determine revisions with deltas to reconstruct fulltext
     def deltachain(&self, *args, **kw) -> PyResult<PyObject> {
-        self.call_cindex(py, "deltachain", args, kw)
+        let index = self.index(py).borrow();
+        let rev = args.get_item(py, 0).extract::<BaseRevision>(py)?.into();
+        let stop_rev =
+            args.get_item(py, 1).extract::<Option<BaseRevision>>(py)?;
+        let rev = index.check_revision(rev).ok_or_else(|| {
+            nodemap_error(py, NodeMapError::RevisionNotInIndex(rev))
+        })?;
+        let stop_rev = if let Some(stop_rev) = stop_rev {
+            let stop_rev = UncheckedRevision(stop_rev);
+            Some(index.check_revision(stop_rev).ok_or_else(|| {
+                nodemap_error(py, NodeMapError::RevisionNotInIndex(stop_rev))
+            })?)
+        } else {None};
+        let (chain, stopped) = index.delta_chain(rev, stop_rev).map_err(|e| {
+            PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string())
+        })?;
+
+        let cresult = self.call_cindex(py, "deltachain", args, kw)?;
+        let cchain: Vec<BaseRevision> =
+            cresult.get_item(py, 0)?.extract::<Vec<BaseRevision>>(py)?;
+        let chain: Vec<_> = chain.into_iter().map(|r| r.0).collect();
+        assert_eq!(chain, cchain);
+        assert_eq!(stopped, cresult.get_item(py, 1)?.extract(py)?);
+
+        Ok(
+            PyTuple::new(
+                py,
+                &[
+                    chain.into_py_object(py).into_object(),
+                    stopped.into_py_object(py).into_object()
+                ]
+            ).into_object()
+        )
+
     }
 
     /// slice planned chunk read to reach a density threshold