##// END OF EJS Templates
remotenames: port partway to python3 by using collections.MutableMapping...
Augie Fackler -
r36266:e37be270 default
parent child Browse files
Show More
@@ -1,295 +1,307 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 import UserDict
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 class lazyremotenamedict(UserDict.DictMixin):
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 def __iter__(self):
114 return iter(self.potentialentries)
115
116 def __len__(self):
117 return len(self.potentialentries)
118
119 def __setitem__(self):
120 raise NotImplementedError
121
122 def __delitem__(self):
123 raise NotImplementedError
124
113 125 def _fetchandcache(self, key):
114 126 if key in self.cache:
115 127 return self.cache[key]
116 128 val = self._resolvedata(key)
117 129 if val is not None:
118 130 self.cache[key] = val
119 131 return val
120 132 else:
121 133 return None
122 134
123 135 def keys(self):
124 136 """ Get a list of bookmark or branch names """
125 137 if not self.loaded:
126 138 self._load()
127 139 return self.potentialentries.keys()
128 140
129 141 def iteritems(self):
130 142 """ Iterate over (name, node) tuples """
131 143
132 144 if not self.loaded:
133 145 self._load()
134 146
135 147 for k, vtup in self.potentialentries.iteritems():
136 148 yield (k, [bin(vtup[0])])
137 149
138 150 class remotenames(dict):
139 151 """
140 152 This class encapsulates all the remotenames state. It also contains
141 153 methods to access that state in convenient ways. Remotenames are lazy
142 154 loaded. Whenever client code needs to ensure the freshest copy of
143 155 remotenames, use the `clearnames` method to force an eventual load.
144 156 """
145 157
146 158 def __init__(self, repo, *args):
147 159 dict.__init__(self, *args)
148 160 self._repo = repo
149 161 self.clearnames()
150 162
151 163 def clearnames(self):
152 164 """ Clear all remote names state """
153 165 self['bookmarks'] = lazyremotenamedict("bookmarks", self._repo)
154 166 self['branches'] = lazyremotenamedict("branches", self._repo)
155 167 self._invalidatecache()
156 168
157 169 def _invalidatecache(self):
158 170 self._nodetobmarks = None
159 171 self._nodetobranch = None
160 172
161 173 def bmarktonodes(self):
162 174 return self['bookmarks']
163 175
164 176 def nodetobmarks(self):
165 177 if not self._nodetobmarks:
166 178 bmarktonodes = self.bmarktonodes()
167 179 self._nodetobmarks = {}
168 180 for name, node in bmarktonodes.iteritems():
169 181 self._nodetobmarks.setdefault(node[0], []).append(name)
170 182 return self._nodetobmarks
171 183
172 184 def branchtonodes(self):
173 185 return self['branches']
174 186
175 187 def nodetobranch(self):
176 188 if not self._nodetobranch:
177 189 branchtonodes = self.branchtonodes()
178 190 self._nodetobranch = {}
179 191 for name, nodes in branchtonodes.iteritems():
180 192 for node in nodes:
181 193 self._nodetobranch.setdefault(node, []).append(name)
182 194 return self._nodetobranch
183 195
184 196 def reposetup(ui, repo):
185 197 if not repo.local():
186 198 return
187 199
188 200 repo._remotenames = remotenames(repo)
189 201 ns = namespaces.namespace
190 202
191 203 if ui.configbool('remotenames', 'bookmarks'):
192 204 remotebookmarkns = ns(
193 205 'remotebookmarks',
194 206 templatename='remotebookmarks',
195 207 logname='remote bookmark',
196 208 colorname='remotebookmark',
197 209 listnames=lambda repo: repo._remotenames.bmarktonodes().keys(),
198 210 namemap=lambda repo, name:
199 211 repo._remotenames.bmarktonodes().get(name, []),
200 212 nodemap=lambda repo, node:
201 213 repo._remotenames.nodetobmarks().get(node, []))
202 214 repo.names.addnamespace(remotebookmarkns)
203 215
204 216 if ui.configbool('remotenames', 'branches'):
205 217 remotebranchns = ns(
206 218 'remotebranches',
207 219 templatename='remotebranches',
208 220 logname='remote branch',
209 221 colorname='remotebranch',
210 222 listnames = lambda repo: repo._remotenames.branchtonodes().keys(),
211 223 namemap = lambda repo, name:
212 224 repo._remotenames.branchtonodes().get(name, []),
213 225 nodemap = lambda repo, node:
214 226 repo._remotenames.nodetobranch().get(node, []))
215 227 repo.names.addnamespace(remotebranchns)
216 228
217 229 @templatekeyword('remotenames')
218 230 def remotenameskw(**args):
219 231 """:remotenames: List of strings. List of remote names associated with the
220 232 changeset.
221 233 """
222 234 args = pycompat.byteskwargs(args)
223 235 repo, ctx = args['repo'], args['ctx']
224 236
225 237 remotenames = []
226 238 if 'remotebookmarks' in repo.names:
227 239 remotenames = repo.names['remotebookmarks'].names(repo, ctx.node())
228 240
229 241 if 'remotebranches' in repo.names:
230 242 remotenames += repo.names['remotebranches'].names(repo, ctx.node())
231 243
232 244 return templatekw.showlist('remotename', remotenames, args,
233 245 plural='remotenames')
234 246
235 247 @templatekeyword('remotebookmarks')
236 248 def remotebookmarkskw(**args):
237 249 """:remotebookmarks: List of strings. List of remote bookmarks associated
238 250 with the changeset.
239 251 """
240 252 args = pycompat.byteskwargs(args)
241 253 repo, ctx = args['repo'], args['ctx']
242 254
243 255 remotebmarks = []
244 256 if 'remotebookmarks' in repo.names:
245 257 remotebmarks = repo.names['remotebookmarks'].names(repo, ctx.node())
246 258
247 259 return templatekw.showlist('remotebookmark', remotebmarks, args,
248 260 plural='remotebookmarks')
249 261
250 262 @templatekeyword('remotebranches')
251 263 def remotebrancheskw(**args):
252 264 """:remotebranches: List of strings. List of remote branches associated
253 265 with the changeset.
254 266 """
255 267 args = pycompat.byteskwargs(args)
256 268 repo, ctx = args['repo'], args['ctx']
257 269
258 270 remotebranches = []
259 271 if 'remotebranches' in repo.names:
260 272 remotebranches = repo.names['remotebranches'].names(repo, ctx.node())
261 273
262 274 return templatekw.showlist('remotebranch', remotebranches, args,
263 275 plural='remotebranches')
264 276
265 277 def _revsetutil(repo, subset, x, rtypes):
266 278 """utility function to return a set of revs based on the rtypes"""
267 279
268 280 revs = set()
269 281 cl = repo.changelog
270 282 for rtype in rtypes:
271 283 if rtype in repo.names:
272 284 ns = repo.names[rtype]
273 285 for name in ns.listnames(repo):
274 286 revs.update(ns.nodes(repo, name))
275 287
276 288 results = (cl.rev(n) for n in revs if cl.hasnode(n))
277 289 return subset & smartset.baseset(sorted(results))
278 290
279 291 @revsetpredicate('remotenames()')
280 292 def remotenamesrevset(repo, subset, x):
281 293 """All changesets which have a remotename on them."""
282 294 revsetlang.getargs(x, 0, 0, _("remotenames takes no arguments"))
283 295 return _revsetutil(repo, subset, x, ('remotebookmarks', 'remotebranches'))
284 296
285 297 @revsetpredicate('remotebranches()')
286 298 def remotebranchesrevset(repo, subset, x):
287 299 """All changesets which are branch heads on remotes."""
288 300 revsetlang.getargs(x, 0, 0, _("remotebranches takes no arguments"))
289 301 return _revsetutil(repo, subset, x, ('remotebranches',))
290 302
291 303 @revsetpredicate('remotebookmarks()')
292 304 def remotebmarksrevset(repo, subset, x):
293 305 """All changesets which have bookmarks on remotes."""
294 306 revsetlang.getargs(x, 0, 0, _("remotebookmarks takes no arguments"))
295 307 return _revsetutil(repo, subset, x, ('remotebookmarks',))
General Comments 0
You need to be logged in to leave comments. Login now