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