diff --git a/rust/hg-core/src/revlog/index.rs b/rust/hg-core/src/revlog/index.rs --- a/rust/hg-core/src/revlog/index.rs +++ b/rust/hg-core/src/revlog/index.rs @@ -544,14 +544,23 @@ impl Index { /// Return the head revisions of this index pub fn head_revs(&self) -> Result, GraphError> { - self.head_revs_filtered(&HashSet::new()) + self.head_revs_filtered(&HashSet::new(), false) + .map(|h| h.unwrap()) + } + + /// Python-specific shortcut to save on PyList creation + pub fn head_revs_shortcut( + &self, + ) -> Result>, GraphError> { + self.head_revs_filtered(&HashSet::new(), true) } /// Return the head revisions of this index pub fn head_revs_filtered( &self, filtered_revs: &HashSet, - ) -> Result, GraphError> { + py_shortcut: bool, + ) -> Result>, GraphError> { { let guard = self .head_revs @@ -562,7 +571,13 @@ impl Index { if !self_head_revs.is_empty() && filtered_revs == self_filtered_revs { - return Ok(self_head_revs.to_owned()); + if py_shortcut { + // Don't copy the revs since we've already cached them + // on the Python side. + return Ok(None); + } else { + return Ok(Some(self_head_revs.to_owned())); + } } } @@ -592,7 +607,7 @@ impl Index { .write() .expect("RwLock on Index.head_revs should not be poisoned") = (as_vec.to_owned(), filtered_revs.to_owned()); - Ok(as_vec) + Ok(Some(as_vec)) } /// Obtain the delta chain for a revision. diff --git a/rust/hg-cpython/src/revlog.rs b/rust/hg-cpython/src/revlog.rs --- a/rust/hg-cpython/src/revlog.rs +++ b/rust/hg-cpython/src/revlog.rs @@ -96,6 +96,7 @@ py_class!(pub class Index |py| { data nodemap_mmap: RefCell>; // Holds a reference to the mmap'ed persistent index data data index_mmap: RefCell>; + data head_revs_py_list: RefCell>; def __new__( _cls, @@ -257,6 +258,7 @@ py_class!(pub class Index |py| { self.nt(py).borrow_mut().take(); self.docket(py).borrow_mut().take(); self.nodemap_mmap(py).borrow_mut().take(); + self.head_revs_py_list(py).borrow_mut().take(); self.index(py).borrow().clear_caches(); Ok(py.None()) } @@ -621,6 +623,7 @@ impl Index { RefCell::new(None), RefCell::new(None), RefCell::new(Some(buf)), + RefCell::new(None), ) } @@ -773,13 +776,19 @@ impl Index { fn inner_headrevs(&self, py: Python) -> PyResult { let index = &*self.index(py).borrow(); - let as_vec: Vec = index - .head_revs() - .map_err(|e| graph_error(py, e))? - .iter() - .map(|r| PyRevision::from(*r).into_py_object(py).into_object()) - .collect(); - Ok(PyList::new(py, &as_vec).into_object()) + if let Some(new_heads) = + index.head_revs_shortcut().map_err(|e| graph_error(py, e))? + { + self.cache_new_heads_py_list(new_heads, py); + } + + Ok(self + .head_revs_py_list(py) + .borrow() + .as_ref() + .expect("head revs should be cached") + .clone_ref(py) + .into_object()) } fn inner_headrevsfiltered( @@ -790,13 +799,35 @@ impl Index { let index = &mut *self.index(py).borrow_mut(); let filtered_revs = rev_pyiter_collect(py, filtered_revs, index)?; - let as_vec: Vec = index - .head_revs_filtered(&filtered_revs) + if let Some(new_heads) = index + .head_revs_filtered(&filtered_revs, true) .map_err(|e| graph_error(py, e))? + { + self.cache_new_heads_py_list(new_heads, py); + } + + Ok(self + .head_revs_py_list(py) + .borrow() + .as_ref() + .expect("head revs should be cached") + .clone_ref(py) + .into_object()) + } + + fn cache_new_heads_py_list( + &self, + new_heads: Vec, + py: Python<'_>, + ) -> PyList { + let as_vec: Vec = new_heads .iter() .map(|r| PyRevision::from(*r).into_py_object(py).into_object()) .collect(); - Ok(PyList::new(py, &as_vec).into_object()) + let new_heads_py_list = PyList::new(py, &as_vec); + *self.head_revs_py_list(py).borrow_mut() = + Some(new_heads_py_list.clone_ref(py)); + new_heads_py_list } fn inner_ancestors(