##// END OF EJS Templates
share: rework config options to be much clearer and easier...
share: rework config options to be much clearer and easier Recently I implemented various boolean configs which control how to behave when there is a share-safe mismatch between source and share repository. Mismatch means that source supports share-safe where as share does not or vice versa. However, while discussion and documentation we realized that it's too complicated and there are some combinations of values which makes no sense. We decided to introduce a config option with 4 possible values which makes controlling and understanding things easier. The config option `share.safe-mismatch.source-{not-}safe` can have following 4 values: * abort (default): error out if there is mismatch * allow: allow to work with respecting share source configuration * {up|down}grade-abort: try to {up|down}grade, if it fails, abort * {up|down}grade-allow: try to {up|down}grade, if it fails, continue in allow mode I am not sure if I can explain 3 config options which I deleted right now in just 5 lines which is a sign of how complex they became. No test changes demonstrate that functionality is same, only names have changed. Differential Revision: https://phab.mercurial-scm.org/D9785

File last commit:

r47050:cc3452d2 default
r47050:cc3452d2 default
Show More
upgrade.py
340 lines | 11.9 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
from .i18n import _
from . import (
error,
Boris Feld
upgrade: more standard creation of the temporary repository...
r35344 hg,
Pierre-Yves David
upgrade: import 'localrepo' globally...
r31893 localrepo,
Pulkit Goyal
sharesafe: introduce functionality to automatically upgrade shares...
r46852 lock as lockmod,
Yuya Nishihara
py3: wrap tempfile.mkdtemp() to use bytes path...
r38183 pycompat,
Pulkit Goyal
sharesafe: introduce functionality to automatically upgrade shares...
r46852 requirements as requirementsmod,
scmutil,
upgrade: split actual upgrade code away from the main module...
r46661 )
from .upgrade_utils import (
upgrade: split definition and management of the actions from the main code...
r46662 actions as upgrade_actions,
upgrade: split actual upgrade code away from the main module...
r46661 engine as upgrade_engine,
Pierre-Yves David
upgrade: extract code in its own module...
r31864 )
Pulkit Goyal
sharesafe: introduce functionality to automatically upgrade shares...
r46852 from .utils import (
stringutil,
)
upgrade: split definition and management of the actions from the main code...
r46662 allformatvariant = upgrade_actions.allformatvariant
Augie Fackler
formatting: blacken the codebase...
r43346
def upgraderepo(
ui,
repo,
run=False,
optimize=None,
backup=True,
manifest=None,
changelog=None,
upgrade: add an explicite --filelogs arguments...
r46612 filelogs=None,
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Pierre-Yves David
upgrade: extract code in its own module...
r31864 """Upgrade a repository in place."""
Boris Feld
upgrade: add '-' in optimization name...
r41120 if optimize is None:
Pulkit Goyal
upgrade: drop support for old style optimization names...
r46825 optimize = {}
Pierre-Yves David
upgrade: extract code in its own module...
r31864 repo = repo.unfiltered()
upgrade: split actual upgrade code away from the main module...
r46661 revlogs = set(upgrade_engine.UPGRADE_ALL_REVLOGS)
upgrade: directly use the upgrade action constant...
r46595 specentries = (
upgrade: split actual upgrade code away from the main module...
r46661 (upgrade_engine.UPGRADE_CHANGELOG, changelog),
(upgrade_engine.UPGRADE_MANIFEST, manifest),
(upgrade_engine.UPGRADE_FILELOGS, filelogs),
upgrade: directly use the upgrade action constant...
r46595 )
upgrade: add an argument to control manifest upgrade...
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()
upgrade: directly use the upgrade action constant...
r46595 for upgrade, enabled in specified:
upgrade: add an argument to control manifest upgrade...
r43098 if enabled:
upgrade: directly use the upgrade action constant...
r46595 revlogs.add(upgrade)
upgrade: add an argument to control manifest upgrade...
r43098 else:
# none are enabled
upgrade: directly use the upgrade action constant...
r46595 for upgrade, __ in specified:
revlogs.discard(upgrade)
upgrade: add an argument to control manifest upgrade...
r43098
Pierre-Yves David
upgrade: extract code in its own module...
r31864 # Ensure the repository can be upgraded.
upgrade: move requirements checking in a dedicated function...
r46663 upgrade_actions.check_source_requirements(repo)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
upgrade: extract the checking of target requirements change...
r46666 default_options = localrepo.defaultcreateopts(repo.ui)
newreqs = localrepo.newreporequirements(repo.ui, default_options)
upgrade: split definition and management of the actions from the main code...
r46662 newreqs.update(upgrade_actions.preservedrequirements(repo))
Pierre-Yves David
upgrade: extract code in its own module...
r31864
upgrade: extract the checking of target requirements change...
r46666 upgrade_actions.check_requirements_changes(repo, newreqs)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
# Find and validate all improvements that can be made.
upgrade: split definition and management of the actions from the main code...
r46662 alloptimizations = upgrade_actions.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)
Augie Fackler
formatting: blacken the codebase...
r43346 if optimize: # anything left is unknown
raise error.Abort(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'unknown optimization action requested: %s')
% b', '.join(sorted(optimize)),
Martin von Zweigbergk
cleanup: join string literals that are already on one line...
r43387 hint=_(b'run without arguments to see valid optimizations'),
Augie Fackler
formatting: blacken the codebase...
r43346 )
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Pulkit Goyal
upgrade: rename finddeficiences() to find_format_upgrades()...
r46822 format_upgrades = upgrade_actions.find_format_upgrades(repo)
Pulkit Goyal
upgrade: rename actions to upgrade_actions...
r46827 up_actions = upgrade_actions.determine_upgrade_actions(
Pulkit Goyal
upgrade: move optimization addition to determineactions()...
r46826 repo, format_upgrades, optimizations, repo.requirements, newreqs
Augie Fackler
formatting: blacken the codebase...
r43346 )
Pulkit Goyal
upgrade: introduce post upgrade and downgrade message for improvements...
r46830 removed_actions = upgrade_actions.find_format_downgrades(repo)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
upgrade: make sure we reclone all revlogs when updating to some format...
r43100 removedreqs = repo.requirements - newreqs
addedreqs = newreqs - repo.requirements
upgrade: split actual upgrade code away from the main module...
r46661 if revlogs != upgrade_engine.UPGRADE_ALL_REVLOGS:
upgrade: split definition and management of the actions from the main code...
r46662 incompatible = upgrade_actions.RECLONES_REQUIREMENTS & (
removedreqs | addedreqs
)
upgrade: make sure we reclone all revlogs when updating to some format...
r43100 if incompatible:
Augie Fackler
formatting: blacken the codebase...
r43346 msg = _(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'ignoring revlogs selection flags, format requirements '
b'change: %s\n'
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.warn(msg % b', '.join(sorted(incompatible)))
upgrade: split actual upgrade code away from the main module...
r46661 revlogs = upgrade_engine.UPGRADE_ALL_REVLOGS
upgrade: make sure we reclone all revlogs when updating to some format...
r43100
upgrade: start moving the "to be happening" data in a dedicated object...
r46673 upgrade_op = upgrade_actions.UpgradeOperation(
Pulkit Goyal
upgrade: move `print_affected_revlogs()` to UpgradeOperation class...
r46804 ui,
upgrade: start moving the "to be happening" data in a dedicated object...
r46673 newreqs,
Pulkit Goyal
upgrade: move `printrequirements()` to UpgradeOperation class...
r46807 repo.requirements,
Pulkit Goyal
upgrade: rename actions to upgrade_actions...
r46827 up_actions,
Pulkit Goyal
upgrade: introduce post upgrade and downgrade message for improvements...
r46830 removed_actions,
upgrade: start moving the "to be happening" data in a dedicated object...
r46673 revlogs,
)
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
Pulkit Goyal
upgrade: rename finddeficiences() to find_format_upgrades()...
r46822 for d in format_upgrades:
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:
upgrade: support the --quiet flag...
r45302 ui.status(
Augie Fackler
formatting: blacken the codebase...
r43346 _(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'repository lacks features recommended by '
b'current config options:\n\n'
Augie Fackler
formatting: blacken the codebase...
r43346 )
)
Pierre-Yves David
upgrade: extract code in its own module...
r31864 for i in fromconfig:
upgrade: support the --quiet flag...
r45302 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
Pierre-Yves David
upgrade: extract code in its own module...
r31864
if onlydefault:
upgrade: support the --quiet flag...
r45302 ui.status(
Augie Fackler
formatting: blacken the codebase...
r43346 _(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'repository lacks features used by the default '
b'config options:\n\n'
Augie Fackler
formatting: blacken the codebase...
r43346 )
)
Pierre-Yves David
upgrade: extract code in its own module...
r31864 for i in onlydefault:
upgrade: support the --quiet flag...
r45302 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
Pierre-Yves David
upgrade: extract code in its own module...
r31864
upgrade: support the --quiet flag...
r45302 ui.status(b'\n')
Pierre-Yves David
upgrade: extract code in its own module...
r31864 else:
Pulkit Goyal
upgrade: rename finddeficiences() to find_format_upgrades()...
r46822 ui.status(_(b'(no format upgrades found in existing repository)\n'))
Pierre-Yves David
upgrade: extract code in its own module...
r31864
upgrade: support the --quiet flag...
r45302 ui.status(
Augie Fackler
formatting: blacken the codebase...
r43346 _(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'performing an upgrade with "--run" will make the following '
b'changes:\n\n'
Augie Fackler
formatting: blacken the codebase...
r43346 )
)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Pulkit Goyal
upgrade: move `printrequirements()` to UpgradeOperation class...
r46807 upgrade_op.print_requirements()
Pulkit Goyal
upgrade: move `printoptimisations() to UpgradeOperation class...
r46806 upgrade_op.print_optimisations()
Pulkit Goyal
upgrade: move `printupgradeactions()` to UpgradeOperation class...
r46805 upgrade_op.print_upgrade_actions()
Pulkit Goyal
upgrade: move `print_affected_revlogs()` to UpgradeOperation class...
r46804 upgrade_op.print_affected_revlogs()
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Pulkit Goyal
upgrade: move printing of unused optimizations to UpgradeOperation class...
r46808 if upgrade_op.unused_optimizations:
upgrade: support the --quiet flag...
r45302 ui.status(
Augie Fackler
formatting: blacken the codebase...
r43346 _(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'additional optimizations are available by specifying '
b'"--optimize <name>":\n\n'
Augie Fackler
formatting: blacken the codebase...
r43346 )
)
Pulkit Goyal
upgrade: move printing of unused optimizations to UpgradeOperation class...
r46808 upgrade_op.print_unused_optimizations()
Pierre-Yves David
upgrade: extract code in its own module...
r31864 return
Pulkit Goyal
upgrade: don't perform anything if nothing to do...
r46848 if not (upgrade_op.upgrade_actions or upgrade_op.removed_actions):
ui.status(_(b'nothing to do\n'))
return
Pierre-Yves David
upgrade: extract code in its own module...
r31864 # Else we're in the run=true case.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui.write(_(b'upgrade will perform the following actions:\n\n'))
Pulkit Goyal
upgrade: move `printrequirements()` to UpgradeOperation class...
r46807 upgrade_op.print_requirements()
Pulkit Goyal
upgrade: move `printoptimisations() to UpgradeOperation class...
r46806 upgrade_op.print_optimisations()
Pulkit Goyal
upgrade: move `printupgradeactions()` to UpgradeOperation class...
r46805 upgrade_op.print_upgrade_actions()
Pulkit Goyal
upgrade: move `print_affected_revlogs()` to UpgradeOperation class...
r46804 upgrade_op.print_affected_revlogs()
Pierre-Yves David
upgrade: extract code in its own module...
r31864
upgrade: support the --quiet flag...
r45302 ui.status(_(b'beginning upgrade...\n'))
Jun Wu
codemod: simplify nested withs...
r33438 with repo.wlock(), repo.lock():
upgrade: support the --quiet flag...
r45302 ui.status(_(b'repository locked and read-only\n'))
Jun Wu
codemod: simplify nested withs...
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
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 tmppath = pycompat.mkdtemp(prefix=b'upgrade.', dir=repo.path)
Jun Wu
codemod: simplify nested withs...
r33438 backuppath = None
try:
upgrade: support the --quiet flag...
r45302 ui.status(
Augie Fackler
formatting: blacken the codebase...
r43346 _(
Pulkit Goyal
upgrade: migrated -> upgraded in ui messages...
r46839 b'creating temporary repository to stage upgraded '
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'data: %s\n'
Augie Fackler
formatting: blacken the codebase...
r43346 )
% 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():
upgrade: split actual upgrade code away from the main module...
r46661 backuppath = upgrade_engine.upgrade(
upgrade: start moving the "to be happening" data in a dedicated object...
r46673 ui, repo, dstrepo, upgrade_op
Augie Fackler
formatting: blacken the codebase...
r43346 )
Pulkit Goyal
upgrade: remove unnecessary `is None` check...
r46838 if not backup:
upgrade: support the --quiet flag...
r45302 ui.status(
Pulkit Goyal
upgrade: add a missing space in status message...
r46824 _(b'removing old repository content %s\n') % backuppath
upgrade: support the --quiet flag...
r45302 )
Boris Feld
debugupgraderepo: add a --no-backup mode...
r41121 repo.vfs.rmtree(backuppath, forcibly=True)
backuppath = None
Pierre-Yves David
upgrade: extract code in its own module...
r31864
Jun Wu
codemod: simplify nested withs...
r33438 finally:
upgrade: support the --quiet flag...
r45302 ui.status(_(b'removing temporary repository %s\n') % tmppath)
Jun Wu
codemod: simplify nested withs...
r33438 repo.vfs.rmtree(tmppath, forcibly=True)
Pierre-Yves David
upgrade: extract code in its own module...
r31864
upgrade: support the --quiet flag...
r45302 if backuppath and not ui.quiet:
Augie Fackler
formatting: blacken the codebase...
r43346 ui.warn(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'copy of old repository backed up at %s\n') % backuppath
Augie Fackler
formatting: blacken the codebase...
r43346 )
ui.warn(
_(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
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
formatting: blacken the codebase...
r43346 )
)
Pulkit Goyal
upgrade: add support for experimental safe share mode...
r46617
Pulkit Goyal
upgrade: introduce post upgrade and downgrade message for improvements...
r46830 upgrade_op.print_post_op_messages()
Pulkit Goyal
sharesafe: introduce functionality to automatically upgrade shares...
r46852
Pulkit Goyal
share: rework config options to be much clearer and easier...
r47050 def upgrade_share_to_safe(
ui, hgvfs, storevfs, current_requirements, mismatch_config
):
Pulkit Goyal
sharesafe: introduce functionality to automatically upgrade shares...
r46852 """Upgrades a share to use share-safe mechanism"""
wlock = None
Pulkit Goyal
upgrade: take lock only for part where it's required...
r47047 store_requirements = localrepo._readrequires(storevfs, False)
Pulkit Goyal
upgrade: re-read current requirements after taking lock...
r47048 original_crequirements = current_requirements.copy()
Pulkit Goyal
upgrade: take lock only for part where it's required...
r47047 # after upgrade, store requires will be shared, so lets find
# the requirements which are not present in store and
# write them to share's .hg/requires
diffrequires = current_requirements - store_requirements
# add share-safe requirement as it will mark the share as share-safe
diffrequires.add(requirementsmod.SHARESAFE_REQUIREMENT)
current_requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
Pulkit Goyal
share: rework config options to be much clearer and easier...
r47050 # in `allow` case, we don't try to upgrade, we just respect the source
# state, update requirements and continue
if mismatch_config == b'allow':
return
Pulkit Goyal
sharesafe: introduce functionality to automatically upgrade shares...
r46852 try:
wlock = lockmod.trylock(ui, hgvfs, b'wlock', 0, 0)
Pulkit Goyal
upgrade: re-read current requirements after taking lock...
r47048 # some process might change the requirement in between, re-read
# and update current_requirements
locked_requirements = localrepo._readrequires(hgvfs, True)
if locked_requirements != original_crequirements:
removed = current_requirements - locked_requirements
# update current_requirements in place because it's passed
# as reference
current_requirements -= removed
current_requirements |= locked_requirements
diffrequires = current_requirements - store_requirements
# add share-safe requirement as it will mark the share as share-safe
diffrequires.add(requirementsmod.SHARESAFE_REQUIREMENT)
current_requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
Pulkit Goyal
sharesafe: introduce functionality to automatically upgrade shares...
r46852 scmutil.writerequires(hgvfs, diffrequires)
ui.warn(_(b'repository upgraded to use share-safe mode\n'))
except error.LockError as e:
Pulkit Goyal
share: rework config options to be much clearer and easier...
r47050 if mismatch_config == b'upgrade-abort':
Pulkit Goyal
sharesafe: introduce config to disallow outdated shares if upgrade fails...
r46856 raise error.Abort(
_(b'failed to upgrade share, got error: %s')
% stringutil.forcebytestr(e.strerror)
)
elif ui.configbool(b'experimental', b'sharesafe-warn-outdated-shares'):
Pulkit Goyal
sharesafe: make warning about outdated share configurable...
r46855 ui.warn(
_(b'failed to upgrade share, got error: %s\n')
% stringutil.forcebytestr(e.strerror)
)
Pulkit Goyal
sharesafe: introduce functionality to automatically upgrade shares...
r46852 finally:
if wlock:
wlock.release()
Pulkit Goyal
sharesafe: add functionality to automatically downgrade shares...
r46853
def downgrade_share_to_non_safe(
ui,
hgvfs,
sharedvfs,
current_requirements,
Pulkit Goyal
share: rework config options to be much clearer and easier...
r47050 mismatch_config,
Pulkit Goyal
sharesafe: add functionality to automatically downgrade shares...
r46853 ):
"""Downgrades a share which use share-safe to not use it"""
wlock = None
Pulkit Goyal
upgrade: take lock only for part where it's required...
r47047 source_requirements = localrepo._readrequires(sharedvfs, True)
Pulkit Goyal
upgrade: re-read current requirements after taking lock...
r47048 original_crequirements = current_requirements.copy()
Pulkit Goyal
upgrade: take lock only for part where it's required...
r47047 # we cannot be 100% sure on which requirements were present in store when
# the source supported share-safe. However, we do know that working
# directory requirements were not there. Hence we remove them
source_requirements -= requirementsmod.WORKING_DIR_REQUIREMENTS
current_requirements |= source_requirements
current_requirements.remove(requirementsmod.SHARESAFE_REQUIREMENT)
Pulkit Goyal
share: rework config options to be much clearer and easier...
r47050 if mismatch_config == b'allow':
return
Pulkit Goyal
upgrade: take lock only for part where it's required...
r47047
Pulkit Goyal
sharesafe: add functionality to automatically downgrade shares...
r46853 try:
wlock = lockmod.trylock(ui, hgvfs, b'wlock', 0, 0)
Pulkit Goyal
upgrade: re-read current requirements after taking lock...
r47048 # some process might change the requirement in between, re-read
# and update current_requirements
locked_requirements = localrepo._readrequires(hgvfs, True)
if locked_requirements != original_crequirements:
removed = current_requirements - locked_requirements
# update current_requirements in place because it's passed
# as reference
current_requirements -= removed
current_requirements |= locked_requirements
current_requirements |= source_requirements
current_requirements -= set(requirementsmod.SHARESAFE_REQUIREMENT)
Pulkit Goyal
sharesafe: add functionality to automatically downgrade shares...
r46853 scmutil.writerequires(hgvfs, current_requirements)
ui.warn(_(b'repository downgraded to not use share-safe mode\n'))
except error.LockError as e:
Pulkit Goyal
share: rework config options to be much clearer and easier...
r47050 # If upgrade-abort is set, abort when upgrade fails, else let the
# process continue as `upgrade-allow` is set
if mismatch_config == b'downgrade-abort':
raise error.Abort(
_(b'failed to downgrade share, got error: %s')
% stringutil.forcebytestr(e.strerror)
)
Pulkit Goyal
sharesafe: add functionality to automatically downgrade shares...
r46853 finally:
if wlock:
wlock.release()