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