##// END OF EJS Templates
remotenames: don't call a set of nodes as "revs"
Yuya Nishihara -
r40103:fba7428a default
parent child Browse files
Show More
@@ -1,398 +1,398 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 (EXPERIMENTAL)
9 """ showing remotebookmarks and remotebranches in UI (EXPERIMENTAL)
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 (default: True)
17 Boolean value to enable or disable showing of remotebookmarks (default: True)
18
18
19 remotenames.branches
19 remotenames.branches
20 Boolean value to enable or disable showing of remotebranches (default: True)
20 Boolean value to enable or disable showing of remotebranches (default: True)
21
21
22 remotenames.hoistedpeer
22 remotenames.hoistedpeer
23 Name of the peer whose remotebookmarks should be hoisted into the top-level
23 Name of the peer whose remotebookmarks should be hoisted into the top-level
24 namespace (default: 'default')
24 namespace (default: 'default')
25 """
25 """
26
26
27 from __future__ import absolute_import
27 from __future__ import absolute_import
28
28
29 from mercurial.i18n import _
29 from mercurial.i18n import _
30
30
31 from mercurial.node import (
31 from mercurial.node import (
32 bin,
32 bin,
33 )
33 )
34 from mercurial import (
34 from mercurial import (
35 bookmarks,
35 bookmarks,
36 extensions,
36 extensions,
37 logexchange,
37 logexchange,
38 namespaces,
38 namespaces,
39 pycompat,
39 pycompat,
40 registrar,
40 registrar,
41 revsetlang,
41 revsetlang,
42 smartset,
42 smartset,
43 templateutil,
43 templateutil,
44 util,
44 util,
45 )
45 )
46
46
47 from mercurial.utils import (
47 from mercurial.utils import (
48 stringutil,
48 stringutil,
49 )
49 )
50
50
51 if pycompat.ispy3:
51 if pycompat.ispy3:
52 import collections.abc
52 import collections.abc
53 mutablemapping = collections.abc.MutableMapping
53 mutablemapping = collections.abc.MutableMapping
54 else:
54 else:
55 import collections
55 import collections
56 mutablemapping = collections.MutableMapping
56 mutablemapping = collections.MutableMapping
57
57
58 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
58 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
59 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
59 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
60 # be specifying the version(s) of Mercurial they are tested with, or
60 # be specifying the version(s) of Mercurial they are tested with, or
61 # leave the attribute unspecified.
61 # leave the attribute unspecified.
62 testedwith = 'ships-with-hg-core'
62 testedwith = 'ships-with-hg-core'
63
63
64 configtable = {}
64 configtable = {}
65 configitem = registrar.configitem(configtable)
65 configitem = registrar.configitem(configtable)
66 templatekeyword = registrar.templatekeyword()
66 templatekeyword = registrar.templatekeyword()
67 revsetpredicate = registrar.revsetpredicate()
67 revsetpredicate = registrar.revsetpredicate()
68
68
69 configitem('remotenames', 'bookmarks',
69 configitem('remotenames', 'bookmarks',
70 default=True,
70 default=True,
71 )
71 )
72 configitem('remotenames', 'branches',
72 configitem('remotenames', 'branches',
73 default=True,
73 default=True,
74 )
74 )
75 configitem('remotenames', 'hoistedpeer',
75 configitem('remotenames', 'hoistedpeer',
76 default='default',
76 default='default',
77 )
77 )
78
78
79 class lazyremotenamedict(mutablemapping):
79 class lazyremotenamedict(mutablemapping):
80 """
80 """
81 Read-only dict-like Class to lazily resolve remotename entries
81 Read-only dict-like Class to lazily resolve remotename entries
82
82
83 We are doing that because remotenames startup was slow.
83 We are doing that because remotenames startup was slow.
84 We lazily read the remotenames file once to figure out the potential entries
84 We lazily read the remotenames file once to figure out the potential entries
85 and store them in self.potentialentries. Then when asked to resolve an
85 and store them in self.potentialentries. Then when asked to resolve an
86 entry, if it is not in self.potentialentries, then it isn't there, if it
86 entry, if it is not in self.potentialentries, then it isn't there, if it
87 is in self.potentialentries we resolve it and store the result in
87 is in self.potentialentries we resolve it and store the result in
88 self.cache. We cannot be lazy is when asked all the entries (keys).
88 self.cache. We cannot be lazy is when asked all the entries (keys).
89 """
89 """
90 def __init__(self, kind, repo):
90 def __init__(self, kind, repo):
91 self.cache = {}
91 self.cache = {}
92 self.potentialentries = {}
92 self.potentialentries = {}
93 self._kind = kind # bookmarks or branches
93 self._kind = kind # bookmarks or branches
94 self._repo = repo
94 self._repo = repo
95 self.loaded = False
95 self.loaded = False
96
96
97 def _load(self):
97 def _load(self):
98 """ Read the remotenames file, store entries matching selected kind """
98 """ Read the remotenames file, store entries matching selected kind """
99 self.loaded = True
99 self.loaded = True
100 repo = self._repo
100 repo = self._repo
101 for node, rpath, rname in logexchange.readremotenamefile(repo,
101 for node, rpath, rname in logexchange.readremotenamefile(repo,
102 self._kind):
102 self._kind):
103 name = rpath + '/' + rname
103 name = rpath + '/' + rname
104 self.potentialentries[name] = (node, rpath, name)
104 self.potentialentries[name] = (node, rpath, name)
105
105
106 def _resolvedata(self, potentialentry):
106 def _resolvedata(self, potentialentry):
107 """ Check that the node for potentialentry exists and return it """
107 """ Check that the node for potentialentry exists and return it """
108 if not potentialentry in self.potentialentries:
108 if not potentialentry in self.potentialentries:
109 return None
109 return None
110 node, remote, name = self.potentialentries[potentialentry]
110 node, remote, name = self.potentialentries[potentialentry]
111 repo = self._repo
111 repo = self._repo
112 binnode = bin(node)
112 binnode = bin(node)
113 # if the node doesn't exist, skip it
113 # if the node doesn't exist, skip it
114 try:
114 try:
115 repo.changelog.rev(binnode)
115 repo.changelog.rev(binnode)
116 except LookupError:
116 except LookupError:
117 return None
117 return None
118 # Skip closed branches
118 # Skip closed branches
119 if (self._kind == 'branches' and repo[binnode].closesbranch()):
119 if (self._kind == 'branches' and repo[binnode].closesbranch()):
120 return None
120 return None
121 return [binnode]
121 return [binnode]
122
122
123 def __getitem__(self, key):
123 def __getitem__(self, key):
124 if not self.loaded:
124 if not self.loaded:
125 self._load()
125 self._load()
126 val = self._fetchandcache(key)
126 val = self._fetchandcache(key)
127 if val is not None:
127 if val is not None:
128 return val
128 return val
129 else:
129 else:
130 raise KeyError()
130 raise KeyError()
131
131
132 def __iter__(self):
132 def __iter__(self):
133 return iter(self.potentialentries)
133 return iter(self.potentialentries)
134
134
135 def __len__(self):
135 def __len__(self):
136 return len(self.potentialentries)
136 return len(self.potentialentries)
137
137
138 def __setitem__(self):
138 def __setitem__(self):
139 raise NotImplementedError
139 raise NotImplementedError
140
140
141 def __delitem__(self):
141 def __delitem__(self):
142 raise NotImplementedError
142 raise NotImplementedError
143
143
144 def _fetchandcache(self, key):
144 def _fetchandcache(self, key):
145 if key in self.cache:
145 if key in self.cache:
146 return self.cache[key]
146 return self.cache[key]
147 val = self._resolvedata(key)
147 val = self._resolvedata(key)
148 if val is not None:
148 if val is not None:
149 self.cache[key] = val
149 self.cache[key] = val
150 return val
150 return val
151 else:
151 else:
152 return None
152 return None
153
153
154 def keys(self):
154 def keys(self):
155 """ Get a list of bookmark or branch names """
155 """ Get a list of bookmark or branch names """
156 if not self.loaded:
156 if not self.loaded:
157 self._load()
157 self._load()
158 return self.potentialentries.keys()
158 return self.potentialentries.keys()
159
159
160 def iteritems(self):
160 def iteritems(self):
161 """ Iterate over (name, node) tuples """
161 """ Iterate over (name, node) tuples """
162
162
163 if not self.loaded:
163 if not self.loaded:
164 self._load()
164 self._load()
165
165
166 for k, vtup in self.potentialentries.iteritems():
166 for k, vtup in self.potentialentries.iteritems():
167 yield (k, [bin(vtup[0])])
167 yield (k, [bin(vtup[0])])
168
168
169 class remotenames(object):
169 class remotenames(object):
170 """
170 """
171 This class encapsulates all the remotenames state. It also contains
171 This class encapsulates all the remotenames state. It also contains
172 methods to access that state in convenient ways. Remotenames are lazy
172 methods to access that state in convenient ways. Remotenames are lazy
173 loaded. Whenever client code needs to ensure the freshest copy of
173 loaded. Whenever client code needs to ensure the freshest copy of
174 remotenames, use the `clearnames` method to force an eventual load.
174 remotenames, use the `clearnames` method to force an eventual load.
175 """
175 """
176
176
177 def __init__(self, repo, *args):
177 def __init__(self, repo, *args):
178 self._repo = repo
178 self._repo = repo
179 self.clearnames()
179 self.clearnames()
180
180
181 def clearnames(self):
181 def clearnames(self):
182 """ Clear all remote names state """
182 """ Clear all remote names state """
183 self.bookmarks = lazyremotenamedict("bookmarks", self._repo)
183 self.bookmarks = lazyremotenamedict("bookmarks", self._repo)
184 self.branches = lazyremotenamedict("branches", self._repo)
184 self.branches = lazyremotenamedict("branches", self._repo)
185 self._invalidatecache()
185 self._invalidatecache()
186
186
187 def _invalidatecache(self):
187 def _invalidatecache(self):
188 self._nodetobmarks = None
188 self._nodetobmarks = None
189 self._nodetobranch = None
189 self._nodetobranch = None
190 self._hoisttonodes = None
190 self._hoisttonodes = None
191 self._nodetohoists = None
191 self._nodetohoists = None
192
192
193 def bmarktonodes(self):
193 def bmarktonodes(self):
194 return self.bookmarks
194 return self.bookmarks
195
195
196 def nodetobmarks(self):
196 def nodetobmarks(self):
197 if not self._nodetobmarks:
197 if not self._nodetobmarks:
198 bmarktonodes = self.bmarktonodes()
198 bmarktonodes = self.bmarktonodes()
199 self._nodetobmarks = {}
199 self._nodetobmarks = {}
200 for name, node in bmarktonodes.iteritems():
200 for name, node in bmarktonodes.iteritems():
201 self._nodetobmarks.setdefault(node[0], []).append(name)
201 self._nodetobmarks.setdefault(node[0], []).append(name)
202 return self._nodetobmarks
202 return self._nodetobmarks
203
203
204 def branchtonodes(self):
204 def branchtonodes(self):
205 return self.branches
205 return self.branches
206
206
207 def nodetobranch(self):
207 def nodetobranch(self):
208 if not self._nodetobranch:
208 if not self._nodetobranch:
209 branchtonodes = self.branchtonodes()
209 branchtonodes = self.branchtonodes()
210 self._nodetobranch = {}
210 self._nodetobranch = {}
211 for name, nodes in branchtonodes.iteritems():
211 for name, nodes in branchtonodes.iteritems():
212 for node in nodes:
212 for node in nodes:
213 self._nodetobranch.setdefault(node, []).append(name)
213 self._nodetobranch.setdefault(node, []).append(name)
214 return self._nodetobranch
214 return self._nodetobranch
215
215
216 def hoisttonodes(self, hoist):
216 def hoisttonodes(self, hoist):
217 if not self._hoisttonodes:
217 if not self._hoisttonodes:
218 marktonodes = self.bmarktonodes()
218 marktonodes = self.bmarktonodes()
219 self._hoisttonodes = {}
219 self._hoisttonodes = {}
220 hoist += '/'
220 hoist += '/'
221 for name, node in marktonodes.iteritems():
221 for name, node in marktonodes.iteritems():
222 if name.startswith(hoist):
222 if name.startswith(hoist):
223 name = name[len(hoist):]
223 name = name[len(hoist):]
224 self._hoisttonodes[name] = node
224 self._hoisttonodes[name] = node
225 return self._hoisttonodes
225 return self._hoisttonodes
226
226
227 def nodetohoists(self, hoist):
227 def nodetohoists(self, hoist):
228 if not self._nodetohoists:
228 if not self._nodetohoists:
229 marktonodes = self.bmarktonodes()
229 marktonodes = self.bmarktonodes()
230 self._nodetohoists = {}
230 self._nodetohoists = {}
231 hoist += '/'
231 hoist += '/'
232 for name, node in marktonodes.iteritems():
232 for name, node in marktonodes.iteritems():
233 if name.startswith(hoist):
233 if name.startswith(hoist):
234 name = name[len(hoist):]
234 name = name[len(hoist):]
235 self._nodetohoists.setdefault(node[0], []).append(name)
235 self._nodetohoists.setdefault(node[0], []).append(name)
236 return self._nodetohoists
236 return self._nodetohoists
237
237
238 def wrapprintbookmarks(orig, ui, repo, fm, bmarks):
238 def wrapprintbookmarks(orig, ui, repo, fm, bmarks):
239 if 'remotebookmarks' not in repo.names:
239 if 'remotebookmarks' not in repo.names:
240 return
240 return
241 ns = repo.names['remotebookmarks']
241 ns = repo.names['remotebookmarks']
242
242
243 for name in ns.listnames(repo):
243 for name in ns.listnames(repo):
244 nodes = ns.nodes(repo, name)
244 nodes = ns.nodes(repo, name)
245 if not nodes:
245 if not nodes:
246 continue
246 continue
247 node = nodes[0]
247 node = nodes[0]
248
248
249 bmarks[name] = (node, ' ', '')
249 bmarks[name] = (node, ' ', '')
250
250
251 return orig(ui, repo, fm, bmarks)
251 return orig(ui, repo, fm, bmarks)
252
252
253 def extsetup(ui):
253 def extsetup(ui):
254 extensions.wrapfunction(bookmarks, '_printbookmarks', wrapprintbookmarks)
254 extensions.wrapfunction(bookmarks, '_printbookmarks', wrapprintbookmarks)
255
255
256 def reposetup(ui, repo):
256 def reposetup(ui, repo):
257
257
258 # set the config option to store remotenames
258 # set the config option to store remotenames
259 repo.ui.setconfig('experimental', 'remotenames', True, 'remotenames-ext')
259 repo.ui.setconfig('experimental', 'remotenames', True, 'remotenames-ext')
260
260
261 if not repo.local():
261 if not repo.local():
262 return
262 return
263
263
264 repo._remotenames = remotenames(repo)
264 repo._remotenames = remotenames(repo)
265 ns = namespaces.namespace
265 ns = namespaces.namespace
266
266
267 if ui.configbool('remotenames', 'bookmarks'):
267 if ui.configbool('remotenames', 'bookmarks'):
268 remotebookmarkns = ns(
268 remotebookmarkns = ns(
269 'remotebookmarks',
269 'remotebookmarks',
270 templatename='remotebookmarks',
270 templatename='remotebookmarks',
271 colorname='remotebookmark',
271 colorname='remotebookmark',
272 logfmt='remote bookmark: %s\n',
272 logfmt='remote bookmark: %s\n',
273 listnames=lambda repo: repo._remotenames.bmarktonodes().keys(),
273 listnames=lambda repo: repo._remotenames.bmarktonodes().keys(),
274 namemap=lambda repo, name:
274 namemap=lambda repo, name:
275 repo._remotenames.bmarktonodes().get(name, []),
275 repo._remotenames.bmarktonodes().get(name, []),
276 nodemap=lambda repo, node:
276 nodemap=lambda repo, node:
277 repo._remotenames.nodetobmarks().get(node, []))
277 repo._remotenames.nodetobmarks().get(node, []))
278 repo.names.addnamespace(remotebookmarkns)
278 repo.names.addnamespace(remotebookmarkns)
279
279
280 # hoisting only works if there are remote bookmarks
280 # hoisting only works if there are remote bookmarks
281 hoist = ui.config('remotenames', 'hoistedpeer')
281 hoist = ui.config('remotenames', 'hoistedpeer')
282 if hoist:
282 if hoist:
283 hoistednamens = ns(
283 hoistednamens = ns(
284 'hoistednames',
284 'hoistednames',
285 templatename='hoistednames',
285 templatename='hoistednames',
286 colorname='hoistedname',
286 colorname='hoistedname',
287 logfmt='hoisted name: %s\n',
287 logfmt='hoisted name: %s\n',
288 listnames = lambda repo:
288 listnames = lambda repo:
289 repo._remotenames.hoisttonodes(hoist).keys(),
289 repo._remotenames.hoisttonodes(hoist).keys(),
290 namemap = lambda repo, name:
290 namemap = lambda repo, name:
291 repo._remotenames.hoisttonodes(hoist).get(name, []),
291 repo._remotenames.hoisttonodes(hoist).get(name, []),
292 nodemap = lambda repo, node:
292 nodemap = lambda repo, node:
293 repo._remotenames.nodetohoists(hoist).get(node, []))
293 repo._remotenames.nodetohoists(hoist).get(node, []))
294 repo.names.addnamespace(hoistednamens)
294 repo.names.addnamespace(hoistednamens)
295
295
296 if ui.configbool('remotenames', 'branches'):
296 if ui.configbool('remotenames', 'branches'):
297 remotebranchns = ns(
297 remotebranchns = ns(
298 'remotebranches',
298 'remotebranches',
299 templatename='remotebranches',
299 templatename='remotebranches',
300 colorname='remotebranch',
300 colorname='remotebranch',
301 logfmt='remote branch: %s\n',
301 logfmt='remote branch: %s\n',
302 listnames = lambda repo: repo._remotenames.branchtonodes().keys(),
302 listnames = lambda repo: repo._remotenames.branchtonodes().keys(),
303 namemap = lambda repo, name:
303 namemap = lambda repo, name:
304 repo._remotenames.branchtonodes().get(name, []),
304 repo._remotenames.branchtonodes().get(name, []),
305 nodemap = lambda repo, node:
305 nodemap = lambda repo, node:
306 repo._remotenames.nodetobranch().get(node, []))
306 repo._remotenames.nodetobranch().get(node, []))
307 repo.names.addnamespace(remotebranchns)
307 repo.names.addnamespace(remotebranchns)
308
308
309 @templatekeyword('remotenames', requires={'repo', 'ctx'})
309 @templatekeyword('remotenames', requires={'repo', 'ctx'})
310 def remotenameskw(context, mapping):
310 def remotenameskw(context, mapping):
311 """List of strings. Remote names associated with the changeset."""
311 """List of strings. Remote names associated with the changeset."""
312 repo = context.resource(mapping, 'repo')
312 repo = context.resource(mapping, 'repo')
313 ctx = context.resource(mapping, 'ctx')
313 ctx = context.resource(mapping, 'ctx')
314
314
315 remotenames = []
315 remotenames = []
316 if 'remotebookmarks' in repo.names:
316 if 'remotebookmarks' in repo.names:
317 remotenames = repo.names['remotebookmarks'].names(repo, ctx.node())
317 remotenames = repo.names['remotebookmarks'].names(repo, ctx.node())
318
318
319 if 'remotebranches' in repo.names:
319 if 'remotebranches' in repo.names:
320 remotenames += repo.names['remotebranches'].names(repo, ctx.node())
320 remotenames += repo.names['remotebranches'].names(repo, ctx.node())
321
321
322 return templateutil.compatlist(context, mapping, 'remotename', remotenames,
322 return templateutil.compatlist(context, mapping, 'remotename', remotenames,
323 plural='remotenames')
323 plural='remotenames')
324
324
325 @templatekeyword('remotebookmarks', requires={'repo', 'ctx'})
325 @templatekeyword('remotebookmarks', requires={'repo', 'ctx'})
326 def remotebookmarkskw(context, mapping):
326 def remotebookmarkskw(context, mapping):
327 """List of strings. Remote bookmarks associated with the changeset."""
327 """List of strings. Remote bookmarks associated with the changeset."""
328 repo = context.resource(mapping, 'repo')
328 repo = context.resource(mapping, 'repo')
329 ctx = context.resource(mapping, 'ctx')
329 ctx = context.resource(mapping, 'ctx')
330
330
331 remotebmarks = []
331 remotebmarks = []
332 if 'remotebookmarks' in repo.names:
332 if 'remotebookmarks' in repo.names:
333 remotebmarks = repo.names['remotebookmarks'].names(repo, ctx.node())
333 remotebmarks = repo.names['remotebookmarks'].names(repo, ctx.node())
334
334
335 return templateutil.compatlist(context, mapping, 'remotebookmark',
335 return templateutil.compatlist(context, mapping, 'remotebookmark',
336 remotebmarks, plural='remotebookmarks')
336 remotebmarks, plural='remotebookmarks')
337
337
338 @templatekeyword('remotebranches', requires={'repo', 'ctx'})
338 @templatekeyword('remotebranches', requires={'repo', 'ctx'})
339 def remotebrancheskw(context, mapping):
339 def remotebrancheskw(context, mapping):
340 """List of strings. Remote branches associated with the changeset."""
340 """List of strings. Remote branches associated with the changeset."""
341 repo = context.resource(mapping, 'repo')
341 repo = context.resource(mapping, 'repo')
342 ctx = context.resource(mapping, 'ctx')
342 ctx = context.resource(mapping, 'ctx')
343
343
344 remotebranches = []
344 remotebranches = []
345 if 'remotebranches' in repo.names:
345 if 'remotebranches' in repo.names:
346 remotebranches = repo.names['remotebranches'].names(repo, ctx.node())
346 remotebranches = repo.names['remotebranches'].names(repo, ctx.node())
347
347
348 return templateutil.compatlist(context, mapping, 'remotebranch',
348 return templateutil.compatlist(context, mapping, 'remotebranch',
349 remotebranches, plural='remotebranches')
349 remotebranches, plural='remotebranches')
350
350
351 def _revsetutil(repo, subset, x, rtypes):
351 def _revsetutil(repo, subset, x, rtypes):
352 """utility function to return a set of revs based on the rtypes"""
352 """utility function to return a set of revs based on the rtypes"""
353 args = revsetlang.getargs(x, 0, 1, _('only one argument accepted'))
353 args = revsetlang.getargs(x, 0, 1, _('only one argument accepted'))
354 if args:
354 if args:
355 kind, pattern, matcher = stringutil.stringmatcher(
355 kind, pattern, matcher = stringutil.stringmatcher(
356 revsetlang.getstring(args[0], _('argument must be a string')))
356 revsetlang.getstring(args[0], _('argument must be a string')))
357 else:
357 else:
358 matcher = util.always
358 matcher = util.always
359
359
360 revs = set()
360 nodes = set()
361 cl = repo.changelog
361 cl = repo.changelog
362 for rtype in rtypes:
362 for rtype in rtypes:
363 if rtype in repo.names:
363 if rtype in repo.names:
364 ns = repo.names[rtype]
364 ns = repo.names[rtype]
365 for name in ns.listnames(repo):
365 for name in ns.listnames(repo):
366 if not matcher(name):
366 if not matcher(name):
367 continue
367 continue
368 revs.update(ns.nodes(repo, name))
368 nodes.update(ns.nodes(repo, name))
369
369
370 results = (cl.rev(n) for n in revs if cl.hasnode(n))
370 revs = (cl.rev(n) for n in nodes if cl.hasnode(n))
371 return subset & smartset.baseset(sorted(results))
371 return subset & smartset.baseset(sorted(revs))
372
372
373 @revsetpredicate('remotenames([name])')
373 @revsetpredicate('remotenames([name])')
374 def remotenamesrevset(repo, subset, x):
374 def remotenamesrevset(repo, subset, x):
375 """All changesets which have a remotename on them. If `name` is
375 """All changesets which have a remotename on them. If `name` is
376 specified, only remotenames of matching remote paths are considered.
376 specified, only remotenames of matching remote paths are considered.
377
377
378 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
378 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
379 """
379 """
380 return _revsetutil(repo, subset, x, ('remotebookmarks', 'remotebranches'))
380 return _revsetutil(repo, subset, x, ('remotebookmarks', 'remotebranches'))
381
381
382 @revsetpredicate('remotebranches([name])')
382 @revsetpredicate('remotebranches([name])')
383 def remotebranchesrevset(repo, subset, x):
383 def remotebranchesrevset(repo, subset, x):
384 """All changesets which are branch heads on remotes. If `name` is
384 """All changesets which are branch heads on remotes. If `name` is
385 specified, only remotenames of matching remote paths are considered.
385 specified, only remotenames of matching remote paths are considered.
386
386
387 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
387 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
388 """
388 """
389 return _revsetutil(repo, subset, x, ('remotebranches',))
389 return _revsetutil(repo, subset, x, ('remotebranches',))
390
390
391 @revsetpredicate('remotebookmarks([name])')
391 @revsetpredicate('remotebookmarks([name])')
392 def remotebmarksrevset(repo, subset, x):
392 def remotebmarksrevset(repo, subset, x):
393 """All changesets which have bookmarks on remotes. If `name` is
393 """All changesets which have bookmarks on remotes. If `name` is
394 specified, only remotenames of matching remote paths are considered.
394 specified, only remotenames of matching remote paths are considered.
395
395
396 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
396 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
397 """
397 """
398 return _revsetutil(repo, subset, x, ('remotebookmarks',))
398 return _revsetutil(repo, subset, x, ('remotebookmarks',))
General Comments 0
You need to be logged in to leave comments. Login now