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