Mercurial > hg
changeset 40697:d98fb3f42f33
context: floor adjustlinkrev graph walk during copy tracing
The `_adjustlinkrev` method gains an optional "stoprev" argument. The linkrev
adjustment will give up once this floor is reached. The relevant functions
using `_adjustlinkrev` are updated to pass an appropriate value in the copy
tracing code.
In some private repository, about 10% of the status call triggered the
pathological case addressed by this change. The speedup varies from one call
to another, the best-observed win is moving from 170s to 11s.
The effect of this change can be seen in the public pypy repository, running the
following command:
hg perftracecopies --source 83c9ff0c0206 --destination 59c79103d5b0
before: 3.401753 seconds
after: 2.634897 seconds (-23%)
author | Boris Feld <boris.feld@octobus.net> |
---|---|
date | Wed, 10 Oct 2018 00:50:37 +0200 |
parents | 69206452a2ac |
children | 3c98c339c6ea |
files | mercurial/context.py |
diffstat | 1 files changed, 23 insertions(+), 6 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/context.py Mon Nov 19 14:30:58 2018 +0000 +++ b/mercurial/context.py Wed Oct 10 00:50:37 2018 +0200 @@ -712,7 +712,7 @@ return True - def _adjustlinkrev(self, srcrev, inclusive=False): + def _adjustlinkrev(self, srcrev, inclusive=False, stoprev=None): """return the first ancestor of <srcrev> introducing <fnode> If the linkrev of the file revision does not point to an ancestor of @@ -721,6 +721,10 @@ :srcrev: the changeset revision we search ancestors from :inclusive: if true, the src revision will also be checked + :stoprev: an optional revision to stop the walk at. If no introduction + of this file content could be found before this floor + revision, the function will returns "None" and stops its + iteration. """ repo = self._repo cl = repo.unfiltered().changelog @@ -748,6 +752,8 @@ fnode = self._filenode path = self._path for a in iteranc: + if stoprev is not None and a < stoprev: + return None ac = cl.read(a) # get changeset data (we avoid object creation) if path in ac[3]: # checking the 'files' field. # The file has been touched, check if the content is @@ -765,7 +771,9 @@ """ if self.linkrev() >= changelogrev: return True - introrev = self._introrev() + introrev = self._introrev(stoprev=changelogrev) + if introrev is None: + return False return introrev >= changelogrev def introrev(self): @@ -779,7 +787,15 @@ """ return self._introrev() - def _introrev(self): + def _introrev(self, stoprev=None): + """ + Same as `introrev` but, with an extra argument to limit changelog + iteration range in some internal usecase. + + If `stoprev` is set, the `introrev` will not be searched past that + `stoprev` revision and "None" might be returned. This is useful to + limit the iteration range. + """ toprev = None attrs = vars(self) if r'_changeid' in attrs: @@ -790,11 +806,12 @@ toprev = self._changectx.rev() if toprev is not None: - return self._adjustlinkrev(toprev, inclusive=True) + return self._adjustlinkrev(toprev, inclusive=True, stoprev=stoprev) elif r'_descendantrev' in attrs: - introrev = self._adjustlinkrev(self._descendantrev) + introrev = self._adjustlinkrev(self._descendantrev, stoprev=stoprev) # be nice and cache the result of the computation - self._changeid = introrev + if introrev is not None: + self._changeid = introrev return introrev else: return self.linkrev()