Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/branchmap.py @ 42001:624d6683c705
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
author | Pulkit Goyal <pulkit@yandex-team.ru> |
---|---|
date | Mon, 18 Mar 2019 18:59:38 +0300 |
parents | 38de3300414f |
children | 662ffdde5adf |
comparison
equal
deleted
inserted
replaced
42000:a66965406528 | 42001:624d6683c705 |
---|---|
125 | 125 |
126 def clear(self): | 126 def clear(self): |
127 self._per_filter.clear() | 127 self._per_filter.clear() |
128 | 128 |
129 | 129 |
130 class branchcache(dict): | 130 class branchcache(object): |
131 """A dict like object that hold branches heads cache. | 131 """A dict like object that hold branches heads cache. |
132 | 132 |
133 This cache is used to avoid costly computations to determine all the | 133 This cache is used to avoid costly computations to determine all the |
134 branch heads of a repo. | 134 branch heads of a repo. |
135 | 135 |
149 branch head closes a branch or not. | 149 branch head closes a branch or not. |
150 """ | 150 """ |
151 | 151 |
152 def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev, | 152 def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev, |
153 filteredhash=None, closednodes=None): | 153 filteredhash=None, closednodes=None): |
154 super(branchcache, self).__init__(entries) | |
155 self.tipnode = tipnode | 154 self.tipnode = tipnode |
156 self.tiprev = tiprev | 155 self.tiprev = tiprev |
157 self.filteredhash = filteredhash | 156 self.filteredhash = filteredhash |
158 # closednodes is a set of nodes that close their branch. If the branch | 157 # closednodes is a set of nodes that close their branch. If the branch |
159 # cache has been updated, it may contain nodes that are no longer | 158 # cache has been updated, it may contain nodes that are no longer |
160 # heads. | 159 # heads. |
161 if closednodes is None: | 160 if closednodes is None: |
162 self._closednodes = set() | 161 self._closednodes = set() |
163 else: | 162 else: |
164 self._closednodes = closednodes | 163 self._closednodes = closednodes |
164 self.entries = dict(entries) | |
165 | |
166 def __iter__(self): | |
167 return iter(self.entries) | |
168 | |
169 def __setitem__(self, key, value): | |
170 self.entries[key] = value | |
171 | |
172 def __getitem__(self, key): | |
173 return self.entries[key] | |
174 | |
175 def setdefault(self, *args): | |
176 return self.entries.setdefault(*args) | |
177 | |
178 def iteritems(self): | |
179 return self.entries.iteritems() | |
180 | |
181 def itervalues(self): | |
182 return self.entries.itervalues() | |
165 | 183 |
166 @classmethod | 184 @classmethod |
167 def fromfile(cls, repo): | 185 def fromfile(cls, repo): |
168 f = None | 186 f = None |
169 try: | 187 try: |
269 for bn, heads in self.iteritems(): | 287 for bn, heads in self.iteritems(): |
270 yield (bn, heads) + self._branchtip(heads) | 288 yield (bn, heads) + self._branchtip(heads) |
271 | 289 |
272 def copy(self): | 290 def copy(self): |
273 """return an deep copy of the branchcache object""" | 291 """return an deep copy of the branchcache object""" |
274 return type(self)( | 292 return branchcache( |
275 self, self.tipnode, self.tiprev, self.filteredhash, | 293 self.entries, self.tipnode, self.tiprev, self.filteredhash, |
276 self._closednodes) | 294 self._closednodes) |
277 | 295 |
278 def write(self, repo): | 296 def write(self, repo): |
279 try: | 297 try: |
280 f = repo.cachevfs(self._filename(repo), "w", atomictemp=True) | 298 f = repo.cachevfs(self._filename(repo), "w", atomictemp=True) |
293 state = 'o' | 311 state = 'o' |
294 f.write("%s %s %s\n" % (hex(node), state, label)) | 312 f.write("%s %s %s\n" % (hex(node), state, label)) |
295 f.close() | 313 f.close() |
296 repo.ui.log('branchcache', | 314 repo.ui.log('branchcache', |
297 'wrote %s branch cache with %d labels and %d nodes\n', | 315 'wrote %s branch cache with %d labels and %d nodes\n', |
298 repo.filtername, len(self), nodecount) | 316 repo.filtername, len(self.entries), nodecount) |
299 except (IOError, OSError, error.Abort) as inst: | 317 except (IOError, OSError, error.Abort) as inst: |
300 # Abort may be raised by read only opener, so log and continue | 318 # Abort may be raised by read only opener, so log and continue |
301 repo.ui.debug("couldn't write branch cache: %s\n" % | 319 repo.ui.debug("couldn't write branch cache: %s\n" % |
302 stringutil.forcebytestr(inst)) | 320 stringutil.forcebytestr(inst)) |
303 | 321 |
349 | 367 |
350 if not self.validfor(repo): | 368 if not self.validfor(repo): |
351 # cache key are not valid anymore | 369 # cache key are not valid anymore |
352 self.tipnode = nullid | 370 self.tipnode = nullid |
353 self.tiprev = nullrev | 371 self.tiprev = nullrev |
354 for heads in self.values(): | 372 for heads in self.itervalues(): |
355 tiprev = max(cl.rev(node) for node in heads) | 373 tiprev = max(cl.rev(node) for node in heads) |
356 if tiprev > self.tiprev: | 374 if tiprev > self.tiprev: |
357 self.tipnode = cl.node(tiprev) | 375 self.tipnode = cl.node(tiprev) |
358 self.tiprev = tiprev | 376 self.tiprev = tiprev |
359 self.filteredhash = scmutil.filteredhash(repo, self.tiprev) | 377 self.filteredhash = scmutil.filteredhash(repo, self.tiprev) |