# HG changeset patch # User Valentin Gatien-Baron # Date 2019-06-10 17:23:14 # Node ID 055c3e2c44f0eaf15290a9bfe3a3dcc7a6a77a47 # Parent 3e42fc243741a805e13a0b92ad468e6db75ba1bb revlog: speed up isancestor Currently, it is implemented on top of commonancestorsheads. Implement it on top of reachableroots instead, as reachableroots could stop walking the graph much sooner than commonancestorsheads. Measuring repo.changelog.isancestorrev on two revisions in a private repository: before: ! wall 0.005175 comb 0.010000 user 0.010000 sys 0.000000 (best of 550) after : ! wall 0.000072 comb 0.000000 user 0.000000 sys 0.000000 (best of 36199) When hg does this kind of operations 1500 times in pull -> bookmarks.comparebookmarks -> bookmarks.validdest, that's 11s that drop from the --profile output. Differential Revision: https://phab.mercurial-scm.org/D6506 diff --git a/mercurial/changelog.py b/mercurial/changelog.py --- a/mercurial/changelog.py +++ b/mercurial/changelog.py @@ -420,9 +420,6 @@ class changelog(revlog.revlog): if i not in self.filteredrevs: yield i - def reachableroots(self, minroot, heads, roots, includepath=False): - return self.index.reachableroots2(minroot, heads, roots, includepath) - def _checknofilteredinrevs(self, revs): """raise the appropriate error if 'revs' contains a filtered revision diff --git a/mercurial/dagop.py b/mercurial/dagop.py --- a/mercurial/dagop.py +++ b/mercurial/dagop.py @@ -259,11 +259,10 @@ def descendantrevs(revs, revsfn, parentr yield rev break -def _reachablerootspure(repo, minroot, roots, heads, includepath): - """See reachableroots""" +def _reachablerootspure(pfunc, minroot, roots, heads, includepath): + """See revlog.reachableroots""" if not roots: return [] - parentrevs = repo.changelog.parentrevs roots = set(roots) visit = list(heads) reachable = set() @@ -280,7 +279,7 @@ def _reachablerootspure(repo, minroot, r reached(rev) if not includepath: continue - parents = parentrevs(rev) + parents = pfunc(rev) seen[rev] = parents for parent in parents: if parent >= minroot and parent not in seen: @@ -296,18 +295,13 @@ def _reachablerootspure(repo, minroot, r return reachable def reachableroots(repo, roots, heads, includepath=False): - """return (heads(:: and ::)) - - If includepath is True, return (::).""" + """See revlog.reachableroots""" if not roots: return baseset() minroot = roots.min() roots = list(roots) heads = list(heads) - try: - revs = repo.changelog.reachableroots(minroot, heads, roots, includepath) - except AttributeError: - revs = _reachablerootspure(repo, minroot, roots, heads, includepath) + revs = repo.changelog.reachableroots(minroot, heads, roots, includepath) revs = baseset(revs) revs.sort() return revs diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -1216,14 +1216,25 @@ class revlog(object): A revision is considered an ancestor of itself. The implementation of this is trivial but the use of - commonancestorsheads is not.""" + reachableroots is not.""" if a == nullrev: return True elif a == b: return True elif a > b: return False - return a in self._commonancestorsheads(a, b) + return bool(self.reachableroots(a, [b], [a], includepath=False)) + + def reachableroots(self, minroot, heads, roots, includepath=False): + """return (heads(:: and ::)) + + If includepath is True, return (::).""" + try: + return self.index.reachableroots2(minroot, heads, roots, + includepath) + except AttributeError: + return dagop._reachablerootspure(self.parentrevs, + minroot, roots, heads, includepath) def ancestor(self, a, b): """calculate the "best" common ancestor of nodes a and b"""