Show More
@@ -0,0 +1,398 b'' | |||||
|
1 | Test file dedicated to testing the divergent troubles from obsolete changeset. | |||
|
2 | ||||
|
3 | This is the most complexe troubles from far so we isolate it in a dedicated | |||
|
4 | file. | |||
|
5 | ||||
|
6 | Enable obsolete | |||
|
7 | ||||
|
8 | $ cat > obs.py << EOF | |||
|
9 | > import mercurial.obsolete | |||
|
10 | > mercurial.obsolete._enabled = True | |||
|
11 | > EOF | |||
|
12 | $ cat >> $HGRCPATH << EOF | |||
|
13 | > [ui] | |||
|
14 | > logtemplate = {rev}:{node|short} {desc}\n | |||
|
15 | > [extensions] | |||
|
16 | > obs=${TESTTMP}/obs.py | |||
|
17 | > [alias] | |||
|
18 | > debugobsolete = debugobsolete -d '0 0' | |||
|
19 | > [phases] | |||
|
20 | > publish=False | |||
|
21 | > EOF | |||
|
22 | ||||
|
23 | ||||
|
24 | $ mkcommit() { | |||
|
25 | > echo "$1" > "$1" | |||
|
26 | > hg add "$1" | |||
|
27 | > hg ci -m "$1" | |||
|
28 | > } | |||
|
29 | $ getid() { | |||
|
30 | > hg id --debug -ir "desc('$1')" | |||
|
31 | > } | |||
|
32 | ||||
|
33 | setup repo | |||
|
34 | ||||
|
35 | $ hg init reference | |||
|
36 | $ cd reference | |||
|
37 | $ mkcommit base | |||
|
38 | $ mkcommit A_0 | |||
|
39 | $ hg up 0 | |||
|
40 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved | |||
|
41 | $ mkcommit A_1 | |||
|
42 | created new head | |||
|
43 | $ hg up 0 | |||
|
44 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved | |||
|
45 | $ mkcommit A_2 | |||
|
46 | created new head | |||
|
47 | $ hg up 0 | |||
|
48 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved | |||
|
49 | $ cd .. | |||
|
50 | ||||
|
51 | ||||
|
52 | $ newcase() { | |||
|
53 | > hg clone -u 0 -q reference $1 | |||
|
54 | > cd $1 | |||
|
55 | > } | |||
|
56 | ||||
|
57 | direct divergence | |||
|
58 | ----------------- | |||
|
59 | ||||
|
60 | A_1 have two direct and divergent successors A_1 and A_1 | |||
|
61 | ||||
|
62 | $ newcase direct | |||
|
63 | $ hg debugobsolete `getid A_0` `getid A_1` | |||
|
64 | $ hg debugobsolete `getid A_0` `getid A_2` | |||
|
65 | $ hg log -G --hidden | |||
|
66 | o 3:392fd25390da A_2 | |||
|
67 | | | |||
|
68 | | o 2:82623d38b9ba A_1 | |||
|
69 | |/ | |||
|
70 | | x 1:007dc284c1f8 A_0 | |||
|
71 | |/ | |||
|
72 | @ 0:d20a80d4def3 base | |||
|
73 | ||||
|
74 | $ hg debugsuccessorssets 'all()' | |||
|
75 | d20a80d4def3 | |||
|
76 | d20a80d4def3 | |||
|
77 | 007dc284c1f8 | |||
|
78 | 82623d38b9ba | |||
|
79 | 392fd25390da | |||
|
80 | 82623d38b9ba | |||
|
81 | 82623d38b9ba | |||
|
82 | 392fd25390da | |||
|
83 | 392fd25390da | |||
|
84 | $ cd .. | |||
|
85 | ||||
|
86 | ||||
|
87 | indirect divergence with known changeset | |||
|
88 | ------------------------------------------- | |||
|
89 | ||||
|
90 | $ newcase indirect_known | |||
|
91 | $ hg debugobsolete `getid A_0` `getid A_1` | |||
|
92 | $ hg debugobsolete `getid A_0` `getid A_2` | |||
|
93 | $ mkcommit A_3 | |||
|
94 | created new head | |||
|
95 | $ hg debugobsolete `getid A_2` `getid A_3` | |||
|
96 | $ hg log -G --hidden | |||
|
97 | @ 4:01f36c5a8fda A_3 | |||
|
98 | | | |||
|
99 | | x 3:392fd25390da A_2 | |||
|
100 | |/ | |||
|
101 | | o 2:82623d38b9ba A_1 | |||
|
102 | |/ | |||
|
103 | | x 1:007dc284c1f8 A_0 | |||
|
104 | |/ | |||
|
105 | o 0:d20a80d4def3 base | |||
|
106 | ||||
|
107 | $ hg debugsuccessorssets 'all()' | |||
|
108 | d20a80d4def3 | |||
|
109 | d20a80d4def3 | |||
|
110 | 007dc284c1f8 | |||
|
111 | 01f36c5a8fda | |||
|
112 | 82623d38b9ba | |||
|
113 | 82623d38b9ba | |||
|
114 | 82623d38b9ba | |||
|
115 | 392fd25390da | |||
|
116 | 01f36c5a8fda | |||
|
117 | 01f36c5a8fda | |||
|
118 | 01f36c5a8fda | |||
|
119 | $ cd .. | |||
|
120 | ||||
|
121 | ||||
|
122 | indirect divergence with known changeset | |||
|
123 | ------------------------------------------- | |||
|
124 | ||||
|
125 | $ newcase indirect_unknown | |||
|
126 | $ hg debugobsolete `getid A_0` aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | |||
|
127 | $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa `getid A_1` | |||
|
128 | $ hg debugobsolete `getid A_0` `getid A_2` | |||
|
129 | $ hg log -G --hidden | |||
|
130 | o 3:392fd25390da A_2 | |||
|
131 | | | |||
|
132 | | o 2:82623d38b9ba A_1 | |||
|
133 | |/ | |||
|
134 | | x 1:007dc284c1f8 A_0 | |||
|
135 | |/ | |||
|
136 | @ 0:d20a80d4def3 base | |||
|
137 | ||||
|
138 | $ hg debugsuccessorssets 'all()' | |||
|
139 | d20a80d4def3 | |||
|
140 | d20a80d4def3 | |||
|
141 | 007dc284c1f8 | |||
|
142 | 82623d38b9ba | |||
|
143 | 392fd25390da | |||
|
144 | 82623d38b9ba | |||
|
145 | 82623d38b9ba | |||
|
146 | 392fd25390da | |||
|
147 | 392fd25390da | |||
|
148 | $ cd .. | |||
|
149 | ||||
|
150 | do not take unknown node in account if they are final | |||
|
151 | ----------------------------------------------------- | |||
|
152 | ||||
|
153 | $ newcase final-unknown | |||
|
154 | $ hg debugobsolete `getid A_0` `getid A_1` | |||
|
155 | $ hg debugobsolete `getid A_1` `getid A_2` | |||
|
156 | $ hg debugobsolete `getid A_0` bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | |||
|
157 | $ hg debugobsolete bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb cccccccccccccccccccccccccccccccccccccccc | |||
|
158 | $ hg debugobsolete `getid A_1` dddddddddddddddddddddddddddddddddddddddd | |||
|
159 | ||||
|
160 | $ hg debugsuccessorssets 'desc('A_0')' | |||
|
161 | 007dc284c1f8 | |||
|
162 | 392fd25390da | |||
|
163 | ||||
|
164 | $ cd .. | |||
|
165 | ||||
|
166 | divergence that converge again is not divergence anymore | |||
|
167 | ----------------------------------------------------- | |||
|
168 | ||||
|
169 | $ newcase converged_divergence | |||
|
170 | $ hg debugobsolete `getid A_0` `getid A_1` | |||
|
171 | $ hg debugobsolete `getid A_0` `getid A_2` | |||
|
172 | $ mkcommit A_3 | |||
|
173 | created new head | |||
|
174 | $ hg debugobsolete `getid A_1` `getid A_3` | |||
|
175 | $ hg debugobsolete `getid A_2` `getid A_3` | |||
|
176 | $ hg log -G --hidden | |||
|
177 | @ 4:01f36c5a8fda A_3 | |||
|
178 | | | |||
|
179 | | x 3:392fd25390da A_2 | |||
|
180 | |/ | |||
|
181 | | x 2:82623d38b9ba A_1 | |||
|
182 | |/ | |||
|
183 | | x 1:007dc284c1f8 A_0 | |||
|
184 | |/ | |||
|
185 | o 0:d20a80d4def3 base | |||
|
186 | ||||
|
187 | $ hg debugsuccessorssets 'all()' | |||
|
188 | d20a80d4def3 | |||
|
189 | d20a80d4def3 | |||
|
190 | 007dc284c1f8 | |||
|
191 | 01f36c5a8fda | |||
|
192 | 82623d38b9ba | |||
|
193 | 01f36c5a8fda | |||
|
194 | 392fd25390da | |||
|
195 | 01f36c5a8fda | |||
|
196 | 01f36c5a8fda | |||
|
197 | 01f36c5a8fda | |||
|
198 | $ cd .. | |||
|
199 | ||||
|
200 | split is not divergences | |||
|
201 | ----------------------------- | |||
|
202 | ||||
|
203 | $ newcase split | |||
|
204 | $ hg debugobsolete `getid A_0` `getid A_1` `getid A_2` | |||
|
205 | $ hg log -G --hidden | |||
|
206 | o 3:392fd25390da A_2 | |||
|
207 | | | |||
|
208 | | o 2:82623d38b9ba A_1 | |||
|
209 | |/ | |||
|
210 | | x 1:007dc284c1f8 A_0 | |||
|
211 | |/ | |||
|
212 | @ 0:d20a80d4def3 base | |||
|
213 | ||||
|
214 | $ hg debugsuccessorssets 'all()' | |||
|
215 | d20a80d4def3 | |||
|
216 | d20a80d4def3 | |||
|
217 | 007dc284c1f8 | |||
|
218 | 82623d38b9ba 392fd25390da | |||
|
219 | 82623d38b9ba | |||
|
220 | 82623d38b9ba | |||
|
221 | 392fd25390da | |||
|
222 | 392fd25390da | |||
|
223 | ||||
|
224 | Even when subsequente rewriting happen | |||
|
225 | ||||
|
226 | $ mkcommit A_3 | |||
|
227 | created new head | |||
|
228 | $ hg debugobsolete `getid A_1` `getid A_3` | |||
|
229 | $ hg up 0 | |||
|
230 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved | |||
|
231 | $ mkcommit A_4 | |||
|
232 | created new head | |||
|
233 | $ hg debugobsolete `getid A_2` `getid A_4` | |||
|
234 | $ hg up 0 | |||
|
235 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved | |||
|
236 | $ mkcommit A_5 | |||
|
237 | created new head | |||
|
238 | $ hg debugobsolete `getid A_4` `getid A_5` | |||
|
239 | $ hg log -G --hidden | |||
|
240 | @ 6:e442cfc57690 A_5 | |||
|
241 | | | |||
|
242 | | x 5:6a411f0d7a0a A_4 | |||
|
243 | |/ | |||
|
244 | | o 4:01f36c5a8fda A_3 | |||
|
245 | |/ | |||
|
246 | | x 3:392fd25390da A_2 | |||
|
247 | |/ | |||
|
248 | | x 2:82623d38b9ba A_1 | |||
|
249 | |/ | |||
|
250 | | x 1:007dc284c1f8 A_0 | |||
|
251 | |/ | |||
|
252 | o 0:d20a80d4def3 base | |||
|
253 | ||||
|
254 | $ hg debugsuccessorssets 'all()' | |||
|
255 | d20a80d4def3 | |||
|
256 | d20a80d4def3 | |||
|
257 | 007dc284c1f8 | |||
|
258 | 01f36c5a8fda e442cfc57690 | |||
|
259 | 82623d38b9ba | |||
|
260 | 01f36c5a8fda | |||
|
261 | 392fd25390da | |||
|
262 | e442cfc57690 | |||
|
263 | 01f36c5a8fda | |||
|
264 | 01f36c5a8fda | |||
|
265 | 6a411f0d7a0a | |||
|
266 | e442cfc57690 | |||
|
267 | e442cfc57690 | |||
|
268 | e442cfc57690 | |||
|
269 | ||||
|
270 | Check more complexe obsolescence graft (with divergence) | |||
|
271 | ||||
|
272 | $ mkcommit B_0; hg up 0 | |||
|
273 | 0 files updated, 0 files merged, 2 files removed, 0 files unresolved | |||
|
274 | $ hg debugobsolete `getid B_0` `getid A_2` | |||
|
275 | $ mkcommit A_7; hg up 0 | |||
|
276 | created new head | |||
|
277 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved | |||
|
278 | $ mkcommit A_8; hg up 0 | |||
|
279 | created new head | |||
|
280 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved | |||
|
281 | $ hg debugobsolete `getid A_5` `getid A_7` `getid A_8` | |||
|
282 | $ mkcommit A_9; hg up 0 | |||
|
283 | created new head | |||
|
284 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved | |||
|
285 | $ hg debugobsolete `getid A_5` `getid A_9` | |||
|
286 | $ hg log -G --hidden | |||
|
287 | o 10:bed64f5d2f5a A_9 | |||
|
288 | | | |||
|
289 | | o 9:14608b260df8 A_8 | |||
|
290 | |/ | |||
|
291 | | o 8:7ae126973a96 A_7 | |||
|
292 | |/ | |||
|
293 | | x 7:3750ebee865d B_0 | |||
|
294 | | | | |||
|
295 | | x 6:e442cfc57690 A_5 | |||
|
296 | |/ | |||
|
297 | | x 5:6a411f0d7a0a A_4 | |||
|
298 | |/ | |||
|
299 | | o 4:01f36c5a8fda A_3 | |||
|
300 | |/ | |||
|
301 | | x 3:392fd25390da A_2 | |||
|
302 | |/ | |||
|
303 | | x 2:82623d38b9ba A_1 | |||
|
304 | |/ | |||
|
305 | | x 1:007dc284c1f8 A_0 | |||
|
306 | |/ | |||
|
307 | @ 0:d20a80d4def3 base | |||
|
308 | ||||
|
309 | $ hg debugsuccessorssets 'all()' | |||
|
310 | d20a80d4def3 | |||
|
311 | d20a80d4def3 | |||
|
312 | 007dc284c1f8 | |||
|
313 | 01f36c5a8fda bed64f5d2f5a | |||
|
314 | 01f36c5a8fda 7ae126973a96 14608b260df8 | |||
|
315 | 82623d38b9ba | |||
|
316 | 01f36c5a8fda | |||
|
317 | 392fd25390da | |||
|
318 | bed64f5d2f5a | |||
|
319 | 7ae126973a96 14608b260df8 | |||
|
320 | 01f36c5a8fda | |||
|
321 | 01f36c5a8fda | |||
|
322 | 6a411f0d7a0a | |||
|
323 | bed64f5d2f5a | |||
|
324 | 7ae126973a96 14608b260df8 | |||
|
325 | e442cfc57690 | |||
|
326 | bed64f5d2f5a | |||
|
327 | 7ae126973a96 14608b260df8 | |||
|
328 | 3750ebee865d | |||
|
329 | bed64f5d2f5a | |||
|
330 | 7ae126973a96 14608b260df8 | |||
|
331 | 7ae126973a96 | |||
|
332 | 7ae126973a96 | |||
|
333 | 14608b260df8 | |||
|
334 | 14608b260df8 | |||
|
335 | bed64f5d2f5a | |||
|
336 | bed64f5d2f5a | |||
|
337 | ||||
|
338 | fix the divergence | |||
|
339 | ||||
|
340 | $ mkcommit A_A; hg up 0 | |||
|
341 | created new head | |||
|
342 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved | |||
|
343 | $ hg debugobsolete `getid A_9` `getid A_A` | |||
|
344 | $ hg debugobsolete `getid A_7` `getid A_A` | |||
|
345 | $ hg debugobsolete `getid A_8` `getid A_A` | |||
|
346 | $ hg log -G --hidden | |||
|
347 | o 11:a139f71be9da A_A | |||
|
348 | | | |||
|
349 | | x 10:bed64f5d2f5a A_9 | |||
|
350 | |/ | |||
|
351 | | x 9:14608b260df8 A_8 | |||
|
352 | |/ | |||
|
353 | | x 8:7ae126973a96 A_7 | |||
|
354 | |/ | |||
|
355 | | x 7:3750ebee865d B_0 | |||
|
356 | | | | |||
|
357 | | x 6:e442cfc57690 A_5 | |||
|
358 | |/ | |||
|
359 | | x 5:6a411f0d7a0a A_4 | |||
|
360 | |/ | |||
|
361 | | o 4:01f36c5a8fda A_3 | |||
|
362 | |/ | |||
|
363 | | x 3:392fd25390da A_2 | |||
|
364 | |/ | |||
|
365 | | x 2:82623d38b9ba A_1 | |||
|
366 | |/ | |||
|
367 | | x 1:007dc284c1f8 A_0 | |||
|
368 | |/ | |||
|
369 | @ 0:d20a80d4def3 base | |||
|
370 | ||||
|
371 | $ hg debugsuccessorssets 'all()' | |||
|
372 | d20a80d4def3 | |||
|
373 | d20a80d4def3 | |||
|
374 | 007dc284c1f8 | |||
|
375 | 01f36c5a8fda a139f71be9da | |||
|
376 | 82623d38b9ba | |||
|
377 | 01f36c5a8fda | |||
|
378 | 392fd25390da | |||
|
379 | a139f71be9da | |||
|
380 | 01f36c5a8fda | |||
|
381 | 01f36c5a8fda | |||
|
382 | 6a411f0d7a0a | |||
|
383 | a139f71be9da | |||
|
384 | e442cfc57690 | |||
|
385 | a139f71be9da | |||
|
386 | 3750ebee865d | |||
|
387 | a139f71be9da | |||
|
388 | 7ae126973a96 | |||
|
389 | a139f71be9da | |||
|
390 | 14608b260df8 | |||
|
391 | a139f71be9da | |||
|
392 | bed64f5d2f5a | |||
|
393 | a139f71be9da | |||
|
394 | a139f71be9da | |||
|
395 | a139f71be9da | |||
|
396 | ||||
|
397 | $ cd .. | |||
|
398 |
@@ -2459,6 +2459,60 b' def debugsub(ui, repo, rev=None):' | |||||
2459 | ui.write((' source %s\n') % v[0]) |
|
2459 | ui.write((' source %s\n') % v[0]) | |
2460 | ui.write((' revision %s\n') % v[1]) |
|
2460 | ui.write((' revision %s\n') % v[1]) | |
2461 |
|
2461 | |||
|
2462 | @command('debugsuccessorssets', | |||
|
2463 | [], | |||
|
2464 | _('[REV]')) | |||
|
2465 | def debugsuccessorssets(ui, repo, *revs): | |||
|
2466 | """show set of successors for revision | |||
|
2467 | ||||
|
2468 | A successors set of changeset A is a consistent group of revisions that | |||
|
2469 | succeed A. It contains non-obsolete changesets only. | |||
|
2470 | ||||
|
2471 | In most cases a changeset A has a single successors set containing a single | |||
|
2472 | successors (changeset A replaced by A'). | |||
|
2473 | ||||
|
2474 | A changeset that is made obsolete with no successors are called "pruned". | |||
|
2475 | Such changesets have no successors sets at all. | |||
|
2476 | ||||
|
2477 | A changeset that has been "split" will have a successors set containing | |||
|
2478 | more than one successors. | |||
|
2479 | ||||
|
2480 | A changeset that has been rewritten in multiple different ways is called | |||
|
2481 | "divergent". Such changesets have multiple successor sets (each of which | |||
|
2482 | may also be split, i.e. have multiple successors). | |||
|
2483 | ||||
|
2484 | Results are displayed as follows:: | |||
|
2485 | ||||
|
2486 | <rev1> | |||
|
2487 | <successors-1A> | |||
|
2488 | <rev2> | |||
|
2489 | <successors-2A> | |||
|
2490 | <successors-2B1> <successors-2B2> <successors-2B3> | |||
|
2491 | ||||
|
2492 | Here rev2 has two possible (i.e. divergent) successors sets. The first | |||
|
2493 | holds one element, whereas the second holds three (i.e. the changeset has | |||
|
2494 | been split). | |||
|
2495 | """ | |||
|
2496 | # passed to successorssets caching computation from one call to another | |||
|
2497 | cache = {} | |||
|
2498 | ctx2str = str | |||
|
2499 | node2str = short | |||
|
2500 | if ui.debug(): | |||
|
2501 | def ctx2str(ctx): | |||
|
2502 | return ctx.hex() | |||
|
2503 | node2str = hex | |||
|
2504 | for rev in scmutil.revrange(repo, revs): | |||
|
2505 | ctx = repo[rev] | |||
|
2506 | ui.write('%s\n'% ctx2str(ctx)) | |||
|
2507 | for succsset in obsolete.successorssets(repo, ctx.node(), cache): | |||
|
2508 | if succsset: | |||
|
2509 | ui.write(' ') | |||
|
2510 | ui.write(node2str(succsset[0])) | |||
|
2511 | for node in succsset[1:]: | |||
|
2512 | ui.write(' ') | |||
|
2513 | ui.write(node2str(node)) | |||
|
2514 | ui.write('\n') | |||
|
2515 | ||||
2462 | @command('debugwalk', walkopts, _('[OPTION]... [FILE]...')) |
|
2516 | @command('debugwalk', walkopts, _('[OPTION]... [FILE]...')) | |
2463 | def debugwalk(ui, repo, *pats, **opts): |
|
2517 | def debugwalk(ui, repo, *pats, **opts): | |
2464 | """show how files match on given patterns""" |
|
2518 | """show how files match on given patterns""" |
@@ -402,6 +402,182 b' def allsuccessors(obsstore, nodes, ignor' | |||||
402 | seen.add(suc) |
|
402 | seen.add(suc) | |
403 | remaining.add(suc) |
|
403 | remaining.add(suc) | |
404 |
|
404 | |||
|
405 | def successorssets(repo, initialnode, cache=None): | |||
|
406 | """Return all set of successors of initial nodes | |||
|
407 | ||||
|
408 | Successors set of changeset A are a group of revision that succeed A. It | |||
|
409 | succeed A as a consistent whole, each revision being only partial | |||
|
410 | replacement. Successors set contains non-obsolete changeset only. | |||
|
411 | ||||
|
412 | In most cases a changeset A have zero (changeset pruned) or a single | |||
|
413 | successors set that contains a single successor (changeset A replaced by | |||
|
414 | A') | |||
|
415 | ||||
|
416 | When changeset is split, it results successors set containing more than | |||
|
417 | a single element. Divergent rewriting will result in multiple successors | |||
|
418 | sets. | |||
|
419 | ||||
|
420 | They are returned as a list of tuples containing all valid successors sets. | |||
|
421 | ||||
|
422 | Final successors unknown locally are considered plain prune (obsoleted | |||
|
423 | without successors). | |||
|
424 | ||||
|
425 | The optional `cache` parameter is a dictionary that may contains | |||
|
426 | precomputed successors sets. It is meant to reuse the computation of | |||
|
427 | previous call to `successorssets` when multiple calls are made at the same | |||
|
428 | time. The cache dictionary is updated in place. The caller is responsible | |||
|
429 | for its live spawn. Code that makes multiple calls to `successorssets` | |||
|
430 | *must* use this cache mechanism or suffer terrible performances.""" | |||
|
431 | ||||
|
432 | succmarkers = repo.obsstore.successors | |||
|
433 | ||||
|
434 | # Stack of nodes we search successors sets for | |||
|
435 | toproceed = [initialnode] | |||
|
436 | # set version of above list for fast loop detection | |||
|
437 | # element added to "toproceed" must be added here | |||
|
438 | stackedset = set(toproceed) | |||
|
439 | if cache is None: | |||
|
440 | cache = {} | |||
|
441 | ||||
|
442 | # This while loop is the flattened version of a recursive search for | |||
|
443 | # successors sets | |||
|
444 | # | |||
|
445 | # def successorssets(x): | |||
|
446 | # successors = directsuccessors(x) | |||
|
447 | # ss = [[]] | |||
|
448 | # for succ in directsuccessors(x): | |||
|
449 | # # product as in itertools cartesian product | |||
|
450 | # ss = product(ss, successorssets(succ)) | |||
|
451 | # return ss | |||
|
452 | # | |||
|
453 | # But we can not use plain recursive calls here: | |||
|
454 | # - that would blow the python call stack | |||
|
455 | # - obsolescence markers may have cycles, we need to handle them. | |||
|
456 | # | |||
|
457 | # The `toproceed` list act as our call stack. Every node we search | |||
|
458 | # successors set for are stacked there. | |||
|
459 | # | |||
|
460 | # The `stackedset` is set version of this stack used to check if a node is | |||
|
461 | # already stacked. This check is used to detect cycles and prevent infinite | |||
|
462 | # loop. | |||
|
463 | # | |||
|
464 | # successors set of all nodes are stored in the `cache` dictionary. | |||
|
465 | # | |||
|
466 | # After this while loop ends we use the cache to return the successors sets | |||
|
467 | # for the node requested by the caller. | |||
|
468 | while toproceed: | |||
|
469 | # Every iteration tries to compute the successors sets of the topmost | |||
|
470 | # node of the stack: CURRENT. | |||
|
471 | # | |||
|
472 | # There are four possible outcomes: | |||
|
473 | # | |||
|
474 | # 1) We already know the successors sets of CURRENT: | |||
|
475 | # -> mission accomplished, pop it from the stack. | |||
|
476 | # 2) Node is not obsolete: | |||
|
477 | # -> the node is its own successors sets. Add it to the cache. | |||
|
478 | # 3) We do not know successors set of direct successors of CURRENT: | |||
|
479 | # -> We add those successors to the stack. | |||
|
480 | # 4) We know successors sets of all direct successors of CURRENT: | |||
|
481 | # -> We can compute CURRENT successors set and add it to the | |||
|
482 | # cache. | |||
|
483 | # | |||
|
484 | current = toproceed[-1] | |||
|
485 | if current in cache: | |||
|
486 | # case (1): We already know the successors sets | |||
|
487 | stackedset.remove(toproceed.pop()) | |||
|
488 | elif current not in succmarkers: | |||
|
489 | # case (2): The node is not obsolete. | |||
|
490 | if current in repo: | |||
|
491 | # We have a valid last successors. | |||
|
492 | cache[current] = [(current,)] | |||
|
493 | else: | |||
|
494 | # Final obsolete version is unknown locally. | |||
|
495 | # Do not count that as a valid successors | |||
|
496 | cache[current] = [] | |||
|
497 | else: | |||
|
498 | # cases (3) and (4) | |||
|
499 | # | |||
|
500 | # We proceed in two phases. Phase 1 aims to distinguish case (3) | |||
|
501 | # from case (4): | |||
|
502 | # | |||
|
503 | # For each direct successors of CURRENT, we check whether its | |||
|
504 | # successors sets are known. If they are not, we stack the | |||
|
505 | # unknown node and proceed to the next iteration of the while | |||
|
506 | # loop. (case 3) | |||
|
507 | # | |||
|
508 | # During this step, we may detect obsolescence cycles: a node | |||
|
509 | # with unknown successors sets but already in the call stack. | |||
|
510 | # In such a situation, we arbitrary set the successors sets of | |||
|
511 | # the node to nothing (node pruned) to break the cycle. | |||
|
512 | # | |||
|
513 | # If no break was encountered we proceeed to phase 2. | |||
|
514 | # | |||
|
515 | # Phase 2 computes successors sets of CURRENT (case 4); see details | |||
|
516 | # in phase 2 itself. | |||
|
517 | # | |||
|
518 | # Note the two levels of iteration in each phase. | |||
|
519 | # - The first one handles obsolescence markers using CURRENT as | |||
|
520 | # precursor (successors markers of CURRENT). | |||
|
521 | # | |||
|
522 | # Having multiple entry here means divergence. | |||
|
523 | # | |||
|
524 | # - The second one handles successors defined in each marker. | |||
|
525 | # | |||
|
526 | # Having none means pruned node, multiple successors means split, | |||
|
527 | # single successors are standard replacement. | |||
|
528 | # | |||
|
529 | for mark in succmarkers[current]: | |||
|
530 | for suc in mark[1]: | |||
|
531 | if suc not in cache: | |||
|
532 | if suc in stackedset: | |||
|
533 | # cycle breaking | |||
|
534 | cache[suc] = [] | |||
|
535 | else: | |||
|
536 | # case (3) If we have not computed successors sets | |||
|
537 | # of one of those successors we add it to the | |||
|
538 | # `toproceed` stack and stop all work for this | |||
|
539 | # iteration. | |||
|
540 | toproceed.append(suc) | |||
|
541 | stackedset.add(suc) | |||
|
542 | break | |||
|
543 | else: | |||
|
544 | continue | |||
|
545 | break | |||
|
546 | else: | |||
|
547 | # case (4): we know all successors sets of all direct | |||
|
548 | # successors | |||
|
549 | # | |||
|
550 | # Successors set contributed by each marker depends on the | |||
|
551 | # successors sets of all its "successors" node. | |||
|
552 | # | |||
|
553 | # Each different marker is a divergence in the obsolescence | |||
|
554 | # history. It contributes successors sets dictinct from other | |||
|
555 | # markers. | |||
|
556 | # | |||
|
557 | # Within a marker, a successor may have divergent successors | |||
|
558 | # sets. In such a case, the marker will contribute multiple | |||
|
559 | # divergent successors sets. If multiple successors have | |||
|
560 | # divergents successors sets, a cartesian product is used. | |||
|
561 | succssets = [] | |||
|
562 | for mark in succmarkers[current]: | |||
|
563 | # successors sets contributed by this marker | |||
|
564 | markss = [[]] | |||
|
565 | for suc in mark[1]: | |||
|
566 | productresult = [] | |||
|
567 | for prefix in markss: | |||
|
568 | for suffix in cache[suc]: | |||
|
569 | newss = list(prefix) | |||
|
570 | for part in suffix: | |||
|
571 | # do not duplicated entry in successors set | |||
|
572 | # first entry wins. | |||
|
573 | if part not in newss: | |||
|
574 | newss.append(part) | |||
|
575 | productresult.append(newss) | |||
|
576 | markss = productresult | |||
|
577 | succssets.extend(markss) | |||
|
578 | cache[current] = list(set(tuple(r) for r in succssets if r)) | |||
|
579 | return cache[initialnode] | |||
|
580 | ||||
405 | def _knownrevs(repo, nodes): |
|
581 | def _knownrevs(repo, nodes): | |
406 | """yield revision numbers of known nodes passed in parameters |
|
582 | """yield revision numbers of known nodes passed in parameters | |
407 |
|
583 |
@@ -96,6 +96,7 b' Show debug commands if there are no othe' | |||||
96 | debugsetparents |
|
96 | debugsetparents | |
97 | debugstate |
|
97 | debugstate | |
98 | debugsub |
|
98 | debugsub | |
|
99 | debugsuccessorssets | |||
99 | debugwalk |
|
100 | debugwalk | |
100 | debugwireargs |
|
101 | debugwireargs | |
101 |
|
102 | |||
@@ -246,6 +247,7 b' Show all commands + options' | |||||
246 | debugsetparents: |
|
247 | debugsetparents: | |
247 | debugstate: nodates, datesort |
|
248 | debugstate: nodates, datesort | |
248 | debugsub: rev |
|
249 | debugsub: rev | |
|
250 | debugsuccessorssets: | |||
249 | debugwalk: include, exclude |
|
251 | debugwalk: include, exclude | |
250 | debugwireargs: three, four, five, ssh, remotecmd, insecure |
|
252 | debugwireargs: three, four, five, ssh, remotecmd, insecure | |
251 | graft: rev, continue, edit, log, currentdate, currentuser, date, user, tool, dry-run |
|
253 | graft: rev, continue, edit, log, currentdate, currentuser, date, user, tool, dry-run |
General Comments 0
You need to be logged in to leave comments.
Login now