# HG changeset patch # User Boris Feld # Date 1539125435 -7200 # Node ID 8a0136f690273d53080e0f4293885f1686dea3a2 # Parent aee94f0a36cd7ddf1ea1435e410bb4e4fa8b5ff9 context: introduce an `isintroducedafter` method and use it in copies Right now, copy tracing make effort to not traverse the graph too much to save performance. It uses a "limit" acting as a floor revision past which data are no longer relevant to the current copy tracing. However, to enforce this limit, it does a call to `filectx.rev()` and that call can trigger a graph traversal on its own. That extra graph traversal is unaware of the current limit and can become very expensive. That cost is increased by the nature of work done in adjust link rev, we are not only walking down the graph, we are also checking the affected file for each revision we walk through. Something significantly more expensive than the walk itself. To work around this we need to make the `filectx` operation aware of the current limit. The first step is to introduce a dedicated method: `isintroducedafter`. We'll then rework that method logic to stop traversal as soon as possible. diff -r aee94f0a36cd -r 8a0136f69027 mercurial/context.py --- a/mercurial/context.py Wed Oct 10 00:50:34 2018 +0200 +++ b/mercurial/context.py Wed Oct 10 00:50:35 2018 +0200 @@ -760,6 +760,12 @@ # result is crash somewhere else at to some point. return lkr + def isintroducedafter(self, changelogrev): + """True if a filectx has been introduced after a given floor revision + """ + return (self.linkrev() >= changelogrev + or self.introrev() >= changelogrev) + def introrev(self): """return the rev of the changeset which introduced this file revision diff -r aee94f0a36cd -r 8a0136f69027 mercurial/copies.py --- a/mercurial/copies.py Wed Oct 10 00:50:34 2018 +0200 +++ b/mercurial/copies.py Wed Oct 10 00:50:35 2018 +0200 @@ -139,7 +139,7 @@ for f in fctx.ancestors(): if am.get(f.path(), None) == f.filenode(): return f - if limit >= 0 and f.linkrev() < limit and f.rev() < limit: + if limit >= 0 and not f.isintroducedafter(limit): return None def _dirstatecopies(d, match=None):