##// END OF EJS Templates
subsetmaker: use SortedSet for the scratch variant...
marmoute -
r49878:5a24bb7f default
parent child Browse files
Show More
@@ -1,172 +1,174 b''
1 1 """revset to select sample of repository
2 2
3 3 Hopefully this is useful to create interesting discovery cases.
4 4 """
5 5
6 6 import collections
7 7 import random
8 8
9 9 from mercurial.i18n import _
10 10
11 11 from mercurial import (
12 12 registrar,
13 13 revset,
14 14 revsetlang,
15 15 smartset,
16 16 )
17 17
18 import sortedcontainers
19
20 SortedSet = sortedcontainers.SortedSet
21
18 22 revsetpredicate = registrar.revsetpredicate()
19 23
20 24
21 25 @revsetpredicate(b'subsetspec("<spec>")')
22 26 def subsetmarkerspec(repo, subset, x):
23 27 """use a shorthand spec as used by search-discovery-case
24 28
25 29 Supported format are:
26 30
27 31 - "scratch-count-seed": not scratch(all(), count, "seed")
28 32 - "randomantichain-seed": ::randomantichain(all(), "seed")
29 33 - "rev-REV": "::REV"
30 34 """
31 35 args = revsetlang.getargs(
32 36 x, 0, 1, _(b'subsetspec("spec") required an argument')
33 37 )
34 38
35 39 spec = revsetlang.getstring(args[0], _(b"spec should be a string"))
36 40 case = spec.split(b'-')
37 41 t = case[0]
38 42 if t == b'scratch':
39 43 spec_revset = b'not scratch(all(), %s, "%s")' % (case[1], case[2])
40 44 elif t == b'randomantichain':
41 45 spec_revset = b'::randomantichain(all(), "%s")' % case[1]
42 46 elif t == b'rev':
43 47 spec_revset = b'::%d' % case[1]
44 48 else:
45 49 assert False, spec
46 50
47 51 selected = repo.revs(spec_revset)
48 52
49 53 return selected & subset
50 54
51 55
52 56 @revsetpredicate(b'scratch(REVS, <count>, [seed])')
53 57 def scratch(repo, subset, x):
54 58 """randomly remove <count> revision from the repository top
55 59
56 60 This subset is created by recursively picking changeset starting from the
57 61 heads. It can be summarized using the following algorithm::
58 62
59 63 selected = set()
60 64 for i in range(<count>):
61 65 unselected = repo.revs("not <selected>")
62 66 candidates = repo.revs("heads(<unselected>)")
63 67 pick = random.choice(candidates)
64 68 selected.add(pick)
65 69 """
66 70 m = _(b"scratch expects revisions, count argument and an optional seed")
67 71 args = revsetlang.getargs(x, 2, 3, m)
68 72 if len(args) == 2:
69 73 x, n = args
70 74 rand = random
71 75 elif len(args) == 3:
72 76 x, n, seed = args
73 77 seed = revsetlang.getinteger(seed, _(b"seed should be a number"))
74 78 rand = random.Random(seed)
75 79 else:
76 80 assert False
77 81
78 82 n = revsetlang.getinteger(n, _(b"scratch expects a number"))
79 83
80 84 selected = set()
81 heads = set()
85 heads = SortedSet()
82 86 children_count = collections.defaultdict(lambda: 0)
83 87 parents = repo.changelog._uncheckedparentrevs
84 88
85 89 baseset = revset.getset(repo, smartset.fullreposet(repo), x)
86 90 baseset.sort()
87 91 for r in baseset:
88 92 heads.add(r)
89 93
90 94 p1, p2 = parents(r)
91 95 if p1 >= 0:
92 96 heads.discard(p1)
93 97 children_count[p1] += 1
94 98 if p2 >= 0:
95 99 heads.discard(p2)
96 100 children_count[p2] += 1
97 101
98 102 for h in heads:
99 103 assert children_count[h] == 0
100 104
101 105 selected = set()
102 106 for x in range(n):
103 107 if not heads:
104 108 break
105 pickable = list(heads)
106 pickable.sort()
107 pick = rand.choice(pickable)
109 pick = rand.choice(heads)
108 110 heads.remove(pick)
109 111 assert pick not in selected
110 112 selected.add(pick)
111 113 p1, p2 = parents(pick)
112 114 if p1 in children_count:
113 115 assert p1 in children_count
114 116 children_count[p1] -= 1
115 117 assert children_count[p1] >= 0
116 118 if children_count[p1] == 0:
117 119 assert p1 not in selected, (r, p1)
118 120 heads.add(p1)
119 121 if p2 in children_count:
120 122 assert p2 in children_count
121 123 children_count[p2] -= 1
122 124 assert children_count[p2] >= 0
123 125 if children_count[p2] == 0:
124 126 assert p2 not in selected, (r, p2)
125 127 heads.add(p2)
126 128
127 129 return smartset.baseset(selected) & subset
128 130
129 131
130 132 @revsetpredicate(b'randomantichain(REVS, [seed])')
131 133 def antichain(repo, subset, x):
132 134 """Pick a random anti-chain in the repository
133 135
134 136 A antichain is a set of changeset where there isn't any element that is
135 137 either a descendant or ancestors of any other element in the set. In other
136 138 word, all the elements are independant. It can be summarized with the
137 139 following algorithm::
138 140
139 141 selected = set()
140 142 unselected = repo.revs('all()')
141 143 while unselected:
142 144 pick = random.choice(unselected)
143 145 selected.add(pick)
144 146 unselected -= repo.revs('::<pick> + <pick>::')
145 147 """
146 148
147 149 args = revsetlang.getargs(
148 150 x, 1, 2, _(b"randomantichain expects revisions and an optional seed")
149 151 )
150 152 if len(args) == 1:
151 153 (x,) = args
152 154 rand = random
153 155 elif len(args) == 2:
154 156 x, seed = args
155 157 seed = revsetlang.getinteger(seed, _(b"seed should be a number"))
156 158 rand = random.Random(seed)
157 159 else:
158 160 assert False
159 161
160 162 selected = set()
161 163
162 164 baseset = revset.getset(repo, smartset.fullreposet(repo), x)
163 165 undecided = baseset
164 166
165 167 while undecided:
166 168 pick = rand.choice(list(undecided))
167 169 selected.add(pick)
168 170 undecided = repo.revs(
169 171 '%ld and not (::%ld or %ld::head())', baseset, selected, selected
170 172 )
171 173
172 174 return smartset.baseset(selected) & subset
General Comments 0
You need to be logged in to leave comments. Login now