Show More
@@ -0,0 +1,337 b'' | |||||
|
1 | Test graph-related template functions | |||
|
2 | ===================================== | |||
|
3 | ||||
|
4 | $ cat <<'EOF' >> $HGRCPATH | |||
|
5 | > [extensions] | |||
|
6 | > drawdag = $RUNTESTDIR/drawdag.py | |||
|
7 | > EOF | |||
|
8 | ||||
|
9 | $ hg init a | |||
|
10 | $ cd a | |||
|
11 | ||||
|
12 | $ hg debugdrawdag <<'EOF' | |||
|
13 | > l | |||
|
14 | > / \ | |||
|
15 | > | k | |||
|
16 | > | |\ | |||
|
17 | > | | j | |||
|
18 | > | | | | |||
|
19 | > i | | | |||
|
20 | > |\ | | | |||
|
21 | > h | | | | |||
|
22 | > | | | | | |||
|
23 | > | g | | | |||
|
24 | > | | | | | |||
|
25 | > f | | | | |||
|
26 | > | |/ / | |||
|
27 | > | e | | |||
|
28 | > | |/ | |||
|
29 | > | d | |||
|
30 | > |/| | |||
|
31 | > c | | |||
|
32 | > | | | |||
|
33 | > b | | |||
|
34 | > | | |||
|
35 | > a | |||
|
36 | > EOF | |||
|
37 | ||||
|
38 | $ hg log -Gq -T'{rev} {tags}\n' | |||
|
39 | o 11 l tip | |||
|
40 | |\ | |||
|
41 | | o 10 i | |||
|
42 | | |\ | |||
|
43 | o \ \ 9 k | |||
|
44 | |\ \ \ | |||
|
45 | +-----o 8 g | |||
|
46 | | | | | |||
|
47 | | o | 7 j | |||
|
48 | | | | | |||
|
49 | | | o 6 h | |||
|
50 | | | | | |||
|
51 | o | | 5 e | |||
|
52 | |/ / | |||
|
53 | | o 4 f | |||
|
54 | | | | |||
|
55 | o | 3 d | |||
|
56 | |\| | |||
|
57 | | o 2 c | |||
|
58 | | | | |||
|
59 | | o 1 b | |||
|
60 | | | |||
|
61 | o 0 a | |||
|
62 | ||||
|
63 | ||||
|
64 | $ cd .. | |||
|
65 | ||||
|
66 | subsetparents | |||
|
67 | ------------- | |||
|
68 | ||||
|
69 | $ cd a | |||
|
70 | ||||
|
71 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("c+i"))}\n' -r 'c+i' | |||
|
72 | o 10 i: 2 | |||
|
73 | : | |||
|
74 | o 2 c: | |||
|
75 | | | |||
|
76 | ~ | |||
|
77 | ||||
|
78 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("c+h+i"))}\n' -r 'c+h+i' | |||
|
79 | o 10 i: 6 | |||
|
80 | |\ | |||
|
81 | o : 6 h: 2 | |||
|
82 | :/ | |||
|
83 | o 2 c: | |||
|
84 | | | |||
|
85 | ~ | |||
|
86 | ||||
|
87 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("c+h+l"))}\n' -r 'c+h+l' | |||
|
88 | o 11 l tip: 6 | |||
|
89 | :\ | |||
|
90 | : o 6 h: 2 | |||
|
91 | :/ | |||
|
92 | o 2 c: | |||
|
93 | | | |||
|
94 | ~ | |||
|
95 | ||||
|
96 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("c+f+l"))}\n' -r 'c+f+l' | |||
|
97 | o 11 l tip: 4 | |||
|
98 | :\ | |||
|
99 | : o 4 f: 2 | |||
|
100 | :/ | |||
|
101 | o 2 c: | |||
|
102 | | | |||
|
103 | ~ | |||
|
104 | ||||
|
105 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("c+h+i+k"))}\n' -r 'c+h+i+k' | |||
|
106 | o 10 i: 6 | |||
|
107 | |\ | |||
|
108 | | : o 9 k: 2 | |||
|
109 | | :/ | |||
|
110 | o : 6 h: 2 | |||
|
111 | :/ | |||
|
112 | o 2 c: | |||
|
113 | | | |||
|
114 | ~ | |||
|
115 | ||||
|
116 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("c+d+h+i+k"))}\n' -r 'c+d+h+i+k' | |||
|
117 | o 10 i: 6 3 | |||
|
118 | |\ | |||
|
119 | | : o 9 k: 3 | |||
|
120 | | :/ | |||
|
121 | o : 6 h: 2 | |||
|
122 | : : | |||
|
123 | : o 3 d: 2 | |||
|
124 | :/| | |||
|
125 | : ~ | |||
|
126 | o 2 c: | |||
|
127 | | | |||
|
128 | ~ | |||
|
129 | ||||
|
130 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("c+j+k+i"))}\n' -r 'c+j+k+i' | |||
|
131 | o 10 i: 2 | |||
|
132 | : | |||
|
133 | : o 9 k: 7 | |||
|
134 | :/| | |||
|
135 | : o 7 j: 2 | |||
|
136 | :/ | |||
|
137 | o 2 c: | |||
|
138 | | | |||
|
139 | ~ | |||
|
140 | ||||
|
141 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("c+e+f+j"))}\n' -r 'c+e+f+j' | |||
|
142 | o 7 j: 2 | |||
|
143 | : | |||
|
144 | : o 5 e: 2 | |||
|
145 | :/ | |||
|
146 | : o 4 f: 2 | |||
|
147 | :/ | |||
|
148 | o 2 c: | |||
|
149 | | | |||
|
150 | ~ | |||
|
151 | ||||
|
152 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("b+e+f+j"))}\n' -r 'b+e+f+j' | |||
|
153 | o 7 j: 1 | |||
|
154 | : | |||
|
155 | : o 5 e: 1 | |||
|
156 | :/ | |||
|
157 | : o 4 f: 1 | |||
|
158 | :/ | |||
|
159 | o 1 b: | |||
|
160 | ||||
|
161 | ||||
|
162 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("a+c+f+g+j+l"))}\n' -r 'a+c+f+g+j+l' | |||
|
163 | o 11 l tip: 4 8 7 | |||
|
164 | :\ | |||
|
165 | : \ | |||
|
166 | : :\ | |||
|
167 | : : \ | |||
|
168 | : : :\ | |||
|
169 | : : : \ | |||
|
170 | : : : :\ | |||
|
171 | : o---+ : 8 g: 0 2 | |||
|
172 | : :/ / / | |||
|
173 | : +---o 7 j: 0 2 | |||
|
174 | : : :/ | |||
|
175 | o---+ 4 f: 2 | |||
|
176 | / / | |||
|
177 | : o 2 c: | |||
|
178 | : | | |||
|
179 | : ~ | |||
|
180 | o 0 a: | |||
|
181 | ||||
|
182 | ||||
|
183 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("b+i+l"))}\n' -r 'b+i+l' | |||
|
184 | o 11 l tip: 10 | |||
|
185 | |\ | |||
|
186 | o : 10 i: 1 | |||
|
187 | :/ | |||
|
188 | o 1 b: | |||
|
189 | ||||
|
190 | ||||
|
191 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("b+i+j+l"))}\n' -r 'b+i+j+l' | |||
|
192 | o 11 l tip: 10 7 | |||
|
193 | |\ | |||
|
194 | | \ | |||
|
195 | | :\ | |||
|
196 | o : : 10 i: 1 | |||
|
197 | :/ / | |||
|
198 | : o 7 j: 1 | |||
|
199 | :/ | |||
|
200 | o 1 b: | |||
|
201 | ||||
|
202 | ||||
|
203 | null in subset: | |||
|
204 | ||||
|
205 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("null+a+c+f"))}\n' -r 'null+a+c+f' | |||
|
206 | o 4 f: 2 | |||
|
207 | | | |||
|
208 | o 2 c: -1 | |||
|
209 | : | |||
|
210 | : o 0 a: -1 | |||
|
211 | :/ | |||
|
212 | @ -1 : -1 | |||
|
213 | ||||
|
214 | ||||
|
215 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("null+a+b+c+f"))}\n' -r 'null+a+b+c+f' | |||
|
216 | o 4 f: 2 | |||
|
217 | | | |||
|
218 | o 2 c: 1 | |||
|
219 | | | |||
|
220 | o 1 b: -1 | |||
|
221 | | | |||
|
222 | | o 0 a: -1 | |||
|
223 | |/ | |||
|
224 | @ -1 : -1 | |||
|
225 | ||||
|
226 | ||||
|
227 | wdir in subset: | |||
|
228 | ||||
|
229 | $ hg update -qC i | |||
|
230 | ||||
|
231 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("f+k+wdir()"))}\n' -r 'f+k+wdir()' | |||
|
232 | o 2147483647 : 4 | |||
|
233 | : | |||
|
234 | : o 9 k: | |||
|
235 | : |\ | |||
|
236 | : ~ ~ | |||
|
237 | o 4 f: | |||
|
238 | | | |||
|
239 | ~ | |||
|
240 | ||||
|
241 | $ hg update -qC null | |||
|
242 | ||||
|
243 | Revisions not in subset: | |||
|
244 | ||||
|
245 | $ hg log -T '{rev} {tags}: {subsetparents(rev, revset("a+c+f+g+j+l"))}\n' | |||
|
246 | 11 l tip: 4 8 7 | |||
|
247 | 10 i: | |||
|
248 | 9 k: | |||
|
249 | 8 g: 0 2 | |||
|
250 | 7 j: 0 2 | |||
|
251 | 6 h: | |||
|
252 | 5 e: | |||
|
253 | 4 f: 2 | |||
|
254 | 3 d: | |||
|
255 | 2 c: | |||
|
256 | 1 b: | |||
|
257 | 0 a: | |||
|
258 | ||||
|
259 | $ hg log -T '{rev} {tags}: {subsetparents(rev, revset("b+c"))}\n' | |||
|
260 | 11 l tip: | |||
|
261 | 10 i: | |||
|
262 | 9 k: | |||
|
263 | 8 g: | |||
|
264 | 7 j: | |||
|
265 | 6 h: | |||
|
266 | 5 e: | |||
|
267 | 4 f: | |||
|
268 | 3 d: | |||
|
269 | 2 c: 1 | |||
|
270 | 1 b: | |||
|
271 | 0 a: | |||
|
272 | ||||
|
273 | $ hg log -T '{rev} {tags}: {subsetparents(rev, revset("b+c"))}\n' -r'reverse(null:2)' | |||
|
274 | 2 c: 1 | |||
|
275 | 1 b: | |||
|
276 | 0 a: | |||
|
277 | -1 : | |||
|
278 | ||||
|
279 | Nothing excluded: | |||
|
280 | ||||
|
281 | $ hg log -T '{rev} {tags}: {subsetparents(rev, revset("null:wdir()"))}\n' -r'reverse(null:wdir())' | |||
|
282 | 2147483647 : -1 | |||
|
283 | 11 l tip: 10 9 | |||
|
284 | 10 i: 6 8 | |||
|
285 | 9 k: 5 7 | |||
|
286 | 8 g: 5 | |||
|
287 | 7 j: 3 | |||
|
288 | 6 h: 4 | |||
|
289 | 5 e: 3 | |||
|
290 | 4 f: 2 | |||
|
291 | 3 d: 0 2 | |||
|
292 | 2 c: 1 | |||
|
293 | 1 b: -1 | |||
|
294 | 0 a: -1 | |||
|
295 | -1 : -1 | |||
|
296 | ||||
|
297 | Uncachable query: | |||
|
298 | ||||
|
299 | $ hg log -Gq -T '{rev} {tags}: {subsetparents(rev, revset("%d:%d", rev, rev - 1))}\n' | |||
|
300 | o 11 l tip: 10 | |||
|
301 | |\ | |||
|
302 | | o 10 i: | |||
|
303 | | |\ | |||
|
304 | o \ \ 9 k: | |||
|
305 | |\ \ \ | |||
|
306 | +-----o 8 g: | |||
|
307 | | | | | |||
|
308 | | o | 7 j: | |||
|
309 | | | | | |||
|
310 | | | o 6 h: | |||
|
311 | | | | | |||
|
312 | o | | 5 e: | |||
|
313 | |/ / | |||
|
314 | | o 4 f: | |||
|
315 | | | | |||
|
316 | o | 3 d: 2 | |||
|
317 | |\| | |||
|
318 | | o 2 c: 1 | |||
|
319 | | | | |||
|
320 | | o 1 b: | |||
|
321 | | | |||
|
322 | o 0 a: -1 | |||
|
323 | ||||
|
324 | ||||
|
325 | Invalid arguments: | |||
|
326 | ||||
|
327 | $ hg log -T '{subsetparents()}\n' | |||
|
328 | hg: parse error: subsetparents expects two arguments | |||
|
329 | [255] | |||
|
330 | $ hg log -T '{subsetparents("a")}\n' | |||
|
331 | hg: parse error: subsetparents expects two arguments | |||
|
332 | [255] | |||
|
333 | $ hg log -T '{subsetparents(rev, extras)}\n' | |||
|
334 | hg: parse error: subsetparents expects a queried revset | |||
|
335 | [255] | |||
|
336 | ||||
|
337 | $ cd .. |
@@ -274,6 +274,238 b' def descendantrevs(revs, revsfn, parentr' | |||||
274 | break |
|
274 | break | |
275 |
|
275 | |||
276 |
|
276 | |||
|
277 | class subsetparentswalker(object): | |||
|
278 | r"""Scan adjacent ancestors in the graph given by the subset | |||
|
279 | ||||
|
280 | This computes parent-child relations in the sub graph filtered by | |||
|
281 | a revset. Primary use case is to draw a revisions graph. | |||
|
282 | ||||
|
283 | In the following example, we consider that the node 'f' has edges to all | |||
|
284 | ancestor nodes, but redundant paths are eliminated. The edge 'f'->'b' | |||
|
285 | is eliminated because there is a path 'f'->'c'->'b' for example. | |||
|
286 | ||||
|
287 | - d - e - | |||
|
288 | / \ | |||
|
289 | a - b - c - f | |||
|
290 | ||||
|
291 | If the node 'c' is filtered out, the edge 'f'->'b' is activated. | |||
|
292 | ||||
|
293 | - d - e - | |||
|
294 | / \ | |||
|
295 | a - b -(c)- f | |||
|
296 | ||||
|
297 | Likewise, if 'd' and 'e' are filtered out, this edge is fully eliminated | |||
|
298 | since there is a path 'f'->'c'->'b'->'a' for 'f'->'a'. | |||
|
299 | ||||
|
300 | (d) (e) | |||
|
301 | ||||
|
302 | a - b - c - f | |||
|
303 | ||||
|
304 | Implementation-wise, 'f' is passed down to 'a' as unresolved through the | |||
|
305 | 'f'->'e'->'d'->'a' path, whereas we do also remember that 'f' has already | |||
|
306 | been resolved while walking down the 'f'->'c'->'b'->'a' path. When | |||
|
307 | processing the node 'a', the unresolved 'f'->'a' path is eliminated as | |||
|
308 | the 'f' end is marked as resolved. | |||
|
309 | ||||
|
310 | Ancestors are searched from the tipmost revision in the subset so the | |||
|
311 | results can be cached. You should specify startrev to narrow the search | |||
|
312 | space to ':startrev'. | |||
|
313 | """ | |||
|
314 | ||||
|
315 | def __init__(self, repo, subset, startrev=None): | |||
|
316 | if startrev is not None: | |||
|
317 | subset = repo.revs(b'%d:null', startrev) & subset | |||
|
318 | ||||
|
319 | # equivalent to 'subset = subset.sorted(reverse=True)', but there's | |||
|
320 | # no such function. | |||
|
321 | fastdesc = subset.fastdesc | |||
|
322 | if fastdesc: | |||
|
323 | desciter = fastdesc() | |||
|
324 | else: | |||
|
325 | if not subset.isdescending() and not subset.istopo(): | |||
|
326 | subset = smartset.baseset(subset) | |||
|
327 | subset.sort(reverse=True) | |||
|
328 | desciter = iter(subset) | |||
|
329 | ||||
|
330 | self._repo = repo | |||
|
331 | self._changelog = repo.changelog | |||
|
332 | self._subset = subset | |||
|
333 | ||||
|
334 | # scanning state (see _scanparents): | |||
|
335 | self._tovisit = [] | |||
|
336 | self._pendingcnt = {} | |||
|
337 | self._pointers = {} | |||
|
338 | self._parents = {} | |||
|
339 | self._inputhead = nullrev # reassigned by self._advanceinput() | |||
|
340 | self._inputtail = desciter | |||
|
341 | self._bottomrev = nullrev | |||
|
342 | self._advanceinput() | |||
|
343 | ||||
|
344 | def parentsset(self, rev): | |||
|
345 | """Look up parents of the given revision in the subset, and returns | |||
|
346 | as a smartset""" | |||
|
347 | return smartset.baseset(self.parents(rev)) | |||
|
348 | ||||
|
349 | def parents(self, rev): | |||
|
350 | """Look up parents of the given revision in the subset | |||
|
351 | ||||
|
352 | The returned revisions are sorted by parent index (p1/p2). | |||
|
353 | """ | |||
|
354 | self._scanparents(rev) | |||
|
355 | return [r for _c, r in sorted(self._parents.get(rev, []))] | |||
|
356 | ||||
|
357 | def _parentrevs(self, rev): | |||
|
358 | try: | |||
|
359 | revs = self._changelog.parentrevs(rev) | |||
|
360 | if revs[-1] == nullrev: | |||
|
361 | return revs[:-1] | |||
|
362 | return revs | |||
|
363 | except error.WdirUnsupported: | |||
|
364 | return tuple(pctx.rev() for pctx in self._repo[None].parents()) | |||
|
365 | ||||
|
366 | def _advanceinput(self): | |||
|
367 | """Advance the input iterator and set the next revision to _inputhead""" | |||
|
368 | if self._inputhead < nullrev: | |||
|
369 | return | |||
|
370 | try: | |||
|
371 | self._inputhead = next(self._inputtail) | |||
|
372 | except StopIteration: | |||
|
373 | self._bottomrev = self._inputhead | |||
|
374 | self._inputhead = nullrev - 1 | |||
|
375 | ||||
|
376 | def _scanparents(self, stoprev): | |||
|
377 | """Scan ancestors until the parents of the specified stoprev are | |||
|
378 | resolved""" | |||
|
379 | ||||
|
380 | # 'tovisit' is the queue of the input revisions and their ancestors. | |||
|
381 | # It will be populated incrementally to minimize the initial cost | |||
|
382 | # of computing the given subset. | |||
|
383 | # | |||
|
384 | # For to-visit revisions, we keep track of | |||
|
385 | # - the number of the unresolved paths: pendingcnt[rev], | |||
|
386 | # - dict of the unresolved descendants and chains: pointers[rev][0], | |||
|
387 | # - set of the already resolved descendants: pointers[rev][1]. | |||
|
388 | # | |||
|
389 | # When a revision is visited, 'pointers[rev]' should be popped and | |||
|
390 | # propagated to its parents accordingly. | |||
|
391 | # | |||
|
392 | # Once all pending paths have been resolved, 'pendingcnt[rev]' becomes | |||
|
393 | # 0 and 'parents[rev]' contains the unsorted list of parent revisions | |||
|
394 | # and p1/p2 chains (excluding linear paths.) | |||
|
395 | ||||
|
396 | subset = self._subset | |||
|
397 | tovisit = self._tovisit # heap queue of [-rev] | |||
|
398 | pendingcnt = self._pendingcnt # {rev: count} for visited revisions | |||
|
399 | pointers = self._pointers # {rev: [{unresolved_rev: chain}, resolved]} | |||
|
400 | parents = self._parents # {rev: [(chain, rev)]} | |||
|
401 | ||||
|
402 | while tovisit or self._inputhead >= nullrev: | |||
|
403 | if pendingcnt.get(stoprev) == 0: | |||
|
404 | return | |||
|
405 | ||||
|
406 | # feed greater revisions from input set to queue | |||
|
407 | if not tovisit: | |||
|
408 | heapq.heappush(tovisit, -self._inputhead) | |||
|
409 | self._advanceinput() | |||
|
410 | while self._inputhead >= -tovisit[0]: | |||
|
411 | heapq.heappush(tovisit, -self._inputhead) | |||
|
412 | self._advanceinput() | |||
|
413 | ||||
|
414 | rev = -heapq.heappop(tovisit) | |||
|
415 | if rev < self._bottomrev: | |||
|
416 | return | |||
|
417 | if rev in pendingcnt and rev not in pointers: | |||
|
418 | continue # already visited | |||
|
419 | ||||
|
420 | curactive = rev in subset | |||
|
421 | pendingcnt.setdefault(rev, 0) # mark as visited | |||
|
422 | if curactive: | |||
|
423 | assert rev not in parents | |||
|
424 | parents[rev] = [] | |||
|
425 | unresolved, resolved = pointers.pop(rev, ({}, set())) | |||
|
426 | ||||
|
427 | if curactive: | |||
|
428 | # reached to active rev, resolve pending descendants' parents | |||
|
429 | for r, c in unresolved.items(): | |||
|
430 | pendingcnt[r] -= 1 | |||
|
431 | assert pendingcnt[r] >= 0 | |||
|
432 | if r in resolved: | |||
|
433 | continue # eliminate redundant path | |||
|
434 | parents[r].append((c, rev)) | |||
|
435 | # mark the descendant 'r' as resolved through this path if | |||
|
436 | # there are still pending pointers. the 'resolved' set may | |||
|
437 | # be concatenated later at a fork revision. | |||
|
438 | if pendingcnt[r] > 0: | |||
|
439 | resolved.add(r) | |||
|
440 | unresolved.clear() | |||
|
441 | # occasionally clean resolved markers. otherwise the set | |||
|
442 | # would grow indefinitely. | |||
|
443 | resolved = {r for r in resolved if pendingcnt[r] > 0} | |||
|
444 | ||||
|
445 | parentrevs = self._parentrevs(rev) | |||
|
446 | bothparentsactive = all(p in subset for p in parentrevs) | |||
|
447 | ||||
|
448 | # set up or propagate tracking pointers if | |||
|
449 | # - one of the parents is not active, | |||
|
450 | # - or descendants' parents are unresolved. | |||
|
451 | if not bothparentsactive or unresolved or resolved: | |||
|
452 | if len(parentrevs) > 1: | |||
|
453 | # 'rev' is a merge revision. increment the pending count | |||
|
454 | # as the 'unresolved' dict will be duplicated. | |||
|
455 | for r in unresolved: | |||
|
456 | pendingcnt[r] += 1 | |||
|
457 | reusable = True # can we avoid copying the tracking pointer? | |||
|
458 | for i, p in enumerate(parentrevs): | |||
|
459 | assert p < rev | |||
|
460 | heapq.heappush(tovisit, -p) | |||
|
461 | if p in pointers: | |||
|
462 | # 'p' is a fork revision. concatenate tracking pointers | |||
|
463 | # and decrement the pending count accordingly. | |||
|
464 | knownunresolved, knownresolved = pointers[p] | |||
|
465 | for r, c in unresolved.items(): | |||
|
466 | c += [b'1', b'2'][i] | |||
|
467 | if r in knownunresolved: | |||
|
468 | # unresolved at both paths | |||
|
469 | pendingcnt[r] -= 1 | |||
|
470 | assert pendingcnt[r] > 0 | |||
|
471 | # take shorter chain | |||
|
472 | knownunresolved[r] = min(c, knownunresolved[r]) | |||
|
473 | else: | |||
|
474 | knownunresolved[r] = c | |||
|
475 | # simply propagate the 'resolved' set as deduplicating | |||
|
476 | # 'unresolved' here would be slightly complicated. | |||
|
477 | knownresolved.update(resolved) | |||
|
478 | elif reusable: | |||
|
479 | pointers[p] = (unresolved, resolved) | |||
|
480 | reusable = False | |||
|
481 | else: | |||
|
482 | pointers[p] = (unresolved.copy(), resolved.copy()) | |||
|
483 | ||||
|
484 | # then, populate the active parents directly and add the current | |||
|
485 | # 'rev' to the tracking pointers of the inactive parents. | |||
|
486 | # 'pointers[p]' may be optimized out if both parents are active. | |||
|
487 | if curactive and bothparentsactive: | |||
|
488 | for i, p in enumerate(parentrevs): | |||
|
489 | c = [b'1', b'2'][i] | |||
|
490 | parents[rev].append((c, p)) | |||
|
491 | # no need to mark 'rev' as resolved since the 'rev' should | |||
|
492 | # be fully resolved (i.e. pendingcnt[rev] == 0) | |||
|
493 | assert pendingcnt[rev] == 0 | |||
|
494 | elif curactive: | |||
|
495 | for i, p in enumerate(parentrevs): | |||
|
496 | unresolved, resolved = pointers[p] | |||
|
497 | assert rev not in unresolved | |||
|
498 | c = [b'1', b'2'][i] | |||
|
499 | if p in subset: | |||
|
500 | parents[rev].append((c, p)) | |||
|
501 | # mark 'rev' as resolved through this path | |||
|
502 | resolved.add(rev) | |||
|
503 | else: | |||
|
504 | pendingcnt[rev] += 1 | |||
|
505 | unresolved[rev] = c | |||
|
506 | assert 0 < pendingcnt[rev] <= 2 | |||
|
507 | ||||
|
508 | ||||
277 | def _reachablerootspure(pfunc, minroot, roots, heads, includepath): |
|
509 | def _reachablerootspure(pfunc, minroot, roots, heads, includepath): | |
278 | """See revlog.reachableroots""" |
|
510 | """See revlog.reachableroots""" | |
279 | if not roots: |
|
511 | if not roots: |
@@ -16,6 +16,7 b' from .node import (' | |||||
16 | ) |
|
16 | ) | |
17 | from . import ( |
|
17 | from . import ( | |
18 | color, |
|
18 | color, | |
|
19 | dagop, | |||
19 | diffutil, |
|
20 | diffutil, | |
20 | encoding, |
|
21 | encoding, | |
21 | error, |
|
22 | error, | |
@@ -842,6 +843,45 b' def startswith(context, mapping, args):' | |||||
842 | return b'' |
|
843 | return b'' | |
843 |
|
844 | |||
844 |
|
845 | |||
|
846 | @templatefunc( | |||
|
847 | b'subsetparents(rev, revset)', | |||
|
848 | argspec=b'rev revset', | |||
|
849 | requires={b'repo', b'cache'}, | |||
|
850 | ) | |||
|
851 | def subsetparents(context, mapping, args): | |||
|
852 | """Look up parents of the rev in the sub graph given by the revset.""" | |||
|
853 | if b'rev' not in args or b'revset' not in args: | |||
|
854 | # i18n: "subsetparents" is a keyword | |||
|
855 | raise error.ParseError(_(b"subsetparents expects two arguments")) | |||
|
856 | ||||
|
857 | repo = context.resource(mapping, b'repo') | |||
|
858 | ||||
|
859 | rev = templateutil.evalinteger(context, mapping, args[b'rev']) | |||
|
860 | ||||
|
861 | # TODO: maybe subsetparents(rev) should be allowed. the default revset | |||
|
862 | # will be the revisions specified by -rREV argument. | |||
|
863 | q = templateutil.evalwrapped(context, mapping, args[b'revset']) | |||
|
864 | if not isinstance(q, templateutil.revslist): | |||
|
865 | # i18n: "subsetparents" is a keyword | |||
|
866 | raise error.ParseError(_(b"subsetparents expects a queried revset")) | |||
|
867 | subset = q.tovalue(context, mapping) | |||
|
868 | key = q.cachekey | |||
|
869 | ||||
|
870 | if key: | |||
|
871 | # cache only if revset query isn't dynamic | |||
|
872 | cache = context.resource(mapping, b'cache') | |||
|
873 | walkercache = cache.setdefault(b'subsetparentswalker', {}) | |||
|
874 | if key in walkercache: | |||
|
875 | walker = walkercache[key] | |||
|
876 | else: | |||
|
877 | walker = dagop.subsetparentswalker(repo, subset) | |||
|
878 | walkercache[key] = walker | |||
|
879 | else: | |||
|
880 | # for one-shot use, specify startrev to limit the search space | |||
|
881 | walker = dagop.subsetparentswalker(repo, subset, startrev=rev) | |||
|
882 | return templateutil.revslist(repo, walker.parentsset(rev)) | |||
|
883 | ||||
|
884 | ||||
845 | @templatefunc(b'word(number, text[, separator])') |
|
885 | @templatefunc(b'word(number, text[, separator])') | |
846 | def word(context, mapping, args): |
|
886 | def word(context, mapping, args): | |
847 | """Return the nth word from a string.""" |
|
887 | """Return the nth word from a string.""" |
General Comments 0
You need to be logged in to leave comments.
Login now