Mercurial > public > mercurial-scm > hg
comparison mercurial/revset.py @ 49447:6dbe74669eba
sort-revset: introduce a `random` variant
This new `sort` variant allows to shuffle any revset. It also allow for
randomly picking element using `first`.
author | Pierre-Yves David <pierre-yves.david@octobus.net> |
---|---|
date | Mon, 25 Jul 2022 05:30:06 +0200 |
parents | 127d33e63d1a |
children | e02dcc625171 |
comparison
equal
deleted
inserted
replaced
49445:cd21f2b4226f | 49447:6dbe74669eba |
---|---|
5 # This software may be used and distributed according to the terms of the | 5 # This software may be used and distributed according to the terms of the |
6 # GNU General Public License version 2 or any later version. | 6 # GNU General Public License version 2 or any later version. |
7 | 7 |
8 | 8 |
9 import binascii | 9 import binascii |
10 import functools | |
11 import random | |
10 import re | 12 import re |
13 import sys | |
11 | 14 |
12 from .i18n import _ | 15 from .i18n import _ |
13 from .pycompat import getattr | 16 from .pycompat import getattr |
14 from .node import ( | 17 from .node import ( |
15 bin, | 18 bin, |
2345 return True | 2348 return True |
2346 | 2349 |
2347 return subset & s.filter(filter, condrepr=b'<roots>') | 2350 return subset & s.filter(filter, condrepr=b'<roots>') |
2348 | 2351 |
2349 | 2352 |
2353 MAXINT = sys.maxsize | |
2354 MININT = -MAXINT - 1 | |
2355 | |
2356 | |
2357 def pick_random(c, gen=random): | |
2358 # exists as its own function to make it possible to overwrite the seed | |
2359 return gen.randint(MININT, MAXINT) | |
2360 | |
2361 | |
2350 _sortkeyfuncs = { | 2362 _sortkeyfuncs = { |
2351 b'rev': scmutil.intrev, | 2363 b'rev': scmutil.intrev, |
2352 b'branch': lambda c: c.branch(), | 2364 b'branch': lambda c: c.branch(), |
2353 b'desc': lambda c: c.description(), | 2365 b'desc': lambda c: c.description(), |
2354 b'user': lambda c: c.user(), | 2366 b'user': lambda c: c.user(), |
2355 b'author': lambda c: c.user(), | 2367 b'author': lambda c: c.user(), |
2356 b'date': lambda c: c.date()[0], | 2368 b'date': lambda c: c.date()[0], |
2357 b'node': scmutil.binnode, | 2369 b'node': scmutil.binnode, |
2370 b'random': pick_random, | |
2358 } | 2371 } |
2359 | 2372 |
2360 | 2373 |
2361 def _getsortargs(x): | 2374 def _getsortargs(x): |
2362 """Parse sort options into (set, [(key, reverse)], opts)""" | 2375 """Parse sort options into (set, [(key, reverse)], opts)""" |
2363 args = getargsdict(x, b'sort', b'set keys topo.firstbranch') | 2376 args = getargsdict( |
2377 x, | |
2378 b'sort', | |
2379 b'set keys topo.firstbranch random.seed', | |
2380 ) | |
2364 if b'set' not in args: | 2381 if b'set' not in args: |
2365 # i18n: "sort" is a keyword | 2382 # i18n: "sort" is a keyword |
2366 raise error.ParseError(_(b'sort requires one or two arguments')) | 2383 raise error.ParseError(_(b'sort requires one or two arguments')) |
2367 keys = b"rev" | 2384 keys = b"rev" |
2368 if b'keys' in args: | 2385 if b'keys' in args: |
2398 b'topo.firstbranch can only be used ' | 2415 b'topo.firstbranch can only be used ' |
2399 b'when using the topo sort key' | 2416 b'when using the topo sort key' |
2400 ) | 2417 ) |
2401 ) | 2418 ) |
2402 | 2419 |
2420 if b'random.seed' in args: | |
2421 if any(k == b'random' for k, reverse in keyflags): | |
2422 s = args[b'random.seed'] | |
2423 seed = getstring(s, _(b"random.seed must be a string")) | |
2424 opts[b'random.seed'] = seed | |
2425 else: | |
2426 # i18n: "random" and "random.seed" are keywords | |
2427 raise error.ParseError( | |
2428 _( | |
2429 b'random.seed can only be used ' | |
2430 b'when using the random sort key' | |
2431 ) | |
2432 ) | |
2433 | |
2403 return args[b'set'], keyflags, opts | 2434 return args[b'set'], keyflags, opts |
2404 | 2435 |
2405 | 2436 |
2406 @predicate( | 2437 @predicate( |
2407 b'sort(set[, [-]key... [, ...]])', safe=True, takeorder=True, weight=10 | 2438 b'sort(set[, [-]key... [, ...]])', safe=True, takeorder=True, weight=10 |
2417 - ``desc`` for the commit message (description), | 2448 - ``desc`` for the commit message (description), |
2418 - ``user`` for user name (``author`` can be used as an alias), | 2449 - ``user`` for user name (``author`` can be used as an alias), |
2419 - ``date`` for the commit date | 2450 - ``date`` for the commit date |
2420 - ``topo`` for a reverse topographical sort | 2451 - ``topo`` for a reverse topographical sort |
2421 - ``node`` the nodeid of the revision | 2452 - ``node`` the nodeid of the revision |
2453 - ``random`` randomly shuffle revisions | |
2422 | 2454 |
2423 The ``topo`` sort order cannot be combined with other sort keys. This sort | 2455 The ``topo`` sort order cannot be combined with other sort keys. This sort |
2424 takes one optional argument, ``topo.firstbranch``, which takes a revset that | 2456 takes one optional argument, ``topo.firstbranch``, which takes a revset that |
2425 specifies what topographical branches to prioritize in the sort. | 2457 specifies what topographical branches to prioritize in the sort. |
2426 | 2458 |
2459 The ``random`` sort takes one optional ``random.seed`` argument to control | |
2460 the pseudo-randomness of the result. | |
2427 """ | 2461 """ |
2428 s, keyflags, opts = _getsortargs(x) | 2462 s, keyflags, opts = _getsortargs(x) |
2429 revs = getset(repo, subset, s, order) | 2463 revs = getset(repo, subset, s, order) |
2430 | 2464 |
2431 if not keyflags or order != defineorder: | 2465 if not keyflags or order != defineorder: |
2446 return revs | 2480 return revs |
2447 | 2481 |
2448 # sort() is guaranteed to be stable | 2482 # sort() is guaranteed to be stable |
2449 ctxs = [repo[r] for r in revs] | 2483 ctxs = [repo[r] for r in revs] |
2450 for k, reverse in reversed(keyflags): | 2484 for k, reverse in reversed(keyflags): |
2451 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse) | 2485 func = _sortkeyfuncs[k] |
2486 if k == b'random' and b'random.seed' in opts: | |
2487 seed = opts[b'random.seed'] | |
2488 r = random.Random(seed) | |
2489 func = functools.partial(func, gen=r) | |
2490 ctxs.sort(key=func, reverse=reverse) | |
2452 return baseset([c.rev() for c in ctxs]) | 2491 return baseset([c.rev() for c in ctxs]) |
2453 | 2492 |
2454 | 2493 |
2455 @predicate(b'subrepo([pattern])') | 2494 @predicate(b'subrepo([pattern])') |
2456 def subrepo(repo, subset, x): | 2495 def subrepo(repo, subset, x): |