rust: MissingAncestors.basesheads()
authorGeorges Racinet <georges.racinet@octobus.net>
Mon, 14 Jan 2019 17:07:39 +0100
changeset 41246 619ee4039bd4
parent 41245 2a8782cc2e16
child 41247 a89b20a49c13
rust: MissingAncestors.basesheads() This new API method on `MissingAncestors` leverages directly the Rust implementation for relative heads of a set, and also lowers the cost of returning the results to Python in the context of discovery. These interchange costs can probably be further reduced by implementing the `partialdiscovery` class in Rust, but that will be investigated in the 5.0 development cycle. Differential Revision: https://phab.mercurial-scm.org/D5584
rust/hg-core/src/ancestors.rs
rust/hg-cpython/src/ancestors.rs
tests/test-rust-ancestor.py
--- a/rust/hg-core/src/ancestors.rs	Mon Jan 14 18:52:01 2019 +0100
+++ b/rust/hg-core/src/ancestors.rs	Mon Jan 14 17:07:39 2019 +0100
@@ -10,6 +10,7 @@
 use super::{Graph, GraphError, Revision, NULL_REVISION};
 use std::cmp::max;
 use std::collections::{BinaryHeap, HashSet};
+use crate::dagops;
 
 /// Iterator over the ancestors of a given list of revisions
 /// This is a generic type, defined and implemented for any Graph, so that
@@ -229,6 +230,19 @@
         &self.bases
     }
 
+    /// Computes the relative heads of current bases.
+    ///
+    /// The object is still usable after this.
+    pub fn bases_heads(&self) -> Result<HashSet<Revision>, GraphError> {
+        dagops::heads(&self.graph, self.bases.iter())
+    }
+
+    /// Consumes the object and returns the relative heads of its bases.
+    pub fn into_bases_heads(mut self) -> Result<HashSet<Revision>, GraphError> {
+        dagops::retain_heads(&self.graph, &mut self.bases)?;
+        Ok(self.bases)
+    }
+
     pub fn add_bases(
         &mut self,
         new_bases: impl IntoIterator<Item = Revision>,
@@ -556,8 +570,8 @@
     }
 
     #[test]
-    /// Test constructor, add/get bases
-    fn test_missing_bases() {
+    /// Test constructor, add/get bases and heads
+    fn test_missing_bases() -> Result<(), GraphError> {
         let mut missing_ancestors =
             MissingAncestors::new(SampleGraph, [5, 3, 1, 3].iter().cloned());
         let mut as_vec: Vec<Revision> =
@@ -569,6 +583,11 @@
         as_vec = missing_ancestors.get_bases().iter().cloned().collect();
         as_vec.sort();
         assert_eq!(as_vec, [1, 3, 5, 7, 8]);
+
+        as_vec = missing_ancestors.bases_heads()?.iter().cloned().collect();
+        as_vec.sort();
+        assert_eq!(as_vec, [3, 5, 7, 8]);
+        Ok(())
     }
 
     fn assert_missing_remove(
--- a/rust/hg-cpython/src/ancestors.rs	Mon Jan 14 18:52:01 2019 +0100
+++ b/rust/hg-cpython/src/ancestors.rs	Mon Jan 14 17:07:39 2019 +0100
@@ -166,6 +166,11 @@
         py_set(py, self.inner(py).borrow().get_bases())
     }
 
+    def basesheads(&self) -> PyResult<PyObject> {
+        let inner = self.inner(py).borrow();
+        py_set(py, &inner.bases_heads().map_err(|e| GraphError::pynew(py, e))?)
+    }
+
     def removeancestorsfrom(&self, revs: PyObject) -> PyResult<PyObject> {
         let mut inner = self.inner(py).borrow_mut();
         // this is very lame: we convert to a Rust set, update it in place
--- a/tests/test-rust-ancestor.py	Mon Jan 14 18:52:01 2019 +0100
+++ b/tests/test-rust-ancestor.py	Mon Jan 14 17:07:39 2019 +0100
@@ -114,6 +114,7 @@
         missanc.addbases({2})
         self.assertEqual(missanc.bases(), {1, 2})
         self.assertEqual(missanc.missingancestors([3]), [3])
+        self.assertEqual(missanc.basesheads(), {2})
 
     def testmissingancestorsremove(self):
         idx = self.parseindex()