##// END OF EJS Templates
sparse-revlog: enabled by default...
sparse-revlog: enabled by default The feature provides large benefits. It now seems mature enough to be enabled by default. * It solves catastrophic issues regarding delta storage in revlog, * It allows for shorter delta chain in all repositories, improving performances. Running benchmark of a wide range of operation did not reveal problematic impact. Performance gains are observed where expected. The format is supported by Mercurial version 4.7. So it seems safe to enable it by default now. Here is a reminder of key numbers regarding this delta strategy effect on repository size and performance. Effect on Size: =============== For repositories with a lot of branches, sparse-revlog significantly improve size, fixing limitation associated with the span of a delta chain. In addition, sparse-revlog, deal well with limitations of the delta chain length. For large repositories, this allows for a stiff reduction of the delta chain without a problematic impact on the repository size. This delta chain length improvement helps all repositories, not just the ones with many branches. As a reminder, here are the default chain limits for each "format": * no-sparse: none * sparse: 1000 Mercurial --------- Manifest Size: limit | none | 1000 ------------|-------------|------------ no-sparse | 6 143 044 | 6 269 496 sparse | 5 798 796 | 5 827 025 Manifest Chain length data limit || none || 1000 value || average | max || average | max ------------||---------|---------||---------|--------- no-sparse || 429 | 1 397 || 397 | 1 000 sparse || 326 | 1 290 || 313 | 1 000 Full Store Size limit | none | 1000 ------------|-------------|------------ no-sparse | 46 944 775 | 47 166 129 sparse | 46 622 445 | 46 723 774 pypy ---- Manifest Size: limit | none | 1000 ------------|-------------|------------ no-sparse | 52 941 760 | 56 200 970 sparse | 26 348 229 | 27 384 133 Manifest Chain length data limit || none || 1000 value || average | max || average | max ------------||---------|---------||---------|--------- no-sparse || 769 | 3 889 || 390 | 1 000 sparse || 1 223 | 3 846 || 495 | 1 000 Full Store Size limit | none | 1000 ------------|-------------|------------ no-sparse | 336 050 203 | 339 309 413 sparse | 338 673 985 | 339 709 889 Mozilla ------- Manifest Size: limit | none | 1000 ------------|----------------|--------------- no-sparse | 215 096 339 | 1 708 853 525 sparse | 188 947 271 | 278 894 170 Manifest Chain length data limit || none || 1000 value || average | max || average | max ------------||---------|---------||---------|-------- no-sparse || 20 454 | 59 562 || 491 | 1 000 sparse || 23 509 | 69 891 || 489 | 1 000 Full Store Size limit | none | 1000 ------------|----------------|--------------- no-sparse | 2 377 578 715 | 3 876 258 798 sparse | 2 441 677 137 | 2 535 997 381 Netbeans -------- Manifest Size: limit | none | 1000 ------------|----------------|--------------- no-sparse | 130 088 982 | 741 590 565 sparse | 118 836 887 | 159 161 207 Manifest Chain length data limit || none || 1000 value || average | max || average | max ------------||---------|---------||---------|--------- no-sparse || 19 321 | 61 397 || 510 | 1 000 sparse || 21 240 | 61 583 || 503 | 1 000 Full Store Size limit | none | 1000 ------------|----------------|--------------- no-sparse | 1 160 013 008 | 1 771 514 591 sparse | 1 164 959 988 | 1 205 284 308 Private repo #1 --------------- Manifest Size: limit | none | 1000 ------------|-----------------|--------------- no-sparse | 33 725 285 081 | 33 724 834 190 sparse | 350 542 420 | 423 470 579 Manifest Chain length data limit || none || 1000 value || average | max || average | max ------------||---------|---------||---------|--------- no-sparse || 282 | 8 885 || 113 | 1 000 snapshot || 3 655 | 8 951 || 530 | 1 000 Full Store Size limit | none | 1000 ------------|----------------|--------------- no-sparse | 41 544 149 652 | 41 543 698 761 sparse | 8 448 037 300 | 8 520 965 459 Effect on speed: ================ Performances are strongly impacted by the delta chain length. Longer chain results in slower revision restoration. For this reason, the 1000 chain limit introduced by sparse-revlog helps repository with previously large chains a lot. In our corpus, this means `netbeans` and `mozilla-central` who suffered from unreasonable manifest delta chain length. Another way sparse revlog helps, is by producing better delta's. For repositories with many branches, the pathological patterns that resulted in many sub-optimal deltas are gone. Smaller delta help with operations where deltas are directly relevant, like bundle. However, the sparse-revlog logic introduces some extra processing and a more throughout testing of possible delta candidates. Adding an extra cost in some cases. This cost is usually counterbalanced by the other performance gain. However, for smaller repositories not affected by delta chain length issues or branching related issues, this might make things a bit slower. However, these are also repository where revlog performance is dwarfed by other costs. Below are the summary of some timing from the performance test suite running at `http://perf.octobus.net/` for a handful of key commands and operation. It is important to keep in mind that most of this command works on the tip part of the repository. The non-sparse and sparse version produce different delta chains and the tip revision can end up at an arbitrary point of these chains. This will impact some performance number listed in this summary. For the record: here is the delta chain length for the tip revision of manifest log in the benchmarked repository: | no-sparse | sparse | mercurial | 94 | 904 | pypy | 23 | 673 | netbeans | 4158 | 258 | mozilla | 63263 | 781 | As you can see, the chain length for mercurial and pypy turn out to be significantly longer. The netbeans and mozilla one get shorter because these repositories benefit from the maximum chain length. Timing for `hg commit`: ----------------------- The time taken by `hg commit` does not varies significantly, no drawback for using sparse here. | no-sparse | sparse | mercurial | 68.1ms | 66.7ms | pypy | 95.0ms | 94.1ms | netbeans | 614.0ms | 611.0ms | mozilla | 1340.0ms | 1.320.0ms | Check the final section for statistics on a wider array of write. Timing for bundling 10 000 changesets ------------------------------------- The repository that benefits from better delta see a good performance boost. The other ones are not significantly affected. | no-sparse | sparse | mercurial | 3.1s | 3.0s | pypy | 25.1s | 7.5s | netbeans | 24.2s | 17.0s | mozilla | 23.7s | 25.0s | Timing for unbundling 1 000 changesets -------------------------------------- Mercurial and mozilla are unaffected. The pypy repository benefit well from the better delta. However, the netbeans repository takes a visible hit. Digging that difference reveals that it comes from the sparse-revlog bundle having to deal with a snapshot that was re-encoded in the bundle. The slow path for adding new a revision had to be triggered for it, slowing things down. The Sparse versions do not have such snapshot to handle similar cases in the tested configuration. | no-sparse | sparse | mercurial | 519ms | 502ms | pypy | 1.270ms | 886ms | netbeans | 1.370ms | 2.250ms | mozilla | 3.230ms | 3.210ms | Netbeans benefits from the better deltas in other dimensions too. For example, the produced bundle is significantly smaller: * netbeans-no-sparse.hg: 2.3MB * netbeans-sparse.hg: 1.9MB Timing to restore the tip most manifest entry: ---------------------------------------------- Nothing surprising here. The timing for mercurial and pypy are within a small range where they won't affect performance much. In our tested case, they are slower as they use a longer chain. Timing for netbeans and mozilla improves a lot. Removing a significant amount of time. | no-sparse | sparse | mercurial | 1.09ms | 3.15ms | pypy | 4.11ms | 10.70ms | netbeans | 239.00ms | 112.00ms | mozilla | 688.00ms | 198.00ms | Reading 100 revision in descending order: ----------------------------------------- We see the same kind of effect when reading the last 100 revisions. Large boost for netbeans and mozilla, as they use much smaller delta chain. Mercurial and pypy longer chain means slower reads, but nothing gets out of control. | no-sparse | sparse | mercurial | 0.089s | 0.268s | pypy | 0.259s | 0.698s | netbeans | 125.000s | 20.600s | mozilla | 23.000s | 11.400s | Writing from full text: statistic for the last 30K revisions ------------------------------------------------------------ This benchmark adds revisions to revlog from their full text. This is similar to the work done during a commit, but for a large amount of revisions so that we get a more relevant view. We see better overall performances with sparse-revlog. The very worst case is usually slower with sparse-revlog, but does not gets out of control. For the vast majorities of the other writes, sparse-revlog is significantly faster for larger repositories. This is reflected in the accumulated rewrite time for netbeans and mozilla. The notable exception is the pypy repository where things get slower. The extra processing is not balanced by shorter delta chain. However, this is to be seen as a blocking issue. First, the overall time spend dealing with revlog for the repository pypy size is small compared to the other costs, so we get slower on operations that matter less than for other larger repository. Second, we still get nice size benefit from using sparse-revlog, smaller repo size brings other usability and speed benefit (eg: bundle size). max time | no-sparse | sparse | mercurial | 0.010143s | 0.045280s | pypy | 0.034924s | 0.243288s | netbeans | 0.605371s | 2.130876s | mozilla | 1.478342s | 3.424541s | 99% time | no-sparse | sparse | mercurial | 0.003774s | 0.003758s | pypy | 0.017387s | 0.025310s | netbeans | 0.576913s | 0.271195s | mozilla | 1.478342s | 0.449661s | 95% time | no-sparse | sparse | mercurial | 0.002069s | 0.002120s | pypy | 0.010141s | 0.014797s | netbeans | 0.540202s | 0.258644s | mozilla | 0.654830s | 0.243440s | full time | no-sparse | sparse | mercurial | 14.15s | 14.87s | pypy | 90.50s | 137.12s | netbeans | 6401.06s | 3411.14s | mozilla | 3086.89s | 1991.97s | Differential Revision: https://phab.mercurial-scm.org/D5345

File last commit:

r40954:3764330f default
r40954:3764330f default
Show More
upgrade.py
897 lines | 32.5 KiB | text/x-python | PythonLexer
Pierre-Yves David
upgrade: update the header comment
r31894 # upgrade.py - functions for in place upgrade of Mercurial repository
Pierre-Yves David
upgrade: extract code in its own module...
r31864 #
Pierre-Yves David
upgrade: update the copyright statement
r31895 # Copyright (c) 2016-present, Gregory Szorc
Pierre-Yves David
upgrade: extract code in its own module...
r31864 #
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from __future__ import absolute_import
import stat
from .i18n import _
from . import (
changelog,
error,
Boris Feld
upgrade: use actual filelog to convert filelog...
r35345 filelog,
Boris Feld
upgrade: more standard creation of the temporary repository...
r35344 hg,
Pierre-Yves David
upgrade: import 'localrepo' globally...
r31893 localrepo,
Pierre-Yves David
upgrade: extract code in its own module...
r31864 manifest,
Yuya Nishihara
py3: wrap tempfile.mkdtemp() to use bytes path...
r38183 pycompat,
Pierre-Yves David
upgrade: extract code in its own module...
r31864 revlog,
scmutil,
util,
vfs as vfsmod,
)
Pierre-Yves David
upgrade: drop the prefix to the 'requiredsourcerequirements' function...
r31865 def requiredsourcerequirements(repo):
Pierre-Yves David
upgrade: extract code in its own module...
r31864 """Obtain requirements required to be present to upgrade a repo.
An upgrade will not be allowed if the repository doesn't have the
requirements returned by this function.
"""
Martin von Zweigbergk
cleanup: use set literals...
r32291 return {
Pierre-Yves David
upgrade: extract code in its own module...
r31864 # Introduced in Mercurial 0.9.2.
'revlogv1',
# Introduced in Mercurial 0.9.2.
'store',
Martin von Zweigbergk
cleanup: use set literals...
r32291 }
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Pierre-Yves David
upgrade: drop the prefix to the 'blocksourcerequirements' function...
r31866 def blocksourcerequirements(repo):
Pierre-Yves David
upgrade: extract code in its own module...
r31864 """Obtain requirements that will prevent an upgrade from occurring.
An upgrade cannot be performed if the source repository contains a
requirements in the returned set.
"""
Martin von Zweigbergk
cleanup: use set literals...
r32291 return {
Pierre-Yves David
upgrade: extract code in its own module...
r31864 # The upgrade code does not yet support these experimental features.
# This is an artificial limitation.
'treemanifest',
# This was a precursor to generaldelta and was never enabled by default.
# It should (hopefully) not exist in the wild.
'parentdelta',
# Upgrade should operate on the actual store, not the shared link.
'shared',
Martin von Zweigbergk
cleanup: use set literals...
r32291 }
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Pierre-Yves David
upgrade: drop the prefix to the 'supportremovedrequirements' function...
r31867 def supportremovedrequirements(repo):
Pierre-Yves David
upgrade: extract code in its own module...
r31864 """Obtain requirements that can be removed during an upgrade.
If an upgrade were to create a repository that dropped a requirement,
the dropped requirement must appear in the returned set for the upgrade
to be allowed.
"""
Paul Morelle
upgrade: enable adding or removing sparse-revlog requirement
r38742 return {
localrepo.SPARSEREVLOG_REQUIREMENT,
}
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Pierre-Yves David
upgrade: drop the prefix to the 'supporteddestrequirements' function...
r31870 def supporteddestrequirements(repo):
Pierre-Yves David
upgrade: extract code in its own module...
r31864 """Obtain requirements that upgrade supports in the destination.
If the result of the upgrade would create requirements not in this set,
the upgrade is disallowed.
Extensions should monkeypatch this to add their custom requirements.
"""
Martin von Zweigbergk
cleanup: use set literals...
r32291 return {
Pierre-Yves David
upgrade: extract code in its own module...
r31864 'dotencode',
'fncache',
'generaldelta',
'revlogv1',
'store',
Paul Morelle
upgrade: enable adding or removing sparse-revlog requirement
r38742 localrepo.SPARSEREVLOG_REQUIREMENT,
Martin von Zweigbergk
cleanup: use set literals...
r32291 }
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Pierre-Yves David
upgrade: drop the prefix to the 'allowednewrequirements' function...
r31869 def allowednewrequirements(repo):
Pierre-Yves David
upgrade: extract code in its own module...
r31864 """Obtain requirements that can be added to a repository during upgrade.
This is used to disallow proposed requirements from being added when
they weren't present before.
We use a list of allowed requirement additions instead of a list of known
bad additions because the whitelist approach is safer and will prevent
future, unknown requirements from accidentally being added.
"""
Martin von Zweigbergk
cleanup: use set literals...
r32291 return {
Pierre-Yves David
upgrade: extract code in its own module...
r31864 'dotencode',
'fncache',
'generaldelta',
Paul Morelle
upgrade: enable adding or removing sparse-revlog requirement
r38742 localrepo.SPARSEREVLOG_REQUIREMENT,
Martin von Zweigbergk
cleanup: use set literals...
r32291 }
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Boris Feld
upgraderepo: allow extension to register preserved requirements...
r35303 def preservedrequirements(repo):
return set()
Pierre-Yves David
upgrade: extract code in its own module...
r31864 deficiency = 'deficiency'
optimisation = 'optimization'
Pierre-Yves David
upgrade: drop the prefix to the 'improvement' class...
r31868 class improvement(object):
Pierre-Yves David
upgrade: extract code in its own module...
r31864 """Represents an improvement that can be made as part of an upgrade.
The following attributes are defined on each instance:
name
Machine-readable string uniquely identifying this improvement. It
will be mapped to an action later in the upgrade process.
type
Either ``deficiency`` or ``optimisation``. A deficiency is an obvious
problem. An optimization is an action (sometimes optional) that
can be taken to further improve the state of the repository.
description
Message intended for humans explaining the improvement in more detail,
including the implications of it. For ``deficiency`` types, should be
worded in the present tense. For ``optimisation`` types, should be
worded in the future tense.
upgrademessage
Message intended for humans explaining what an upgrade addressing this
issue will do. Should be worded in the future tense.
"""
Pierre-Yves David
upgrade: introduce a 'formatvariant' class...
r32030 def __init__(self, name, type, description, upgrademessage):
Pierre-Yves David
upgrade: extract code in its own module...
r31864 self.name = name
self.type = type
self.description = description
self.upgrademessage = upgrademessage
Pierre-Yves David
upgrade: implement equality for 'improvement' object...
r31902 def __eq__(self, other):
if not isinstance(other, improvement):
# This is what python tell use to do
return NotImplemented
return self.name == other.name
Pierre-Yves David
upgrade: implement '__ne__' on 'improvement' class...
r32028 def __ne__(self, other):
return not self == other
Pierre-Yves David
upgrade: implement '__hash__' on 'improvement' class...
r32029 def __hash__(self):
return hash(self.name)
Pierre-Yves David
upgrade: register all format variants in a list...
r32032 allformatvariant = []
def registerformatvariant(cls):
allformatvariant.append(cls)
return cls
Pierre-Yves David
upgrade: introduce a 'formatvariant' class...
r32030 class formatvariant(improvement):
Pierre-Yves David
upgrade: move descriptions and selection logic in individual classes...
r32031 """an improvement subclass dedicated to repository format"""
type = deficiency
### The following attributes should be defined for each class:
# machine-readable string uniquely identifying this improvement. it will be
# mapped to an action later in the upgrade process.
name = None
Pierre-Yves David
upgrade: introduce a 'formatvariant' class...
r32030
Pierre-Yves David
upgrade: move descriptions and selection logic in individual classes...
r32031 # message intended for humans explaining the improvement in more detail,
# including the implications of it ``deficiency`` types, should be worded
# in the present tense.
description = None
# message intended for humans explaining what an upgrade addressing this
# issue will do. should be worded in the future tense.
upgrademessage = None
Pierre-Yves David
upgrade: introduce a 'formatvariant' class...
r32030
Pierre-Yves David
upgrade: move descriptions and selection logic in individual classes...
r32031 # value of current Mercurial default for new repository
default = None
def __init__(self):
raise NotImplementedError()
@staticmethod
def fromrepo(repo):
"""current value of the variant in the repository"""
raise NotImplementedError()
Pierre-Yves David
upgrade: introduce a 'formatvariant' class...
r32030
Pierre-Yves David
upgrade: move descriptions and selection logic in individual classes...
r32031 @staticmethod
def fromconfig(repo):
"""current value of the variant in the configuration"""
raise NotImplementedError()
class requirementformatvariant(formatvariant):
"""formatvariant based on a 'requirement' name.
Many format variant are controlled by a 'requirement'. We define a small
subclass to factor the code.
Pierre-Yves David
upgrade: introduce a 'formatvariant' class...
r32030 """
Pierre-Yves David
upgrade: move descriptions and selection logic in individual classes...
r32031 # the requirement that control this format variant
_requirement = None
@staticmethod
Gregory Szorc
localrepo: pass ui to newreporequirements() (API)...
r39583 def _newreporequirements(ui):
Gregory Szorc
localrepo: define storage backend in creation options (API)...
r40032 return localrepo.newreporequirements(
ui, localrepo.defaultcreateopts(ui))
Pierre-Yves David
upgrade: move descriptions and selection logic in individual classes...
r32031
@classmethod
def fromrepo(cls, repo):
assert cls._requirement is not None
return cls._requirement in repo.requirements
@classmethod
def fromconfig(cls, repo):
assert cls._requirement is not None
Gregory Szorc
localrepo: pass ui to newreporequirements() (API)...
r39583 return cls._requirement in cls._newreporequirements(repo.ui)
Pierre-Yves David
upgrade: move descriptions and selection logic in individual classes...
r32031
Pierre-Yves David
upgrade: register all format variants in a list...
r32032 @registerformatvariant
Pierre-Yves David
upgrade: move descriptions and selection logic in individual classes...
r32031 class fncache(requirementformatvariant):
name = 'fncache'
_requirement = 'fncache'
default = True
description = _('long and reserved filenames may not work correctly; '
'repository performance is sub-optimal')
upgrademessage = _('repository will be more resilient to storing '
'certain paths and performance of certain '
'operations should be improved')
Pierre-Yves David
upgrade: register all format variants in a list...
r32032 @registerformatvariant
Pierre-Yves David
upgrade: move descriptions and selection logic in individual classes...
r32031 class dotencode(requirementformatvariant):
name = 'dotencode'
_requirement = 'dotencode'
default = True
description = _('storage of filenames beginning with a period or '
'space may not work correctly')
upgrademessage = _('repository will be better able to store files '
'beginning with a space or period')
Pierre-Yves David
upgrade: register all format variants in a list...
r32032 @registerformatvariant
Pierre-Yves David
upgrade: move descriptions and selection logic in individual classes...
r32031 class generaldelta(requirementformatvariant):
name = 'generaldelta'
_requirement = 'generaldelta'
default = True
description = _('deltas within internal storage are unable to '
'choose optimal revisions; repository is larger and '
'slower than it could be; interaction with other '
'repositories may require extra network and CPU '
'resources, making "hg push" and "hg pull" slower')
upgrademessage = _('repository storage will be able to create '
'optimal deltas; new repository data will be '
'smaller and read times should decrease; '
'interacting with other repositories using this '
'storage model should require less network and '
'CPU resources, making "hg push" and "hg pull" '
'faster')
Pierre-Yves David
upgrade: register all format variants in a list...
r32032 @registerformatvariant
Paul Morelle
upgrade: add information about sparse-revlog...
r38741 class sparserevlog(requirementformatvariant):
name = 'sparserevlog'
_requirement = localrepo.SPARSEREVLOG_REQUIREMENT
Boris Feld
sparse-revlog: enabled by default...
r40954 default = True
Paul Morelle
upgrade: add information about sparse-revlog...
r38741
description = _('in order to limit disk reading and memory usage on older '
'version, the span of a delta chain from its root to its '
'end is limited, whatever the relevant data in this span. '
'This can severly limit Mercurial ability to build good '
'chain of delta resulting is much more storage space being '
'taken and limit reusability of on disk delta during '
'exchange.'
)
upgrademessage = _('Revlog supports delta chain with more unused data '
'between payload. These gaps will be skipped at read '
'time. This allows for better delta chains, making a '
'better compression and faster exchange with server.')
@registerformatvariant
Pierre-Yves David
upgrade: move descriptions and selection logic in individual classes...
r32031 class removecldeltachain(formatvariant):
Boris Feld
upgrade: rename 'removecldeltachain' to 'plain-cl-delta'...
r35336 name = 'plain-cl-delta'
Pierre-Yves David
upgrade: move descriptions and selection logic in individual classes...
r32031
default = True
description = _('changelog storage is using deltas instead of '
'raw entries; changelog reading and any '
'operation relying on changelog data are slower '
'than they could be')
upgrademessage = _('changelog storage will be reformated to '
'store raw entries; changelog reading will be '
'faster; changelog size may be reduced')
@staticmethod
def fromrepo(repo):
# Mercurial 4.0 changed changelogs to not use delta chains. Search for
# changelogs with deltas.
cl = repo.changelog
chainbase = cl.chainbase
return all(rev == chainbase(rev) for rev in cl)
@staticmethod
def fromconfig(repo):
return True
Pierre-Yves David
upgrade: introduce a 'formatvariant' class...
r32030
Boris Feld
upgrade: register compression as a format variants...
r35341 @registerformatvariant
class compressionengine(formatvariant):
name = 'compression'
default = 'zlib'
description = _('Compresion algorithm used to compress data. '
'Some engine are faster than other')
upgrademessage = _('revlog content will be recompressed with the new '
'algorithm.')
@classmethod
def fromrepo(cls, repo):
for req in repo.requirements:
if req.startswith('exp-compression-'):
return req.split('-', 2)[2]
return 'zlib'
@classmethod
def fromconfig(cls, repo):
return repo.ui.config('experimental', 'format.compression')
Pierre-Yves David
upgrade: split finding deficiencies from finding optimisations...
r31896 def finddeficiencies(repo):
"""returns a list of deficiencies that the repo suffer from"""
deficiencies = []
Pierre-Yves David
upgrade: extract code in its own module...
r31864
# We could detect lack of revlogv1 and store here, but they were added
# in 0.9.2 and we don't support upgrading repos without these
# requirements, so let's not bother.
Pierre-Yves David
upgrade: register all format variants in a list...
r32032 for fv in allformatvariant:
if not fv.fromrepo(repo):
deficiencies.append(fv)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Pierre-Yves David
upgrade: split finding deficiencies from finding optimisations...
r31896 return deficiencies
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Pierre-Yves David
upgrade: split finding deficiencies from finding optimisations...
r31896 def findoptimizations(repo):
"""Determine optimisation that could be used during upgrade"""
Pierre-Yves David
upgrade: extract code in its own module...
r31864 # These are unconditionally added. There is logic later that figures out
# which ones to apply.
Pierre-Yves David
upgrade: split finding deficiencies from finding optimisations...
r31896 optimizations = []
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Pierre-Yves David
upgrade: split finding deficiencies from finding optimisations...
r31896 optimizations.append(improvement(
Pierre-Yves David
upgrade: extract code in its own module...
r31864 name='redeltaparent',
type=optimisation,
description=_('deltas within internal storage will be recalculated to '
'choose an optimal base revision where this was not '
'already done; the size of the repository may shrink and '
'various operations may become faster; the first time '
'this optimization is performed could slow down upgrade '
'execution considerably; subsequent invocations should '
'not run noticeably slower'),
upgrademessage=_('deltas within internal storage will choose a new '
'base revision if needed')))
Pierre-Yves David
upgrade: split finding deficiencies from finding optimisations...
r31896 optimizations.append(improvement(
Pierre-Yves David
upgrade: extract code in its own module...
r31864 name='redeltamultibase',
type=optimisation,
description=_('deltas within internal storage will be recalculated '
'against multiple base revision and the smallest '
'difference will be used; the size of the repository may '
'shrink significantly when there are many merges; this '
'optimization will slow down execution in proportion to '
'the number of merges in the repository and the amount '
'of files in the repository; this slow down should not '
'be significant unless there are tens of thousands of '
'files and thousands of merges'),
upgrademessage=_('deltas within internal storage will choose an '
'optimal delta by computing deltas against multiple '
'parents; may slow down execution time '
'significantly')))
Pierre-Yves David
upgrade: split finding deficiencies from finding optimisations...
r31896 optimizations.append(improvement(
Pierre-Yves David
upgrade: extract code in its own module...
r31864 name='redeltaall',
type=optimisation,
description=_('deltas within internal storage will always be '
'recalculated without reusing prior deltas; this will '
'likely make execution run several times slower; this '
'optimization is typically not needed'),
upgrademessage=_('deltas within internal storage will be fully '
'recomputed; this will likely drastically slow down '
'execution time')))
Boris Feld
upgrade: add a 'redeltafullall' mode...
r35346 optimizations.append(improvement(
name='redeltafulladd',
type=optimisation,
description=_('every revision will be re-added as if it was new '
'content. It will go through the full storage '
'mechanism giving extensions a chance to process it '
'(eg. lfs). This is similar to "redeltaall" but even '
'slower since more logic is involved.'),
upgrademessage=_('each revision will be added as new content to the '
'internal storage; this will likely drastically slow '
'down execution time, but some extensions might need '
'it')))
Pierre-Yves David
upgrade: split finding deficiencies from finding optimisations...
r31896 return optimizations
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Pierre-Yves David
upgrade: simplify 'determineactions'...
r31900 def determineactions(repo, deficiencies, sourcereqs, destreqs):
Pierre-Yves David
upgrade: extract code in its own module...
r31864 """Determine upgrade actions that will be performed.
Pierre-Yves David
upgrade: split finding deficiencies from finding optimisations...
r31896 Given a list of improvements as returned by ``finddeficiencies`` and
``findoptimizations``, determine the list of upgrade actions that
will be performed.
Pierre-Yves David
upgrade: extract code in its own module...
r31864
The role of this function is to filter improvements if needed, apply
recommended optimizations from the improvements list that make sense,
etc.
Returns a list of action names.
"""
newactions = []
Pierre-Yves David
upgrade: drop the prefix to the 'supporteddestrequirements' function...
r31870 knownreqs = supporteddestrequirements(repo)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Pierre-Yves David
upgrade: simplify 'determineactions'...
r31900 for d in deficiencies:
name = d.name
Pierre-Yves David
upgrade: extract code in its own module...
r31864
# If the action is a requirement that doesn't show up in the
# destination requirements, prune the action.
if name in knownreqs and name not in destreqs:
continue
Pierre-Yves David
upgrade: use 'improvement' object for action too...
r31903 newactions.append(d)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
# FUTURE consider adding some optimizations here for certain transitions.
# e.g. adding generaldelta could schedule parent redeltas.
return newactions
def _revlogfrompath(repo, path):
"""Obtain a revlog from a repo path.
An instance of the appropriate class is returned.
"""
if path == '00changelog.i':
return changelog.changelog(repo.svfs)
elif path.endswith('00manifest.i'):
mandir = path[:-len('00manifest.i')]
Gregory Szorc
manifest: rename dir argument and attribute to tree...
r39279 return manifest.manifestrevlog(repo.svfs, tree=mandir)
Pierre-Yves David
upgrade: extract code in its own module...
r31864 else:
Boris Feld
upgrade: use actual filelog to convert filelog...
r35345 #reverse of "/".join(("data", path + ".i"))
return filelog.filelog(repo.svfs, path[5:-2])
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Boris Feld
upgrade: clarify "aggressivemergedelta" handling...
r40872 def _copyrevlogs(ui, srcrepo, dstrepo, tr, deltareuse, forcedeltabothparents):
Pierre-Yves David
upgrade: extract code in its own module...
r31864 """Copy revlogs between 2 repos."""
revcount = 0
srcsize = 0
srcrawsize = 0
dstsize = 0
fcount = 0
frevcount = 0
fsrcsize = 0
frawsize = 0
fdstsize = 0
mcount = 0
mrevcount = 0
msrcsize = 0
mrawsize = 0
mdstsize = 0
crevcount = 0
csrcsize = 0
crawsize = 0
cdstsize = 0
# Perform a pass to collect metadata. This validates we can open all
# source files and allows a unified progress bar to be displayed.
for unencoded, encoded, size in srcrepo.store.walk():
if unencoded.endswith('.d'):
continue
rl = _revlogfrompath(srcrepo, unencoded)
Gregory Szorc
upgrade: use storageinfo() for obtaining storage metadata...
r39906 info = rl.storageinfo(exclusivefiles=True, revisionscount=True,
trackedsize=True, storedsize=True)
Gregory Szorc
upgrade: report size of backing files, not internal storage size...
r39893
Gregory Szorc
upgrade: use storageinfo() for obtaining storage metadata...
r39906 revcount += info['revisionscount'] or 0
datasize = info['storedsize'] or 0
rawsize = info['trackedsize'] or 0
Pierre-Yves David
upgrade: extract code in its own module...
r31864
srcsize += datasize
srcrawsize += rawsize
# This is for the separate progress bars.
if isinstance(rl, changelog.changelog):
crevcount += len(rl)
csrcsize += datasize
crawsize += rawsize
elif isinstance(rl, manifest.manifestrevlog):
mcount += 1
mrevcount += len(rl)
msrcsize += datasize
mrawsize += rawsize
Gregory Szorc
upgrade: sniff for filelog type...
r37462 elif isinstance(rl, filelog.filelog):
Pierre-Yves David
upgrade: extract code in its own module...
r31864 fcount += 1
frevcount += len(rl)
fsrcsize += datasize
frawsize += rawsize
Gregory Szorc
upgrade: sniff for filelog type...
r37462 else:
error.ProgrammingError('unknown revlog type')
Pierre-Yves David
upgrade: extract code in its own module...
r31864
if not revcount:
return
ui.write(_('migrating %d total revisions (%d in filelogs, %d in manifests, '
'%d in changelog)\n') %
(revcount, frevcount, mrevcount, crevcount))
ui.write(_('migrating %s in store; %s tracked data\n') % (
(util.bytecount(srcsize), util.bytecount(srcrawsize))))
# Used to keep track of progress.
Martin von Zweigbergk
upgrade: use progress helper...
r38418 progress = None
Pierre-Yves David
upgrade: extract code in its own module...
r31864 def oncopiedrevision(rl, rev, node):
Martin von Zweigbergk
upgrade: use progress helper...
r38418 progress.increment()
Pierre-Yves David
upgrade: extract code in its own module...
r31864
# Do the actual copying.
# FUTURE this operation can be farmed off to worker processes.
seen = set()
for unencoded, encoded, size in srcrepo.store.walk():
if unencoded.endswith('.d'):
continue
oldrl = _revlogfrompath(srcrepo, unencoded)
newrl = _revlogfrompath(dstrepo, unencoded)
if isinstance(oldrl, changelog.changelog) and 'c' not in seen:
ui.write(_('finished migrating %d manifest revisions across %d '
'manifests; change in size: %s\n') %
(mrevcount, mcount, util.bytecount(mdstsize - msrcsize)))
ui.write(_('migrating changelog containing %d revisions '
'(%s in store; %s tracked data)\n') %
(crevcount, util.bytecount(csrcsize),
util.bytecount(crawsize)))
seen.add('c')
Martin von Zweigbergk
upgrade: use progress helper...
r38418 progress = srcrepo.ui.makeprogress(_('changelog revisions'),
total=crevcount)
Pierre-Yves David
upgrade: extract code in its own module...
r31864 elif isinstance(oldrl, manifest.manifestrevlog) and 'm' not in seen:
ui.write(_('finished migrating %d filelog revisions across %d '
'filelogs; change in size: %s\n') %
(frevcount, fcount, util.bytecount(fdstsize - fsrcsize)))
ui.write(_('migrating %d manifests containing %d revisions '
'(%s in store; %s tracked data)\n') %
(mcount, mrevcount, util.bytecount(msrcsize),
util.bytecount(mrawsize)))
seen.add('m')
Martin von Zweigbergk
upgrade: close progress after each revlog...
r38417 if progress:
Martin von Zweigbergk
upgrade: use progress helper...
r38418 progress.complete()
progress = srcrepo.ui.makeprogress(_('manifest revisions'),
total=mrevcount)
Pierre-Yves David
upgrade: extract code in its own module...
r31864 elif 'f' not in seen:
ui.write(_('migrating %d filelogs containing %d revisions '
'(%s in store; %s tracked data)\n') %
(fcount, frevcount, util.bytecount(fsrcsize),
util.bytecount(frawsize)))
seen.add('f')
Martin von Zweigbergk
upgrade: close progress after each revlog...
r38417 if progress:
Martin von Zweigbergk
upgrade: use progress helper...
r38418 progress.complete()
progress = srcrepo.ui.makeprogress(_('file revisions'),
total=frevcount)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
ui.note(_('cloning %d revisions from %s\n') % (len(oldrl), unencoded))
oldrl.clone(tr, newrl, addrevisioncb=oncopiedrevision,
deltareuse=deltareuse,
Boris Feld
upgrade: clarify "aggressivemergedelta" handling...
r40872 forcedeltabothparents=forcedeltabothparents)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Gregory Szorc
upgrade: use storageinfo() for obtaining storage metadata...
r39906 info = newrl.storageinfo(storedsize=True)
datasize = info['storedsize'] or 0
Pierre-Yves David
upgrade: extract code in its own module...
r31864
dstsize += datasize
if isinstance(newrl, changelog.changelog):
cdstsize += datasize
elif isinstance(newrl, manifest.manifestrevlog):
mdstsize += datasize
else:
fdstsize += datasize
Martin von Zweigbergk
upgrade: use progress helper...
r38418 progress.complete()
Pierre-Yves David
upgrade: extract code in its own module...
r31864
ui.write(_('finished migrating %d changelog revisions; change in size: '
'%s\n') % (crevcount, util.bytecount(cdstsize - csrcsize)))
ui.write(_('finished migrating %d total revisions; total change in store '
'size: %s\n') % (revcount, util.bytecount(dstsize - srcsize)))
Pierre-Yves David
upgrade: drop the prefix to the '_filterstorefile' function...
r31873 def _filterstorefile(srcrepo, dstrepo, requirements, path, mode, st):
Pierre-Yves David
upgrade: extract code in its own module...
r31864 """Determine whether to copy a store file during upgrade.
This function is called when migrating store files from ``srcrepo`` to
``dstrepo`` as part of upgrading a repository.
Args:
srcrepo: repo we are copying from
dstrepo: repo we are copying to
requirements: set of requirements for ``dstrepo``
path: store file being examined
mode: the ``ST_MODE`` file type of ``path``
st: ``stat`` data structure for ``path``
Function should return ``True`` if the file is to be copied.
"""
# Skip revlogs.
if path.endswith(('.i', '.d')):
return False
# Skip transaction related files.
if path.startswith('undo'):
return False
# Only copy regular files.
if mode != stat.S_IFREG:
return False
# Skip other skipped files.
if path in ('lock', 'fncache'):
return False
return True
Pierre-Yves David
upgrade: drop the prefix to the '_finishdatamigration' function...
r31874 def _finishdatamigration(ui, srcrepo, dstrepo, requirements):
Pierre-Yves David
upgrade: extract code in its own module...
r31864 """Hook point for extensions to perform additional actions during upgrade.
This function is called after revlogs and store files have been copied but
before the new store is swapped into the original location.
"""
def _upgraderepo(ui, srcrepo, dstrepo, requirements, actions):
"""Do the low-level work of upgrading a repository.
The upgrade is effectively performed as a copy between a source
repository and a temporary destination repository.
The source repository is unmodified for as long as possible so the
upgrade can abort at any time without causing loss of service for
readers and without corrupting the source repository.
"""
assert srcrepo.currentwlock()
assert dstrepo.currentwlock()
ui.write(_('(it is safe to interrupt this process any time before '
'data migration completes)\n'))
if 'redeltaall' in actions:
deltareuse = revlog.revlog.DELTAREUSENEVER
elif 'redeltaparent' in actions:
deltareuse = revlog.revlog.DELTAREUSESAMEREVS
elif 'redeltamultibase' in actions:
deltareuse = revlog.revlog.DELTAREUSESAMEREVS
Paul Morelle
upgraderepo: select correct deltareuse depending on actions...
r35616 elif 'redeltafulladd' in actions:
Boris Feld
upgrade: add a 'redeltafullall' mode...
r35346 deltareuse = revlog.revlog.DELTAREUSEFULLADD
Pierre-Yves David
upgrade: extract code in its own module...
r31864 else:
deltareuse = revlog.revlog.DELTAREUSEALWAYS
with dstrepo.transaction('upgrade') as tr:
_copyrevlogs(ui, srcrepo, dstrepo, tr, deltareuse,
'redeltamultibase' in actions)
# Now copy other files in the store directory.
Yuya Nishihara
merge with stable
r31875 # The sorted() makes execution deterministic.
for p, kind, st in sorted(srcrepo.store.vfs.readdir('', stat=True)):
Pierre-Yves David
upgrade: drop the prefix to the '_filterstorefile' function...
r31873 if not _filterstorefile(srcrepo, dstrepo, requirements,
Pierre-Yves David
upgrade: extract code in its own module...
r31864 p, kind, st):
continue
srcrepo.ui.write(_('copying %s\n') % p)
Yuya Nishihara
merge with stable
r31875 src = srcrepo.store.rawvfs.join(p)
dst = dstrepo.store.rawvfs.join(p)
Pierre-Yves David
upgrade: extract code in its own module...
r31864 util.copyfile(src, dst, copystat=True)
Pierre-Yves David
upgrade: drop the prefix to the '_finishdatamigration' function...
r31874 _finishdatamigration(ui, srcrepo, dstrepo, requirements)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
ui.write(_('data fully migrated to temporary repository\n'))
Yuya Nishihara
py3: wrap tempfile.mkdtemp() to use bytes path...
r38183 backuppath = pycompat.mkdtemp(prefix='upgradebackup.', dir=srcrepo.path)
Pierre-Yves David
upgrade: extract code in its own module...
r31864 backupvfs = vfsmod.vfs(backuppath)
# Make a backup of requires file first, as it is the first to be modified.
util.copyfile(srcrepo.vfs.join('requires'), backupvfs.join('requires'))
# We install an arbitrary requirement that clients must not support
# as a mechanism to lock out new clients during the data swap. This is
# better than allowing a client to continue while the repository is in
# an inconsistent state.
ui.write(_('marking source repository as being upgraded; clients will be '
'unable to read from repository\n'))
scmutil.writerequires(srcrepo.vfs,
Martin von Zweigbergk
cleanup: use set literals...
r32291 srcrepo.requirements | {'upgradeinprogress'})
Pierre-Yves David
upgrade: extract code in its own module...
r31864
ui.write(_('starting in-place swap of repository data\n'))
ui.write(_('replaced files will be backed up at %s\n') %
backuppath)
# Now swap in the new store directory. Doing it as a rename should make
# the operation nearly instantaneous and atomic (at least in well-behaved
# environments).
ui.write(_('replacing store...\n'))
tstart = util.timer()
util.rename(srcrepo.spath, backupvfs.join('store'))
util.rename(dstrepo.spath, srcrepo.spath)
elapsed = util.timer() - tstart
ui.write(_('store replacement complete; repository was inconsistent for '
'%0.1fs\n') % elapsed)
# We first write the requirements file. Any new requirements will lock
# out legacy clients.
ui.write(_('finalizing requirements file and making repository readable '
'again\n'))
scmutil.writerequires(srcrepo.vfs, requirements)
# The lock file from the old store won't be removed because nothing has a
# reference to its new location. So clean it up manually. Alternatively, we
# could update srcrepo.svfs and other variables to point to the new
# location. This is simpler.
backupvfs.unlink('store/lock')
return backuppath
def upgraderepo(ui, repo, run=False, optimize=None):
"""Upgrade a repository in place."""
optimize = set(optimize or [])
repo = repo.unfiltered()
# Ensure the repository can be upgraded.
Pierre-Yves David
upgrade: drop the prefix to the 'requiredsourcerequirements' function...
r31865 missingreqs = requiredsourcerequirements(repo) - repo.requirements
Pierre-Yves David
upgrade: extract code in its own module...
r31864 if missingreqs:
raise error.Abort(_('cannot upgrade repository; requirement '
'missing: %s') % _(', ').join(sorted(missingreqs)))
Pierre-Yves David
upgrade: drop the prefix to the 'blocksourcerequirements' function...
r31866 blockedreqs = blocksourcerequirements(repo) & repo.requirements
Pierre-Yves David
upgrade: extract code in its own module...
r31864 if blockedreqs:
raise error.Abort(_('cannot upgrade repository; unsupported source '
'requirement: %s') %
_(', ').join(sorted(blockedreqs)))
# FUTURE there is potentially a need to control the wanted requirements via
# command arguments or via an extension hook point.
Gregory Szorc
localrepo: define storage backend in creation options (API)...
r40032 newreqs = localrepo.newreporequirements(
repo.ui, localrepo.defaultcreateopts(repo.ui))
Boris Feld
upgraderepo: allow extension to register preserved requirements...
r35303 newreqs.update(preservedrequirements(repo))
Pierre-Yves David
upgrade: extract code in its own module...
r31864
noremovereqs = (repo.requirements - newreqs -
Pierre-Yves David
upgrade: drop the prefix to the 'supportremovedrequirements' function...
r31867 supportremovedrequirements(repo))
Pierre-Yves David
upgrade: extract code in its own module...
r31864 if noremovereqs:
raise error.Abort(_('cannot upgrade repository; requirement would be '
'removed: %s') % _(', ').join(sorted(noremovereqs)))
noaddreqs = (newreqs - repo.requirements -
Pierre-Yves David
upgrade: drop the prefix to the 'allowednewrequirements' function...
r31869 allowednewrequirements(repo))
Pierre-Yves David
upgrade: extract code in its own module...
r31864 if noaddreqs:
raise error.Abort(_('cannot upgrade repository; do not support adding '
'requirement: %s') %
_(', ').join(sorted(noaddreqs)))
Pierre-Yves David
upgrade: drop the prefix to the 'supporteddestrequirements' function...
r31870 unsupportedreqs = newreqs - supporteddestrequirements(repo)
Pierre-Yves David
upgrade: extract code in its own module...
r31864 if unsupportedreqs:
raise error.Abort(_('cannot upgrade repository; do not support '
'destination requirement: %s') %
_(', ').join(sorted(unsupportedreqs)))
# Find and validate all improvements that can be made.
Pierre-Yves David
upgrade: filter optimizations outside of 'determineactions'...
r31899 alloptimizations = findoptimizations(repo)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Pierre-Yves David
upgrade: filter optimizations outside of 'determineactions'...
r31899 # Apply and Validate arguments.
optimizations = []
for o in alloptimizations:
if o.name in optimize:
optimizations.append(o)
optimize.discard(o.name)
if optimize: # anything left is unknown
Pierre-Yves David
upgrade: extract code in its own module...
r31864 raise error.Abort(_('unknown optimization action requested: %s') %
Pierre-Yves David
upgrade: filter optimizations outside of 'determineactions'...
r31899 ', '.join(sorted(optimize)),
Pierre-Yves David
upgrade: extract code in its own module...
r31864 hint=_('run without arguments to see valid '
'optimizations'))
Pierre-Yves David
upgrade: simplify optimisations validation...
r31897 deficiencies = finddeficiencies(repo)
Pierre-Yves David
upgrade: filter optimizations outside of 'determineactions'...
r31899 actions = determineactions(repo, deficiencies, repo.requirements, newreqs)
Pierre-Yves David
upgrade: use 'improvement' object for action too...
r31903 actions.extend(o for o in sorted(optimizations)
Pierre-Yves David
upgrade: filter optimizations outside of 'determineactions'...
r31899 # determineactions could have added optimisation
Pierre-Yves David
upgrade: use 'improvement' object for action too...
r31903 if o not in actions)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
def printrequirements():
ui.write(_('requirements\n'))
ui.write(_(' preserved: %s\n') %
_(', ').join(sorted(newreqs & repo.requirements)))
if repo.requirements - newreqs:
ui.write(_(' removed: %s\n') %
_(', ').join(sorted(repo.requirements - newreqs)))
if newreqs - repo.requirements:
ui.write(_(' added: %s\n') %
_(', ').join(sorted(newreqs - repo.requirements)))
ui.write('\n')
def printupgradeactions():
Pierre-Yves David
upgrade: use 'improvement' object for action too...
r31903 for a in actions:
ui.write('%s\n %s\n\n' % (a.name, a.upgrademessage))
Pierre-Yves David
upgrade: extract code in its own module...
r31864
if not run:
fromconfig = []
Pierre-Yves David
upgrade: simplify the "origin" dispatch in dry run...
r31904 onlydefault = []
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Pierre-Yves David
upgrade: simplify some of the initial dispatch for dry run...
r31901 for d in deficiencies:
Pierre-Yves David
upgrade: move descriptions and selection logic in individual classes...
r32031 if d.fromconfig(repo):
Pierre-Yves David
upgrade: simplify some of the initial dispatch for dry run...
r31901 fromconfig.append(d)
Pierre-Yves David
upgrade: move descriptions and selection logic in individual classes...
r32031 elif d.default:
Pierre-Yves David
upgrade: simplify the "origin" dispatch in dry run...
r31904 onlydefault.append(d)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Pierre-Yves David
upgrade: simplify the "origin" dispatch in dry run...
r31904 if fromconfig or onlydefault:
Pierre-Yves David
upgrade: extract code in its own module...
r31864
if fromconfig:
ui.write(_('repository lacks features recommended by '
'current config options:\n\n'))
for i in fromconfig:
ui.write('%s\n %s\n\n' % (i.name, i.description))
if onlydefault:
ui.write(_('repository lacks features used by the default '
'config options:\n\n'))
for i in onlydefault:
ui.write('%s\n %s\n\n' % (i.name, i.description))
ui.write('\n')
else:
ui.write(_('(no feature deficiencies found in existing '
'repository)\n'))
ui.write(_('performing an upgrade with "--run" will make the following '
'changes:\n\n'))
printrequirements()
printupgradeactions()
Pierre-Yves David
upgrade: use 'improvement' object for action too...
r31903 unusedoptimize = [i for i in alloptimizations if i not in actions]
Pierre-Yves David
upgrade: extract code in its own module...
r31864 if unusedoptimize:
ui.write(_('additional optimizations are available by specifying '
'"--optimize <name>":\n\n'))
for i in unusedoptimize:
ui.write(_('%s\n %s\n\n') % (i.name, i.description))
return
# Else we're in the run=true case.
ui.write(_('upgrade will perform the following actions:\n\n'))
printrequirements()
printupgradeactions()
Pierre-Yves David
upgrade: use 'improvement' object for action too...
r31903 upgradeactions = [a.name for a in actions]
Pierre-Yves David
upgrade: extract code in its own module...
r31864 ui.write(_('beginning upgrade...\n'))
Jun Wu
codemod: simplify nested withs...
r33438 with repo.wlock(), repo.lock():
ui.write(_('repository locked and read-only\n'))
# Our strategy for upgrading the repository is to create a new,
# temporary repository, write data to it, then do a swap of the
# data. There are less heavyweight ways to do this, but it is easier
# to create a new repo object than to instantiate all the components
# (like the store) separately.
Yuya Nishihara
py3: wrap tempfile.mkdtemp() to use bytes path...
r38183 tmppath = pycompat.mkdtemp(prefix='upgrade.', dir=repo.path)
Jun Wu
codemod: simplify nested withs...
r33438 backuppath = None
try:
ui.write(_('creating temporary repository to stage migrated '
'data: %s\n') % tmppath)
Boris Feld
upgrade: use the repository 'ui' as the base for the new repository...
r35343
Yuya Nishihara
upgrade: simplify workaround for repo.ui.copy()...
r35380 # clone ui without using ui.copy because repo.ui is protected
repoui = repo.ui.__class__(repo.ui)
dstrepo = hg.repository(repoui, path=tmppath, create=True)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Jun Wu
codemod: simplify nested withs...
r33438 with dstrepo.wlock(), dstrepo.lock():
backuppath = _upgraderepo(ui, repo, dstrepo, newreqs,
upgradeactions)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Jun Wu
codemod: simplify nested withs...
r33438 finally:
ui.write(_('removing temporary repository %s\n') % tmppath)
repo.vfs.rmtree(tmppath, forcibly=True)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Jun Wu
codemod: simplify nested withs...
r33438 if backuppath:
ui.warn(_('copy of old repository backed up at %s\n') %
backuppath)
ui.warn(_('the old repository will not be deleted; remove '
'it to free up disk space once the upgraded '
'repository is verified\n'))