Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/revset.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | 911e25dc9d8c |
children | 687b865b95ad |
comparison
equal
deleted
inserted
replaced
43075:57875cf423c9 | 43076:2372284d9457 |
---|---|
91 # ^^^^^^ | 91 # ^^^^^^ |
92 # For most revsets, 'define' means using the order this subset provides | 92 # For most revsets, 'define' means using the order this subset provides |
93 # | 93 # |
94 # There are a few revsets that always redefine the order if 'define' is | 94 # There are a few revsets that always redefine the order if 'define' is |
95 # specified: 'sort(X)', 'reverse(X)', 'x:y'. | 95 # specified: 'sort(X)', 'reverse(X)', 'x:y'. |
96 anyorder = 'any' # don't care the order, could be even random-shuffled | 96 anyorder = 'any' # don't care the order, could be even random-shuffled |
97 defineorder = 'define' # ALWAYS redefine, or ALWAYS follow the current order | 97 defineorder = 'define' # ALWAYS redefine, or ALWAYS follow the current order |
98 followorder = 'follow' # MUST follow the current order | 98 followorder = 'follow' # MUST follow the current order |
99 | 99 |
100 # helpers | 100 # helpers |
101 | |
101 | 102 |
102 def getset(repo, subset, x, order=defineorder): | 103 def getset(repo, subset, x, order=defineorder): |
103 if not x: | 104 if not x: |
104 raise error.ParseError(_("missing argument")) | 105 raise error.ParseError(_("missing argument")) |
105 return methods[x[0]](repo, subset, *x[1:], order=order) | 106 return methods[x[0]](repo, subset, *x[1:], order=order) |
107 | |
106 | 108 |
107 def _getrevsource(repo, r): | 109 def _getrevsource(repo, r): |
108 extra = repo[r].extra() | 110 extra = repo[r].extra() |
109 for label in ('source', 'transplant_source', 'rebase_source'): | 111 for label in ('source', 'transplant_source', 'rebase_source'): |
110 if label in extra: | 112 if label in extra: |
112 return repo[extra[label]].rev() | 114 return repo[extra[label]].rev() |
113 except error.RepoLookupError: | 115 except error.RepoLookupError: |
114 pass | 116 pass |
115 return None | 117 return None |
116 | 118 |
119 | |
117 def _sortedb(xs): | 120 def _sortedb(xs): |
118 return sorted(pycompat.rapply(pycompat.maybebytestr, xs)) | 121 return sorted(pycompat.rapply(pycompat.maybebytestr, xs)) |
119 | 122 |
123 | |
120 # operator methods | 124 # operator methods |
125 | |
121 | 126 |
122 def stringset(repo, subset, x, order): | 127 def stringset(repo, subset, x, order): |
123 if not x: | 128 if not x: |
124 raise error.ParseError(_("empty string is not a valid revision")) | 129 raise error.ParseError(_("empty string is not a valid revision")) |
125 x = scmutil.intrev(scmutil.revsymbol(repo, x)) | 130 x = scmutil.intrev(scmutil.revsymbol(repo, x)) |
126 if x in subset or x in _virtualrevs and isinstance(subset, fullreposet): | 131 if x in subset or x in _virtualrevs and isinstance(subset, fullreposet): |
127 return baseset([x]) | 132 return baseset([x]) |
128 return baseset() | 133 return baseset() |
129 | 134 |
135 | |
130 def rawsmartset(repo, subset, x, order): | 136 def rawsmartset(repo, subset, x, order): |
131 """argument is already a smartset, use that directly""" | 137 """argument is already a smartset, use that directly""" |
132 if order == followorder: | 138 if order == followorder: |
133 return subset & x | 139 return subset & x |
134 else: | 140 else: |
135 return x & subset | 141 return x & subset |
136 | 142 |
143 | |
137 def rangeset(repo, subset, x, y, order): | 144 def rangeset(repo, subset, x, y, order): |
138 m = getset(repo, fullreposet(repo), x) | 145 m = getset(repo, fullreposet(repo), x) |
139 n = getset(repo, fullreposet(repo), y) | 146 n = getset(repo, fullreposet(repo), y) |
140 | 147 |
141 if not m or not n: | 148 if not m or not n: |
142 return baseset() | 149 return baseset() |
143 return _makerangeset(repo, subset, m.first(), n.last(), order) | 150 return _makerangeset(repo, subset, m.first(), n.last(), order) |
144 | 151 |
152 | |
145 def rangeall(repo, subset, x, order): | 153 def rangeall(repo, subset, x, order): |
146 assert x is None | 154 assert x is None |
147 return _makerangeset(repo, subset, 0, repo.changelog.tiprev(), order) | 155 return _makerangeset(repo, subset, 0, repo.changelog.tiprev(), order) |
156 | |
148 | 157 |
149 def rangepre(repo, subset, y, order): | 158 def rangepre(repo, subset, y, order): |
150 # ':y' can't be rewritten to '0:y' since '0' may be hidden | 159 # ':y' can't be rewritten to '0:y' since '0' may be hidden |
151 n = getset(repo, fullreposet(repo), y) | 160 n = getset(repo, fullreposet(repo), y) |
152 if not n: | 161 if not n: |
153 return baseset() | 162 return baseset() |
154 return _makerangeset(repo, subset, 0, n.last(), order) | 163 return _makerangeset(repo, subset, 0, n.last(), order) |
155 | 164 |
165 | |
156 def rangepost(repo, subset, x, order): | 166 def rangepost(repo, subset, x, order): |
157 m = getset(repo, fullreposet(repo), x) | 167 m = getset(repo, fullreposet(repo), x) |
158 if not m: | 168 if not m: |
159 return baseset() | 169 return baseset() |
160 return _makerangeset(repo, subset, m.first(), repo.changelog.tiprev(), | 170 return _makerangeset( |
161 order) | 171 repo, subset, m.first(), repo.changelog.tiprev(), order |
172 ) | |
173 | |
162 | 174 |
163 def _makerangeset(repo, subset, m, n, order): | 175 def _makerangeset(repo, subset, m, n, order): |
164 if m == n: | 176 if m == n: |
165 r = baseset([m]) | 177 r = baseset([m]) |
166 elif n == node.wdirrev: | 178 elif n == node.wdirrev: |
176 return r & subset | 188 return r & subset |
177 else: | 189 else: |
178 # carrying the sorting over when possible would be more efficient | 190 # carrying the sorting over when possible would be more efficient |
179 return subset & r | 191 return subset & r |
180 | 192 |
193 | |
181 def dagrange(repo, subset, x, y, order): | 194 def dagrange(repo, subset, x, y, order): |
182 r = fullreposet(repo) | 195 r = fullreposet(repo) |
183 xs = dagop.reachableroots(repo, getset(repo, r, x), getset(repo, r, y), | 196 xs = dagop.reachableroots( |
184 includepath=True) | 197 repo, getset(repo, r, x), getset(repo, r, y), includepath=True |
198 ) | |
185 return subset & xs | 199 return subset & xs |
200 | |
186 | 201 |
187 def andset(repo, subset, x, y, order): | 202 def andset(repo, subset, x, y, order): |
188 if order == anyorder: | 203 if order == anyorder: |
189 yorder = anyorder | 204 yorder = anyorder |
190 else: | 205 else: |
191 yorder = followorder | 206 yorder = followorder |
192 return getset(repo, getset(repo, subset, x, order), y, yorder) | 207 return getset(repo, getset(repo, subset, x, order), y, yorder) |
208 | |
193 | 209 |
194 def andsmallyset(repo, subset, x, y, order): | 210 def andsmallyset(repo, subset, x, y, order): |
195 # 'andsmally(x, y)' is equivalent to 'and(x, y)', but faster when y is small | 211 # 'andsmally(x, y)' is equivalent to 'and(x, y)', but faster when y is small |
196 if order == anyorder: | 212 if order == anyorder: |
197 yorder = anyorder | 213 yorder = anyorder |
198 else: | 214 else: |
199 yorder = followorder | 215 yorder = followorder |
200 return getset(repo, getset(repo, subset, y, yorder), x, order) | 216 return getset(repo, getset(repo, subset, y, yorder), x, order) |
201 | 217 |
218 | |
202 def differenceset(repo, subset, x, y, order): | 219 def differenceset(repo, subset, x, y, order): |
203 return getset(repo, subset, x, order) - getset(repo, subset, y, anyorder) | 220 return getset(repo, subset, x, order) - getset(repo, subset, y, anyorder) |
221 | |
204 | 222 |
205 def _orsetlist(repo, subset, xs, order): | 223 def _orsetlist(repo, subset, xs, order): |
206 assert xs | 224 assert xs |
207 if len(xs) == 1: | 225 if len(xs) == 1: |
208 return getset(repo, subset, xs[0], order) | 226 return getset(repo, subset, xs[0], order) |
209 p = len(xs) // 2 | 227 p = len(xs) // 2 |
210 a = _orsetlist(repo, subset, xs[:p], order) | 228 a = _orsetlist(repo, subset, xs[:p], order) |
211 b = _orsetlist(repo, subset, xs[p:], order) | 229 b = _orsetlist(repo, subset, xs[p:], order) |
212 return a + b | 230 return a + b |
231 | |
213 | 232 |
214 def orset(repo, subset, x, order): | 233 def orset(repo, subset, x, order): |
215 xs = getlist(x) | 234 xs = getlist(x) |
216 if not xs: | 235 if not xs: |
217 return baseset() | 236 return baseset() |
219 # slow path to take the subset order | 238 # slow path to take the subset order |
220 return subset & _orsetlist(repo, fullreposet(repo), xs, anyorder) | 239 return subset & _orsetlist(repo, fullreposet(repo), xs, anyorder) |
221 else: | 240 else: |
222 return _orsetlist(repo, subset, xs, order) | 241 return _orsetlist(repo, subset, xs, order) |
223 | 242 |
243 | |
224 def notset(repo, subset, x, order): | 244 def notset(repo, subset, x, order): |
225 return subset - getset(repo, subset, x, anyorder) | 245 return subset - getset(repo, subset, x, anyorder) |
226 | 246 |
247 | |
227 def relationset(repo, subset, x, y, order): | 248 def relationset(repo, subset, x, y, order): |
228 raise error.ParseError(_("can't use a relation in this context")) | 249 raise error.ParseError(_("can't use a relation in this context")) |
250 | |
229 | 251 |
230 def _splitrange(a, b): | 252 def _splitrange(a, b): |
231 """Split range with bounds a and b into two ranges at 0 and return two | 253 """Split range with bounds a and b into two ranges at 0 and return two |
232 tuples of numbers for use as startdepth and stopdepth arguments of | 254 tuples of numbers for use as startdepth and stopdepth arguments of |
233 revancestors and revdescendants. | 255 revancestors and revdescendants. |
255 ancdepths = (-min(b, 0), -a + 1) | 277 ancdepths = (-min(b, 0), -a + 1) |
256 if b > 0: | 278 if b > 0: |
257 descdepths = (max(a, 0), b + 1) | 279 descdepths = (max(a, 0), b + 1) |
258 return ancdepths, descdepths | 280 return ancdepths, descdepths |
259 | 281 |
282 | |
260 def generationsrel(repo, subset, x, rel, z, order): | 283 def generationsrel(repo, subset, x, rel, z, order): |
261 # TODO: rewrite tests, and drop startdepth argument from ancestors() and | 284 # TODO: rewrite tests, and drop startdepth argument from ancestors() and |
262 # descendants() predicates | 285 # descendants() predicates |
263 a, b = getintrange(z, | 286 a, b = getintrange( |
264 _('relation subscript must be an integer or a range'), | 287 z, |
265 _('relation subscript bounds must be integers'), | 288 _('relation subscript must be an integer or a range'), |
266 deffirst=-(dagop.maxlogdepth - 1), | 289 _('relation subscript bounds must be integers'), |
267 deflast=+(dagop.maxlogdepth - 1)) | 290 deffirst=-(dagop.maxlogdepth - 1), |
291 deflast=+(dagop.maxlogdepth - 1), | |
292 ) | |
268 (ancstart, ancstop), (descstart, descstop) = _splitrange(a, b) | 293 (ancstart, ancstop), (descstart, descstop) = _splitrange(a, b) |
269 | 294 |
270 if ancstart is None and descstart is None: | 295 if ancstart is None and descstart is None: |
271 return baseset() | 296 return baseset() |
272 | 297 |
282 elif descstart is not None: | 307 elif descstart is not None: |
283 s = dagop.revdescendants(repo, revs, False, descstart, descstop) | 308 s = dagop.revdescendants(repo, revs, False, descstart, descstop) |
284 | 309 |
285 return subset & s | 310 return subset & s |
286 | 311 |
312 | |
287 def relsubscriptset(repo, subset, x, y, z, order): | 313 def relsubscriptset(repo, subset, x, y, z, order): |
288 # this is pretty basic implementation of 'x#y[z]' operator, still | 314 # this is pretty basic implementation of 'x#y[z]' operator, still |
289 # experimental so undocumented. see the wiki for further ideas. | 315 # experimental so undocumented. see the wiki for further ideas. |
290 # https://www.mercurial-scm.org/wiki/RevsetOperatorPlan | 316 # https://www.mercurial-scm.org/wiki/RevsetOperatorPlan |
291 rel = getsymbol(y) | 317 rel = getsymbol(y) |
293 return subscriptrelations[rel](repo, subset, x, rel, z, order) | 319 return subscriptrelations[rel](repo, subset, x, rel, z, order) |
294 | 320 |
295 relnames = [r for r in subscriptrelations.keys() if len(r) > 1] | 321 relnames = [r for r in subscriptrelations.keys() if len(r) > 1] |
296 raise error.UnknownIdentifier(rel, relnames) | 322 raise error.UnknownIdentifier(rel, relnames) |
297 | 323 |
324 | |
298 def subscriptset(repo, subset, x, y, order): | 325 def subscriptset(repo, subset, x, y, order): |
299 raise error.ParseError(_("can't use a subscript in this context")) | 326 raise error.ParseError(_("can't use a subscript in this context")) |
300 | 327 |
328 | |
301 def listset(repo, subset, *xs, **opts): | 329 def listset(repo, subset, *xs, **opts): |
302 raise error.ParseError(_("can't use a list in this context"), | 330 raise error.ParseError( |
303 hint=_('see \'hg help "revsets.x or y"\'')) | 331 _("can't use a list in this context"), |
332 hint=_('see \'hg help "revsets.x or y"\''), | |
333 ) | |
334 | |
304 | 335 |
305 def keyvaluepair(repo, subset, k, v, order): | 336 def keyvaluepair(repo, subset, k, v, order): |
306 raise error.ParseError(_("can't use a key-value pair in this context")) | 337 raise error.ParseError(_("can't use a key-value pair in this context")) |
338 | |
307 | 339 |
308 def func(repo, subset, a, b, order): | 340 def func(repo, subset, a, b, order): |
309 f = getsymbol(a) | 341 f = getsymbol(a) |
310 if f in symbols: | 342 if f in symbols: |
311 func = symbols[f] | 343 func = symbols[f] |
316 keep = lambda fn: getattr(fn, '__doc__', None) is not None | 348 keep = lambda fn: getattr(fn, '__doc__', None) is not None |
317 | 349 |
318 syms = [s for (s, fn) in symbols.items() if keep(fn)] | 350 syms = [s for (s, fn) in symbols.items() if keep(fn)] |
319 raise error.UnknownIdentifier(f, syms) | 351 raise error.UnknownIdentifier(f, syms) |
320 | 352 |
353 | |
321 # functions | 354 # functions |
322 | 355 |
323 # symbols are callables like: | 356 # symbols are callables like: |
324 # fn(repo, subset, x) | 357 # fn(repo, subset, x) |
325 # with: | 358 # with: |
333 # functions that just return a lot of changesets (like all) don't count here | 366 # functions that just return a lot of changesets (like all) don't count here |
334 safesymbols = set() | 367 safesymbols = set() |
335 | 368 |
336 predicate = registrar.revsetpredicate() | 369 predicate = registrar.revsetpredicate() |
337 | 370 |
371 | |
338 @predicate('_destupdate') | 372 @predicate('_destupdate') |
339 def _destupdate(repo, subset, x): | 373 def _destupdate(repo, subset, x): |
340 # experimental revset for update destination | 374 # experimental revset for update destination |
341 args = getargsdict(x, 'limit', 'clean') | 375 args = getargsdict(x, 'limit', 'clean') |
342 return subset & baseset([destutil.destupdate(repo, | 376 return subset & baseset( |
343 **pycompat.strkwargs(args))[0]]) | 377 [destutil.destupdate(repo, **pycompat.strkwargs(args))[0]] |
378 ) | |
379 | |
344 | 380 |
345 @predicate('_destmerge') | 381 @predicate('_destmerge') |
346 def _destmerge(repo, subset, x): | 382 def _destmerge(repo, subset, x): |
347 # experimental revset for merge destination | 383 # experimental revset for merge destination |
348 sourceset = None | 384 sourceset = None |
349 if x is not None: | 385 if x is not None: |
350 sourceset = getset(repo, fullreposet(repo), x) | 386 sourceset = getset(repo, fullreposet(repo), x) |
351 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)]) | 387 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)]) |
352 | 388 |
389 | |
353 @predicate('adds(pattern)', safe=True, weight=30) | 390 @predicate('adds(pattern)', safe=True, weight=30) |
354 def adds(repo, subset, x): | 391 def adds(repo, subset, x): |
355 """Changesets that add a file matching pattern. | 392 """Changesets that add a file matching pattern. |
356 | 393 |
357 The pattern without explicit kind like ``glob:`` is expected to be | 394 The pattern without explicit kind like ``glob:`` is expected to be |
359 directory. | 396 directory. |
360 """ | 397 """ |
361 # i18n: "adds" is a keyword | 398 # i18n: "adds" is a keyword |
362 pat = getstring(x, _("adds requires a pattern")) | 399 pat = getstring(x, _("adds requires a pattern")) |
363 return checkstatus(repo, subset, pat, 1) | 400 return checkstatus(repo, subset, pat, 1) |
401 | |
364 | 402 |
365 @predicate('ancestor(*changeset)', safe=True, weight=0.5) | 403 @predicate('ancestor(*changeset)', safe=True, weight=0.5) |
366 def ancestor(repo, subset, x): | 404 def ancestor(repo, subset, x): |
367 """A greatest common ancestor of the changesets. | 405 """A greatest common ancestor of the changesets. |
368 | 406 |
381 r = scmutil.intrev(anc) | 419 r = scmutil.intrev(anc) |
382 if r in subset: | 420 if r in subset: |
383 return baseset([r]) | 421 return baseset([r]) |
384 return baseset() | 422 return baseset() |
385 | 423 |
386 def _ancestors(repo, subset, x, followfirst=False, startdepth=None, | 424 |
387 stopdepth=None): | 425 def _ancestors( |
426 repo, subset, x, followfirst=False, startdepth=None, stopdepth=None | |
427 ): | |
388 heads = getset(repo, fullreposet(repo), x) | 428 heads = getset(repo, fullreposet(repo), x) |
389 if not heads: | 429 if not heads: |
390 return baseset() | 430 return baseset() |
391 s = dagop.revancestors(repo, heads, followfirst, startdepth, stopdepth) | 431 s = dagop.revancestors(repo, heads, followfirst, startdepth, stopdepth) |
392 return subset & s | 432 return subset & s |
433 | |
393 | 434 |
394 @predicate('ancestors(set[, depth])', safe=True) | 435 @predicate('ancestors(set[, depth])', safe=True) |
395 def ancestors(repo, subset, x): | 436 def ancestors(repo, subset, x): |
396 """Changesets that are ancestors of changesets in set, including the | 437 """Changesets that are ancestors of changesets in set, including the |
397 given changesets themselves. | 438 given changesets themselves. |
404 if 'set' not in args: | 445 if 'set' not in args: |
405 # i18n: "ancestors" is a keyword | 446 # i18n: "ancestors" is a keyword |
406 raise error.ParseError(_('ancestors takes at least 1 argument')) | 447 raise error.ParseError(_('ancestors takes at least 1 argument')) |
407 startdepth = stopdepth = None | 448 startdepth = stopdepth = None |
408 if 'startdepth' in args: | 449 if 'startdepth' in args: |
409 n = getinteger(args['startdepth'], | 450 n = getinteger( |
410 "ancestors expects an integer startdepth") | 451 args['startdepth'], "ancestors expects an integer startdepth" |
452 ) | |
411 if n < 0: | 453 if n < 0: |
412 raise error.ParseError("negative startdepth") | 454 raise error.ParseError("negative startdepth") |
413 startdepth = n | 455 startdepth = n |
414 if 'depth' in args: | 456 if 'depth' in args: |
415 # i18n: "ancestors" is a keyword | 457 # i18n: "ancestors" is a keyword |
416 n = getinteger(args['depth'], _("ancestors expects an integer depth")) | 458 n = getinteger(args['depth'], _("ancestors expects an integer depth")) |
417 if n < 0: | 459 if n < 0: |
418 raise error.ParseError(_("negative depth")) | 460 raise error.ParseError(_("negative depth")) |
419 stopdepth = n + 1 | 461 stopdepth = n + 1 |
420 return _ancestors(repo, subset, args['set'], | 462 return _ancestors( |
421 startdepth=startdepth, stopdepth=stopdepth) | 463 repo, subset, args['set'], startdepth=startdepth, stopdepth=stopdepth |
464 ) | |
465 | |
422 | 466 |
423 @predicate('_firstancestors', safe=True) | 467 @predicate('_firstancestors', safe=True) |
424 def _firstancestors(repo, subset, x): | 468 def _firstancestors(repo, subset, x): |
425 # ``_firstancestors(set)`` | 469 # ``_firstancestors(set)`` |
426 # Like ``ancestors(set)`` but follows only the first parents. | 470 # Like ``ancestors(set)`` but follows only the first parents. |
427 return _ancestors(repo, subset, x, followfirst=True) | 471 return _ancestors(repo, subset, x, followfirst=True) |
472 | |
428 | 473 |
429 def _childrenspec(repo, subset, x, n, order): | 474 def _childrenspec(repo, subset, x, n, order): |
430 """Changesets that are the Nth child of a changeset | 475 """Changesets that are the Nth child of a changeset |
431 in set. | 476 in set. |
432 """ | 477 """ |
436 c = repo[r].children() | 481 c = repo[r].children() |
437 if len(c) == 0: | 482 if len(c) == 0: |
438 break | 483 break |
439 if len(c) > 1: | 484 if len(c) > 1: |
440 raise error.RepoLookupError( | 485 raise error.RepoLookupError( |
441 _("revision in set has more than one child")) | 486 _("revision in set has more than one child") |
487 ) | |
442 r = c[0].rev() | 488 r = c[0].rev() |
443 else: | 489 else: |
444 cs.add(r) | 490 cs.add(r) |
445 return subset & cs | 491 return subset & cs |
492 | |
446 | 493 |
447 def ancestorspec(repo, subset, x, n, order): | 494 def ancestorspec(repo, subset, x, n, order): |
448 """``set~n`` | 495 """``set~n`` |
449 Changesets that are the Nth ancestor (first parents only) of a changeset | 496 Changesets that are the Nth ancestor (first parents only) of a changeset |
450 in set. | 497 in set. |
462 except error.WdirUnsupported: | 509 except error.WdirUnsupported: |
463 r = repo[r].p1().rev() | 510 r = repo[r].p1().rev() |
464 ps.add(r) | 511 ps.add(r) |
465 return subset & ps | 512 return subset & ps |
466 | 513 |
514 | |
467 @predicate('author(string)', safe=True, weight=10) | 515 @predicate('author(string)', safe=True, weight=10) |
468 def author(repo, subset, x): | 516 def author(repo, subset, x): |
469 """Alias for ``user(string)``. | 517 """Alias for ``user(string)``. |
470 """ | 518 """ |
471 # i18n: "author" is a keyword | 519 # i18n: "author" is a keyword |
472 n = getstring(x, _("author requires a string")) | 520 n = getstring(x, _("author requires a string")) |
473 kind, pattern, matcher = _substringmatcher(n, casesensitive=False) | 521 kind, pattern, matcher = _substringmatcher(n, casesensitive=False) |
474 return subset.filter(lambda x: matcher(repo[x].user()), | 522 return subset.filter( |
475 condrepr=('<user %r>', n)) | 523 lambda x: matcher(repo[x].user()), condrepr=('<user %r>', n) |
524 ) | |
525 | |
476 | 526 |
477 @predicate('bisect(string)', safe=True) | 527 @predicate('bisect(string)', safe=True) |
478 def bisect(repo, subset, x): | 528 def bisect(repo, subset, x): |
479 """Changesets marked in the specified bisect status: | 529 """Changesets marked in the specified bisect status: |
480 | 530 |
489 # i18n: "bisect" is a keyword | 539 # i18n: "bisect" is a keyword |
490 status = getstring(x, _("bisect requires a string")).lower() | 540 status = getstring(x, _("bisect requires a string")).lower() |
491 state = set(hbisect.get(repo, status)) | 541 state = set(hbisect.get(repo, status)) |
492 return subset & state | 542 return subset & state |
493 | 543 |
544 | |
494 # Backward-compatibility | 545 # Backward-compatibility |
495 # - no help entry so that we do not advertise it any more | 546 # - no help entry so that we do not advertise it any more |
496 @predicate('bisected', safe=True) | 547 @predicate('bisected', safe=True) |
497 def bisected(repo, subset, x): | 548 def bisected(repo, subset, x): |
498 return bisect(repo, subset, x) | 549 return bisect(repo, subset, x) |
499 | 550 |
551 | |
500 @predicate('bookmark([name])', safe=True) | 552 @predicate('bookmark([name])', safe=True) |
501 def bookmark(repo, subset, x): | 553 def bookmark(repo, subset, x): |
502 """The named bookmark or all bookmarks. | 554 """The named bookmark or all bookmarks. |
503 | 555 |
504 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`. | 556 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`. |
505 """ | 557 """ |
506 # i18n: "bookmark" is a keyword | 558 # i18n: "bookmark" is a keyword |
507 args = getargs(x, 0, 1, _('bookmark takes one or no arguments')) | 559 args = getargs(x, 0, 1, _('bookmark takes one or no arguments')) |
508 if args: | 560 if args: |
509 bm = getstring(args[0], | 561 bm = getstring( |
510 # i18n: "bookmark" is a keyword | 562 args[0], |
511 _('the argument to bookmark must be a string')) | 563 # i18n: "bookmark" is a keyword |
564 _('the argument to bookmark must be a string'), | |
565 ) | |
512 kind, pattern, matcher = stringutil.stringmatcher(bm) | 566 kind, pattern, matcher = stringutil.stringmatcher(bm) |
513 bms = set() | 567 bms = set() |
514 if kind == 'literal': | 568 if kind == 'literal': |
515 if bm == pattern: | 569 if bm == pattern: |
516 pattern = repo._bookmarks.expandname(pattern) | 570 pattern = repo._bookmarks.expandname(pattern) |
517 bmrev = repo._bookmarks.get(pattern, None) | 571 bmrev = repo._bookmarks.get(pattern, None) |
518 if not bmrev: | 572 if not bmrev: |
519 raise error.RepoLookupError(_("bookmark '%s' does not exist") | 573 raise error.RepoLookupError( |
520 % pattern) | 574 _("bookmark '%s' does not exist") % pattern |
575 ) | |
521 bms.add(repo[bmrev].rev()) | 576 bms.add(repo[bmrev].rev()) |
522 else: | 577 else: |
523 matchrevs = set() | 578 matchrevs = set() |
524 for name, bmrev in repo._bookmarks.iteritems(): | 579 for name, bmrev in repo._bookmarks.iteritems(): |
525 if matcher(name): | 580 if matcher(name): |
529 else: | 584 else: |
530 bms = {repo[r].rev() for r in repo._bookmarks.values()} | 585 bms = {repo[r].rev() for r in repo._bookmarks.values()} |
531 bms -= {node.nullrev} | 586 bms -= {node.nullrev} |
532 return subset & bms | 587 return subset & bms |
533 | 588 |
589 | |
534 @predicate('branch(string or set)', safe=True, weight=10) | 590 @predicate('branch(string or set)', safe=True, weight=10) |
535 def branch(repo, subset, x): | 591 def branch(repo, subset, x): |
536 """ | 592 """ |
537 All changesets belonging to the given branch or the branches of the given | 593 All changesets belonging to the given branch or the branches of the given |
538 changesets. | 594 changesets. |
539 | 595 |
540 Pattern matching is supported for `string`. See | 596 Pattern matching is supported for `string`. See |
541 :hg:`help revisions.patterns`. | 597 :hg:`help revisions.patterns`. |
542 """ | 598 """ |
543 getbi = repo.revbranchcache().branchinfo | 599 getbi = repo.revbranchcache().branchinfo |
600 | |
544 def getbranch(r): | 601 def getbranch(r): |
545 try: | 602 try: |
546 return getbi(r)[0] | 603 return getbi(r)[0] |
547 except error.WdirUnsupported: | 604 except error.WdirUnsupported: |
548 return repo[r].branch() | 605 return repo[r].branch() |
556 kind, pattern, matcher = stringutil.stringmatcher(b) | 613 kind, pattern, matcher = stringutil.stringmatcher(b) |
557 if kind == 'literal': | 614 if kind == 'literal': |
558 # note: falls through to the revspec case if no branch with | 615 # note: falls through to the revspec case if no branch with |
559 # this name exists and pattern kind is not specified explicitly | 616 # this name exists and pattern kind is not specified explicitly |
560 if repo.branchmap().hasbranch(pattern): | 617 if repo.branchmap().hasbranch(pattern): |
561 return subset.filter(lambda r: matcher(getbranch(r)), | 618 return subset.filter( |
562 condrepr=('<branch %r>', b)) | 619 lambda r: matcher(getbranch(r)), condrepr=('<branch %r>', b) |
620 ) | |
563 if b.startswith('literal:'): | 621 if b.startswith('literal:'): |
564 raise error.RepoLookupError(_("branch '%s' does not exist") | 622 raise error.RepoLookupError( |
565 % pattern) | 623 _("branch '%s' does not exist") % pattern |
624 ) | |
566 else: | 625 else: |
567 return subset.filter(lambda r: matcher(getbranch(r)), | 626 return subset.filter( |
568 condrepr=('<branch %r>', b)) | 627 lambda r: matcher(getbranch(r)), condrepr=('<branch %r>', b) |
628 ) | |
569 | 629 |
570 s = getset(repo, fullreposet(repo), x) | 630 s = getset(repo, fullreposet(repo), x) |
571 b = set() | 631 b = set() |
572 for r in s: | 632 for r in s: |
573 b.add(getbranch(r)) | 633 b.add(getbranch(r)) |
574 c = s.__contains__ | 634 c = s.__contains__ |
575 return subset.filter(lambda r: c(r) or getbranch(r) in b, | 635 return subset.filter( |
576 condrepr=lambda: '<branch %r>' % _sortedb(b)) | 636 lambda r: c(r) or getbranch(r) in b, |
637 condrepr=lambda: '<branch %r>' % _sortedb(b), | |
638 ) | |
639 | |
577 | 640 |
578 @predicate('phasedivergent()', safe=True) | 641 @predicate('phasedivergent()', safe=True) |
579 def phasedivergent(repo, subset, x): | 642 def phasedivergent(repo, subset, x): |
580 """Mutable changesets marked as successors of public changesets. | 643 """Mutable changesets marked as successors of public changesets. |
581 | 644 |
584 """ | 647 """ |
585 # i18n: "phasedivergent" is a keyword | 648 # i18n: "phasedivergent" is a keyword |
586 getargs(x, 0, 0, _("phasedivergent takes no arguments")) | 649 getargs(x, 0, 0, _("phasedivergent takes no arguments")) |
587 phasedivergent = obsmod.getrevs(repo, 'phasedivergent') | 650 phasedivergent = obsmod.getrevs(repo, 'phasedivergent') |
588 return subset & phasedivergent | 651 return subset & phasedivergent |
652 | |
589 | 653 |
590 @predicate('bundle()', safe=True) | 654 @predicate('bundle()', safe=True) |
591 def bundle(repo, subset, x): | 655 def bundle(repo, subset, x): |
592 """Changesets in the bundle. | 656 """Changesets in the bundle. |
593 | 657 |
596 try: | 660 try: |
597 bundlerevs = repo.changelog.bundlerevs | 661 bundlerevs = repo.changelog.bundlerevs |
598 except AttributeError: | 662 except AttributeError: |
599 raise error.Abort(_("no bundle provided - specify with -R")) | 663 raise error.Abort(_("no bundle provided - specify with -R")) |
600 return subset & bundlerevs | 664 return subset & bundlerevs |
665 | |
601 | 666 |
602 def checkstatus(repo, subset, pat, field): | 667 def checkstatus(repo, subset, pat, field): |
603 """Helper for status-related revsets (adds, removes, modifies). | 668 """Helper for status-related revsets (adds, removes, modifies). |
604 The field parameter says which kind is desired: | 669 The field parameter says which kind is desired: |
605 0: modified | 670 0: modified |
607 2: removed | 672 2: removed |
608 """ | 673 """ |
609 hasset = matchmod.patkind(pat) == 'set' | 674 hasset = matchmod.patkind(pat) == 'set' |
610 | 675 |
611 mcache = [None] | 676 mcache = [None] |
677 | |
612 def matches(x): | 678 def matches(x): |
613 c = repo[x] | 679 c = repo[x] |
614 if not mcache[0] or hasset: | 680 if not mcache[0] or hasset: |
615 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c) | 681 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c) |
616 m = mcache[0] | 682 m = mcache[0] |
635 if m(f): | 701 if m(f): |
636 return True | 702 return True |
637 | 703 |
638 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat)) | 704 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat)) |
639 | 705 |
706 | |
640 def _children(repo, subset, parentset): | 707 def _children(repo, subset, parentset): |
641 if not parentset: | 708 if not parentset: |
642 return baseset() | 709 return baseset() |
643 cs = set() | 710 cs = set() |
644 pr = repo.changelog.parentrevs | 711 pr = repo.changelog.parentrevs |
652 cs.add(r) | 719 cs.add(r) |
653 if p2 != nullrev and p2 in parentset: | 720 if p2 != nullrev and p2 in parentset: |
654 cs.add(r) | 721 cs.add(r) |
655 return baseset(cs) | 722 return baseset(cs) |
656 | 723 |
724 | |
657 @predicate('children(set)', safe=True) | 725 @predicate('children(set)', safe=True) |
658 def children(repo, subset, x): | 726 def children(repo, subset, x): |
659 """Child changesets of changesets in set. | 727 """Child changesets of changesets in set. |
660 """ | 728 """ |
661 s = getset(repo, fullreposet(repo), x) | 729 s = getset(repo, fullreposet(repo), x) |
662 cs = _children(repo, subset, s) | 730 cs = _children(repo, subset, s) |
663 return subset & cs | 731 return subset & cs |
664 | 732 |
733 | |
665 @predicate('closed()', safe=True, weight=10) | 734 @predicate('closed()', safe=True, weight=10) |
666 def closed(repo, subset, x): | 735 def closed(repo, subset, x): |
667 """Changeset is closed. | 736 """Changeset is closed. |
668 """ | 737 """ |
669 # i18n: "closed" is a keyword | 738 # i18n: "closed" is a keyword |
670 getargs(x, 0, 0, _("closed takes no arguments")) | 739 getargs(x, 0, 0, _("closed takes no arguments")) |
671 return subset.filter(lambda r: repo[r].closesbranch(), | 740 return subset.filter( |
672 condrepr='<branch closed>') | 741 lambda r: repo[r].closesbranch(), condrepr='<branch closed>' |
742 ) | |
743 | |
673 | 744 |
674 # for internal use | 745 # for internal use |
675 @predicate('_commonancestorheads(set)', safe=True) | 746 @predicate('_commonancestorheads(set)', safe=True) |
676 def _commonancestorheads(repo, subset, x): | 747 def _commonancestorheads(repo, subset, x): |
677 # This is an internal method is for quickly calculating "heads(::x and | 748 # This is an internal method is for quickly calculating "heads(::x and |
681 # merge will find. | 752 # merge will find. |
682 startrevs = getset(repo, fullreposet(repo), x, order=anyorder) | 753 startrevs = getset(repo, fullreposet(repo), x, order=anyorder) |
683 | 754 |
684 ancs = repo.changelog._commonancestorsheads(*list(startrevs)) | 755 ancs = repo.changelog._commonancestorsheads(*list(startrevs)) |
685 return subset & baseset(ancs) | 756 return subset & baseset(ancs) |
757 | |
686 | 758 |
687 @predicate('commonancestors(set)', safe=True) | 759 @predicate('commonancestors(set)', safe=True) |
688 def commonancestors(repo, subset, x): | 760 def commonancestors(repo, subset, x): |
689 """Changesets that are ancestors of every changeset in set. | 761 """Changesets that are ancestors of every changeset in set. |
690 """ | 762 """ |
692 if not startrevs: | 764 if not startrevs: |
693 return baseset() | 765 return baseset() |
694 for r in startrevs: | 766 for r in startrevs: |
695 subset &= dagop.revancestors(repo, baseset([r])) | 767 subset &= dagop.revancestors(repo, baseset([r])) |
696 return subset | 768 return subset |
769 | |
697 | 770 |
698 @predicate('contains(pattern)', weight=100) | 771 @predicate('contains(pattern)', weight=100) |
699 def contains(repo, subset, x): | 772 def contains(repo, subset, x): |
700 """The revision's manifest contains a file matching pattern (but might not | 773 """The revision's manifest contains a file matching pattern (but might not |
701 modify it). See :hg:`help patterns` for information about file patterns. | 774 modify it). See :hg:`help patterns` for information about file patterns. |
720 return True | 793 return True |
721 return False | 794 return False |
722 | 795 |
723 return subset.filter(matches, condrepr=('<contains %r>', pat)) | 796 return subset.filter(matches, condrepr=('<contains %r>', pat)) |
724 | 797 |
798 | |
725 @predicate('converted([id])', safe=True) | 799 @predicate('converted([id])', safe=True) |
726 def converted(repo, subset, x): | 800 def converted(repo, subset, x): |
727 """Changesets converted from the given identifier in the old repository if | 801 """Changesets converted from the given identifier in the old repository if |
728 present, or all converted changesets if no identifier is specified. | 802 present, or all converted changesets if no identifier is specified. |
729 """ | 803 """ |
740 | 814 |
741 def _matchvalue(r): | 815 def _matchvalue(r): |
742 source = repo[r].extra().get('convert_revision', None) | 816 source = repo[r].extra().get('convert_revision', None) |
743 return source is not None and (rev is None or source.startswith(rev)) | 817 return source is not None and (rev is None or source.startswith(rev)) |
744 | 818 |
745 return subset.filter(lambda r: _matchvalue(r), | 819 return subset.filter( |
746 condrepr=('<converted %r>', rev)) | 820 lambda r: _matchvalue(r), condrepr=('<converted %r>', rev) |
821 ) | |
822 | |
747 | 823 |
748 @predicate('date(interval)', safe=True, weight=10) | 824 @predicate('date(interval)', safe=True, weight=10) |
749 def date(repo, subset, x): | 825 def date(repo, subset, x): |
750 """Changesets within the interval, see :hg:`help dates`. | 826 """Changesets within the interval, see :hg:`help dates`. |
751 """ | 827 """ |
752 # i18n: "date" is a keyword | 828 # i18n: "date" is a keyword |
753 ds = getstring(x, _("date requires a string")) | 829 ds = getstring(x, _("date requires a string")) |
754 dm = dateutil.matchdate(ds) | 830 dm = dateutil.matchdate(ds) |
755 return subset.filter(lambda x: dm(repo[x].date()[0]), | 831 return subset.filter( |
756 condrepr=('<date %r>', ds)) | 832 lambda x: dm(repo[x].date()[0]), condrepr=('<date %r>', ds) |
833 ) | |
834 | |
757 | 835 |
758 @predicate('desc(string)', safe=True, weight=10) | 836 @predicate('desc(string)', safe=True, weight=10) |
759 def desc(repo, subset, x): | 837 def desc(repo, subset, x): |
760 """Search commit message for string. The match is case-insensitive. | 838 """Search commit message for string. The match is case-insensitive. |
761 | 839 |
765 # i18n: "desc" is a keyword | 843 # i18n: "desc" is a keyword |
766 ds = getstring(x, _("desc requires a string")) | 844 ds = getstring(x, _("desc requires a string")) |
767 | 845 |
768 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False) | 846 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False) |
769 | 847 |
770 return subset.filter(lambda r: matcher(repo[r].description()), | 848 return subset.filter( |
771 condrepr=('<desc %r>', ds)) | 849 lambda r: matcher(repo[r].description()), condrepr=('<desc %r>', ds) |
772 | 850 ) |
773 def _descendants(repo, subset, x, followfirst=False, startdepth=None, | 851 |
774 stopdepth=None): | 852 |
853 def _descendants( | |
854 repo, subset, x, followfirst=False, startdepth=None, stopdepth=None | |
855 ): | |
775 roots = getset(repo, fullreposet(repo), x) | 856 roots = getset(repo, fullreposet(repo), x) |
776 if not roots: | 857 if not roots: |
777 return baseset() | 858 return baseset() |
778 s = dagop.revdescendants(repo, roots, followfirst, startdepth, stopdepth) | 859 s = dagop.revdescendants(repo, roots, followfirst, startdepth, stopdepth) |
779 return subset & s | 860 return subset & s |
861 | |
780 | 862 |
781 @predicate('descendants(set[, depth])', safe=True) | 863 @predicate('descendants(set[, depth])', safe=True) |
782 def descendants(repo, subset, x): | 864 def descendants(repo, subset, x): |
783 """Changesets which are descendants of changesets in set, including the | 865 """Changesets which are descendants of changesets in set, including the |
784 given changesets themselves. | 866 given changesets themselves. |
791 if 'set' not in args: | 873 if 'set' not in args: |
792 # i18n: "descendants" is a keyword | 874 # i18n: "descendants" is a keyword |
793 raise error.ParseError(_('descendants takes at least 1 argument')) | 875 raise error.ParseError(_('descendants takes at least 1 argument')) |
794 startdepth = stopdepth = None | 876 startdepth = stopdepth = None |
795 if 'startdepth' in args: | 877 if 'startdepth' in args: |
796 n = getinteger(args['startdepth'], | 878 n = getinteger( |
797 "descendants expects an integer startdepth") | 879 args['startdepth'], "descendants expects an integer startdepth" |
880 ) | |
798 if n < 0: | 881 if n < 0: |
799 raise error.ParseError("negative startdepth") | 882 raise error.ParseError("negative startdepth") |
800 startdepth = n | 883 startdepth = n |
801 if 'depth' in args: | 884 if 'depth' in args: |
802 # i18n: "descendants" is a keyword | 885 # i18n: "descendants" is a keyword |
803 n = getinteger(args['depth'], _("descendants expects an integer depth")) | 886 n = getinteger(args['depth'], _("descendants expects an integer depth")) |
804 if n < 0: | 887 if n < 0: |
805 raise error.ParseError(_("negative depth")) | 888 raise error.ParseError(_("negative depth")) |
806 stopdepth = n + 1 | 889 stopdepth = n + 1 |
807 return _descendants(repo, subset, args['set'], | 890 return _descendants( |
808 startdepth=startdepth, stopdepth=stopdepth) | 891 repo, subset, args['set'], startdepth=startdepth, stopdepth=stopdepth |
892 ) | |
893 | |
809 | 894 |
810 @predicate('_firstdescendants', safe=True) | 895 @predicate('_firstdescendants', safe=True) |
811 def _firstdescendants(repo, subset, x): | 896 def _firstdescendants(repo, subset, x): |
812 # ``_firstdescendants(set)`` | 897 # ``_firstdescendants(set)`` |
813 # Like ``descendants(set)`` but follows only the first parents. | 898 # Like ``descendants(set)`` but follows only the first parents. |
814 return _descendants(repo, subset, x, followfirst=True) | 899 return _descendants(repo, subset, x, followfirst=True) |
900 | |
815 | 901 |
816 @predicate('destination([set])', safe=True, weight=10) | 902 @predicate('destination([set])', safe=True, weight=10) |
817 def destination(repo, subset, x): | 903 def destination(repo, subset, x): |
818 """Changesets that were created by a graft, transplant or rebase operation, | 904 """Changesets that were created by a graft, transplant or rebase operation, |
819 with the given revisions specified as the source. Omitting the optional set | 905 with the given revisions specified as the source. Omitting the optional set |
853 break | 939 break |
854 | 940 |
855 r = src | 941 r = src |
856 src = _getrevsource(repo, r) | 942 src = _getrevsource(repo, r) |
857 | 943 |
858 return subset.filter(dests.__contains__, | 944 return subset.filter( |
859 condrepr=lambda: '<destination %r>' % _sortedb(dests)) | 945 dests.__contains__, |
946 condrepr=lambda: '<destination %r>' % _sortedb(dests), | |
947 ) | |
948 | |
860 | 949 |
861 @predicate('contentdivergent()', safe=True) | 950 @predicate('contentdivergent()', safe=True) |
862 def contentdivergent(repo, subset, x): | 951 def contentdivergent(repo, subset, x): |
863 """ | 952 """ |
864 Final successors of changesets with an alternative set of final | 953 Final successors of changesets with an alternative set of final |
866 """ | 955 """ |
867 # i18n: "contentdivergent" is a keyword | 956 # i18n: "contentdivergent" is a keyword |
868 getargs(x, 0, 0, _("contentdivergent takes no arguments")) | 957 getargs(x, 0, 0, _("contentdivergent takes no arguments")) |
869 contentdivergent = obsmod.getrevs(repo, 'contentdivergent') | 958 contentdivergent = obsmod.getrevs(repo, 'contentdivergent') |
870 return subset & contentdivergent | 959 return subset & contentdivergent |
960 | |
871 | 961 |
872 @predicate('expectsize(set[, size])', safe=True, takeorder=True) | 962 @predicate('expectsize(set[, size])', safe=True, takeorder=True) |
873 def expectsize(repo, subset, x, order): | 963 def expectsize(repo, subset, x, order): |
874 """Return the given revset if size matches the revset size. | 964 """Return the given revset if size matches the revset size. |
875 Abort if the revset doesn't expect given size. | 965 Abort if the revset doesn't expect given size. |
882 minsize = 0 | 972 minsize = 0 |
883 maxsize = len(repo) + 1 | 973 maxsize = len(repo) + 1 |
884 err = '' | 974 err = '' |
885 if 'size' not in args or 'set' not in args: | 975 if 'size' not in args or 'set' not in args: |
886 raise error.ParseError(_('invalid set of arguments')) | 976 raise error.ParseError(_('invalid set of arguments')) |
887 minsize, maxsize = getintrange(args['size'], | 977 minsize, maxsize = getintrange( |
888 _('expectsize requires a size range' | 978 args['size'], |
889 ' or a positive integer'), | 979 _('expectsize requires a size range' ' or a positive integer'), |
890 _('size range bounds must be integers'), | 980 _('size range bounds must be integers'), |
891 minsize, maxsize) | 981 minsize, |
982 maxsize, | |
983 ) | |
892 if minsize < 0 or maxsize < 0: | 984 if minsize < 0 or maxsize < 0: |
893 raise error.ParseError(_('negative size')) | 985 raise error.ParseError(_('negative size')) |
894 rev = getset(repo, fullreposet(repo), args['set'], order=order) | 986 rev = getset(repo, fullreposet(repo), args['set'], order=order) |
895 if minsize != maxsize and (len(rev) < minsize or len(rev) > maxsize): | 987 if minsize != maxsize and (len(rev) < minsize or len(rev) > maxsize): |
896 err = _('revset size mismatch.' | 988 err = _( |
897 ' expected between %d and %d, got %d') % (minsize, maxsize, | 989 'revset size mismatch.' ' expected between %d and %d, got %d' |
898 len(rev)) | 990 ) % (minsize, maxsize, len(rev)) |
899 elif minsize == maxsize and len(rev) != minsize: | 991 elif minsize == maxsize and len(rev) != minsize: |
900 err = _('revset size mismatch.' | 992 err = _('revset size mismatch.' ' expected %d, got %d') % ( |
901 ' expected %d, got %d') % (minsize, len(rev)) | 993 minsize, |
994 len(rev), | |
995 ) | |
902 if err: | 996 if err: |
903 raise error.RepoLookupError(err) | 997 raise error.RepoLookupError(err) |
904 if order == followorder: | 998 if order == followorder: |
905 return subset & rev | 999 return subset & rev |
906 else: | 1000 else: |
907 return rev & subset | 1001 return rev & subset |
908 | 1002 |
1003 | |
909 @predicate('extdata(source)', safe=False, weight=100) | 1004 @predicate('extdata(source)', safe=False, weight=100) |
910 def extdata(repo, subset, x): | 1005 def extdata(repo, subset, x): |
911 """Changesets in the specified extdata source. (EXPERIMENTAL)""" | 1006 """Changesets in the specified extdata source. (EXPERIMENTAL)""" |
912 # i18n: "extdata" is a keyword | 1007 # i18n: "extdata" is a keyword |
913 args = getargsdict(x, 'extdata', 'source') | 1008 args = getargsdict(x, 'extdata', 'source') |
914 source = getstring(args.get('source'), | 1009 source = getstring( |
915 # i18n: "extdata" is a keyword | 1010 args.get('source'), |
916 _('extdata takes at least 1 string argument')) | 1011 # i18n: "extdata" is a keyword |
1012 _('extdata takes at least 1 string argument'), | |
1013 ) | |
917 data = scmutil.extdatasource(repo, source) | 1014 data = scmutil.extdatasource(repo, source) |
918 return subset & baseset(data) | 1015 return subset & baseset(data) |
1016 | |
919 | 1017 |
920 @predicate('extinct()', safe=True) | 1018 @predicate('extinct()', safe=True) |
921 def extinct(repo, subset, x): | 1019 def extinct(repo, subset, x): |
922 """Obsolete changesets with obsolete descendants only. | 1020 """Obsolete changesets with obsolete descendants only. |
923 """ | 1021 """ |
924 # i18n: "extinct" is a keyword | 1022 # i18n: "extinct" is a keyword |
925 getargs(x, 0, 0, _("extinct takes no arguments")) | 1023 getargs(x, 0, 0, _("extinct takes no arguments")) |
926 extincts = obsmod.getrevs(repo, 'extinct') | 1024 extincts = obsmod.getrevs(repo, 'extinct') |
927 return subset & extincts | 1025 return subset & extincts |
1026 | |
928 | 1027 |
929 @predicate('extra(label, [value])', safe=True) | 1028 @predicate('extra(label, [value])', safe=True) |
930 def extra(repo, subset, x): | 1029 def extra(repo, subset, x): |
931 """Changesets with the given label in the extra metadata, with the given | 1030 """Changesets with the given label in the extra metadata, with the given |
932 optional value. | 1031 optional value. |
937 args = getargsdict(x, 'extra', 'label value') | 1036 args = getargsdict(x, 'extra', 'label value') |
938 if 'label' not in args: | 1037 if 'label' not in args: |
939 # i18n: "extra" is a keyword | 1038 # i18n: "extra" is a keyword |
940 raise error.ParseError(_('extra takes at least 1 argument')) | 1039 raise error.ParseError(_('extra takes at least 1 argument')) |
941 # i18n: "extra" is a keyword | 1040 # i18n: "extra" is a keyword |
942 label = getstring(args['label'], _('first argument to extra must be ' | 1041 label = getstring( |
943 'a string')) | 1042 args['label'], _('first argument to extra must be ' 'a string') |
1043 ) | |
944 value = None | 1044 value = None |
945 | 1045 |
946 if 'value' in args: | 1046 if 'value' in args: |
947 # i18n: "extra" is a keyword | 1047 # i18n: "extra" is a keyword |
948 value = getstring(args['value'], _('second argument to extra must be ' | 1048 value = getstring( |
949 'a string')) | 1049 args['value'], _('second argument to extra must be ' 'a string') |
1050 ) | |
950 kind, value, matcher = stringutil.stringmatcher(value) | 1051 kind, value, matcher = stringutil.stringmatcher(value) |
951 | 1052 |
952 def _matchvalue(r): | 1053 def _matchvalue(r): |
953 extra = repo[r].extra() | 1054 extra = repo[r].extra() |
954 return label in extra and (value is None or matcher(extra[label])) | 1055 return label in extra and (value is None or matcher(extra[label])) |
955 | 1056 |
956 return subset.filter(lambda r: _matchvalue(r), | 1057 return subset.filter( |
957 condrepr=('<extra[%r] %r>', label, value)) | 1058 lambda r: _matchvalue(r), condrepr=('<extra[%r] %r>', label, value) |
1059 ) | |
1060 | |
958 | 1061 |
959 @predicate('filelog(pattern)', safe=True) | 1062 @predicate('filelog(pattern)', safe=True) |
960 def filelog(repo, subset, x): | 1063 def filelog(repo, subset, x): |
961 """Changesets connected to the specified filelog. | 1064 """Changesets connected to the specified filelog. |
962 | 1065 |
1017 # deletion in changelog | 1120 # deletion in changelog |
1018 continue | 1121 continue |
1019 | 1122 |
1020 return subset & s | 1123 return subset & s |
1021 | 1124 |
1125 | |
1022 @predicate('first(set, [n])', safe=True, takeorder=True, weight=0) | 1126 @predicate('first(set, [n])', safe=True, takeorder=True, weight=0) |
1023 def first(repo, subset, x, order): | 1127 def first(repo, subset, x, order): |
1024 """An alias for limit(). | 1128 """An alias for limit(). |
1025 """ | 1129 """ |
1026 return limit(repo, subset, x, order) | 1130 return limit(repo, subset, x, order) |
1131 | |
1027 | 1132 |
1028 def _follow(repo, subset, x, name, followfirst=False): | 1133 def _follow(repo, subset, x, name, followfirst=False): |
1029 args = getargsdict(x, name, 'file startrev') | 1134 args = getargsdict(x, name, 'file startrev') |
1030 revs = None | 1135 revs = None |
1031 if 'startrev' in args: | 1136 if 'startrev' in args: |
1037 fctxs = [] | 1142 fctxs = [] |
1038 for r in revs: | 1143 for r in revs: |
1039 ctx = mctx = repo[r] | 1144 ctx = mctx = repo[r] |
1040 if r is None: | 1145 if r is None: |
1041 ctx = repo['.'] | 1146 ctx = repo['.'] |
1042 m = matchmod.match(repo.root, repo.getcwd(), [x], | 1147 m = matchmod.match( |
1043 ctx=mctx, default='path') | 1148 repo.root, repo.getcwd(), [x], ctx=mctx, default='path' |
1149 ) | |
1044 fctxs.extend(ctx[f].introfilectx() for f in ctx.manifest().walk(m)) | 1150 fctxs.extend(ctx[f].introfilectx() for f in ctx.manifest().walk(m)) |
1045 s = dagop.filerevancestors(fctxs, followfirst) | 1151 s = dagop.filerevancestors(fctxs, followfirst) |
1046 else: | 1152 else: |
1047 if revs is None: | 1153 if revs is None: |
1048 revs = baseset([repo['.'].rev()]) | 1154 revs = baseset([repo['.'].rev()]) |
1049 s = dagop.revancestors(repo, revs, followfirst) | 1155 s = dagop.revancestors(repo, revs, followfirst) |
1050 | 1156 |
1051 return subset & s | 1157 return subset & s |
1052 | 1158 |
1159 | |
1053 @predicate('follow([file[, startrev]])', safe=True) | 1160 @predicate('follow([file[, startrev]])', safe=True) |
1054 def follow(repo, subset, x): | 1161 def follow(repo, subset, x): |
1055 """ | 1162 """ |
1056 An alias for ``::.`` (ancestors of the working directory's first parent). | 1163 An alias for ``::.`` (ancestors of the working directory's first parent). |
1057 If file pattern is specified, the histories of files matching given | 1164 If file pattern is specified, the histories of files matching given |
1058 pattern in the revision given by startrev are followed, including copies. | 1165 pattern in the revision given by startrev are followed, including copies. |
1059 """ | 1166 """ |
1060 return _follow(repo, subset, x, 'follow') | 1167 return _follow(repo, subset, x, 'follow') |
1168 | |
1061 | 1169 |
1062 @predicate('_followfirst', safe=True) | 1170 @predicate('_followfirst', safe=True) |
1063 def _followfirst(repo, subset, x): | 1171 def _followfirst(repo, subset, x): |
1064 # ``followfirst([file[, startrev]])`` | 1172 # ``followfirst([file[, startrev]])`` |
1065 # Like ``follow([file[, startrev]])`` but follows only the first parent | 1173 # Like ``follow([file[, startrev]])`` but follows only the first parent |
1066 # of every revisions or files revisions. | 1174 # of every revisions or files revisions. |
1067 return _follow(repo, subset, x, '_followfirst', followfirst=True) | 1175 return _follow(repo, subset, x, '_followfirst', followfirst=True) |
1068 | 1176 |
1069 @predicate('followlines(file, fromline:toline[, startrev=., descend=False])', | 1177 |
1070 safe=True) | 1178 @predicate( |
1179 'followlines(file, fromline:toline[, startrev=., descend=False])', safe=True | |
1180 ) | |
1071 def followlines(repo, subset, x): | 1181 def followlines(repo, subset, x): |
1072 """Changesets modifying `file` in line range ('fromline', 'toline'). | 1182 """Changesets modifying `file` in line range ('fromline', 'toline'). |
1073 | 1183 |
1074 Line range corresponds to 'file' content at 'startrev' and should hence be | 1184 Line range corresponds to 'file' content at 'startrev' and should hence be |
1075 consistent with file size. If startrev is not specified, working directory's | 1185 consistent with file size. If startrev is not specified, working directory's |
1087 if 'startrev' in args: | 1197 if 'startrev' in args: |
1088 revs = getset(repo, fullreposet(repo), args['startrev']) | 1198 revs = getset(repo, fullreposet(repo), args['startrev']) |
1089 if len(revs) != 1: | 1199 if len(revs) != 1: |
1090 raise error.ParseError( | 1200 raise error.ParseError( |
1091 # i18n: "followlines" is a keyword | 1201 # i18n: "followlines" is a keyword |
1092 _("followlines expects exactly one revision")) | 1202 _("followlines expects exactly one revision") |
1203 ) | |
1093 rev = revs.last() | 1204 rev = revs.last() |
1094 | 1205 |
1095 pat = getstring(args['file'], _("followlines requires a pattern")) | 1206 pat = getstring(args['file'], _("followlines requires a pattern")) |
1096 # i18n: "followlines" is a keyword | 1207 # i18n: "followlines" is a keyword |
1097 msg = _("followlines expects exactly one file") | 1208 msg = _("followlines expects exactly one file") |
1098 fname = scmutil.parsefollowlinespattern(repo, rev, pat, msg) | 1209 fname = scmutil.parsefollowlinespattern(repo, rev, pat, msg) |
1099 fromline, toline = util.processlinerange( | 1210 fromline, toline = util.processlinerange( |
1100 *getintrange(args['lines'][0], | 1211 *getintrange( |
1101 # i18n: "followlines" is a keyword | 1212 args['lines'][0], |
1102 _("followlines expects a line number or a range"), | 1213 # i18n: "followlines" is a keyword |
1103 _("line range bounds must be integers"))) | 1214 _("followlines expects a line number or a range"), |
1215 _("line range bounds must be integers"), | |
1216 ) | |
1217 ) | |
1104 | 1218 |
1105 fctx = repo[rev].filectx(fname) | 1219 fctx = repo[rev].filectx(fname) |
1106 descend = False | 1220 descend = False |
1107 if 'descend' in args: | 1221 if 'descend' in args: |
1108 descend = getboolean(args['descend'], | 1222 descend = getboolean( |
1109 # i18n: "descend" is a keyword | 1223 args['descend'], |
1110 _("descend argument must be a boolean")) | 1224 # i18n: "descend" is a keyword |
1225 _("descend argument must be a boolean"), | |
1226 ) | |
1111 if descend: | 1227 if descend: |
1112 rs = generatorset( | 1228 rs = generatorset( |
1113 (c.rev() for c, _linerange | 1229 ( |
1114 in dagop.blockdescendants(fctx, fromline, toline)), | 1230 c.rev() |
1115 iterasc=True) | 1231 for c, _linerange in dagop.blockdescendants( |
1232 fctx, fromline, toline | |
1233 ) | |
1234 ), | |
1235 iterasc=True, | |
1236 ) | |
1116 else: | 1237 else: |
1117 rs = generatorset( | 1238 rs = generatorset( |
1118 (c.rev() for c, _linerange | 1239 ( |
1119 in dagop.blockancestors(fctx, fromline, toline)), | 1240 c.rev() |
1120 iterasc=False) | 1241 for c, _linerange in dagop.blockancestors( |
1242 fctx, fromline, toline | |
1243 ) | |
1244 ), | |
1245 iterasc=False, | |
1246 ) | |
1121 return subset & rs | 1247 return subset & rs |
1248 | |
1122 | 1249 |
1123 @predicate('all()', safe=True) | 1250 @predicate('all()', safe=True) |
1124 def getall(repo, subset, x): | 1251 def getall(repo, subset, x): |
1125 """All changesets, the same as ``0:tip``. | 1252 """All changesets, the same as ``0:tip``. |
1126 """ | 1253 """ |
1127 # i18n: "all" is a keyword | 1254 # i18n: "all" is a keyword |
1128 getargs(x, 0, 0, _("all takes no arguments")) | 1255 getargs(x, 0, 0, _("all takes no arguments")) |
1129 return subset & spanset(repo) # drop "null" if any | 1256 return subset & spanset(repo) # drop "null" if any |
1257 | |
1130 | 1258 |
1131 @predicate('grep(regex)', weight=10) | 1259 @predicate('grep(regex)', weight=10) |
1132 def grep(repo, subset, x): | 1260 def grep(repo, subset, x): |
1133 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')`` | 1261 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')`` |
1134 to ensure special escape characters are handled correctly. Unlike | 1262 to ensure special escape characters are handled correctly. Unlike |
1137 try: | 1265 try: |
1138 # i18n: "grep" is a keyword | 1266 # i18n: "grep" is a keyword |
1139 gr = re.compile(getstring(x, _("grep requires a string"))) | 1267 gr = re.compile(getstring(x, _("grep requires a string"))) |
1140 except re.error as e: | 1268 except re.error as e: |
1141 raise error.ParseError( | 1269 raise error.ParseError( |
1142 _('invalid match pattern: %s') % stringutil.forcebytestr(e)) | 1270 _('invalid match pattern: %s') % stringutil.forcebytestr(e) |
1271 ) | |
1143 | 1272 |
1144 def matches(x): | 1273 def matches(x): |
1145 c = repo[x] | 1274 c = repo[x] |
1146 for e in c.files() + [c.user(), c.description()]: | 1275 for e in c.files() + [c.user(), c.description()]: |
1147 if gr.search(e): | 1276 if gr.search(e): |
1148 return True | 1277 return True |
1149 return False | 1278 return False |
1150 | 1279 |
1151 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern)) | 1280 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern)) |
1281 | |
1152 | 1282 |
1153 @predicate('_matchfiles', safe=True) | 1283 @predicate('_matchfiles', safe=True) |
1154 def _matchfiles(repo, subset, x): | 1284 def _matchfiles(repo, subset, x): |
1155 # _matchfiles takes a revset list of prefixed arguments: | 1285 # _matchfiles takes a revset list of prefixed arguments: |
1156 # | 1286 # |
1176 inc.append(value) | 1306 inc.append(value) |
1177 elif prefix == 'x:': | 1307 elif prefix == 'x:': |
1178 exc.append(value) | 1308 exc.append(value) |
1179 elif prefix == 'r:': | 1309 elif prefix == 'r:': |
1180 if rev is not None: | 1310 if rev is not None: |
1181 raise error.ParseError('_matchfiles expected at most one ' | 1311 raise error.ParseError( |
1182 'revision') | 1312 '_matchfiles expected at most one ' 'revision' |
1183 if value == '': # empty means working directory | 1313 ) |
1314 if value == '': # empty means working directory | |
1184 rev = node.wdirrev | 1315 rev = node.wdirrev |
1185 else: | 1316 else: |
1186 rev = value | 1317 rev = value |
1187 elif prefix == 'd:': | 1318 elif prefix == 'd:': |
1188 if default is not None: | 1319 if default is not None: |
1189 raise error.ParseError('_matchfiles expected at most one ' | 1320 raise error.ParseError( |
1190 'default mode') | 1321 '_matchfiles expected at most one ' 'default mode' |
1322 ) | |
1191 default = value | 1323 default = value |
1192 else: | 1324 else: |
1193 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix) | 1325 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix) |
1194 if not default: | 1326 if not default: |
1195 default = 'glob' | 1327 default = 'glob' |
1199 | 1331 |
1200 # This directly read the changelog data as creating changectx for all | 1332 # This directly read the changelog data as creating changectx for all |
1201 # revisions is quite expensive. | 1333 # revisions is quite expensive. |
1202 getfiles = repo.changelog.readfiles | 1334 getfiles = repo.changelog.readfiles |
1203 wdirrev = node.wdirrev | 1335 wdirrev = node.wdirrev |
1336 | |
1204 def matches(x): | 1337 def matches(x): |
1205 if x == wdirrev: | 1338 if x == wdirrev: |
1206 files = repo[x].files() | 1339 files = repo[x].files() |
1207 else: | 1340 else: |
1208 files = getfiles(x) | 1341 files = getfiles(x) |
1209 | 1342 |
1210 if not mcache[0] or (hasset and rev is None): | 1343 if not mcache[0] or (hasset and rev is None): |
1211 r = x if rev is None else rev | 1344 r = x if rev is None else rev |
1212 mcache[0] = matchmod.match(repo.root, repo.getcwd(), pats, | 1345 mcache[0] = matchmod.match( |
1213 include=inc, exclude=exc, ctx=repo[r], | 1346 repo.root, |
1214 default=default) | 1347 repo.getcwd(), |
1348 pats, | |
1349 include=inc, | |
1350 exclude=exc, | |
1351 ctx=repo[r], | |
1352 default=default, | |
1353 ) | |
1215 m = mcache[0] | 1354 m = mcache[0] |
1216 | 1355 |
1217 for f in files: | 1356 for f in files: |
1218 if m(f): | 1357 if m(f): |
1219 return True | 1358 return True |
1220 return False | 1359 return False |
1221 | 1360 |
1222 return subset.filter(matches, | 1361 return subset.filter( |
1223 condrepr=('<matchfiles patterns=%r, include=%r ' | 1362 matches, |
1224 'exclude=%r, default=%r, rev=%r>', | 1363 condrepr=( |
1225 pats, inc, exc, default, rev)) | 1364 '<matchfiles patterns=%r, include=%r ' |
1365 'exclude=%r, default=%r, rev=%r>', | |
1366 pats, | |
1367 inc, | |
1368 exc, | |
1369 default, | |
1370 rev, | |
1371 ), | |
1372 ) | |
1373 | |
1226 | 1374 |
1227 @predicate('file(pattern)', safe=True, weight=10) | 1375 @predicate('file(pattern)', safe=True, weight=10) |
1228 def hasfile(repo, subset, x): | 1376 def hasfile(repo, subset, x): |
1229 """Changesets affecting files matched by pattern. | 1377 """Changesets affecting files matched by pattern. |
1230 | 1378 |
1234 This predicate uses ``glob:`` as the default kind of pattern. | 1382 This predicate uses ``glob:`` as the default kind of pattern. |
1235 """ | 1383 """ |
1236 # i18n: "file" is a keyword | 1384 # i18n: "file" is a keyword |
1237 pat = getstring(x, _("file requires a pattern")) | 1385 pat = getstring(x, _("file requires a pattern")) |
1238 return _matchfiles(repo, subset, ('string', 'p:' + pat)) | 1386 return _matchfiles(repo, subset, ('string', 'p:' + pat)) |
1387 | |
1239 | 1388 |
1240 @predicate('head()', safe=True) | 1389 @predicate('head()', safe=True) |
1241 def head(repo, subset, x): | 1390 def head(repo, subset, x): |
1242 """Changeset is a named branch head. | 1391 """Changeset is a named branch head. |
1243 """ | 1392 """ |
1246 hs = set() | 1395 hs = set() |
1247 cl = repo.changelog | 1396 cl = repo.changelog |
1248 for ls in repo.branchmap().iterheads(): | 1397 for ls in repo.branchmap().iterheads(): |
1249 hs.update(cl.rev(h) for h in ls) | 1398 hs.update(cl.rev(h) for h in ls) |
1250 return subset & baseset(hs) | 1399 return subset & baseset(hs) |
1400 | |
1251 | 1401 |
1252 @predicate('heads(set)', safe=True, takeorder=True) | 1402 @predicate('heads(set)', safe=True, takeorder=True) |
1253 def heads(repo, subset, x, order): | 1403 def heads(repo, subset, x, order): |
1254 """Members of set with no children in set. | 1404 """Members of set with no children in set. |
1255 """ | 1405 """ |
1268 heads.difference_update(wdirparents) | 1418 heads.difference_update(wdirparents) |
1269 heads.add(node.wdirrev) | 1419 heads.add(node.wdirrev) |
1270 heads = baseset(heads) | 1420 heads = baseset(heads) |
1271 return subset & heads | 1421 return subset & heads |
1272 | 1422 |
1423 | |
1273 @predicate('hidden()', safe=True) | 1424 @predicate('hidden()', safe=True) |
1274 def hidden(repo, subset, x): | 1425 def hidden(repo, subset, x): |
1275 """Hidden changesets. | 1426 """Hidden changesets. |
1276 """ | 1427 """ |
1277 # i18n: "hidden" is a keyword | 1428 # i18n: "hidden" is a keyword |
1278 getargs(x, 0, 0, _("hidden takes no arguments")) | 1429 getargs(x, 0, 0, _("hidden takes no arguments")) |
1279 hiddenrevs = repoview.filterrevs(repo, 'visible') | 1430 hiddenrevs = repoview.filterrevs(repo, 'visible') |
1280 return subset & hiddenrevs | 1431 return subset & hiddenrevs |
1281 | 1432 |
1433 | |
1282 @predicate('keyword(string)', safe=True, weight=10) | 1434 @predicate('keyword(string)', safe=True, weight=10) |
1283 def keyword(repo, subset, x): | 1435 def keyword(repo, subset, x): |
1284 """Search commit message, user name, and names of changed files for | 1436 """Search commit message, user name, and names of changed files for |
1285 string. The match is case-insensitive. | 1437 string. The match is case-insensitive. |
1286 | 1438 |
1290 # i18n: "keyword" is a keyword | 1442 # i18n: "keyword" is a keyword |
1291 kw = encoding.lower(getstring(x, _("keyword requires a string"))) | 1443 kw = encoding.lower(getstring(x, _("keyword requires a string"))) |
1292 | 1444 |
1293 def matches(r): | 1445 def matches(r): |
1294 c = repo[r] | 1446 c = repo[r] |
1295 return any(kw in encoding.lower(t) | 1447 return any( |
1296 for t in c.files() + [c.user(), c.description()]) | 1448 kw in encoding.lower(t) |
1449 for t in c.files() + [c.user(), c.description()] | |
1450 ) | |
1297 | 1451 |
1298 return subset.filter(matches, condrepr=('<keyword %r>', kw)) | 1452 return subset.filter(matches, condrepr=('<keyword %r>', kw)) |
1453 | |
1299 | 1454 |
1300 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True, weight=0) | 1455 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True, weight=0) |
1301 def limit(repo, subset, x, order): | 1456 def limit(repo, subset, x, order): |
1302 """First n members of set, defaulting to 1, starting from offset. | 1457 """First n members of set, defaulting to 1, starting from offset. |
1303 """ | 1458 """ |
1317 ls = os.slice(ofs, ofs + lim) | 1472 ls = os.slice(ofs, ofs + lim) |
1318 if order == followorder and lim > 1: | 1473 if order == followorder and lim > 1: |
1319 return subset & ls | 1474 return subset & ls |
1320 return ls & subset | 1475 return ls & subset |
1321 | 1476 |
1477 | |
1322 @predicate('last(set, [n])', safe=True, takeorder=True) | 1478 @predicate('last(set, [n])', safe=True, takeorder=True) |
1323 def last(repo, subset, x, order): | 1479 def last(repo, subset, x, order): |
1324 """Last n members of set, defaulting to 1. | 1480 """Last n members of set, defaulting to 1. |
1325 """ | 1481 """ |
1326 # i18n: "last" is a keyword | 1482 # i18n: "last" is a keyword |
1337 if order == followorder and lim > 1: | 1493 if order == followorder and lim > 1: |
1338 return subset & ls | 1494 return subset & ls |
1339 ls.reverse() | 1495 ls.reverse() |
1340 return ls & subset | 1496 return ls & subset |
1341 | 1497 |
1498 | |
1342 @predicate('max(set)', safe=True) | 1499 @predicate('max(set)', safe=True) |
1343 def maxrev(repo, subset, x): | 1500 def maxrev(repo, subset, x): |
1344 """Changeset with highest revision number in set. | 1501 """Changeset with highest revision number in set. |
1345 """ | 1502 """ |
1346 os = getset(repo, fullreposet(repo), x) | 1503 os = getset(repo, fullreposet(repo), x) |
1352 # os.max() throws a ValueError when the collection is empty. | 1509 # os.max() throws a ValueError when the collection is empty. |
1353 # Same as python's max(). | 1510 # Same as python's max(). |
1354 pass | 1511 pass |
1355 return baseset(datarepr=('<max %r, %r>', subset, os)) | 1512 return baseset(datarepr=('<max %r, %r>', subset, os)) |
1356 | 1513 |
1514 | |
1357 @predicate('merge()', safe=True) | 1515 @predicate('merge()', safe=True) |
1358 def merge(repo, subset, x): | 1516 def merge(repo, subset, x): |
1359 """Changeset is a merge changeset. | 1517 """Changeset is a merge changeset. |
1360 """ | 1518 """ |
1361 # i18n: "merge" is a keyword | 1519 # i18n: "merge" is a keyword |
1362 getargs(x, 0, 0, _("merge takes no arguments")) | 1520 getargs(x, 0, 0, _("merge takes no arguments")) |
1363 cl = repo.changelog | 1521 cl = repo.changelog |
1364 nullrev = node.nullrev | 1522 nullrev = node.nullrev |
1523 | |
1365 def ismerge(r): | 1524 def ismerge(r): |
1366 try: | 1525 try: |
1367 return cl.parentrevs(r)[1] != nullrev | 1526 return cl.parentrevs(r)[1] != nullrev |
1368 except error.WdirUnsupported: | 1527 except error.WdirUnsupported: |
1369 return bool(repo[r].p2()) | 1528 return bool(repo[r].p2()) |
1529 | |
1370 return subset.filter(ismerge, condrepr='<merge>') | 1530 return subset.filter(ismerge, condrepr='<merge>') |
1531 | |
1371 | 1532 |
1372 @predicate('branchpoint()', safe=True) | 1533 @predicate('branchpoint()', safe=True) |
1373 def branchpoint(repo, subset, x): | 1534 def branchpoint(repo, subset, x): |
1374 """Changesets with more than one child. | 1535 """Changesets with more than one child. |
1375 """ | 1536 """ |
1379 if not subset: | 1540 if not subset: |
1380 return baseset() | 1541 return baseset() |
1381 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset | 1542 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset |
1382 # (and if it is not, it should.) | 1543 # (and if it is not, it should.) |
1383 baserev = min(subset) | 1544 baserev = min(subset) |
1384 parentscount = [0]*(len(repo) - baserev) | 1545 parentscount = [0] * (len(repo) - baserev) |
1385 for r in cl.revs(start=baserev + 1): | 1546 for r in cl.revs(start=baserev + 1): |
1386 for p in cl.parentrevs(r): | 1547 for p in cl.parentrevs(r): |
1387 if p >= baserev: | 1548 if p >= baserev: |
1388 parentscount[p - baserev] += 1 | 1549 parentscount[p - baserev] += 1 |
1389 return subset.filter(lambda r: parentscount[r - baserev] > 1, | 1550 return subset.filter( |
1390 condrepr='<branchpoint>') | 1551 lambda r: parentscount[r - baserev] > 1, condrepr='<branchpoint>' |
1552 ) | |
1553 | |
1391 | 1554 |
1392 @predicate('min(set)', safe=True) | 1555 @predicate('min(set)', safe=True) |
1393 def minrev(repo, subset, x): | 1556 def minrev(repo, subset, x): |
1394 """Changeset with lowest revision number in set. | 1557 """Changeset with lowest revision number in set. |
1395 """ | 1558 """ |
1402 # os.min() throws a ValueError when the collection is empty. | 1565 # os.min() throws a ValueError when the collection is empty. |
1403 # Same as python's min(). | 1566 # Same as python's min(). |
1404 pass | 1567 pass |
1405 return baseset(datarepr=('<min %r, %r>', subset, os)) | 1568 return baseset(datarepr=('<min %r, %r>', subset, os)) |
1406 | 1569 |
1570 | |
1407 @predicate('modifies(pattern)', safe=True, weight=30) | 1571 @predicate('modifies(pattern)', safe=True, weight=30) |
1408 def modifies(repo, subset, x): | 1572 def modifies(repo, subset, x): |
1409 """Changesets modifying files matched by pattern. | 1573 """Changesets modifying files matched by pattern. |
1410 | 1574 |
1411 The pattern without explicit kind like ``glob:`` is expected to be | 1575 The pattern without explicit kind like ``glob:`` is expected to be |
1414 """ | 1578 """ |
1415 # i18n: "modifies" is a keyword | 1579 # i18n: "modifies" is a keyword |
1416 pat = getstring(x, _("modifies requires a pattern")) | 1580 pat = getstring(x, _("modifies requires a pattern")) |
1417 return checkstatus(repo, subset, pat, 0) | 1581 return checkstatus(repo, subset, pat, 0) |
1418 | 1582 |
1583 | |
1419 @predicate('named(namespace)') | 1584 @predicate('named(namespace)') |
1420 def named(repo, subset, x): | 1585 def named(repo, subset, x): |
1421 """The changesets in a given namespace. | 1586 """The changesets in a given namespace. |
1422 | 1587 |
1423 Pattern matching is supported for `namespace`. See | 1588 Pattern matching is supported for `namespace`. See |
1424 :hg:`help revisions.patterns`. | 1589 :hg:`help revisions.patterns`. |
1425 """ | 1590 """ |
1426 # i18n: "named" is a keyword | 1591 # i18n: "named" is a keyword |
1427 args = getargs(x, 1, 1, _('named requires a namespace argument')) | 1592 args = getargs(x, 1, 1, _('named requires a namespace argument')) |
1428 | 1593 |
1429 ns = getstring(args[0], | 1594 ns = getstring( |
1430 # i18n: "named" is a keyword | 1595 args[0], |
1431 _('the argument to named must be a string')) | 1596 # i18n: "named" is a keyword |
1597 _('the argument to named must be a string'), | |
1598 ) | |
1432 kind, pattern, matcher = stringutil.stringmatcher(ns) | 1599 kind, pattern, matcher = stringutil.stringmatcher(ns) |
1433 namespaces = set() | 1600 namespaces = set() |
1434 if kind == 'literal': | 1601 if kind == 'literal': |
1435 if pattern not in repo.names: | 1602 if pattern not in repo.names: |
1436 raise error.RepoLookupError(_("namespace '%s' does not exist") | 1603 raise error.RepoLookupError(_("namespace '%s' does not exist") % ns) |
1437 % ns) | |
1438 namespaces.add(repo.names[pattern]) | 1604 namespaces.add(repo.names[pattern]) |
1439 else: | 1605 else: |
1440 for name, ns in repo.names.iteritems(): | 1606 for name, ns in repo.names.iteritems(): |
1441 if matcher(name): | 1607 if matcher(name): |
1442 namespaces.add(ns) | 1608 namespaces.add(ns) |
1447 if name not in ns.deprecated: | 1613 if name not in ns.deprecated: |
1448 names.update(repo[n].rev() for n in ns.nodes(repo, name)) | 1614 names.update(repo[n].rev() for n in ns.nodes(repo, name)) |
1449 | 1615 |
1450 names -= {node.nullrev} | 1616 names -= {node.nullrev} |
1451 return subset & names | 1617 return subset & names |
1618 | |
1452 | 1619 |
1453 @predicate('id(string)', safe=True) | 1620 @predicate('id(string)', safe=True) |
1454 def node_(repo, subset, x): | 1621 def node_(repo, subset, x): |
1455 """Revision non-ambiguously specified by the given hex string prefix. | 1622 """Revision non-ambiguously specified by the given hex string prefix. |
1456 """ | 1623 """ |
1479 if rn is None: | 1646 if rn is None: |
1480 return baseset() | 1647 return baseset() |
1481 result = baseset([rn]) | 1648 result = baseset([rn]) |
1482 return result & subset | 1649 return result & subset |
1483 | 1650 |
1651 | |
1484 @predicate('none()', safe=True) | 1652 @predicate('none()', safe=True) |
1485 def none(repo, subset, x): | 1653 def none(repo, subset, x): |
1486 """No changesets. | 1654 """No changesets. |
1487 """ | 1655 """ |
1488 # i18n: "none" is a keyword | 1656 # i18n: "none" is a keyword |
1489 getargs(x, 0, 0, _("none takes no arguments")) | 1657 getargs(x, 0, 0, _("none takes no arguments")) |
1490 return baseset() | 1658 return baseset() |
1659 | |
1491 | 1660 |
1492 @predicate('obsolete()', safe=True) | 1661 @predicate('obsolete()', safe=True) |
1493 def obsolete(repo, subset, x): | 1662 def obsolete(repo, subset, x): |
1494 """Mutable changeset with a newer version.""" | 1663 """Mutable changeset with a newer version.""" |
1495 # i18n: "obsolete" is a keyword | 1664 # i18n: "obsolete" is a keyword |
1496 getargs(x, 0, 0, _("obsolete takes no arguments")) | 1665 getargs(x, 0, 0, _("obsolete takes no arguments")) |
1497 obsoletes = obsmod.getrevs(repo, 'obsolete') | 1666 obsoletes = obsmod.getrevs(repo, 'obsolete') |
1498 return subset & obsoletes | 1667 return subset & obsoletes |
1499 | 1668 |
1669 | |
1500 @predicate('only(set, [set])', safe=True) | 1670 @predicate('only(set, [set])', safe=True) |
1501 def only(repo, subset, x): | 1671 def only(repo, subset, x): |
1502 """Changesets that are ancestors of the first set that are not ancestors | 1672 """Changesets that are ancestors of the first set that are not ancestors |
1503 of any other head in the repo. If a second set is specified, the result | 1673 of any other head in the repo. If a second set is specified, the result |
1504 is ancestors of the first set that are not ancestors of the second set | 1674 is ancestors of the first set that are not ancestors of the second set |
1511 if len(args) == 1: | 1681 if len(args) == 1: |
1512 if not include: | 1682 if not include: |
1513 return baseset() | 1683 return baseset() |
1514 | 1684 |
1515 descendants = set(dagop.revdescendants(repo, include, False)) | 1685 descendants = set(dagop.revdescendants(repo, include, False)) |
1516 exclude = [rev for rev in cl.headrevs() | 1686 exclude = [ |
1517 if not rev in descendants and not rev in include] | 1687 rev |
1688 for rev in cl.headrevs() | |
1689 if not rev in descendants and not rev in include | |
1690 ] | |
1518 else: | 1691 else: |
1519 exclude = getset(repo, fullreposet(repo), args[1]) | 1692 exclude = getset(repo, fullreposet(repo), args[1]) |
1520 | 1693 |
1521 results = set(cl.findmissingrevs(common=exclude, heads=include)) | 1694 results = set(cl.findmissingrevs(common=exclude, heads=include)) |
1522 # XXX we should turn this into a baseset instead of a set, smartset may do | 1695 # XXX we should turn this into a baseset instead of a set, smartset may do |
1523 # some optimizations from the fact this is a baseset. | 1696 # some optimizations from the fact this is a baseset. |
1524 return subset & results | 1697 return subset & results |
1698 | |
1525 | 1699 |
1526 @predicate('origin([set])', safe=True) | 1700 @predicate('origin([set])', safe=True) |
1527 def origin(repo, subset, x): | 1701 def origin(repo, subset, x): |
1528 """ | 1702 """ |
1529 Changesets that were specified as a source for the grafts, transplants or | 1703 Changesets that were specified as a source for the grafts, transplants or |
1553 o -= {None} | 1727 o -= {None} |
1554 # XXX we should turn this into a baseset instead of a set, smartset may do | 1728 # XXX we should turn this into a baseset instead of a set, smartset may do |
1555 # some optimizations from the fact this is a baseset. | 1729 # some optimizations from the fact this is a baseset. |
1556 return subset & o | 1730 return subset & o |
1557 | 1731 |
1732 | |
1558 @predicate('outgoing([path])', safe=False, weight=10) | 1733 @predicate('outgoing([path])', safe=False, weight=10) |
1559 def outgoing(repo, subset, x): | 1734 def outgoing(repo, subset, x): |
1560 """Changesets not found in the specified destination repository, or the | 1735 """Changesets not found in the specified destination repository, or the |
1561 default push location. | 1736 default push location. |
1562 """ | 1737 """ |
1563 # Avoid cycles. | 1738 # Avoid cycles. |
1564 from . import ( | 1739 from . import ( |
1565 discovery, | 1740 discovery, |
1566 hg, | 1741 hg, |
1567 ) | 1742 ) |
1743 | |
1568 # i18n: "outgoing" is a keyword | 1744 # i18n: "outgoing" is a keyword |
1569 l = getargs(x, 0, 1, _("outgoing takes one or no arguments")) | 1745 l = getargs(x, 0, 1, _("outgoing takes one or no arguments")) |
1570 # i18n: "outgoing" is a keyword | 1746 # i18n: "outgoing" is a keyword |
1571 dest = l and getstring(l[0], _("outgoing requires a repository path")) or '' | 1747 dest = l and getstring(l[0], _("outgoing requires a repository path")) or '' |
1572 if not dest: | 1748 if not dest: |
1573 # ui.paths.getpath() explicitly tests for None, not just a boolean | 1749 # ui.paths.getpath() explicitly tests for None, not just a boolean |
1574 dest = None | 1750 dest = None |
1575 path = repo.ui.paths.getpath(dest, default=('default-push', 'default')) | 1751 path = repo.ui.paths.getpath(dest, default=('default-push', 'default')) |
1576 if not path: | 1752 if not path: |
1577 raise error.Abort(_('default repository not configured!'), | 1753 raise error.Abort( |
1578 hint=_("see 'hg help config.paths'")) | 1754 _('default repository not configured!'), |
1755 hint=_("see 'hg help config.paths'"), | |
1756 ) | |
1579 dest = path.pushloc or path.loc | 1757 dest = path.pushloc or path.loc |
1580 branches = path.branch, [] | 1758 branches = path.branch, [] |
1581 | 1759 |
1582 revs, checkout = hg.addbranchrevs(repo, repo, branches, []) | 1760 revs, checkout = hg.addbranchrevs(repo, repo, branches, []) |
1583 if revs: | 1761 if revs: |
1587 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs) | 1765 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs) |
1588 repo.ui.popbuffer() | 1766 repo.ui.popbuffer() |
1589 cl = repo.changelog | 1767 cl = repo.changelog |
1590 o = {cl.rev(r) for r in outgoing.missing} | 1768 o = {cl.rev(r) for r in outgoing.missing} |
1591 return subset & o | 1769 return subset & o |
1770 | |
1592 | 1771 |
1593 @predicate('p1([set])', safe=True) | 1772 @predicate('p1([set])', safe=True) |
1594 def p1(repo, subset, x): | 1773 def p1(repo, subset, x): |
1595 """First parent of changesets in set, or the working directory. | 1774 """First parent of changesets in set, or the working directory. |
1596 """ | 1775 """ |
1609 ps.add(repo[r].p1().rev()) | 1788 ps.add(repo[r].p1().rev()) |
1610 ps -= {node.nullrev} | 1789 ps -= {node.nullrev} |
1611 # XXX we should turn this into a baseset instead of a set, smartset may do | 1790 # XXX we should turn this into a baseset instead of a set, smartset may do |
1612 # some optimizations from the fact this is a baseset. | 1791 # some optimizations from the fact this is a baseset. |
1613 return subset & ps | 1792 return subset & ps |
1793 | |
1614 | 1794 |
1615 @predicate('p2([set])', safe=True) | 1795 @predicate('p2([set])', safe=True) |
1616 def p2(repo, subset, x): | 1796 def p2(repo, subset, x): |
1617 """Second parent of changesets in set, or the working directory. | 1797 """Second parent of changesets in set, or the working directory. |
1618 """ | 1798 """ |
1638 ps -= {node.nullrev} | 1818 ps -= {node.nullrev} |
1639 # XXX we should turn this into a baseset instead of a set, smartset may do | 1819 # XXX we should turn this into a baseset instead of a set, smartset may do |
1640 # some optimizations from the fact this is a baseset. | 1820 # some optimizations from the fact this is a baseset. |
1641 return subset & ps | 1821 return subset & ps |
1642 | 1822 |
1823 | |
1643 def parentpost(repo, subset, x, order): | 1824 def parentpost(repo, subset, x, order): |
1644 return p1(repo, subset, x) | 1825 return p1(repo, subset, x) |
1826 | |
1645 | 1827 |
1646 @predicate('parents([set])', safe=True) | 1828 @predicate('parents([set])', safe=True) |
1647 def parents(repo, subset, x): | 1829 def parents(repo, subset, x): |
1648 """ | 1830 """ |
1649 The set of all parents for all changesets in set, or the working directory. | 1831 The set of all parents for all changesets in set, or the working directory. |
1661 except error.WdirUnsupported: | 1843 except error.WdirUnsupported: |
1662 up(p.rev() for p in repo[r].parents()) | 1844 up(p.rev() for p in repo[r].parents()) |
1663 ps -= {node.nullrev} | 1845 ps -= {node.nullrev} |
1664 return subset & ps | 1846 return subset & ps |
1665 | 1847 |
1848 | |
1666 def _phase(repo, subset, *targets): | 1849 def _phase(repo, subset, *targets): |
1667 """helper to select all rev in <targets> phases""" | 1850 """helper to select all rev in <targets> phases""" |
1668 return repo._phasecache.getrevset(repo, targets, subset) | 1851 return repo._phasecache.getrevset(repo, targets, subset) |
1669 | 1852 |
1853 | |
1670 @predicate('_phase(idx)', safe=True) | 1854 @predicate('_phase(idx)', safe=True) |
1671 def phase(repo, subset, x): | 1855 def phase(repo, subset, x): |
1672 l = getargs(x, 1, 1, ("_phase requires one argument")) | 1856 l = getargs(x, 1, 1, "_phase requires one argument") |
1673 target = getinteger(l[0], ("_phase expects a number")) | 1857 target = getinteger(l[0], "_phase expects a number") |
1674 return _phase(repo, subset, target) | 1858 return _phase(repo, subset, target) |
1859 | |
1675 | 1860 |
1676 @predicate('draft()', safe=True) | 1861 @predicate('draft()', safe=True) |
1677 def draft(repo, subset, x): | 1862 def draft(repo, subset, x): |
1678 """Changeset in draft phase.""" | 1863 """Changeset in draft phase.""" |
1679 # i18n: "draft" is a keyword | 1864 # i18n: "draft" is a keyword |
1680 getargs(x, 0, 0, _("draft takes no arguments")) | 1865 getargs(x, 0, 0, _("draft takes no arguments")) |
1681 target = phases.draft | 1866 target = phases.draft |
1682 return _phase(repo, subset, target) | 1867 return _phase(repo, subset, target) |
1683 | 1868 |
1869 | |
1684 @predicate('secret()', safe=True) | 1870 @predicate('secret()', safe=True) |
1685 def secret(repo, subset, x): | 1871 def secret(repo, subset, x): |
1686 """Changeset in secret phase.""" | 1872 """Changeset in secret phase.""" |
1687 # i18n: "secret" is a keyword | 1873 # i18n: "secret" is a keyword |
1688 getargs(x, 0, 0, _("secret takes no arguments")) | 1874 getargs(x, 0, 0, _("secret takes no arguments")) |
1689 target = phases.secret | 1875 target = phases.secret |
1690 return _phase(repo, subset, target) | 1876 return _phase(repo, subset, target) |
1691 | 1877 |
1878 | |
1692 @predicate('stack([revs])', safe=True) | 1879 @predicate('stack([revs])', safe=True) |
1693 def stack(repo, subset, x): | 1880 def stack(repo, subset, x): |
1694 """Experimental revset for the stack of changesets or working directory | 1881 """Experimental revset for the stack of changesets or working directory |
1695 parent. (EXPERIMENTAL) | 1882 parent. (EXPERIMENTAL) |
1696 """ | 1883 """ |
1701 for revision in getset(repo, fullreposet(repo), x): | 1888 for revision in getset(repo, fullreposet(repo), x): |
1702 currentstack = stackmod.getstack(repo, revision) | 1889 currentstack = stackmod.getstack(repo, revision) |
1703 stacks = stacks + currentstack | 1890 stacks = stacks + currentstack |
1704 | 1891 |
1705 return subset & stacks | 1892 return subset & stacks |
1893 | |
1706 | 1894 |
1707 def parentspec(repo, subset, x, n, order): | 1895 def parentspec(repo, subset, x, n, order): |
1708 """``set^0`` | 1896 """``set^0`` |
1709 The set. | 1897 The set. |
1710 ``set^1`` (or ``set^``), ``set^2`` | 1898 ``set^1`` (or ``set^``), ``set^2`` |
1735 parents = repo[r].parents() | 1923 parents = repo[r].parents() |
1736 if len(parents) == 2: | 1924 if len(parents) == 2: |
1737 ps.add(parents[1].rev()) | 1925 ps.add(parents[1].rev()) |
1738 return subset & ps | 1926 return subset & ps |
1739 | 1927 |
1928 | |
1740 @predicate('present(set)', safe=True, takeorder=True) | 1929 @predicate('present(set)', safe=True, takeorder=True) |
1741 def present(repo, subset, x, order): | 1930 def present(repo, subset, x, order): |
1742 """An empty set, if any revision in set isn't found; otherwise, | 1931 """An empty set, if any revision in set isn't found; otherwise, |
1743 all revisions in set. | 1932 all revisions in set. |
1744 | 1933 |
1748 """ | 1937 """ |
1749 try: | 1938 try: |
1750 return getset(repo, subset, x, order) | 1939 return getset(repo, subset, x, order) |
1751 except error.RepoLookupError: | 1940 except error.RepoLookupError: |
1752 return baseset() | 1941 return baseset() |
1942 | |
1753 | 1943 |
1754 # for internal use | 1944 # for internal use |
1755 @predicate('_notpublic', safe=True) | 1945 @predicate('_notpublic', safe=True) |
1756 def _notpublic(repo, subset, x): | 1946 def _notpublic(repo, subset, x): |
1757 getargs(x, 0, 0, "_notpublic takes no arguments") | 1947 getargs(x, 0, 0, "_notpublic takes no arguments") |
1758 return _phase(repo, subset, phases.draft, phases.secret) | 1948 return _phase(repo, subset, phases.draft, phases.secret) |
1949 | |
1759 | 1950 |
1760 # for internal use | 1951 # for internal use |
1761 @predicate('_phaseandancestors(phasename, set)', safe=True) | 1952 @predicate('_phaseandancestors(phasename, set)', safe=True) |
1762 def _phaseandancestors(repo, subset, x): | 1953 def _phaseandancestors(repo, subset, x): |
1763 # equivalent to (phasename() & ancestors(set)) but more efficient | 1954 # equivalent to (phasename() & ancestors(set)) but more efficient |
1768 | 1959 |
1769 draft = phases.draft | 1960 draft = phases.draft |
1770 secret = phases.secret | 1961 secret = phases.secret |
1771 phasenamemap = { | 1962 phasenamemap = { |
1772 '_notpublic': draft, | 1963 '_notpublic': draft, |
1773 'draft': draft, # follow secret's ancestors | 1964 'draft': draft, # follow secret's ancestors |
1774 'secret': secret, | 1965 'secret': secret, |
1775 } | 1966 } |
1776 if phasename not in phasenamemap: | 1967 if phasename not in phasenamemap: |
1777 raise error.ParseError('%r is not a valid phasename' % phasename) | 1968 raise error.ParseError('%r is not a valid phasename' % phasename) |
1778 | 1969 |
1782 def cutfunc(rev): | 1973 def cutfunc(rev): |
1783 return getphase(repo, rev) < minimalphase | 1974 return getphase(repo, rev) < minimalphase |
1784 | 1975 |
1785 revs = dagop.revancestors(repo, s, cutfunc=cutfunc) | 1976 revs = dagop.revancestors(repo, s, cutfunc=cutfunc) |
1786 | 1977 |
1787 if phasename == 'draft': # need to remove secret changesets | 1978 if phasename == 'draft': # need to remove secret changesets |
1788 revs = revs.filter(lambda r: getphase(repo, r) == draft) | 1979 revs = revs.filter(lambda r: getphase(repo, r) == draft) |
1789 return subset & revs | 1980 return subset & revs |
1981 | |
1790 | 1982 |
1791 @predicate('public()', safe=True) | 1983 @predicate('public()', safe=True) |
1792 def public(repo, subset, x): | 1984 def public(repo, subset, x): |
1793 """Changeset in public phase.""" | 1985 """Changeset in public phase.""" |
1794 # i18n: "public" is a keyword | 1986 # i18n: "public" is a keyword |
1795 getargs(x, 0, 0, _("public takes no arguments")) | 1987 getargs(x, 0, 0, _("public takes no arguments")) |
1796 return _phase(repo, subset, phases.public) | 1988 return _phase(repo, subset, phases.public) |
1797 | 1989 |
1990 | |
1798 @predicate('remote([id [,path]])', safe=False) | 1991 @predicate('remote([id [,path]])', safe=False) |
1799 def remote(repo, subset, x): | 1992 def remote(repo, subset, x): |
1800 """Local revision that corresponds to the given identifier in a | 1993 """Local revision that corresponds to the given identifier in a |
1801 remote repository, if present. Here, the '.' identifier is a | 1994 remote repository, if present. Here, the '.' identifier is a |
1802 synonym for the current local branch. | 1995 synonym for the current local branch. |
1803 """ | 1996 """ |
1804 | 1997 |
1805 from . import hg # avoid start-up nasties | 1998 from . import hg # avoid start-up nasties |
1999 | |
1806 # i18n: "remote" is a keyword | 2000 # i18n: "remote" is a keyword |
1807 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments")) | 2001 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments")) |
1808 | 2002 |
1809 q = '.' | 2003 q = '.' |
1810 if len(l) > 0: | 2004 if len(l) > 0: |
1811 # i18n: "remote" is a keyword | 2005 # i18n: "remote" is a keyword |
1812 q = getstring(l[0], _("remote requires a string id")) | 2006 q = getstring(l[0], _("remote requires a string id")) |
1813 if q == '.': | 2007 if q == '.': |
1814 q = repo['.'].branch() | 2008 q = repo['.'].branch() |
1815 | 2009 |
1816 dest = '' | 2010 dest = '' |
1828 r = repo[n].rev() | 2022 r = repo[n].rev() |
1829 if r in subset: | 2023 if r in subset: |
1830 return baseset([r]) | 2024 return baseset([r]) |
1831 return baseset() | 2025 return baseset() |
1832 | 2026 |
2027 | |
1833 @predicate('removes(pattern)', safe=True, weight=30) | 2028 @predicate('removes(pattern)', safe=True, weight=30) |
1834 def removes(repo, subset, x): | 2029 def removes(repo, subset, x): |
1835 """Changesets which remove files matching pattern. | 2030 """Changesets which remove files matching pattern. |
1836 | 2031 |
1837 The pattern without explicit kind like ``glob:`` is expected to be | 2032 The pattern without explicit kind like ``glob:`` is expected to be |
1839 directory. | 2034 directory. |
1840 """ | 2035 """ |
1841 # i18n: "removes" is a keyword | 2036 # i18n: "removes" is a keyword |
1842 pat = getstring(x, _("removes requires a pattern")) | 2037 pat = getstring(x, _("removes requires a pattern")) |
1843 return checkstatus(repo, subset, pat, 2) | 2038 return checkstatus(repo, subset, pat, 2) |
2039 | |
1844 | 2040 |
1845 @predicate('rev(number)', safe=True) | 2041 @predicate('rev(number)', safe=True) |
1846 def rev(repo, subset, x): | 2042 def rev(repo, subset, x): |
1847 """Revision with the given numeric identifier. | 2043 """Revision with the given numeric identifier. |
1848 """ | 2044 """ |
1856 raise error.ParseError(_("rev expects a number")) | 2052 raise error.ParseError(_("rev expects a number")) |
1857 if l not in repo.changelog and l not in _virtualrevs: | 2053 if l not in repo.changelog and l not in _virtualrevs: |
1858 return baseset() | 2054 return baseset() |
1859 return subset & baseset([l]) | 2055 return subset & baseset([l]) |
1860 | 2056 |
2057 | |
1861 @predicate('_rev(number)', safe=True) | 2058 @predicate('_rev(number)', safe=True) |
1862 def _rev(repo, subset, x): | 2059 def _rev(repo, subset, x): |
1863 # internal version of "rev(x)" that raise error if "x" is invalid | 2060 # internal version of "rev(x)" that raise error if "x" is invalid |
1864 # i18n: "rev" is a keyword | 2061 # i18n: "rev" is a keyword |
1865 l = getargs(x, 1, 1, _("rev requires one argument")) | 2062 l = getargs(x, 1, 1, _("rev requires one argument")) |
1867 # i18n: "rev" is a keyword | 2064 # i18n: "rev" is a keyword |
1868 l = int(getstring(l[0], _("rev requires a number"))) | 2065 l = int(getstring(l[0], _("rev requires a number"))) |
1869 except (TypeError, ValueError): | 2066 except (TypeError, ValueError): |
1870 # i18n: "rev" is a keyword | 2067 # i18n: "rev" is a keyword |
1871 raise error.ParseError(_("rev expects a number")) | 2068 raise error.ParseError(_("rev expects a number")) |
1872 repo.changelog.node(l) # check that the rev exists | 2069 repo.changelog.node(l) # check that the rev exists |
1873 return subset & baseset([l]) | 2070 return subset & baseset([l]) |
2071 | |
1874 | 2072 |
1875 @predicate('revset(set)', safe=True, takeorder=True) | 2073 @predicate('revset(set)', safe=True, takeorder=True) |
1876 def revsetpredicate(repo, subset, x, order): | 2074 def revsetpredicate(repo, subset, x, order): |
1877 """Strictly interpret the content as a revset. | 2075 """Strictly interpret the content as a revset. |
1878 | 2076 |
1879 The content of this special predicate will be strictly interpreted as a | 2077 The content of this special predicate will be strictly interpreted as a |
1880 revset. For example, ``revset(id(0))`` will be interpreted as "id(0)" | 2078 revset. For example, ``revset(id(0))`` will be interpreted as "id(0)" |
1881 without possible ambiguity with a "id(0)" bookmark or tag. | 2079 without possible ambiguity with a "id(0)" bookmark or tag. |
1882 """ | 2080 """ |
1883 return getset(repo, subset, x, order) | 2081 return getset(repo, subset, x, order) |
2082 | |
1884 | 2083 |
1885 @predicate('matching(revision [, field])', safe=True) | 2084 @predicate('matching(revision [, field])', safe=True) |
1886 def matching(repo, subset, x): | 2085 def matching(repo, subset, x): |
1887 """Changesets in which a given set of fields match the set of fields in the | 2086 """Changesets in which a given set of fields match the set of fields in the |
1888 selected revision or set. | 2087 selected revision or set. |
1912 | 2111 |
1913 revs = getset(repo, fullreposet(repo), l[0]) | 2112 revs = getset(repo, fullreposet(repo), l[0]) |
1914 | 2113 |
1915 fieldlist = ['metadata'] | 2114 fieldlist = ['metadata'] |
1916 if len(l) > 1: | 2115 if len(l) > 1: |
1917 fieldlist = getstring(l[1], | 2116 fieldlist = getstring( |
1918 # i18n: "matching" is a keyword | 2117 l[1], |
1919 _("matching requires a string " | 2118 # i18n: "matching" is a keyword |
1920 "as its second argument")).split() | 2119 _("matching requires a string " "as its second argument"), |
2120 ).split() | |
1921 | 2121 |
1922 # Make sure that there are no repeated fields, | 2122 # Make sure that there are no repeated fields, |
1923 # expand the 'special' 'metadata' field type | 2123 # expand the 'special' 'metadata' field type |
1924 # and check the 'files' whenever we check the 'diff' | 2124 # and check the 'files' whenever we check the 'diff' |
1925 fields = [] | 2125 fields = [] |
1941 fields.discard('summary') | 2141 fields.discard('summary') |
1942 | 2142 |
1943 # We may want to match more than one field | 2143 # We may want to match more than one field |
1944 # Not all fields take the same amount of time to be matched | 2144 # Not all fields take the same amount of time to be matched |
1945 # Sort the selected fields in order of increasing matching cost | 2145 # Sort the selected fields in order of increasing matching cost |
1946 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary', | 2146 fieldorder = [ |
1947 'files', 'description', 'substate', 'diff'] | 2147 'phase', |
2148 'parents', | |
2149 'user', | |
2150 'date', | |
2151 'branch', | |
2152 'summary', | |
2153 'files', | |
2154 'description', | |
2155 'substate', | |
2156 'diff', | |
2157 ] | |
2158 | |
1948 def fieldkeyfunc(f): | 2159 def fieldkeyfunc(f): |
1949 try: | 2160 try: |
1950 return fieldorder.index(f) | 2161 return fieldorder.index(f) |
1951 except ValueError: | 2162 except ValueError: |
1952 # assume an unknown field is very costly | 2163 # assume an unknown field is very costly |
1953 return len(fieldorder) | 2164 return len(fieldorder) |
2165 | |
1954 fields = list(fields) | 2166 fields = list(fields) |
1955 fields.sort(key=fieldkeyfunc) | 2167 fields.sort(key=fieldkeyfunc) |
1956 | 2168 |
1957 # Each field will be matched with its own "getfield" function | 2169 # Each field will be matched with its own "getfield" function |
1958 # which will be added to the getfieldfuncs array of functions | 2170 # which will be added to the getfieldfuncs array of functions |
1965 'files': lambda r: repo[r].files(), | 2177 'files': lambda r: repo[r].files(), |
1966 'parents': lambda r: repo[r].parents(), | 2178 'parents': lambda r: repo[r].parents(), |
1967 'phase': lambda r: repo[r].phase(), | 2179 'phase': lambda r: repo[r].phase(), |
1968 'substate': lambda r: repo[r].substate, | 2180 'substate': lambda r: repo[r].substate, |
1969 'summary': lambda r: repo[r].description().splitlines()[0], | 2181 'summary': lambda r: repo[r].description().splitlines()[0], |
1970 'diff': lambda r: list(repo[r].diff( | 2182 'diff': lambda r: list( |
1971 opts=diffutil.diffallopts(repo.ui, {'git': True}))), | 2183 repo[r].diff(opts=diffutil.diffallopts(repo.ui, {'git': True})) |
2184 ), | |
1972 } | 2185 } |
1973 for info in fields: | 2186 for info in fields: |
1974 getfield = _funcs.get(info, None) | 2187 getfield = _funcs.get(info, None) |
1975 if getfield is None: | 2188 if getfield is None: |
1976 raise error.ParseError( | 2189 raise error.ParseError( |
1977 # i18n: "matching" is a keyword | 2190 # i18n: "matching" is a keyword |
1978 _("unexpected field name passed to matching: %s") % info) | 2191 _("unexpected field name passed to matching: %s") |
2192 % info | |
2193 ) | |
1979 getfieldfuncs.append(getfield) | 2194 getfieldfuncs.append(getfield) |
1980 # convert the getfield array of functions into a "getinfo" function | 2195 # convert the getfield array of functions into a "getinfo" function |
1981 # which returns an array of field values (or a single value if there | 2196 # which returns an array of field values (or a single value if there |
1982 # is only one field to match) | 2197 # is only one field to match) |
1983 getinfo = lambda r: [f(r) for f in getfieldfuncs] | 2198 getinfo = lambda r: [f(r) for f in getfieldfuncs] |
1993 return True | 2208 return True |
1994 return False | 2209 return False |
1995 | 2210 |
1996 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs)) | 2211 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs)) |
1997 | 2212 |
2213 | |
1998 @predicate('reverse(set)', safe=True, takeorder=True, weight=0) | 2214 @predicate('reverse(set)', safe=True, takeorder=True, weight=0) |
1999 def reverse(repo, subset, x, order): | 2215 def reverse(repo, subset, x, order): |
2000 """Reverse order of set. | 2216 """Reverse order of set. |
2001 """ | 2217 """ |
2002 l = getset(repo, subset, x, order) | 2218 l = getset(repo, subset, x, order) |
2003 if order == defineorder: | 2219 if order == defineorder: |
2004 l.reverse() | 2220 l.reverse() |
2005 return l | 2221 return l |
2006 | 2222 |
2223 | |
2007 @predicate('roots(set)', safe=True) | 2224 @predicate('roots(set)', safe=True) |
2008 def roots(repo, subset, x): | 2225 def roots(repo, subset, x): |
2009 """Changesets in set with no parent changeset in set. | 2226 """Changesets in set with no parent changeset in set. |
2010 """ | 2227 """ |
2011 s = getset(repo, fullreposet(repo), x) | 2228 s = getset(repo, fullreposet(repo), x) |
2012 parents = repo.changelog.parentrevs | 2229 parents = repo.changelog.parentrevs |
2230 | |
2013 def filter(r): | 2231 def filter(r): |
2014 for p in parents(r): | 2232 for p in parents(r): |
2015 if 0 <= p and p in s: | 2233 if 0 <= p and p in s: |
2016 return False | 2234 return False |
2017 return True | 2235 return True |
2236 | |
2018 return subset & s.filter(filter, condrepr='<roots>') | 2237 return subset & s.filter(filter, condrepr='<roots>') |
2238 | |
2019 | 2239 |
2020 _sortkeyfuncs = { | 2240 _sortkeyfuncs = { |
2021 'rev': lambda c: c.rev(), | 2241 'rev': lambda c: c.rev(), |
2022 'branch': lambda c: c.branch(), | 2242 'branch': lambda c: c.branch(), |
2023 'desc': lambda c: c.description(), | 2243 'desc': lambda c: c.description(), |
2024 'user': lambda c: c.user(), | 2244 'user': lambda c: c.user(), |
2025 'author': lambda c: c.user(), | 2245 'author': lambda c: c.user(), |
2026 'date': lambda c: c.date()[0], | 2246 'date': lambda c: c.date()[0], |
2027 } | 2247 } |
2248 | |
2028 | 2249 |
2029 def _getsortargs(x): | 2250 def _getsortargs(x): |
2030 """Parse sort options into (set, [(key, reverse)], opts)""" | 2251 """Parse sort options into (set, [(key, reverse)], opts)""" |
2031 args = getargsdict(x, 'sort', 'set keys topo.firstbranch') | 2252 args = getargsdict(x, 'sort', 'set keys topo.firstbranch') |
2032 if 'set' not in args: | 2253 if 'set' not in args: |
2038 keys = getstring(args['keys'], _("sort spec must be a string")) | 2259 keys = getstring(args['keys'], _("sort spec must be a string")) |
2039 | 2260 |
2040 keyflags = [] | 2261 keyflags = [] |
2041 for k in keys.split(): | 2262 for k in keys.split(): |
2042 fk = k | 2263 fk = k |
2043 reverse = (k.startswith('-')) | 2264 reverse = k.startswith('-') |
2044 if reverse: | 2265 if reverse: |
2045 k = k[1:] | 2266 k = k[1:] |
2046 if k not in _sortkeyfuncs and k != 'topo': | 2267 if k not in _sortkeyfuncs and k != 'topo': |
2047 raise error.ParseError( | 2268 raise error.ParseError( |
2048 _("unknown sort key %r") % pycompat.bytestr(fk)) | 2269 _("unknown sort key %r") % pycompat.bytestr(fk) |
2270 ) | |
2049 keyflags.append((k, reverse)) | 2271 keyflags.append((k, reverse)) |
2050 | 2272 |
2051 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags): | 2273 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags): |
2052 # i18n: "topo" is a keyword | 2274 # i18n: "topo" is a keyword |
2053 raise error.ParseError(_('topo sort order cannot be combined ' | 2275 raise error.ParseError( |
2054 'with other sort keys')) | 2276 _('topo sort order cannot be combined ' 'with other sort keys') |
2277 ) | |
2055 | 2278 |
2056 opts = {} | 2279 opts = {} |
2057 if 'topo.firstbranch' in args: | 2280 if 'topo.firstbranch' in args: |
2058 if any(k == 'topo' for k, reverse in keyflags): | 2281 if any(k == 'topo' for k, reverse in keyflags): |
2059 opts['topo.firstbranch'] = args['topo.firstbranch'] | 2282 opts['topo.firstbranch'] = args['topo.firstbranch'] |
2060 else: | 2283 else: |
2061 # i18n: "topo" and "topo.firstbranch" are keywords | 2284 # i18n: "topo" and "topo.firstbranch" are keywords |
2062 raise error.ParseError(_('topo.firstbranch can only be used ' | 2285 raise error.ParseError( |
2063 'when using the topo sort key')) | 2286 _( |
2287 'topo.firstbranch can only be used ' | |
2288 'when using the topo sort key' | |
2289 ) | |
2290 ) | |
2064 | 2291 |
2065 return args['set'], keyflags, opts | 2292 return args['set'], keyflags, opts |
2066 | 2293 |
2067 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True, | 2294 |
2068 weight=10) | 2295 @predicate( |
2296 'sort(set[, [-]key... [, ...]])', safe=True, takeorder=True, weight=10 | |
2297 ) | |
2069 def sort(repo, subset, x, order): | 2298 def sort(repo, subset, x, order): |
2070 """Sort set by keys. The default sort order is ascending, specify a key | 2299 """Sort set by keys. The default sort order is ascending, specify a key |
2071 as ``-key`` to sort in descending order. | 2300 as ``-key`` to sort in descending order. |
2072 | 2301 |
2073 The keys can be: | 2302 The keys can be: |
2094 return revs | 2323 return revs |
2095 elif keyflags[0][0] == "topo": | 2324 elif keyflags[0][0] == "topo": |
2096 firstbranch = () | 2325 firstbranch = () |
2097 if 'topo.firstbranch' in opts: | 2326 if 'topo.firstbranch' in opts: |
2098 firstbranch = getset(repo, subset, opts['topo.firstbranch']) | 2327 firstbranch = getset(repo, subset, opts['topo.firstbranch']) |
2099 revs = baseset(dagop.toposort(revs, repo.changelog.parentrevs, | 2328 revs = baseset( |
2100 firstbranch), | 2329 dagop.toposort(revs, repo.changelog.parentrevs, firstbranch), |
2101 istopo=True) | 2330 istopo=True, |
2331 ) | |
2102 if keyflags[0][1]: | 2332 if keyflags[0][1]: |
2103 revs.reverse() | 2333 revs.reverse() |
2104 return revs | 2334 return revs |
2105 | 2335 |
2106 # sort() is guaranteed to be stable | 2336 # sort() is guaranteed to be stable |
2107 ctxs = [repo[r] for r in revs] | 2337 ctxs = [repo[r] for r in revs] |
2108 for k, reverse in reversed(keyflags): | 2338 for k, reverse in reversed(keyflags): |
2109 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse) | 2339 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse) |
2110 return baseset([c.rev() for c in ctxs]) | 2340 return baseset([c.rev() for c in ctxs]) |
2111 | 2341 |
2342 | |
2112 @predicate('subrepo([pattern])') | 2343 @predicate('subrepo([pattern])') |
2113 def subrepo(repo, subset, x): | 2344 def subrepo(repo, subset, x): |
2114 """Changesets that add, modify or remove the given subrepo. If no subrepo | 2345 """Changesets that add, modify or remove the given subrepo. If no subrepo |
2115 pattern is named, any subrepo changes are returned. | 2346 pattern is named, any subrepo changes are returned. |
2116 """ | 2347 """ |
2150 return any(submatches(c.p1().substate.keys())) | 2381 return any(submatches(c.p1().substate.keys())) |
2151 | 2382 |
2152 return False | 2383 return False |
2153 | 2384 |
2154 return subset.filter(matches, condrepr=('<subrepo %r>', pat)) | 2385 return subset.filter(matches, condrepr=('<subrepo %r>', pat)) |
2386 | |
2155 | 2387 |
2156 def _mapbynodefunc(repo, s, f): | 2388 def _mapbynodefunc(repo, s, f): |
2157 """(repo, smartset, [node] -> [node]) -> smartset | 2389 """(repo, smartset, [node] -> [node]) -> smartset |
2158 | 2390 |
2159 Helper method to map a smartset to another smartset given a function only | 2391 Helper method to map a smartset to another smartset given a function only |
2164 torev = cl.rev | 2396 torev = cl.rev |
2165 tonode = cl.node | 2397 tonode = cl.node |
2166 nodemap = cl.nodemap | 2398 nodemap = cl.nodemap |
2167 result = set(torev(n) for n in f(tonode(r) for r in s) if n in nodemap) | 2399 result = set(torev(n) for n in f(tonode(r) for r in s) if n in nodemap) |
2168 return smartset.baseset(result - repo.changelog.filteredrevs) | 2400 return smartset.baseset(result - repo.changelog.filteredrevs) |
2401 | |
2169 | 2402 |
2170 @predicate('successors(set)', safe=True) | 2403 @predicate('successors(set)', safe=True) |
2171 def successors(repo, subset, x): | 2404 def successors(repo, subset, x): |
2172 """All successors for set, including the given set themselves""" | 2405 """All successors for set, including the given set themselves""" |
2173 s = getset(repo, fullreposet(repo), x) | 2406 s = getset(repo, fullreposet(repo), x) |
2174 f = lambda nodes: obsutil.allsuccessors(repo.obsstore, nodes) | 2407 f = lambda nodes: obsutil.allsuccessors(repo.obsstore, nodes) |
2175 d = _mapbynodefunc(repo, s, f) | 2408 d = _mapbynodefunc(repo, s, f) |
2176 return subset & d | 2409 return subset & d |
2177 | 2410 |
2411 | |
2178 def _substringmatcher(pattern, casesensitive=True): | 2412 def _substringmatcher(pattern, casesensitive=True): |
2179 kind, pattern, matcher = stringutil.stringmatcher( | 2413 kind, pattern, matcher = stringutil.stringmatcher( |
2180 pattern, casesensitive=casesensitive) | 2414 pattern, casesensitive=casesensitive |
2415 ) | |
2181 if kind == 'literal': | 2416 if kind == 'literal': |
2182 if not casesensitive: | 2417 if not casesensitive: |
2183 pattern = encoding.lower(pattern) | 2418 pattern = encoding.lower(pattern) |
2184 matcher = lambda s: pattern in encoding.lower(s) | 2419 matcher = lambda s: pattern in encoding.lower(s) |
2185 else: | 2420 else: |
2186 matcher = lambda s: pattern in s | 2421 matcher = lambda s: pattern in s |
2187 return kind, pattern, matcher | 2422 return kind, pattern, matcher |
2188 | 2423 |
2424 | |
2189 @predicate('tag([name])', safe=True) | 2425 @predicate('tag([name])', safe=True) |
2190 def tag(repo, subset, x): | 2426 def tag(repo, subset, x): |
2191 """The specified tag by name, or all tagged revisions if no name is given. | 2427 """The specified tag by name, or all tagged revisions if no name is given. |
2192 | 2428 |
2193 Pattern matching is supported for `name`. See | 2429 Pattern matching is supported for `name`. See |
2195 """ | 2431 """ |
2196 # i18n: "tag" is a keyword | 2432 # i18n: "tag" is a keyword |
2197 args = getargs(x, 0, 1, _("tag takes one or no arguments")) | 2433 args = getargs(x, 0, 1, _("tag takes one or no arguments")) |
2198 cl = repo.changelog | 2434 cl = repo.changelog |
2199 if args: | 2435 if args: |
2200 pattern = getstring(args[0], | 2436 pattern = getstring( |
2201 # i18n: "tag" is a keyword | 2437 args[0], |
2202 _('the argument to tag must be a string')) | 2438 # i18n: "tag" is a keyword |
2439 _('the argument to tag must be a string'), | |
2440 ) | |
2203 kind, pattern, matcher = stringutil.stringmatcher(pattern) | 2441 kind, pattern, matcher = stringutil.stringmatcher(pattern) |
2204 if kind == 'literal': | 2442 if kind == 'literal': |
2205 # avoid resolving all tags | 2443 # avoid resolving all tags |
2206 tn = repo._tagscache.tags.get(pattern, None) | 2444 tn = repo._tagscache.tags.get(pattern, None) |
2207 if tn is None: | 2445 if tn is None: |
2208 raise error.RepoLookupError(_("tag '%s' does not exist") | 2446 raise error.RepoLookupError( |
2209 % pattern) | 2447 _("tag '%s' does not exist") % pattern |
2448 ) | |
2210 s = {repo[tn].rev()} | 2449 s = {repo[tn].rev()} |
2211 else: | 2450 else: |
2212 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)} | 2451 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)} |
2213 else: | 2452 else: |
2214 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'} | 2453 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'} |
2215 return subset & s | 2454 return subset & s |
2216 | 2455 |
2456 | |
2217 @predicate('tagged', safe=True) | 2457 @predicate('tagged', safe=True) |
2218 def tagged(repo, subset, x): | 2458 def tagged(repo, subset, x): |
2219 return tag(repo, subset, x) | 2459 return tag(repo, subset, x) |
2460 | |
2220 | 2461 |
2221 @predicate('orphan()', safe=True) | 2462 @predicate('orphan()', safe=True) |
2222 def orphan(repo, subset, x): | 2463 def orphan(repo, subset, x): |
2223 """Non-obsolete changesets with obsolete ancestors. (EXPERIMENTAL) | 2464 """Non-obsolete changesets with obsolete ancestors. (EXPERIMENTAL) |
2224 """ | 2465 """ |
2234 | 2475 |
2235 Pattern matching is supported for `string`. See | 2476 Pattern matching is supported for `string`. See |
2236 :hg:`help revisions.patterns`. | 2477 :hg:`help revisions.patterns`. |
2237 """ | 2478 """ |
2238 return author(repo, subset, x) | 2479 return author(repo, subset, x) |
2480 | |
2239 | 2481 |
2240 @predicate('wdir()', safe=True, weight=0) | 2482 @predicate('wdir()', safe=True, weight=0) |
2241 def wdir(repo, subset, x): | 2483 def wdir(repo, subset, x): |
2242 """Working directory. (EXPERIMENTAL)""" | 2484 """Working directory. (EXPERIMENTAL)""" |
2243 # i18n: "wdir" is a keyword | 2485 # i18n: "wdir" is a keyword |
2244 getargs(x, 0, 0, _("wdir takes no arguments")) | 2486 getargs(x, 0, 0, _("wdir takes no arguments")) |
2245 if node.wdirrev in subset or isinstance(subset, fullreposet): | 2487 if node.wdirrev in subset or isinstance(subset, fullreposet): |
2246 return baseset([node.wdirrev]) | 2488 return baseset([node.wdirrev]) |
2247 return baseset() | 2489 return baseset() |
2490 | |
2248 | 2491 |
2249 def _orderedlist(repo, subset, x): | 2492 def _orderedlist(repo, subset, x): |
2250 s = getstring(x, "internal error") | 2493 s = getstring(x, "internal error") |
2251 if not s: | 2494 if not s: |
2252 return baseset() | 2495 return baseset() |
2266 revs = stringset(repo, subset, t, defineorder) | 2509 revs = stringset(repo, subset, t, defineorder) |
2267 | 2510 |
2268 for r in revs: | 2511 for r in revs: |
2269 if r in seen: | 2512 if r in seen: |
2270 continue | 2513 continue |
2271 if (r in subset | 2514 if ( |
2272 or r in _virtualrevs and isinstance(subset, fullreposet)): | 2515 r in subset |
2516 or r in _virtualrevs | |
2517 and isinstance(subset, fullreposet) | |
2518 ): | |
2273 ls.append(r) | 2519 ls.append(r) |
2274 seen.add(r) | 2520 seen.add(r) |
2275 return baseset(ls) | 2521 return baseset(ls) |
2522 | |
2276 | 2523 |
2277 # for internal use | 2524 # for internal use |
2278 @predicate('_list', safe=True, takeorder=True) | 2525 @predicate('_list', safe=True, takeorder=True) |
2279 def _list(repo, subset, x, order): | 2526 def _list(repo, subset, x, order): |
2280 if order == followorder: | 2527 if order == followorder: |
2281 # slow path to take the subset order | 2528 # slow path to take the subset order |
2282 return subset & _orderedlist(repo, fullreposet(repo), x) | 2529 return subset & _orderedlist(repo, fullreposet(repo), x) |
2283 else: | 2530 else: |
2284 return _orderedlist(repo, subset, x) | 2531 return _orderedlist(repo, subset, x) |
2285 | 2532 |
2533 | |
2286 def _orderedintlist(repo, subset, x): | 2534 def _orderedintlist(repo, subset, x): |
2287 s = getstring(x, "internal error") | 2535 s = getstring(x, "internal error") |
2288 if not s: | 2536 if not s: |
2289 return baseset() | 2537 return baseset() |
2290 ls = [int(r) for r in s.split('\0')] | 2538 ls = [int(r) for r in s.split('\0')] |
2291 s = subset | 2539 s = subset |
2292 return baseset([r for r in ls if r in s]) | 2540 return baseset([r for r in ls if r in s]) |
2541 | |
2293 | 2542 |
2294 # for internal use | 2543 # for internal use |
2295 @predicate('_intlist', safe=True, takeorder=True, weight=0) | 2544 @predicate('_intlist', safe=True, takeorder=True, weight=0) |
2296 def _intlist(repo, subset, x, order): | 2545 def _intlist(repo, subset, x, order): |
2297 if order == followorder: | 2546 if order == followorder: |
2298 # slow path to take the subset order | 2547 # slow path to take the subset order |
2299 return subset & _orderedintlist(repo, fullreposet(repo), x) | 2548 return subset & _orderedintlist(repo, fullreposet(repo), x) |
2300 else: | 2549 else: |
2301 return _orderedintlist(repo, subset, x) | 2550 return _orderedintlist(repo, subset, x) |
2302 | 2551 |
2552 | |
2303 def _orderedhexlist(repo, subset, x): | 2553 def _orderedhexlist(repo, subset, x): |
2304 s = getstring(x, "internal error") | 2554 s = getstring(x, "internal error") |
2305 if not s: | 2555 if not s: |
2306 return baseset() | 2556 return baseset() |
2307 cl = repo.changelog | 2557 cl = repo.changelog |
2308 ls = [cl.rev(node.bin(r)) for r in s.split('\0')] | 2558 ls = [cl.rev(node.bin(r)) for r in s.split('\0')] |
2309 s = subset | 2559 s = subset |
2310 return baseset([r for r in ls if r in s]) | 2560 return baseset([r for r in ls if r in s]) |
2311 | 2561 |
2562 | |
2312 # for internal use | 2563 # for internal use |
2313 @predicate('_hexlist', safe=True, takeorder=True) | 2564 @predicate('_hexlist', safe=True, takeorder=True) |
2314 def _hexlist(repo, subset, x, order): | 2565 def _hexlist(repo, subset, x, order): |
2315 if order == followorder: | 2566 if order == followorder: |
2316 # slow path to take the subset order | 2567 # slow path to take the subset order |
2317 return subset & _orderedhexlist(repo, fullreposet(repo), x) | 2568 return subset & _orderedhexlist(repo, fullreposet(repo), x) |
2318 else: | 2569 else: |
2319 return _orderedhexlist(repo, subset, x) | 2570 return _orderedhexlist(repo, subset, x) |
2571 | |
2320 | 2572 |
2321 methods = { | 2573 methods = { |
2322 "range": rangeset, | 2574 "range": rangeset, |
2323 "rangeall": rangeall, | 2575 "rangeall": rangeall, |
2324 "rangepre": rangepre, | 2576 "rangepre": rangepre, |
2346 subscriptrelations = { | 2598 subscriptrelations = { |
2347 "g": generationsrel, | 2599 "g": generationsrel, |
2348 "generations": generationsrel, | 2600 "generations": generationsrel, |
2349 } | 2601 } |
2350 | 2602 |
2603 | |
2351 def lookupfn(repo): | 2604 def lookupfn(repo): |
2352 return lambda symbol: scmutil.isrevsymbol(repo, symbol) | 2605 return lambda symbol: scmutil.isrevsymbol(repo, symbol) |
2606 | |
2353 | 2607 |
2354 def match(ui, spec, lookup=None): | 2608 def match(ui, spec, lookup=None): |
2355 """Create a matcher for a single revision spec""" | 2609 """Create a matcher for a single revision spec""" |
2356 return matchany(ui, [spec], lookup=lookup) | 2610 return matchany(ui, [spec], lookup=lookup) |
2357 | 2611 |
2612 | |
2358 def matchany(ui, specs, lookup=None, localalias=None): | 2613 def matchany(ui, specs, lookup=None, localalias=None): |
2359 """Create a matcher that will include any revisions matching one of the | 2614 """Create a matcher that will include any revisions matching one of the |
2360 given specs | 2615 given specs |
2361 | 2616 |
2362 If lookup function is not None, the parser will first attempt to handle | 2617 If lookup function is not None, the parser will first attempt to handle |
2364 | 2619 |
2365 If localalias is not None, it is a dict {name: definitionstring}. It takes | 2620 If localalias is not None, it is a dict {name: definitionstring}. It takes |
2366 precedence over [revsetalias] config section. | 2621 precedence over [revsetalias] config section. |
2367 """ | 2622 """ |
2368 if not specs: | 2623 if not specs: |
2624 | |
2369 def mfunc(repo, subset=None): | 2625 def mfunc(repo, subset=None): |
2370 return baseset() | 2626 return baseset() |
2627 | |
2371 return mfunc | 2628 return mfunc |
2372 if not all(specs): | 2629 if not all(specs): |
2373 raise error.ParseError(_("empty query")) | 2630 raise error.ParseError(_("empty query")) |
2374 if len(specs) == 1: | 2631 if len(specs) == 1: |
2375 tree = revsetlang.parse(specs[0], lookup) | 2632 tree = revsetlang.parse(specs[0], lookup) |
2376 else: | 2633 else: |
2377 tree = ('or', | 2634 tree = ( |
2378 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs)) | 2635 'or', |
2636 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs), | |
2637 ) | |
2379 | 2638 |
2380 aliases = [] | 2639 aliases = [] |
2381 warn = None | 2640 warn = None |
2382 if ui: | 2641 if ui: |
2383 aliases.extend(ui.configitems('revsetalias')) | 2642 aliases.extend(ui.configitems('revsetalias')) |
2389 tree = revsetlang.foldconcat(tree) | 2648 tree = revsetlang.foldconcat(tree) |
2390 tree = revsetlang.analyze(tree) | 2649 tree = revsetlang.analyze(tree) |
2391 tree = revsetlang.optimize(tree) | 2650 tree = revsetlang.optimize(tree) |
2392 return makematcher(tree) | 2651 return makematcher(tree) |
2393 | 2652 |
2653 | |
2394 def makematcher(tree): | 2654 def makematcher(tree): |
2395 """Create a matcher from an evaluatable tree""" | 2655 """Create a matcher from an evaluatable tree""" |
2656 | |
2396 def mfunc(repo, subset=None, order=None): | 2657 def mfunc(repo, subset=None, order=None): |
2397 if order is None: | 2658 if order is None: |
2398 if subset is None: | 2659 if subset is None: |
2399 order = defineorder # 'x' | 2660 order = defineorder # 'x' |
2400 else: | 2661 else: |
2401 order = followorder # 'subset & x' | 2662 order = followorder # 'subset & x' |
2402 if subset is None: | 2663 if subset is None: |
2403 subset = fullreposet(repo) | 2664 subset = fullreposet(repo) |
2404 return getset(repo, subset, tree, order) | 2665 return getset(repo, subset, tree, order) |
2666 | |
2405 return mfunc | 2667 return mfunc |
2668 | |
2406 | 2669 |
2407 def loadpredicate(ui, extname, registrarobj): | 2670 def loadpredicate(ui, extname, registrarobj): |
2408 """Load revset predicates from specified registrarobj | 2671 """Load revset predicates from specified registrarobj |
2409 """ | 2672 """ |
2410 for name, func in registrarobj._table.iteritems(): | 2673 for name, func in registrarobj._table.iteritems(): |
2411 symbols[name] = func | 2674 symbols[name] = func |
2412 if func._safe: | 2675 if func._safe: |
2413 safesymbols.add(name) | 2676 safesymbols.add(name) |
2414 | 2677 |
2678 | |
2415 # load built-in predicates explicitly to setup safesymbols | 2679 # load built-in predicates explicitly to setup safesymbols |
2416 loadpredicate(None, None, predicate) | 2680 loadpredicate(None, None, predicate) |
2417 | 2681 |
2418 # tell hggettext to extract docstrings from these functions: | 2682 # tell hggettext to extract docstrings from these functions: |
2419 i18nfunctions = symbols.values() | 2683 i18nfunctions = symbols.values() |