diff --git a/mercurial/repoview.py b/mercurial/repoview.py --- a/mercurial/repoview.py +++ b/mercurial/repoview.py @@ -305,6 +305,10 @@ class filteredchangelogmixin: raise error.FilteredIndexError(rev) return revs + def _head_node_ids(self): + # no Rust fast path implemented yet, so just loop in Python + return [self.node(r) for r in self.headrevs()] + def headrevs(self, revs=None): if revs is None: try: diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -2362,6 +2362,12 @@ class revlog: ishead[e[5]] = ishead[e[6]] = 0 # my parent are not return [r for r, val in enumerate(ishead) if val] + def _head_node_ids(self): + try: + return self.index.head_node_ids() + except AttributeError: + return [self.node(r) for r in self.headrevs()] + def heads(self, start=None, stop=None): """return the list of all nodes that have no children @@ -2373,8 +2379,7 @@ class revlog: if start is None and stop is None: if not len(self): return [self.nullid] - return [self.node(r) for r in self.headrevs()] - + return self._head_node_ids() if start is None: start = nullrev else: 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 @@ -307,6 +307,12 @@ py_class!(pub class Index |py| { Ok(rust_res) } + /// get head nodeids + def head_node_ids(&self) -> PyResult { + let rust_res = self.inner_head_node_ids(py)?; + Ok(rust_res) + } + /// get filtered head revisions def headrevsfiltered(&self, *args, **_kw) -> PyResult { let rust_res = self.inner_headrevsfiltered(py, &args.get_item(py, 0))?; @@ -774,6 +780,32 @@ impl Index { }) } + fn inner_head_node_ids(&self, py: Python) -> PyResult { + let index = &*self.index(py).borrow(); + + // We don't use the shortcut here, as it's actually slower to loop + // through the cached `PyList` than to re-do the whole computation for + // large lists, which are the performance sensitive ones anyway. + let head_revs = index.head_revs().map_err(|e| graph_error(py, e))?; + let res: Vec<_> = head_revs + .iter() + .map(|r| { + PyBytes::new( + py, + index + .node(*r) + .expect("rev should have been in the index") + .as_bytes(), + ) + .into_object() + }) + .collect(); + + self.cache_new_heads_py_list(head_revs, py); + + Ok(PyList::new(py, &res).into_object()) + } + fn inner_headrevs(&self, py: Python) -> PyResult { let index = &*self.index(py).borrow(); if let Some(new_heads) =