hgext/remotenames.py
changeset 36061 be72f6420f3c
parent 36060 cabe8ef5c71e
child 36062 382aefea8faf
equal deleted inserted replaced
36060:cabe8ef5c71e 36061:be72f6420f3c
     8 
     8 
     9 """ showing remotebookmarks and remotebranches in UI """
     9 """ showing remotebookmarks and remotebranches in UI """
    10 
    10 
    11 from __future__ import absolute_import
    11 from __future__ import absolute_import
    12 
    12 
       
    13 import UserDict
       
    14 
       
    15 from mercurial.node import (
       
    16     bin,
       
    17 )
    13 from mercurial import (
    18 from mercurial import (
    14     logexchange,
    19     logexchange,
    15 )
    20 )
    16 
    21 
    17 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
    22 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
    18 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
    23 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
    19 # be specifying the version(s) of Mercurial they are tested with, or
    24 # be specifying the version(s) of Mercurial they are tested with, or
    20 # leave the attribute unspecified.
    25 # leave the attribute unspecified.
    21 testedwith = 'ships-with-hg-core'
    26 testedwith = 'ships-with-hg-core'
    22 
    27 
       
    28 class lazyremotenamedict(UserDict.DictMixin):
       
    29     """
       
    30     Read-only dict-like Class to lazily resolve remotename entries
       
    31 
       
    32     We are doing that because remotenames startup was slow.
       
    33     We lazily read the remotenames file once to figure out the potential entries
       
    34     and store them in self.potentialentries. Then when asked to resolve an
       
    35     entry, if it is not in self.potentialentries, then it isn't there, if it
       
    36     is in self.potentialentries we resolve it and store the result in
       
    37     self.cache. We cannot be lazy is when asked all the entries (keys).
       
    38     """
       
    39     def __init__(self, kind, repo):
       
    40         self.cache = {}
       
    41         self.potentialentries = {}
       
    42         self._kind = kind # bookmarks or branches
       
    43         self._repo = repo
       
    44         self.loaded = False
       
    45 
       
    46     def _load(self):
       
    47         """ Read the remotenames file, store entries matching selected kind """
       
    48         self.loaded = True
       
    49         repo = self._repo
       
    50         for node, rpath, rname in logexchange.readremotenamefile(repo,
       
    51                                                                 self._kind):
       
    52             name = rpath + '/' + rname
       
    53             self.potentialentries[name] = (node, rpath, name)
       
    54 
       
    55     def _resolvedata(self, potentialentry):
       
    56         """ Check that the node for potentialentry exists and return it """
       
    57         if not potentialentry in self.potentialentries:
       
    58             return None
       
    59         node, remote, name = self.potentialentries[potentialentry]
       
    60         repo = self._repo
       
    61         binnode = bin(node)
       
    62         # if the node doesn't exist, skip it
       
    63         try:
       
    64             repo.changelog.rev(binnode)
       
    65         except LookupError:
       
    66             return None
       
    67         # Skip closed branches
       
    68         if (self._kind == 'branches' and repo[binnode].closesbranch()):
       
    69             return None
       
    70         return [binnode]
       
    71 
       
    72     def __getitem__(self, key):
       
    73         if not self.loaded:
       
    74             self._load()
       
    75         val = self._fetchandcache(key)
       
    76         if val is not None:
       
    77             return val
       
    78         else:
       
    79             raise KeyError()
       
    80 
       
    81     def _fetchandcache(self, key):
       
    82         if key in self.cache:
       
    83             return self.cache[key]
       
    84         val = self._resolvedata(key)
       
    85         if val is not None:
       
    86             self.cache[key] = val
       
    87             return val
       
    88         else:
       
    89             return None
       
    90 
       
    91     def keys(self):
       
    92         """ Get a list of bookmark or branch names """
       
    93         if not self.loaded:
       
    94             self._load()
       
    95         return self.potentialentries.keys()
       
    96 
       
    97     def iteritems(self):
       
    98         """ Iterate over (name, node) tuples """
       
    99 
       
   100         if not self.loaded:
       
   101             self._load()
       
   102 
       
   103         for k, vtup in self.potentialentries.iteritems():
       
   104             yield (k, [bin(vtup[0])])
       
   105 
    23 class remotenames(dict):
   106 class remotenames(dict):
    24     """
   107     """
    25     This class encapsulates all the remotenames state. It also contains
   108     This class encapsulates all the remotenames state. It also contains
    26     methods to access that state in convenient ways.
   109     methods to access that state in convenient ways. Remotenames are lazy
       
   110     loaded. Whenever client code needs to ensure the freshest copy of
       
   111     remotenames, use the `clearnames` method to force an eventual load.
    27     """
   112     """
    28 
   113 
    29     def __init__(self, repo, *args):
   114     def __init__(self, repo, *args):
    30         dict.__init__(self, *args)
   115         dict.__init__(self, *args)
    31         self._repo = repo
   116         self._repo = repo
    32         self['bookmarks'] = {}
   117         self.clearnames()
    33         self['branches'] = {}
       
    34         self.loadnames()
       
    35         self._loadednames = True
       
    36 
       
    37     def loadnames(self):
       
    38         """ loads the remotenames information from the remotenames file """
       
    39         for rtype in ('bookmarks', 'branches'):
       
    40             for node, rpath, name in logexchange.readremotenamefile(self._repo,
       
    41                                                                     rtype):
       
    42                 rname = rpath + '/' + name
       
    43                 self[rtype][rname] = [node]
       
    44 
   118 
    45     def clearnames(self):
   119     def clearnames(self):
    46         """ Clear all remote names state """
   120         """ Clear all remote names state """
    47         self['bookmarks'] = {}
   121         self['bookmarks'] = lazyremotenamedict("bookmarks", self._repo)
    48         self['branches'] = {}
   122         self['branches'] = lazyremotenamedict("branches", self._repo)
    49         self._invalidatecache()
   123         self._invalidatecache()
    50         self._loadednames = False
       
    51 
   124 
    52     def _invalidatecache(self):
   125     def _invalidatecache(self):
    53         self._nodetobmarks = None
   126         self._nodetobmarks = None
    54         self._nodetobranch = None
   127         self._nodetobranch = None
    55 
   128