Mercurial > hg
comparison mercurial/localrepo.py @ 7654:816b708f23af
store all heads of a branch in the branch cache
All heads of branches will be stored in a new cache file 'branchheads.cache'
within the .hg directory. The old 'branch.cache' file from older versions
will be ignored.
The new cache contents are formatted line-by-line as '{node} {branchtag}\n'.
This is the same as the previous format. Now, every head is recorded in
an oldest -> tipmost order.
The localrepo.branchheads function is reworked to use the data from the cache.
author | John Mulligan <phlogistonjohn@asynchrono.us> |
---|---|
date | Wed, 14 Jan 2009 21:47:38 -0500 |
parents | 182b7114d35a |
children | cce37dab7ad6 |
comparison
equal
deleted
inserted
replaced
7653:0641fd8a4bb4 | 7654:816b708f23af |
---|---|
358 for t, n in self.tags().iteritems(): | 358 for t, n in self.tags().iteritems(): |
359 self.nodetagscache.setdefault(n, []).append(t) | 359 self.nodetagscache.setdefault(n, []).append(t) |
360 return self.nodetagscache.get(node, []) | 360 return self.nodetagscache.get(node, []) |
361 | 361 |
362 def _branchtags(self, partial, lrev): | 362 def _branchtags(self, partial, lrev): |
363 # TODO: rename this function? | |
363 tiprev = len(self) - 1 | 364 tiprev = len(self) - 1 |
364 if lrev != tiprev: | 365 if lrev != tiprev: |
365 self._updatebranchcache(partial, lrev+1, tiprev+1) | 366 self._updatebranchcache(partial, lrev+1, tiprev+1) |
366 self._writebranchcache(partial, self.changelog.tip(), tiprev) | 367 self._writebranchcache(partial, self.changelog.tip(), tiprev) |
367 | 368 |
368 return partial | 369 return partial |
369 | 370 |
370 def branchtags(self): | 371 def _branchheads(self): |
371 tip = self.changelog.tip() | 372 tip = self.changelog.tip() |
372 if self.branchcache is not None and self._branchcachetip == tip: | 373 if self.branchcache is not None and self._branchcachetip == tip: |
373 return self.branchcache | 374 return self.branchcache |
374 | 375 |
375 oldtip = self._branchcachetip | 376 oldtip = self._branchcachetip |
383 else: | 384 else: |
384 lrev = self.changelog.rev(oldtip) | 385 lrev = self.changelog.rev(oldtip) |
385 partial = self._ubranchcache | 386 partial = self._ubranchcache |
386 | 387 |
387 self._branchtags(partial, lrev) | 388 self._branchtags(partial, lrev) |
389 # this private cache holds all heads (not just tips) | |
390 self._ubranchcache = partial | |
388 | 391 |
389 # the branch cache is stored on disk as UTF-8, but in the local | 392 # the branch cache is stored on disk as UTF-8, but in the local |
390 # charset internally | 393 # charset internally |
391 for k, v in partial.iteritems(): | 394 for k, v in partial.iteritems(): |
392 self.branchcache[util.tolocal(k)] = v | 395 self.branchcache[util.tolocal(k)] = v |
393 self._ubranchcache = partial | |
394 return self.branchcache | 396 return self.branchcache |
397 | |
398 | |
399 def branchtags(self): | |
400 '''return a dict where branch names map to the tipmost head of | |
401 the branch''' | |
402 return dict([(k, v[-1]) for (k, v) in self._branchheads().iteritems()]) | |
395 | 403 |
396 def _readbranchcache(self): | 404 def _readbranchcache(self): |
397 partial = {} | 405 partial = {} |
398 try: | 406 try: |
399 f = self.opener("branch.cache") | 407 f = self.opener("branchheads.cache") |
400 lines = f.read().split('\n') | 408 lines = f.read().split('\n') |
401 f.close() | 409 f.close() |
402 except (IOError, OSError): | 410 except (IOError, OSError): |
403 return {}, nullid, nullrev | 411 return {}, nullid, nullrev |
404 | 412 |
409 # invalidate the cache | 417 # invalidate the cache |
410 raise ValueError('invalidating branch cache (tip differs)') | 418 raise ValueError('invalidating branch cache (tip differs)') |
411 for l in lines: | 419 for l in lines: |
412 if not l: continue | 420 if not l: continue |
413 node, label = l.split(" ", 1) | 421 node, label = l.split(" ", 1) |
414 partial[label.strip()] = bin(node) | 422 partial.setdefault(label.strip(), []).append(bin(node)) |
415 except KeyboardInterrupt: | 423 except KeyboardInterrupt: |
416 raise | 424 raise |
417 except Exception, inst: | 425 except Exception, inst: |
418 if self.ui.debugflag: | 426 if self.ui.debugflag: |
419 self.ui.warn(str(inst), '\n') | 427 self.ui.warn(str(inst), '\n') |
420 partial, last, lrev = {}, nullid, nullrev | 428 partial, last, lrev = {}, nullid, nullrev |
421 return partial, last, lrev | 429 return partial, last, lrev |
422 | 430 |
423 def _writebranchcache(self, branches, tip, tiprev): | 431 def _writebranchcache(self, branches, tip, tiprev): |
424 try: | 432 try: |
425 f = self.opener("branch.cache", "w", atomictemp=True) | 433 f = self.opener("branchheads.cache", "w", atomictemp=True) |
426 f.write("%s %s\n" % (hex(tip), tiprev)) | 434 f.write("%s %s\n" % (hex(tip), tiprev)) |
427 for label, node in branches.iteritems(): | 435 for label, nodes in branches.iteritems(): |
428 f.write("%s %s\n" % (hex(node), label)) | 436 for node in nodes: |
437 f.write("%s %s\n" % (hex(node), label)) | |
429 f.rename() | 438 f.rename() |
430 except (IOError, OSError): | 439 except (IOError, OSError): |
431 pass | 440 pass |
432 | 441 |
433 def _updatebranchcache(self, partial, start, end): | 442 def _updatebranchcache(self, partial, start, end): |
434 for r in xrange(start, end): | 443 for r in xrange(start, end): |
435 c = self[r] | 444 c = self[r] |
436 b = c.branch() | 445 b = c.branch() |
437 partial[b] = c.node() | 446 bheads = partial.setdefault(b, []) |
447 bheads.append(c.node()) | |
448 for p in c.parents(): | |
449 pn = p.node() | |
450 if pn in bheads: | |
451 bheads.remove(pn) | |
438 | 452 |
439 def lookup(self, key): | 453 def lookup(self, key): |
440 if isinstance(key, int): | 454 if isinstance(key, int): |
441 return self.changelog.node(key) | 455 return self.changelog.node(key) |
442 elif key == '.': | 456 elif key == '.': |
1169 return [n for (r, n) in util.sort(heads)] | 1183 return [n for (r, n) in util.sort(heads)] |
1170 | 1184 |
1171 def branchheads(self, branch=None, start=None): | 1185 def branchheads(self, branch=None, start=None): |
1172 if branch is None: | 1186 if branch is None: |
1173 branch = self[None].branch() | 1187 branch = self[None].branch() |
1174 branches = self.branchtags() | 1188 branches = self._branchheads() |
1175 if branch not in branches: | 1189 if branch not in branches: |
1176 return [] | 1190 return [] |
1177 # The basic algorithm is this: | 1191 bheads = branches[branch] |
1178 # | 1192 # the cache returns heads ordered lowest to highest |
1179 # Start from the branch tip since there are no later revisions that can | 1193 bheads.reverse() |
1180 # possibly be in this branch, and the tip is a guaranteed head. | |
1181 # | |
1182 # Remember the tip's parents as the first ancestors, since these by | |
1183 # definition are not heads. | |
1184 # | |
1185 # Step backwards from the brach tip through all the revisions. We are | |
1186 # guaranteed by the rules of Mercurial that we will now be visiting the | |
1187 # nodes in reverse topological order (children before parents). | |
1188 # | |
1189 # If a revision is one of the ancestors of a head then we can toss it | |
1190 # out of the ancestors set (we've already found it and won't be | |
1191 # visiting it again) and put its parents in the ancestors set. | |
1192 # | |
1193 # Otherwise, if a revision is in the branch it's another head, since it | |
1194 # wasn't in the ancestor list of an existing head. So add it to the | |
1195 # head list, and add its parents to the ancestor list. | |
1196 # | |
1197 # If it is not in the branch ignore it. | |
1198 # | |
1199 # Once we have a list of heads, use nodesbetween to filter out all the | |
1200 # heads that cannot be reached from startrev. There may be a more | |
1201 # efficient way to do this as part of the previous algorithm. | |
1202 | |
1203 set = util.set | |
1204 heads = [self.changelog.rev(branches[branch])] | |
1205 # Don't care if ancestors contains nullrev or not. | |
1206 ancestors = set(self.changelog.parentrevs(heads[0])) | |
1207 for rev in xrange(heads[0] - 1, nullrev, -1): | |
1208 if rev in ancestors: | |
1209 ancestors.update(self.changelog.parentrevs(rev)) | |
1210 ancestors.remove(rev) | |
1211 elif self[rev].branch() == branch: | |
1212 heads.append(rev) | |
1213 ancestors.update(self.changelog.parentrevs(rev)) | |
1214 heads = [self.changelog.node(rev) for rev in heads] | |
1215 if start is not None: | 1194 if start is not None: |
1216 heads = self.changelog.nodesbetween([start], heads)[2] | 1195 # filter out the heads that cannot be reached from startrev |
1217 return heads | 1196 bheads = self.changelog.nodesbetween([start], bheads)[2] |
1197 return bheads | |
1218 | 1198 |
1219 def branches(self, nodes): | 1199 def branches(self, nodes): |
1220 if not nodes: | 1200 if not nodes: |
1221 nodes = [self.changelog.tip()] | 1201 nodes = [self.changelog.tip()] |
1222 b = [] | 1202 b = [] |