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 |