# HG changeset patch # User Patrick Mezard # Date 1349509327 -7200 # Node ID bd9c292665fdb14573e0e9d6410d710b4137a4e3 # Parent a80b01ceb1fca28b42784f29c27a9807121bc0ea editor: add a small changectx cache Reverts including a lot of files create many actions like: R bar (from trunk/bar:4343) For each of these files, an open_file() call is made and the parent mercurial revision is loaded to detect copies in issamefile(). This results in a big slowdown, easily reduced by caching the changectx. diff --git a/hgsubversion/editor.py b/hgsubversion/editor.py --- a/hgsubversion/editor.py +++ b/hgsubversion/editor.py @@ -204,9 +204,9 @@ class CopiedFile(object): self.path = path self.copypath = copypath - def resolve(self, repo, ctx=None): + def resolve(self, getctxfn, ctx=None): if ctx is None: - ctx = repo[self.node] + ctx = getctxfn(self.node) fctx = ctx[self.path] data = fctx.data() flags = fctx.flags() @@ -235,6 +235,7 @@ class HgEditor(svnwrap.Editor): # A mapping of file paths to batons self._openpaths = {} self._deleted = set() + self._getctx = util.lrucachefunc(self.repo.changectx, 3) def _openfile(self, path, data, isexec, islink, copypath, create=False): if path in self._openpaths: @@ -277,7 +278,7 @@ class HgEditor(svnwrap.Editor): ha = self.meta.get_parent_revision(self.current.rev.revnum, branch) if ha == revlog.nullid: return - ctx = self.repo.changectx(ha) + ctx = self._getctx(ha) if br_path not in ctx: br_path2 = '' if br_path != '': @@ -300,7 +301,7 @@ class HgEditor(svnwrap.Editor): if path in self._svncopies: copy = self._svncopies.pop(path) - base, isexec, islink, copypath = copy.resolve(self.repo) + base, isexec, islink, copypath = copy.resolve(self._getctx) return self._openfile(path, base, isexec, islink, copypath) if not self.meta.is_path_valid(path): @@ -313,7 +314,7 @@ class HgEditor(svnwrap.Editor): # replacing branch as parent, but svn delta editor provides delta # agains replaced branch. parent = self.meta.get_parent_revision(baserev + 1, branch, True) - ctx = self.repo[parent] + ctx = self._getctx(parent) if fpath not in ctx: self.current.addmissing(path) return None @@ -354,7 +355,7 @@ class HgEditor(svnwrap.Editor): # agains replaced branch. ha = self.meta.get_parent_revision(copyfrom_revision + 1, from_branch, True) - ctx = self.repo.changectx(ha) + ctx = self._getctx(ha) if from_file not in ctx: self.current.addmissing(path) return None @@ -367,7 +368,7 @@ class HgEditor(svnwrap.Editor): parentid = self.meta.get_parent_revision( self.current.rev.revnum, branch) if parentid != revlog.nullid: - parentctx = self.repo.changectx(parentid) + parentctx = self._getctx(parentid) if util.issamefile(parentctx, ctx, from_file): copypath = from_file return self._openfile(path, fctx.data(), 'x' in flags, 'l' in flags, @@ -428,7 +429,7 @@ class HgEditor(svnwrap.Editor): if new_hash == node.nullid: self.current.addmissing('%s/' % path) return path - fromctx = self.repo.changectx(new_hash) + fromctx = self._getctx(new_hash) if frompath != '/' and frompath != '': frompath = '%s/' % frompath else: @@ -463,7 +464,7 @@ class HgEditor(svnwrap.Editor): parentid = self.meta.get_parent_revision( self.current.rev.revnum, branch) if parentid != revlog.nullid: - parentctx = self.repo.changectx(parentid) + parentctx = self._getctx(parentid) for k, v in copies.iteritems(): if util.issamefile(parentctx, fromctx, v): svncopies[k].copypath = v @@ -584,9 +585,8 @@ class HgEditor(svnwrap.Editor): for path, copy in self._svncopies.iteritems(): nodes.setdefault(copy.node, []).append((path, copy)) for node, copies in nodes.iteritems(): - ctx = self.repo[node] for path, copy in copies: - data, isexec, islink, copied = copy.resolve(self.repo, ctx) + data, isexec, islink, copied = copy.resolve(self._getctx) self.current.set(path, data, isexec, islink, copied) self._svncopies.clear() diff --git a/hgsubversion/util.py b/hgsubversion/util.py --- a/hgsubversion/util.py +++ b/hgsubversion/util.py @@ -2,6 +2,7 @@ import errno import re import os import urllib +from collections import deque from mercurial import cmdutil from mercurial import error @@ -336,3 +337,32 @@ def getfilestoresize(ui): else: size = -1 return size + +# Copy-paste from mercurial.util to avoid having to deal with backward +# compatibility, plus the cache size is configurable. +def lrucachefunc(func, size): + '''cache most recent results of function calls''' + cache = {} + order = deque() + if func.func_code.co_argcount == 1: + def f(arg): + if arg not in cache: + if len(cache) > size: + del cache[order.popleft()] + cache[arg] = func(arg) + else: + order.remove(arg) + order.append(arg) + return cache[arg] + else: + def f(*args): + if args not in cache: + if len(cache) > size: + del cache[order.popleft()] + cache[args] = func(*args) + else: + order.remove(args) + order.append(args) + return cache[args] + + return f