Mercurial > hg
comparison contrib/perf-utils/subsetmaker.py @ 46766:cb70dabe5718
perf-helper: add a small extension with revsets to select repository subset
Playing with discovery requires building interesting case. To do this we
need revsets to try to generate them. We start with a quite simple one. See
documentation for details.
Differential Revision: https://phab.mercurial-scm.org/D10221
author | Pierre-Yves David <pierre-yves.david@octobus.net> |
---|---|
date | Mon, 15 Mar 2021 16:35:54 +0100 |
parents | |
children | 36b4640ccb6a |
comparison
equal
deleted
inserted
replaced
46765:62c2857a174b | 46766:cb70dabe5718 |
---|---|
1 """revset to select sample of repository | |
2 | |
3 Hopefully this is useful to create interesting discovery cases. | |
4 """ | |
5 | |
6 import collections | |
7 import random | |
8 | |
9 from mercurial.i18n import _ | |
10 | |
11 from mercurial import ( | |
12 registrar, | |
13 revset, | |
14 revsetlang, | |
15 smartset, | |
16 ) | |
17 | |
18 revsetpredicate = registrar.revsetpredicate() | |
19 | |
20 | |
21 @revsetpredicate(b'scratch(REVS, <count>, [seed])') | |
22 def scratch(repo, subset, x): | |
23 """randomly remove <count> revision from the repository top | |
24 | |
25 This subset is created by recursively picking changeset starting from the | |
26 heads. It can be summarized using the following algorithm:: | |
27 | |
28 selected = set() | |
29 for i in range(<count>): | |
30 unselected = repo.revs("not <selected>") | |
31 candidates = repo.revs("heads(<unselected>)") | |
32 pick = random.choice(candidates) | |
33 selected.add(pick) | |
34 """ | |
35 m = _(b"scratch expects revisions, count argument and an optional seed") | |
36 args = revsetlang.getargs(x, 2, 3, m) | |
37 if len(args) == 2: | |
38 x, n = args | |
39 rand = random | |
40 elif len(args) == 3: | |
41 x, n, seed = args | |
42 seed = revsetlang.getinteger(seed, _(b"seed should be a number")) | |
43 rand = random.Random(seed) | |
44 else: | |
45 assert False | |
46 | |
47 n = revsetlang.getinteger(n, _(b"scratch expects a number")) | |
48 | |
49 selected = set() | |
50 heads = set() | |
51 children_count = collections.defaultdict(lambda: 0) | |
52 parents = repo.changelog._uncheckedparentrevs | |
53 | |
54 baseset = revset.getset(repo, smartset.fullreposet(repo), x) | |
55 baseset.sort() | |
56 for r in baseset: | |
57 heads.add(r) | |
58 | |
59 p1, p2 = parents(r) | |
60 if p1 >= 0: | |
61 heads.discard(p1) | |
62 children_count[p1] += 1 | |
63 if p2 >= 0: | |
64 heads.discard(p2) | |
65 children_count[p2] += 1 | |
66 | |
67 for h in heads: | |
68 assert children_count[h] == 0 | |
69 | |
70 selected = set() | |
71 for x in range(n): | |
72 if not heads: | |
73 break | |
74 pick = rand.choice(list(heads)) | |
75 heads.remove(pick) | |
76 assert pick not in selected | |
77 selected.add(pick) | |
78 p1, p2 = parents(pick) | |
79 if p1 in children_count: | |
80 assert p1 in children_count | |
81 children_count[p1] -= 1 | |
82 assert children_count[p1] >= 0 | |
83 if children_count[p1] == 0: | |
84 assert p1 not in selected, (r, p1) | |
85 heads.add(p1) | |
86 if p2 in children_count: | |
87 assert p2 in children_count | |
88 children_count[p2] -= 1 | |
89 assert children_count[p2] >= 0 | |
90 if children_count[p2] == 0: | |
91 assert p2 not in selected, (r, p2) | |
92 heads.add(p2) | |
93 | |
94 return smartset.baseset(selected) & subset |