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 = [] |