upgrade.py
320 lines
| 10.4 KiB
| text/x-python
|
PythonLexer
/ mercurial / upgrade.py
Pierre-Yves David
|
r31894 | # upgrade.py - functions for in place upgrade of Mercurial repository | ||
Pierre-Yves David
|
r31864 | # | ||
Pierre-Yves David
|
r31895 | # Copyright (c) 2016-present, Gregory Szorc | ||
Pierre-Yves David
|
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 | ||||
from .i18n import _ | ||||
from . import ( | ||||
error, | ||||
Boris Feld
|
r35344 | hg, | ||
Pierre-Yves David
|
r31893 | localrepo, | ||
Yuya Nishihara
|
r38183 | pycompat, | ||
r46661 | ) | |||
from .upgrade_utils import ( | ||||
r46662 | actions as upgrade_actions, | |||
r46661 | engine as upgrade_engine, | |||
Pierre-Yves David
|
r31864 | ) | ||
r46662 | allformatvariant = upgrade_actions.allformatvariant | |||
Augie Fackler
|
r43346 | |||
Boris Feld
|
r41120 | # search without '-' to support older form on newer client. | ||
# | ||||
# We don't enforce backward compatibility for debug command so this | ||||
# might eventually be dropped. However, having to use two different | ||||
# forms in script when comparing result is anoying enough to add | ||||
# backward compatibility for a while. | ||||
legacy_opts_map = { | ||||
Augie Fackler
|
r43347 | b'redeltaparent': b're-delta-parent', | ||
b'redeltamultibase': b're-delta-multibase', | ||||
b'redeltaall': b're-delta-all', | ||||
b'redeltafulladd': b're-delta-fulladd', | ||||
Boris Feld
|
r41120 | } | ||
Augie Fackler
|
r43346 | |||
def upgraderepo( | ||||
ui, | ||||
repo, | ||||
run=False, | ||||
optimize=None, | ||||
backup=True, | ||||
manifest=None, | ||||
changelog=None, | ||||
r46612 | filelogs=None, | |||
Augie Fackler
|
r43346 | ): | ||
Pierre-Yves David
|
r31864 | """Upgrade a repository in place.""" | ||
Boris Feld
|
r41120 | if optimize is None: | ||
optimize = [] | ||||
Augie Fackler
|
r44937 | optimize = {legacy_opts_map.get(o, o) for o in optimize} | ||
Pierre-Yves David
|
r31864 | repo = repo.unfiltered() | ||
r46661 | revlogs = set(upgrade_engine.UPGRADE_ALL_REVLOGS) | |||
r46595 | specentries = ( | |||
r46661 | (upgrade_engine.UPGRADE_CHANGELOG, changelog), | |||
(upgrade_engine.UPGRADE_MANIFEST, manifest), | ||||
(upgrade_engine.UPGRADE_FILELOGS, filelogs), | ||||
r46595 | ) | |||
r43098 | specified = [(y, x) for (y, x) in specentries if x is not None] | |||
if specified: | ||||
# we have some limitation on revlogs to be recloned | ||||
if any(x for y, x in specified): | ||||
revlogs = set() | ||||
r46595 | for upgrade, enabled in specified: | |||
r43098 | if enabled: | |||
r46595 | revlogs.add(upgrade) | |||
r43098 | else: | |||
# none are enabled | ||||
r46595 | for upgrade, __ in specified: | |||
revlogs.discard(upgrade) | ||||
r43098 | ||||
Pierre-Yves David
|
r31864 | # Ensure the repository can be upgraded. | ||
r46663 | upgrade_actions.check_source_requirements(repo) | |||
Pierre-Yves David
|
r31864 | |||
r46666 | default_options = localrepo.defaultcreateopts(repo.ui) | |||
newreqs = localrepo.newreporequirements(repo.ui, default_options) | ||||
r46662 | newreqs.update(upgrade_actions.preservedrequirements(repo)) | |||
Pierre-Yves David
|
r31864 | |||
r46666 | upgrade_actions.check_requirements_changes(repo, newreqs) | |||
Pierre-Yves David
|
r31864 | |||
# Find and validate all improvements that can be made. | ||||
r46662 | alloptimizations = upgrade_actions.findoptimizations(repo) | |||
Pierre-Yves David
|
r31864 | |||
Pierre-Yves David
|
r31899 | # Apply and Validate arguments. | ||
optimizations = [] | ||||
for o in alloptimizations: | ||||
if o.name in optimize: | ||||
optimizations.append(o) | ||||
optimize.discard(o.name) | ||||
Augie Fackler
|
r43346 | if optimize: # anything left is unknown | ||
raise error.Abort( | ||||
Augie Fackler
|
r43347 | _(b'unknown optimization action requested: %s') | ||
% b', '.join(sorted(optimize)), | ||||
Martin von Zweigbergk
|
r43387 | hint=_(b'run without arguments to see valid optimizations'), | ||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r31864 | |||
r46662 | deficiencies = upgrade_actions.finddeficiencies(repo) | |||
actions = upgrade_actions.determineactions( | ||||
repo, deficiencies, repo.requirements, newreqs | ||||
) | ||||
Augie Fackler
|
r43346 | actions.extend( | ||
o | ||||
for o in sorted(optimizations) | ||||
# determineactions could have added optimisation | ||||
if o not in actions | ||||
) | ||||
Pierre-Yves David
|
r31864 | |||
r43100 | removedreqs = repo.requirements - newreqs | |||
addedreqs = newreqs - repo.requirements | ||||
r46661 | if revlogs != upgrade_engine.UPGRADE_ALL_REVLOGS: | |||
r46662 | incompatible = upgrade_actions.RECLONES_REQUIREMENTS & ( | |||
removedreqs | addedreqs | ||||
) | ||||
r43100 | if incompatible: | |||
Augie Fackler
|
r43346 | msg = _( | ||
Augie Fackler
|
r43347 | b'ignoring revlogs selection flags, format requirements ' | ||
b'change: %s\n' | ||||
Augie Fackler
|
r43346 | ) | ||
Augie Fackler
|
r43347 | ui.warn(msg % b', '.join(sorted(incompatible))) | ||
r46661 | revlogs = upgrade_engine.UPGRADE_ALL_REVLOGS | |||
r43100 | ||||
r44257 | def write_labeled(l, label): | |||
first = True | ||||
for r in sorted(l): | ||||
if not first: | ||||
ui.write(b', ') | ||||
ui.write(r, label=label) | ||||
first = False | ||||
Pierre-Yves David
|
r31864 | def printrequirements(): | ||
Augie Fackler
|
r43347 | ui.write(_(b'requirements\n')) | ||
r44257 | ui.write(_(b' preserved: ')) | |||
write_labeled( | ||||
newreqs & repo.requirements, "upgrade-repo.requirement.preserved" | ||||
Augie Fackler
|
r43346 | ) | ||
r44257 | ui.write((b'\n')) | |||
removed = repo.requirements - newreqs | ||||
Pierre-Yves David
|
r31864 | if repo.requirements - newreqs: | ||
r44257 | ui.write(_(b' removed: ')) | |||
write_labeled(removed, "upgrade-repo.requirement.removed") | ||||
ui.write((b'\n')) | ||||
added = newreqs - repo.requirements | ||||
if added: | ||||
ui.write(_(b' added: ')) | ||||
write_labeled(added, "upgrade-repo.requirement.added") | ||||
ui.write((b'\n')) | ||||
Augie Fackler
|
r43347 | ui.write(b'\n') | ||
Pierre-Yves David
|
r31864 | |||
r45301 | def printoptimisations(): | |||
r46662 | optimisations = [ | |||
a for a in actions if a.type == upgrade_actions.OPTIMISATION | ||||
] | ||||
r45301 | optimisations.sort(key=lambda a: a.name) | |||
if optimisations: | ||||
ui.write(_(b'optimisations: ')) | ||||
write_labeled( | ||||
[a.name for a in optimisations], | ||||
"upgrade-repo.optimisation.performed", | ||||
) | ||||
ui.write(b'\n\n') | ||||
Pierre-Yves David
|
r31864 | def printupgradeactions(): | ||
Pierre-Yves David
|
r31903 | for a in actions: | ||
r45302 | ui.status(b'%s\n %s\n\n' % (a.name, a.upgrademessage)) | |||
Pierre-Yves David
|
r31864 | |||
r46649 | def print_affected_revlogs(): | |||
if not revlogs: | ||||
ui.write((b'no revlogs to process\n')) | ||||
else: | ||||
ui.write((b'processed revlogs:\n')) | ||||
for r in sorted(revlogs): | ||||
ui.write((b' - %s\n' % r)) | ||||
ui.write((b'\n')) | ||||
Pierre-Yves David
|
r31864 | if not run: | ||
fromconfig = [] | ||||
Pierre-Yves David
|
r31904 | onlydefault = [] | ||
Pierre-Yves David
|
r31864 | |||
Pierre-Yves David
|
r31901 | for d in deficiencies: | ||
Pierre-Yves David
|
r32031 | if d.fromconfig(repo): | ||
Pierre-Yves David
|
r31901 | fromconfig.append(d) | ||
Pierre-Yves David
|
r32031 | elif d.default: | ||
Pierre-Yves David
|
r31904 | onlydefault.append(d) | ||
Pierre-Yves David
|
r31864 | |||
Pierre-Yves David
|
r31904 | if fromconfig or onlydefault: | ||
Pierre-Yves David
|
r31864 | |||
if fromconfig: | ||||
r45302 | ui.status( | |||
Augie Fackler
|
r43346 | _( | ||
Augie Fackler
|
r43347 | b'repository lacks features recommended by ' | ||
b'current config options:\n\n' | ||||
Augie Fackler
|
r43346 | ) | ||
) | ||||
Pierre-Yves David
|
r31864 | for i in fromconfig: | ||
r45302 | ui.status(b'%s\n %s\n\n' % (i.name, i.description)) | |||
Pierre-Yves David
|
r31864 | |||
if onlydefault: | ||||
r45302 | ui.status( | |||
Augie Fackler
|
r43346 | _( | ||
Augie Fackler
|
r43347 | b'repository lacks features used by the default ' | ||
b'config options:\n\n' | ||||
Augie Fackler
|
r43346 | ) | ||
) | ||||
Pierre-Yves David
|
r31864 | for i in onlydefault: | ||
r45302 | ui.status(b'%s\n %s\n\n' % (i.name, i.description)) | |||
Pierre-Yves David
|
r31864 | |||
r45302 | ui.status(b'\n') | |||
Pierre-Yves David
|
r31864 | else: | ||
r45302 | ui.status( | |||
Augie Fackler
|
r43347 | _( | ||
b'(no feature deficiencies found in existing ' | ||||
b'repository)\n' | ||||
) | ||||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r31864 | |||
r45302 | ui.status( | |||
Augie Fackler
|
r43346 | _( | ||
Augie Fackler
|
r43347 | b'performing an upgrade with "--run" will make the following ' | ||
b'changes:\n\n' | ||||
Augie Fackler
|
r43346 | ) | ||
) | ||||
Pierre-Yves David
|
r31864 | |||
printrequirements() | ||||
r45301 | printoptimisations() | |||
Pierre-Yves David
|
r31864 | printupgradeactions() | ||
r46649 | print_affected_revlogs() | |||
Pierre-Yves David
|
r31864 | |||
Pierre-Yves David
|
r31903 | unusedoptimize = [i for i in alloptimizations if i not in actions] | ||
Pierre-Yves David
|
r31864 | if unusedoptimize: | ||
r45302 | ui.status( | |||
Augie Fackler
|
r43346 | _( | ||
Augie Fackler
|
r43347 | b'additional optimizations are available by specifying ' | ||
b'"--optimize <name>":\n\n' | ||||
Augie Fackler
|
r43346 | ) | ||
) | ||||
Pierre-Yves David
|
r31864 | for i in unusedoptimize: | ||
r45302 | ui.status(_(b'%s\n %s\n\n') % (i.name, i.description)) | |||
Pierre-Yves David
|
r31864 | return | ||
# Else we're in the run=true case. | ||||
Augie Fackler
|
r43347 | ui.write(_(b'upgrade will perform the following actions:\n\n')) | ||
Pierre-Yves David
|
r31864 | printrequirements() | ||
r45301 | printoptimisations() | |||
Pierre-Yves David
|
r31864 | printupgradeactions() | ||
r46649 | print_affected_revlogs() | |||
Pierre-Yves David
|
r31864 | |||
Pierre-Yves David
|
r31903 | upgradeactions = [a.name for a in actions] | ||
r45302 | ui.status(_(b'beginning upgrade...\n')) | |||
Jun Wu
|
r33438 | with repo.wlock(), repo.lock(): | ||
r45302 | ui.status(_(b'repository locked and read-only\n')) | |||
Jun Wu
|
r33438 | # 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. | ||||
Augie Fackler
|
r43347 | tmppath = pycompat.mkdtemp(prefix=b'upgrade.', dir=repo.path) | ||
Jun Wu
|
r33438 | backuppath = None | ||
try: | ||||
r45302 | ui.status( | |||
Augie Fackler
|
r43346 | _( | ||
Augie Fackler
|
r43347 | b'creating temporary repository to stage migrated ' | ||
b'data: %s\n' | ||||
Augie Fackler
|
r43346 | ) | ||
% tmppath | ||||
) | ||||
Boris Feld
|
r35343 | |||
Yuya Nishihara
|
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
|
r31864 | |||
Jun Wu
|
r33438 | with dstrepo.wlock(), dstrepo.lock(): | ||
r46661 | backuppath = upgrade_engine.upgrade( | |||
Augie Fackler
|
r43346 | ui, repo, dstrepo, newreqs, upgradeactions, revlogs=revlogs | ||
) | ||||
Boris Feld
|
r41121 | if not (backup or backuppath is None): | ||
r45302 | ui.status( | |||
_(b'removing old repository content%s\n') % backuppath | ||||
) | ||||
Boris Feld
|
r41121 | repo.vfs.rmtree(backuppath, forcibly=True) | ||
backuppath = None | ||||
Pierre-Yves David
|
r31864 | |||
Jun Wu
|
r33438 | finally: | ||
r45302 | ui.status(_(b'removing temporary repository %s\n') % tmppath) | |||
Jun Wu
|
r33438 | repo.vfs.rmtree(tmppath, forcibly=True) | ||
Pierre-Yves David
|
r31864 | |||
r45302 | if backuppath and not ui.quiet: | |||
Augie Fackler
|
r43346 | ui.warn( | ||
Augie Fackler
|
r43347 | _(b'copy of old repository backed up at %s\n') % backuppath | ||
Augie Fackler
|
r43346 | ) | ||
ui.warn( | ||||
_( | ||||
Augie Fackler
|
r43347 | b'the old repository will not be deleted; remove ' | ||
b'it to free up disk space once the upgraded ' | ||||
b'repository is verified\n' | ||||
Augie Fackler
|
r43346 | ) | ||
) | ||||
Pulkit Goyal
|
r46617 | |||
r46662 | if upgrade_actions.sharesafe.name in addedreqs: | |||
Pulkit Goyal
|
r46617 | ui.warn( | ||
_( | ||||
b'repository upgraded to share safe mode, existing' | ||||
b' shares will still work in old non-safe mode. ' | ||||
b'Re-share existing shares to use them in safe mode' | ||||
b' New shares will be created in safe mode.\n' | ||||
) | ||||
) | ||||
r46662 | if upgrade_actions.sharesafe.name in removedreqs: | |||
Pulkit Goyal
|
r46618 | ui.warn( | ||
_( | ||||
b'repository downgraded to not use share safe mode, ' | ||||
b'existing shares will not work and needs to' | ||||
b' be reshared.\n' | ||||
) | ||||
) | ||||