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