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):