# HG changeset patch # User Pulkit Goyal # Date 2019-03-18 15:59:38 # Node ID 624d6683c705e80e841c6fa1bd3b72273203670a # Parent a669654065284444bc75ea8654b9e05928bcbb48 branchmap: remove the dict interface from the branchcache class (API) The current branchmap computation involves reading the whole branchmap from disk, validating all the nodes even if they are not required. This leads to a lot of time on repos which have large branchmap or a lot of branches. On large repos, this can validate around 1000's of nodes. On some operations, like finding whether a branch exists or not, we don't need to validate all the nodes. Or updating heads for a single branch. Before this patch, branchcache class was having dict interface and it was hard to keep track of reads. This patch removes the dict interface. Upcoming patches will implement lazy loading and validation of data and implement better API's. Differential Revision: https://phab.mercurial-scm.org/D6151 diff --git a/mercurial/branchmap.py b/mercurial/branchmap.py --- a/mercurial/branchmap.py +++ b/mercurial/branchmap.py @@ -127,7 +127,7 @@ class BranchMapCache(object): self._per_filter.clear() -class branchcache(dict): +class branchcache(object): """A dict like object that hold branches heads cache. This cache is used to avoid costly computations to determine all the @@ -151,7 +151,6 @@ class branchcache(dict): def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev, filteredhash=None, closednodes=None): - super(branchcache, self).__init__(entries) self.tipnode = tipnode self.tiprev = tiprev self.filteredhash = filteredhash @@ -162,6 +161,25 @@ class branchcache(dict): self._closednodes = set() else: self._closednodes = closednodes + self.entries = dict(entries) + + def __iter__(self): + return iter(self.entries) + + def __setitem__(self, key, value): + self.entries[key] = value + + def __getitem__(self, key): + return self.entries[key] + + def setdefault(self, *args): + return self.entries.setdefault(*args) + + def iteritems(self): + return self.entries.iteritems() + + def itervalues(self): + return self.entries.itervalues() @classmethod def fromfile(cls, repo): @@ -271,8 +289,8 @@ class branchcache(dict): def copy(self): """return an deep copy of the branchcache object""" - return type(self)( - self, self.tipnode, self.tiprev, self.filteredhash, + return branchcache( + self.entries, self.tipnode, self.tiprev, self.filteredhash, self._closednodes) def write(self, repo): @@ -295,7 +313,7 @@ class branchcache(dict): f.close() repo.ui.log('branchcache', 'wrote %s branch cache with %d labels and %d nodes\n', - repo.filtername, len(self), nodecount) + repo.filtername, len(self.entries), nodecount) except (IOError, OSError, error.Abort) as inst: # Abort may be raised by read only opener, so log and continue repo.ui.debug("couldn't write branch cache: %s\n" % @@ -351,7 +369,7 @@ class branchcache(dict): # cache key are not valid anymore self.tipnode = nullid self.tiprev = nullrev - for heads in self.values(): + for heads in self.itervalues(): tiprev = max(cl.rev(node) for node in heads) if tiprev > self.tiprev: self.tipnode = cl.node(tiprev) diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -1556,7 +1556,7 @@ class localrepository(object): return scmutil.revsymbol(self, key).node() def lookupbranch(self, key): - if key in self.branchmap(): + if key in self.branchmap().entries: return key return scmutil.revsymbol(self, key).branch() @@ -2730,7 +2730,7 @@ class localrepository(object): if branch is None: branch = self[None].branch() branches = self.branchmap() - if branch not in branches: + if branch not in branches.entries: return [] # the cache returns heads ordered lowest to highest bheads = list(reversed(branches.branchheads(branch, closed=closed)))