##// END OF EJS Templates
remotenames: don't inherit the remotenames class from dict class...
Pulkit Goyal -
r36481:1bd132a0 default
parent child Browse files
Show More
@@ -1,301 +1,300 b''
1 1 # remotenames.py - extension to display remotenames
2 2 #
3 3 # Copyright 2017 Augie Fackler <raf@durin42.com>
4 4 # Copyright 2017 Sean Farley <sean@farley.io>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 """ showing remotebookmarks and remotebranches in UI
10 10
11 11 By default both remotebookmarks and remotebranches are turned on. Config knob to
12 12 control the individually are as follows.
13 13
14 14 Config options to tweak the default behaviour:
15 15
16 16 remotenames.bookmarks
17 17 Boolean value to enable or disable showing of remotebookmarks
18 18
19 19 remotenames.branches
20 20 Boolean value to enable or disable showing of remotebranches
21 21 """
22 22
23 23 from __future__ import absolute_import
24 24
25 25 import collections
26 26
27 27 from mercurial.i18n import _
28 28
29 29 from mercurial.node import (
30 30 bin,
31 31 )
32 32 from mercurial import (
33 33 logexchange,
34 34 namespaces,
35 35 pycompat,
36 36 registrar,
37 37 revsetlang,
38 38 smartset,
39 39 templatekw,
40 40 )
41 41
42 42 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
43 43 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
44 44 # be specifying the version(s) of Mercurial they are tested with, or
45 45 # leave the attribute unspecified.
46 46 testedwith = 'ships-with-hg-core'
47 47
48 48 configtable = {}
49 49 configitem = registrar.configitem(configtable)
50 50 templatekeyword = registrar.templatekeyword()
51 51 revsetpredicate = registrar.revsetpredicate()
52 52
53 53 configitem('remotenames', 'bookmarks',
54 54 default=True,
55 55 )
56 56 configitem('remotenames', 'branches',
57 57 default=True,
58 58 )
59 59
60 60 class lazyremotenamedict(collections.MutableMapping):
61 61 """
62 62 Read-only dict-like Class to lazily resolve remotename entries
63 63
64 64 We are doing that because remotenames startup was slow.
65 65 We lazily read the remotenames file once to figure out the potential entries
66 66 and store them in self.potentialentries. Then when asked to resolve an
67 67 entry, if it is not in self.potentialentries, then it isn't there, if it
68 68 is in self.potentialentries we resolve it and store the result in
69 69 self.cache. We cannot be lazy is when asked all the entries (keys).
70 70 """
71 71 def __init__(self, kind, repo):
72 72 self.cache = {}
73 73 self.potentialentries = {}
74 74 self._kind = kind # bookmarks or branches
75 75 self._repo = repo
76 76 self.loaded = False
77 77
78 78 def _load(self):
79 79 """ Read the remotenames file, store entries matching selected kind """
80 80 self.loaded = True
81 81 repo = self._repo
82 82 for node, rpath, rname in logexchange.readremotenamefile(repo,
83 83 self._kind):
84 84 name = rpath + '/' + rname
85 85 self.potentialentries[name] = (node, rpath, name)
86 86
87 87 def _resolvedata(self, potentialentry):
88 88 """ Check that the node for potentialentry exists and return it """
89 89 if not potentialentry in self.potentialentries:
90 90 return None
91 91 node, remote, name = self.potentialentries[potentialentry]
92 92 repo = self._repo
93 93 binnode = bin(node)
94 94 # if the node doesn't exist, skip it
95 95 try:
96 96 repo.changelog.rev(binnode)
97 97 except LookupError:
98 98 return None
99 99 # Skip closed branches
100 100 if (self._kind == 'branches' and repo[binnode].closesbranch()):
101 101 return None
102 102 return [binnode]
103 103
104 104 def __getitem__(self, key):
105 105 if not self.loaded:
106 106 self._load()
107 107 val = self._fetchandcache(key)
108 108 if val is not None:
109 109 return val
110 110 else:
111 111 raise KeyError()
112 112
113 113 def __iter__(self):
114 114 return iter(self.potentialentries)
115 115
116 116 def __len__(self):
117 117 return len(self.potentialentries)
118 118
119 119 def __setitem__(self):
120 120 raise NotImplementedError
121 121
122 122 def __delitem__(self):
123 123 raise NotImplementedError
124 124
125 125 def _fetchandcache(self, key):
126 126 if key in self.cache:
127 127 return self.cache[key]
128 128 val = self._resolvedata(key)
129 129 if val is not None:
130 130 self.cache[key] = val
131 131 return val
132 132 else:
133 133 return None
134 134
135 135 def keys(self):
136 136 """ Get a list of bookmark or branch names """
137 137 if not self.loaded:
138 138 self._load()
139 139 return self.potentialentries.keys()
140 140
141 141 def iteritems(self):
142 142 """ Iterate over (name, node) tuples """
143 143
144 144 if not self.loaded:
145 145 self._load()
146 146
147 147 for k, vtup in self.potentialentries.iteritems():
148 148 yield (k, [bin(vtup[0])])
149 149
150 class remotenames(dict):
150 class remotenames(object):
151 151 """
152 152 This class encapsulates all the remotenames state. It also contains
153 153 methods to access that state in convenient ways. Remotenames are lazy
154 154 loaded. Whenever client code needs to ensure the freshest copy of
155 155 remotenames, use the `clearnames` method to force an eventual load.
156 156 """
157 157
158 158 def __init__(self, repo, *args):
159 dict.__init__(self, *args)
160 159 self._repo = repo
161 160 self.clearnames()
162 161
163 162 def clearnames(self):
164 163 """ Clear all remote names state """
165 self['bookmarks'] = lazyremotenamedict("bookmarks", self._repo)
166 self['branches'] = lazyremotenamedict("branches", self._repo)
164 self.bookmarks = lazyremotenamedict("bookmarks", self._repo)
165 self.branches = lazyremotenamedict("branches", self._repo)
167 166 self._invalidatecache()
168 167
169 168 def _invalidatecache(self):
170 169 self._nodetobmarks = None
171 170 self._nodetobranch = None
172 171
173 172 def bmarktonodes(self):
174 return self['bookmarks']
173 return self.bookmarks
175 174
176 175 def nodetobmarks(self):
177 176 if not self._nodetobmarks:
178 177 bmarktonodes = self.bmarktonodes()
179 178 self._nodetobmarks = {}
180 179 for name, node in bmarktonodes.iteritems():
181 180 self._nodetobmarks.setdefault(node[0], []).append(name)
182 181 return self._nodetobmarks
183 182
184 183 def branchtonodes(self):
185 return self['branches']
184 return self.branches
186 185
187 186 def nodetobranch(self):
188 187 if not self._nodetobranch:
189 188 branchtonodes = self.branchtonodes()
190 189 self._nodetobranch = {}
191 190 for name, nodes in branchtonodes.iteritems():
192 191 for node in nodes:
193 192 self._nodetobranch.setdefault(node, []).append(name)
194 193 return self._nodetobranch
195 194
196 195 def reposetup(ui, repo):
197 196 if not repo.local():
198 197 return
199 198
200 199 repo._remotenames = remotenames(repo)
201 200 ns = namespaces.namespace
202 201
203 202 if ui.configbool('remotenames', 'bookmarks'):
204 203 remotebookmarkns = ns(
205 204 'remotebookmarks',
206 205 templatename='remotebookmarks',
207 206 colorname='remotebookmark',
208 207 logfmt='remote bookmark: %s\n',
209 208 listnames=lambda repo: repo._remotenames.bmarktonodes().keys(),
210 209 namemap=lambda repo, name:
211 210 repo._remotenames.bmarktonodes().get(name, []),
212 211 nodemap=lambda repo, node:
213 212 repo._remotenames.nodetobmarks().get(node, []))
214 213 repo.names.addnamespace(remotebookmarkns)
215 214
216 215 if ui.configbool('remotenames', 'branches'):
217 216 remotebranchns = ns(
218 217 'remotebranches',
219 218 templatename='remotebranches',
220 219 colorname='remotebranch',
221 220 logfmt='remote branch: %s\n',
222 221 listnames = lambda repo: repo._remotenames.branchtonodes().keys(),
223 222 namemap = lambda repo, name:
224 223 repo._remotenames.branchtonodes().get(name, []),
225 224 nodemap = lambda repo, node:
226 225 repo._remotenames.nodetobranch().get(node, []))
227 226 repo.names.addnamespace(remotebranchns)
228 227
229 228 @templatekeyword('remotenames')
230 229 def remotenameskw(**args):
231 230 """List of strings. Remote names associated with the changeset."""
232 231 args = pycompat.byteskwargs(args)
233 232 repo, ctx = args['repo'], args['ctx']
234 233
235 234 remotenames = []
236 235 if 'remotebookmarks' in repo.names:
237 236 remotenames = repo.names['remotebookmarks'].names(repo, ctx.node())
238 237
239 238 if 'remotebranches' in repo.names:
240 239 remotenames += repo.names['remotebranches'].names(repo, ctx.node())
241 240
242 241 return templatekw.showlist('remotename', remotenames, args,
243 242 plural='remotenames')
244 243
245 244 @templatekeyword('remotebookmarks')
246 245 def remotebookmarkskw(**args):
247 246 """List of strings. Remote bookmarks associated with the changeset."""
248 247 args = pycompat.byteskwargs(args)
249 248 repo, ctx = args['repo'], args['ctx']
250 249
251 250 remotebmarks = []
252 251 if 'remotebookmarks' in repo.names:
253 252 remotebmarks = repo.names['remotebookmarks'].names(repo, ctx.node())
254 253
255 254 return templatekw.showlist('remotebookmark', remotebmarks, args,
256 255 plural='remotebookmarks')
257 256
258 257 @templatekeyword('remotebranches')
259 258 def remotebrancheskw(**args):
260 259 """List of strings. Remote branches associated with the changeset."""
261 260 args = pycompat.byteskwargs(args)
262 261 repo, ctx = args['repo'], args['ctx']
263 262
264 263 remotebranches = []
265 264 if 'remotebranches' in repo.names:
266 265 remotebranches = repo.names['remotebranches'].names(repo, ctx.node())
267 266
268 267 return templatekw.showlist('remotebranch', remotebranches, args,
269 268 plural='remotebranches')
270 269
271 270 def _revsetutil(repo, subset, x, rtypes):
272 271 """utility function to return a set of revs based on the rtypes"""
273 272
274 273 revs = set()
275 274 cl = repo.changelog
276 275 for rtype in rtypes:
277 276 if rtype in repo.names:
278 277 ns = repo.names[rtype]
279 278 for name in ns.listnames(repo):
280 279 revs.update(ns.nodes(repo, name))
281 280
282 281 results = (cl.rev(n) for n in revs if cl.hasnode(n))
283 282 return subset & smartset.baseset(sorted(results))
284 283
285 284 @revsetpredicate('remotenames()')
286 285 def remotenamesrevset(repo, subset, x):
287 286 """All changesets which have a remotename on them."""
288 287 revsetlang.getargs(x, 0, 0, _("remotenames takes no arguments"))
289 288 return _revsetutil(repo, subset, x, ('remotebookmarks', 'remotebranches'))
290 289
291 290 @revsetpredicate('remotebranches()')
292 291 def remotebranchesrevset(repo, subset, x):
293 292 """All changesets which are branch heads on remotes."""
294 293 revsetlang.getargs(x, 0, 0, _("remotebranches takes no arguments"))
295 294 return _revsetutil(repo, subset, x, ('remotebranches',))
296 295
297 296 @revsetpredicate('remotebookmarks()')
298 297 def remotebmarksrevset(repo, subset, x):
299 298 """All changesets which have bookmarks on remotes."""
300 299 revsetlang.getargs(x, 0, 0, _("remotebookmarks takes no arguments"))
301 300 return _revsetutil(repo, subset, x, ('remotebookmarks',))
General Comments 0
You need to be logged in to leave comments. Login now