diff --git a/mercurial/branchmap.py b/mercurial/branchmap.py
--- a/mercurial/branchmap.py
+++ b/mercurial/branchmap.py
@@ -884,8 +884,7 @@ class BranchCacheV3(_LocalBranchCache):
         elif self.tiprev == cl.tiprev():
             return cl.headrevs()
         else:
-            # XXX passing tiprev as ceiling of cl.headrevs could be faster
-            heads = cl.headrevs(cl.revs(stop=self.tiprev))
+            heads = cl.headrevs(stop_rev=self.tiprev + 1)
             return heads
 
     def _write_header(self, fp) -> None:
diff --git a/mercurial/repoview.py b/mercurial/repoview.py
--- a/mercurial/repoview.py
+++ b/mercurial/repoview.py
@@ -310,9 +310,14 @@ class filteredchangelogmixin:
         # 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):
+    def headrevs(self, revs=None, stop_rev=None):
         if revs is None:
-            return self.index.headrevs(self.filteredrevs)
+            filtered = self.filteredrevs
+            if stop_rev is not None and stop_rev < len(self.index):
+                filtered = set(self.filteredrevs)
+                filtered.update(range(stop_rev, len(self.index)))
+            return self.index.headrevs(filtered)
+        assert stop_rev is None
 
         revs = self._checknofilteredinrevs(revs)
         return super(filteredchangelogmixin, self).headrevs(revs)
diff --git a/mercurial/revlog.py b/mercurial/revlog.py
--- a/mercurial/revlog.py
+++ b/mercurial/revlog.py
@@ -2380,9 +2380,15 @@ class revlog:
         assert heads
         return (orderedout, roots, heads)
 
-    def headrevs(self, revs=None):
+    def headrevs(self, revs=None, stop_rev=None):
         if revs is None:
-            return self.index.headrevs()
+            excluded = None
+            if stop_rev is not None and stop_rev < len(self.index):
+                # We should let the native code handle it, but that a
+                # simple enough first step.
+                excluded = range(stop_rev, len(self.index))
+            return self.index.headrevs(excluded)
+        assert stop_rev is None
         if rustdagop is not None and self.index.rust_ext_compat:
             return rustdagop.headrevs(self.index, revs)
         return dagop.headrevs(revs, self._uncheckedparentrevs)