mercurial/localrepo.py
changeset 7654 816b708f23af
parent 7644 182b7114d35a
child 7655 cce37dab7ad6
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 = []