rust-index: add support for `headrevsfiltered`
authorRaphaël Gomès <rgomes@octobus.net>
Mon, 30 Oct 2023 11:14:25 +0100
changeset 51216 9f876765cbe2
parent 51215 a7bba7df9189
child 51217 898674a4dbc7
rust-index: add support for `headrevsfiltered` The implementation is merged with that of `headrevs` also to make sure that caches are up to date.
rust/hg-core/src/revlog/index.rs
rust/hg-cpython/src/revlog.rs
--- a/rust/hg-core/src/revlog/index.rs	Tue Sep 19 15:21:43 2023 +0200
+++ b/rust/hg-core/src/revlog/index.rs	Mon Oct 30 11:14:25 2023 +0100
@@ -263,6 +263,9 @@
     /// Cache of the head revisions in this index, kept in sync. Should
     /// be accessed via the [`Self::head_revs`] method.
     head_revs: Vec<Revision>,
+    /// Cache of the last filtered revisions in this index, used to make sure
+    /// we haven't changed filters when returning the cached `head_revs`.
+    filtered_revs: HashSet<Revision>,
 }
 
 impl Debug for Index {
@@ -363,6 +366,7 @@
                     uses_generaldelta,
                     is_inline: true,
                     head_revs: vec![],
+                    filtered_revs: HashSet::new(),
                 })
             } else {
                 Err(HgError::corrupted("unexpected inline revlog length"))
@@ -374,6 +378,7 @@
                 uses_generaldelta,
                 is_inline: false,
                 head_revs: vec![],
+                filtered_revs: HashSet::new(),
             })
         }
     }
@@ -520,13 +525,36 @@
 
     /// Return the head revisions of this index
     pub fn head_revs(&mut self) -> Result<Vec<Revision>, GraphError> {
-        if !self.head_revs.is_empty() {
+        self.head_revs_filtered(&HashSet::new())
+    }
+
+    /// Return the head revisions of this index
+    pub fn head_revs_filtered(
+        &mut self,
+        filtered_revs: &HashSet<Revision>,
+    ) -> Result<Vec<Revision>, GraphError> {
+        if !self.head_revs.is_empty() && filtered_revs == &self.filtered_revs {
             return Ok(self.head_revs.to_owned());
         }
-        let mut revs: HashSet<Revision, RandomState> = (0..self.len())
-            .into_iter()
-            .map(|i| Revision(i as BaseRevision))
-            .collect();
+        let mut revs: HashSet<Revision, RandomState> =
+            if filtered_revs.is_empty() {
+                (0..self.len())
+                    .into_iter()
+                    .map(|i| Revision(i as BaseRevision))
+                    .collect()
+            } else {
+                (0..self.len())
+                    .into_iter()
+                    .filter_map(|i| {
+                        let r = Revision(i as BaseRevision);
+                        if filtered_revs.contains(&r) {
+                            None
+                        } else {
+                            Some(r)
+                        }
+                    })
+                    .collect()
+            };
         dagops::retain_heads(self, &mut revs)?;
         if self.is_empty() {
             revs.insert(NULL_REVISION);
@@ -535,6 +563,7 @@
             revs.into_iter().map(Into::into).collect();
         as_vec.sort_unstable();
         self.head_revs = as_vec.to_owned();
+        self.filtered_revs = filtered_revs.to_owned();
         Ok(as_vec)
     }
 
--- a/rust/hg-cpython/src/revlog.rs	Tue Sep 19 15:21:43 2023 +0200
+++ b/rust/hg-cpython/src/revlog.rs	Mon Oct 30 11:14:25 2023 +0100
@@ -7,6 +7,8 @@
 
 use crate::{
     cindex,
+    conversion::rev_pyiter_collect,
+    exceptions::GraphError,
     utils::{node_from_py_bytes, node_from_py_object},
     PyRevision,
 };
@@ -259,7 +261,20 @@
 
     /// get filtered head revisions
     def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> {
-        self.call_cindex(py, "headrevsfiltered", args, kw)
+        let rust_res = self.inner_headrevsfiltered(py, &args.get_item(py, 0))?;
+        let c_res = self.call_cindex(py, "headrevsfiltered", args, kw)?;
+        assert_eq!(
+            rust_res.len(),
+            c_res.len(py)?,
+            "filtered heads differ {:?} {}",
+            rust_res,
+            c_res
+        );
+        for (index, rev) in rust_res.iter().enumerate() {
+            let c_rev: BaseRevision = c_res.get_item(py, index)?.extract(py)?;
+            assert_eq!(c_rev, rev.0);
+        }
+        Ok(c_res)
     }
 
     /// True if the object is a snapshot
@@ -797,6 +812,19 @@
             .collect();
         Ok(PyList::new(py, &as_vec).into_object())
     }
+
+    fn inner_headrevsfiltered(
+        &self,
+        py: Python,
+        filtered_revs: &PyObject,
+    ) -> PyResult<Vec<Revision>> {
+        let index = &mut *self.index(py).borrow_mut();
+        let filtered_revs = rev_pyiter_collect(py, filtered_revs, index)?;
+
+        index
+            .head_revs_filtered(&filtered_revs)
+            .map_err(|e| GraphError::pynew(py, e))
+    }
 }
 
 fn revlog_error(py: Python) -> PyErr {