11 from . import ( |
11 from . import ( |
12 error, |
12 error, |
13 hg, |
13 hg, |
14 localrepo, |
14 localrepo, |
15 pycompat, |
15 pycompat, |
16 requirements, |
|
17 util, |
|
18 ) |
16 ) |
19 |
17 |
20 from .upgrade_utils import ( |
18 from .upgrade_utils import ( |
|
19 actions as upgrade_actions, |
21 engine as upgrade_engine, |
20 engine as upgrade_engine, |
22 ) |
21 ) |
23 |
22 |
24 from .utils import compression |
23 allformatvariant = upgrade_actions.allformatvariant |
25 |
|
26 # list of requirements that request a clone of all revlog if added/removed |
|
27 RECLONES_REQUIREMENTS = { |
|
28 b'generaldelta', |
|
29 requirements.SPARSEREVLOG_REQUIREMENT, |
|
30 } |
|
31 |
|
32 |
|
33 def requiredsourcerequirements(repo): |
|
34 """Obtain requirements required to be present to upgrade a repo. |
|
35 |
|
36 An upgrade will not be allowed if the repository doesn't have the |
|
37 requirements returned by this function. |
|
38 """ |
|
39 return { |
|
40 # Introduced in Mercurial 0.9.2. |
|
41 b'revlogv1', |
|
42 # Introduced in Mercurial 0.9.2. |
|
43 b'store', |
|
44 } |
|
45 |
|
46 |
|
47 def blocksourcerequirements(repo): |
|
48 """Obtain requirements that will prevent an upgrade from occurring. |
|
49 |
|
50 An upgrade cannot be performed if the source repository contains a |
|
51 requirements in the returned set. |
|
52 """ |
|
53 return { |
|
54 # The upgrade code does not yet support these experimental features. |
|
55 # This is an artificial limitation. |
|
56 requirements.TREEMANIFEST_REQUIREMENT, |
|
57 # This was a precursor to generaldelta and was never enabled by default. |
|
58 # It should (hopefully) not exist in the wild. |
|
59 b'parentdelta', |
|
60 # Upgrade should operate on the actual store, not the shared link. |
|
61 requirements.SHARED_REQUIREMENT, |
|
62 } |
|
63 |
|
64 |
|
65 def supportremovedrequirements(repo): |
|
66 """Obtain requirements that can be removed during an upgrade. |
|
67 |
|
68 If an upgrade were to create a repository that dropped a requirement, |
|
69 the dropped requirement must appear in the returned set for the upgrade |
|
70 to be allowed. |
|
71 """ |
|
72 supported = { |
|
73 requirements.SPARSEREVLOG_REQUIREMENT, |
|
74 requirements.SIDEDATA_REQUIREMENT, |
|
75 requirements.COPIESSDC_REQUIREMENT, |
|
76 requirements.NODEMAP_REQUIREMENT, |
|
77 requirements.SHARESAFE_REQUIREMENT, |
|
78 } |
|
79 for name in compression.compengines: |
|
80 engine = compression.compengines[name] |
|
81 if engine.available() and engine.revlogheader(): |
|
82 supported.add(b'exp-compression-%s' % name) |
|
83 if engine.name() == b'zstd': |
|
84 supported.add(b'revlog-compression-zstd') |
|
85 return supported |
|
86 |
|
87 |
|
88 def supporteddestrequirements(repo): |
|
89 """Obtain requirements that upgrade supports in the destination. |
|
90 |
|
91 If the result of the upgrade would create requirements not in this set, |
|
92 the upgrade is disallowed. |
|
93 |
|
94 Extensions should monkeypatch this to add their custom requirements. |
|
95 """ |
|
96 supported = { |
|
97 b'dotencode', |
|
98 b'fncache', |
|
99 b'generaldelta', |
|
100 b'revlogv1', |
|
101 b'store', |
|
102 requirements.SPARSEREVLOG_REQUIREMENT, |
|
103 requirements.SIDEDATA_REQUIREMENT, |
|
104 requirements.COPIESSDC_REQUIREMENT, |
|
105 requirements.NODEMAP_REQUIREMENT, |
|
106 requirements.SHARESAFE_REQUIREMENT, |
|
107 } |
|
108 for name in compression.compengines: |
|
109 engine = compression.compengines[name] |
|
110 if engine.available() and engine.revlogheader(): |
|
111 supported.add(b'exp-compression-%s' % name) |
|
112 if engine.name() == b'zstd': |
|
113 supported.add(b'revlog-compression-zstd') |
|
114 return supported |
|
115 |
|
116 |
|
117 def allowednewrequirements(repo): |
|
118 """Obtain requirements that can be added to a repository during upgrade. |
|
119 |
|
120 This is used to disallow proposed requirements from being added when |
|
121 they weren't present before. |
|
122 |
|
123 We use a list of allowed requirement additions instead of a list of known |
|
124 bad additions because the whitelist approach is safer and will prevent |
|
125 future, unknown requirements from accidentally being added. |
|
126 """ |
|
127 supported = { |
|
128 b'dotencode', |
|
129 b'fncache', |
|
130 b'generaldelta', |
|
131 requirements.SPARSEREVLOG_REQUIREMENT, |
|
132 requirements.SIDEDATA_REQUIREMENT, |
|
133 requirements.COPIESSDC_REQUIREMENT, |
|
134 requirements.NODEMAP_REQUIREMENT, |
|
135 requirements.SHARESAFE_REQUIREMENT, |
|
136 } |
|
137 for name in compression.compengines: |
|
138 engine = compression.compengines[name] |
|
139 if engine.available() and engine.revlogheader(): |
|
140 supported.add(b'exp-compression-%s' % name) |
|
141 if engine.name() == b'zstd': |
|
142 supported.add(b'revlog-compression-zstd') |
|
143 return supported |
|
144 |
|
145 |
|
146 def preservedrequirements(repo): |
|
147 return set() |
|
148 |
|
149 |
|
150 DEFICIENCY = b'deficiency' |
|
151 OPTIMISATION = b'optimization' |
|
152 |
|
153 |
|
154 class improvement(object): |
|
155 """Represents an improvement that can be made as part of an upgrade. |
|
156 |
|
157 The following attributes are defined on each instance: |
|
158 |
|
159 name |
|
160 Machine-readable string uniquely identifying this improvement. It |
|
161 will be mapped to an action later in the upgrade process. |
|
162 |
|
163 type |
|
164 Either ``DEFICIENCY`` or ``OPTIMISATION``. A deficiency is an obvious |
|
165 problem. An optimization is an action (sometimes optional) that |
|
166 can be taken to further improve the state of the repository. |
|
167 |
|
168 description |
|
169 Message intended for humans explaining the improvement in more detail, |
|
170 including the implications of it. For ``DEFICIENCY`` types, should be |
|
171 worded in the present tense. For ``OPTIMISATION`` types, should be |
|
172 worded in the future tense. |
|
173 |
|
174 upgrademessage |
|
175 Message intended for humans explaining what an upgrade addressing this |
|
176 issue will do. Should be worded in the future tense. |
|
177 """ |
|
178 |
|
179 def __init__(self, name, type, description, upgrademessage): |
|
180 self.name = name |
|
181 self.type = type |
|
182 self.description = description |
|
183 self.upgrademessage = upgrademessage |
|
184 |
|
185 def __eq__(self, other): |
|
186 if not isinstance(other, improvement): |
|
187 # This is what python tell use to do |
|
188 return NotImplemented |
|
189 return self.name == other.name |
|
190 |
|
191 def __ne__(self, other): |
|
192 return not (self == other) |
|
193 |
|
194 def __hash__(self): |
|
195 return hash(self.name) |
|
196 |
|
197 |
|
198 allformatvariant = [] |
|
199 |
|
200 |
|
201 def registerformatvariant(cls): |
|
202 allformatvariant.append(cls) |
|
203 return cls |
|
204 |
|
205 |
|
206 class formatvariant(improvement): |
|
207 """an improvement subclass dedicated to repository format""" |
|
208 |
|
209 type = DEFICIENCY |
|
210 ### The following attributes should be defined for each class: |
|
211 |
|
212 # machine-readable string uniquely identifying this improvement. it will be |
|
213 # mapped to an action later in the upgrade process. |
|
214 name = None |
|
215 |
|
216 # message intended for humans explaining the improvement in more detail, |
|
217 # including the implications of it ``DEFICIENCY`` types, should be worded |
|
218 # in the present tense. |
|
219 description = None |
|
220 |
|
221 # message intended for humans explaining what an upgrade addressing this |
|
222 # issue will do. should be worded in the future tense. |
|
223 upgrademessage = None |
|
224 |
|
225 # value of current Mercurial default for new repository |
|
226 default = None |
|
227 |
|
228 def __init__(self): |
|
229 raise NotImplementedError() |
|
230 |
|
231 @staticmethod |
|
232 def fromrepo(repo): |
|
233 """current value of the variant in the repository""" |
|
234 raise NotImplementedError() |
|
235 |
|
236 @staticmethod |
|
237 def fromconfig(repo): |
|
238 """current value of the variant in the configuration""" |
|
239 raise NotImplementedError() |
|
240 |
|
241 |
|
242 class requirementformatvariant(formatvariant): |
|
243 """formatvariant based on a 'requirement' name. |
|
244 |
|
245 Many format variant are controlled by a 'requirement'. We define a small |
|
246 subclass to factor the code. |
|
247 """ |
|
248 |
|
249 # the requirement that control this format variant |
|
250 _requirement = None |
|
251 |
|
252 @staticmethod |
|
253 def _newreporequirements(ui): |
|
254 return localrepo.newreporequirements( |
|
255 ui, localrepo.defaultcreateopts(ui) |
|
256 ) |
|
257 |
|
258 @classmethod |
|
259 def fromrepo(cls, repo): |
|
260 assert cls._requirement is not None |
|
261 return cls._requirement in repo.requirements |
|
262 |
|
263 @classmethod |
|
264 def fromconfig(cls, repo): |
|
265 assert cls._requirement is not None |
|
266 return cls._requirement in cls._newreporequirements(repo.ui) |
|
267 |
|
268 |
|
269 @registerformatvariant |
|
270 class fncache(requirementformatvariant): |
|
271 name = b'fncache' |
|
272 |
|
273 _requirement = b'fncache' |
|
274 |
|
275 default = True |
|
276 |
|
277 description = _( |
|
278 b'long and reserved filenames may not work correctly; ' |
|
279 b'repository performance is sub-optimal' |
|
280 ) |
|
281 |
|
282 upgrademessage = _( |
|
283 b'repository will be more resilient to storing ' |
|
284 b'certain paths and performance of certain ' |
|
285 b'operations should be improved' |
|
286 ) |
|
287 |
|
288 |
|
289 @registerformatvariant |
|
290 class dotencode(requirementformatvariant): |
|
291 name = b'dotencode' |
|
292 |
|
293 _requirement = b'dotencode' |
|
294 |
|
295 default = True |
|
296 |
|
297 description = _( |
|
298 b'storage of filenames beginning with a period or ' |
|
299 b'space may not work correctly' |
|
300 ) |
|
301 |
|
302 upgrademessage = _( |
|
303 b'repository will be better able to store files ' |
|
304 b'beginning with a space or period' |
|
305 ) |
|
306 |
|
307 |
|
308 @registerformatvariant |
|
309 class generaldelta(requirementformatvariant): |
|
310 name = b'generaldelta' |
|
311 |
|
312 _requirement = b'generaldelta' |
|
313 |
|
314 default = True |
|
315 |
|
316 description = _( |
|
317 b'deltas within internal storage are unable to ' |
|
318 b'choose optimal revisions; repository is larger and ' |
|
319 b'slower than it could be; interaction with other ' |
|
320 b'repositories may require extra network and CPU ' |
|
321 b'resources, making "hg push" and "hg pull" slower' |
|
322 ) |
|
323 |
|
324 upgrademessage = _( |
|
325 b'repository storage will be able to create ' |
|
326 b'optimal deltas; new repository data will be ' |
|
327 b'smaller and read times should decrease; ' |
|
328 b'interacting with other repositories using this ' |
|
329 b'storage model should require less network and ' |
|
330 b'CPU resources, making "hg push" and "hg pull" ' |
|
331 b'faster' |
|
332 ) |
|
333 |
|
334 |
|
335 @registerformatvariant |
|
336 class sharedsafe(requirementformatvariant): |
|
337 name = b'exp-sharesafe' |
|
338 _requirement = requirements.SHARESAFE_REQUIREMENT |
|
339 |
|
340 default = False |
|
341 |
|
342 description = _( |
|
343 b'old shared repositories do not share source repository ' |
|
344 b'requirements and config. This leads to various problems ' |
|
345 b'when the source repository format is upgraded or some new ' |
|
346 b'extensions are enabled.' |
|
347 ) |
|
348 |
|
349 upgrademessage = _( |
|
350 b'Upgrades a repository to share-safe format so that future ' |
|
351 b'shares of this repository share its requirements and configs.' |
|
352 ) |
|
353 |
|
354 |
|
355 @registerformatvariant |
|
356 class sparserevlog(requirementformatvariant): |
|
357 name = b'sparserevlog' |
|
358 |
|
359 _requirement = requirements.SPARSEREVLOG_REQUIREMENT |
|
360 |
|
361 default = True |
|
362 |
|
363 description = _( |
|
364 b'in order to limit disk reading and memory usage on older ' |
|
365 b'version, the span of a delta chain from its root to its ' |
|
366 b'end is limited, whatever the relevant data in this span. ' |
|
367 b'This can severly limit Mercurial ability to build good ' |
|
368 b'chain of delta resulting is much more storage space being ' |
|
369 b'taken and limit reusability of on disk delta during ' |
|
370 b'exchange.' |
|
371 ) |
|
372 |
|
373 upgrademessage = _( |
|
374 b'Revlog supports delta chain with more unused data ' |
|
375 b'between payload. These gaps will be skipped at read ' |
|
376 b'time. This allows for better delta chains, making a ' |
|
377 b'better compression and faster exchange with server.' |
|
378 ) |
|
379 |
|
380 |
|
381 @registerformatvariant |
|
382 class sidedata(requirementformatvariant): |
|
383 name = b'sidedata' |
|
384 |
|
385 _requirement = requirements.SIDEDATA_REQUIREMENT |
|
386 |
|
387 default = False |
|
388 |
|
389 description = _( |
|
390 b'Allows storage of extra data alongside a revision, ' |
|
391 b'unlocking various caching options.' |
|
392 ) |
|
393 |
|
394 upgrademessage = _(b'Allows storage of extra data alongside a revision.') |
|
395 |
|
396 |
|
397 @registerformatvariant |
|
398 class persistentnodemap(requirementformatvariant): |
|
399 name = b'persistent-nodemap' |
|
400 |
|
401 _requirement = requirements.NODEMAP_REQUIREMENT |
|
402 |
|
403 default = False |
|
404 |
|
405 description = _( |
|
406 b'persist the node -> rev mapping on disk to speedup lookup' |
|
407 ) |
|
408 |
|
409 upgrademessage = _(b'Speedup revision lookup by node id.') |
|
410 |
|
411 |
|
412 @registerformatvariant |
|
413 class copiessdc(requirementformatvariant): |
|
414 name = b'copies-sdc' |
|
415 |
|
416 _requirement = requirements.COPIESSDC_REQUIREMENT |
|
417 |
|
418 default = False |
|
419 |
|
420 description = _(b'Stores copies information alongside changesets.') |
|
421 |
|
422 upgrademessage = _( |
|
423 b'Allows to use more efficient algorithm to deal with ' b'copy tracing.' |
|
424 ) |
|
425 |
|
426 |
|
427 @registerformatvariant |
|
428 class removecldeltachain(formatvariant): |
|
429 name = b'plain-cl-delta' |
|
430 |
|
431 default = True |
|
432 |
|
433 description = _( |
|
434 b'changelog storage is using deltas instead of ' |
|
435 b'raw entries; changelog reading and any ' |
|
436 b'operation relying on changelog data are slower ' |
|
437 b'than they could be' |
|
438 ) |
|
439 |
|
440 upgrademessage = _( |
|
441 b'changelog storage will be reformated to ' |
|
442 b'store raw entries; changelog reading will be ' |
|
443 b'faster; changelog size may be reduced' |
|
444 ) |
|
445 |
|
446 @staticmethod |
|
447 def fromrepo(repo): |
|
448 # Mercurial 4.0 changed changelogs to not use delta chains. Search for |
|
449 # changelogs with deltas. |
|
450 cl = repo.changelog |
|
451 chainbase = cl.chainbase |
|
452 return all(rev == chainbase(rev) for rev in cl) |
|
453 |
|
454 @staticmethod |
|
455 def fromconfig(repo): |
|
456 return True |
|
457 |
|
458 |
|
459 @registerformatvariant |
|
460 class compressionengine(formatvariant): |
|
461 name = b'compression' |
|
462 default = b'zlib' |
|
463 |
|
464 description = _( |
|
465 b'Compresion algorithm used to compress data. ' |
|
466 b'Some engine are faster than other' |
|
467 ) |
|
468 |
|
469 upgrademessage = _( |
|
470 b'revlog content will be recompressed with the new algorithm.' |
|
471 ) |
|
472 |
|
473 @classmethod |
|
474 def fromrepo(cls, repo): |
|
475 # we allow multiple compression engine requirement to co-exist because |
|
476 # strickly speaking, revlog seems to support mixed compression style. |
|
477 # |
|
478 # The compression used for new entries will be "the last one" |
|
479 compression = b'zlib' |
|
480 for req in repo.requirements: |
|
481 prefix = req.startswith |
|
482 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'): |
|
483 compression = req.split(b'-', 2)[2] |
|
484 return compression |
|
485 |
|
486 @classmethod |
|
487 def fromconfig(cls, repo): |
|
488 compengines = repo.ui.configlist(b'format', b'revlog-compression') |
|
489 # return the first valid value as the selection code would do |
|
490 for comp in compengines: |
|
491 if comp in util.compengines: |
|
492 return comp |
|
493 |
|
494 # no valide compression found lets display it all for clarity |
|
495 return b','.join(compengines) |
|
496 |
|
497 |
|
498 @registerformatvariant |
|
499 class compressionlevel(formatvariant): |
|
500 name = b'compression-level' |
|
501 default = b'default' |
|
502 |
|
503 description = _(b'compression level') |
|
504 |
|
505 upgrademessage = _(b'revlog content will be recompressed') |
|
506 |
|
507 @classmethod |
|
508 def fromrepo(cls, repo): |
|
509 comp = compressionengine.fromrepo(repo) |
|
510 level = None |
|
511 if comp == b'zlib': |
|
512 level = repo.ui.configint(b'storage', b'revlog.zlib.level') |
|
513 elif comp == b'zstd': |
|
514 level = repo.ui.configint(b'storage', b'revlog.zstd.level') |
|
515 if level is None: |
|
516 return b'default' |
|
517 return bytes(level) |
|
518 |
|
519 @classmethod |
|
520 def fromconfig(cls, repo): |
|
521 comp = compressionengine.fromconfig(repo) |
|
522 level = None |
|
523 if comp == b'zlib': |
|
524 level = repo.ui.configint(b'storage', b'revlog.zlib.level') |
|
525 elif comp == b'zstd': |
|
526 level = repo.ui.configint(b'storage', b'revlog.zstd.level') |
|
527 if level is None: |
|
528 return b'default' |
|
529 return bytes(level) |
|
530 |
|
531 |
|
532 def finddeficiencies(repo): |
|
533 """returns a list of deficiencies that the repo suffer from""" |
|
534 deficiencies = [] |
|
535 |
|
536 # We could detect lack of revlogv1 and store here, but they were added |
|
537 # in 0.9.2 and we don't support upgrading repos without these |
|
538 # requirements, so let's not bother. |
|
539 |
|
540 for fv in allformatvariant: |
|
541 if not fv.fromrepo(repo): |
|
542 deficiencies.append(fv) |
|
543 |
|
544 return deficiencies |
|
545 |
|
546 |
24 |
547 # search without '-' to support older form on newer client. |
25 # search without '-' to support older form on newer client. |
548 # |
26 # |
549 # We don't enforce backward compatibility for debug command so this |
27 # We don't enforce backward compatibility for debug command so this |
550 # might eventually be dropped. However, having to use two different |
28 # might eventually be dropped. However, having to use two different |
554 b'redeltaparent': b're-delta-parent', |
32 b'redeltaparent': b're-delta-parent', |
555 b'redeltamultibase': b're-delta-multibase', |
33 b'redeltamultibase': b're-delta-multibase', |
556 b'redeltaall': b're-delta-all', |
34 b'redeltaall': b're-delta-all', |
557 b'redeltafulladd': b're-delta-fulladd', |
35 b'redeltafulladd': b're-delta-fulladd', |
558 } |
36 } |
559 |
|
560 ALL_OPTIMISATIONS = [] |
|
561 |
|
562 |
|
563 def register_optimization(obj): |
|
564 ALL_OPTIMISATIONS.append(obj) |
|
565 return obj |
|
566 |
|
567 |
|
568 register_optimization( |
|
569 improvement( |
|
570 name=b're-delta-parent', |
|
571 type=OPTIMISATION, |
|
572 description=_( |
|
573 b'deltas within internal storage will be recalculated to ' |
|
574 b'choose an optimal base revision where this was not ' |
|
575 b'already done; the size of the repository may shrink and ' |
|
576 b'various operations may become faster; the first time ' |
|
577 b'this optimization is performed could slow down upgrade ' |
|
578 b'execution considerably; subsequent invocations should ' |
|
579 b'not run noticeably slower' |
|
580 ), |
|
581 upgrademessage=_( |
|
582 b'deltas within internal storage will choose a new ' |
|
583 b'base revision if needed' |
|
584 ), |
|
585 ) |
|
586 ) |
|
587 |
|
588 register_optimization( |
|
589 improvement( |
|
590 name=b're-delta-multibase', |
|
591 type=OPTIMISATION, |
|
592 description=_( |
|
593 b'deltas within internal storage will be recalculated ' |
|
594 b'against multiple base revision and the smallest ' |
|
595 b'difference will be used; the size of the repository may ' |
|
596 b'shrink significantly when there are many merges; this ' |
|
597 b'optimization will slow down execution in proportion to ' |
|
598 b'the number of merges in the repository and the amount ' |
|
599 b'of files in the repository; this slow down should not ' |
|
600 b'be significant unless there are tens of thousands of ' |
|
601 b'files and thousands of merges' |
|
602 ), |
|
603 upgrademessage=_( |
|
604 b'deltas within internal storage will choose an ' |
|
605 b'optimal delta by computing deltas against multiple ' |
|
606 b'parents; may slow down execution time ' |
|
607 b'significantly' |
|
608 ), |
|
609 ) |
|
610 ) |
|
611 |
|
612 register_optimization( |
|
613 improvement( |
|
614 name=b're-delta-all', |
|
615 type=OPTIMISATION, |
|
616 description=_( |
|
617 b'deltas within internal storage will always be ' |
|
618 b'recalculated without reusing prior deltas; this will ' |
|
619 b'likely make execution run several times slower; this ' |
|
620 b'optimization is typically not needed' |
|
621 ), |
|
622 upgrademessage=_( |
|
623 b'deltas within internal storage will be fully ' |
|
624 b'recomputed; this will likely drastically slow down ' |
|
625 b'execution time' |
|
626 ), |
|
627 ) |
|
628 ) |
|
629 |
|
630 register_optimization( |
|
631 improvement( |
|
632 name=b're-delta-fulladd', |
|
633 type=OPTIMISATION, |
|
634 description=_( |
|
635 b'every revision will be re-added as if it was new ' |
|
636 b'content. It will go through the full storage ' |
|
637 b'mechanism giving extensions a chance to process it ' |
|
638 b'(eg. lfs). This is similar to "re-delta-all" but even ' |
|
639 b'slower since more logic is involved.' |
|
640 ), |
|
641 upgrademessage=_( |
|
642 b'each revision will be added as new content to the ' |
|
643 b'internal storage; this will likely drastically slow ' |
|
644 b'down execution time, but some extensions might need ' |
|
645 b'it' |
|
646 ), |
|
647 ) |
|
648 ) |
|
649 |
|
650 |
|
651 def findoptimizations(repo): |
|
652 """Determine optimisation that could be used during upgrade""" |
|
653 # These are unconditionally added. There is logic later that figures out |
|
654 # which ones to apply. |
|
655 return list(ALL_OPTIMISATIONS) |
|
656 |
|
657 |
|
658 def determineactions(repo, deficiencies, sourcereqs, destreqs): |
|
659 """Determine upgrade actions that will be performed. |
|
660 |
|
661 Given a list of improvements as returned by ``finddeficiencies`` and |
|
662 ``findoptimizations``, determine the list of upgrade actions that |
|
663 will be performed. |
|
664 |
|
665 The role of this function is to filter improvements if needed, apply |
|
666 recommended optimizations from the improvements list that make sense, |
|
667 etc. |
|
668 |
|
669 Returns a list of action names. |
|
670 """ |
|
671 newactions = [] |
|
672 |
|
673 for d in deficiencies: |
|
674 name = d._requirement |
|
675 |
|
676 # If the action is a requirement that doesn't show up in the |
|
677 # destination requirements, prune the action. |
|
678 if name is not None and name not in destreqs: |
|
679 continue |
|
680 |
|
681 newactions.append(d) |
|
682 |
|
683 # FUTURE consider adding some optimizations here for certain transitions. |
|
684 # e.g. adding generaldelta could schedule parent redeltas. |
|
685 |
|
686 return newactions |
|
687 |
37 |
688 |
38 |
689 def upgraderepo( |
39 def upgraderepo( |
690 ui, |
40 ui, |
691 repo, |
41 repo, |