##// END OF EJS Templates
interfaces: move peer `capabilities()` to the `ipeercapabilities` interface...
interfaces: move peer `capabilities()` to the `ipeercapabilities` interface I'm not sure why this was on the `ipeercommands` interface. It appears to be because these interfaces started out as `_basewirecommands` to hold wire commands, back in 558f5b2ee10e. The capabilities interface wasn't split out until 98861a2298b5, when it pulled the capability related methods off of the `ipeerbase` interface. Perhaps it was an oversight to not look at the commands interface because, while this is a wire command, both `sshpeer` and `httppeer` now perform a handshake while instantiating the peer object, and cache a fixed list of capabilities in that object. Likewise, `localpeer` is given a fixed set of capabilities when instantiated. Back in 558f5b2ee10e, `httppeer` looks like it issued a wire command when this method was called, but `sshpeer` obtained and cached the capabilities when instantiated, and this method always returned a fixed value. There's a perfectly good interface with other capability related methods, and having it here makes it easier to implement the base `peer` mixin class.

File last commit:

r52757:1c5810ce default
r53417:1554bd50 default
Show More
auto_upgrade.py
257 lines | 8.6 KiB | text/x-python | PythonLexer
# upgrade.py - functions for automatic upgrade of Mercurial repository
#
# Copyright (c) 2022-present, Pierre-Yves David
#
# 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 annotations
from ..i18n import _
from .. import (
error,
requirements as requirementsmod,
scmutil,
)
from . import (
actions,
engine,
)
class AutoUpgradeOperation(actions.BaseOperation):
"""A limited Upgrade Operation used to run simple auto upgrade task
(Expand it as needed in the future)
"""
def __init__(self, req):
super().__init__(
new_requirements=req,
backup_store=False,
)
def get_share_safe_action(repo):
"""return an automatic-upgrade action for `share-safe` if applicable
If no action is needed, return None, otherwise return a callback to upgrade
or downgrade the repository according the configuration and repository
format.
"""
ui = repo.ui
requirements = repo.requirements
auto_upgrade_share_source = ui.configbool(
b'format',
b'use-share-safe.automatic-upgrade-of-mismatching-repositories',
)
auto_upgrade_quiet = ui.configbool(
b'format',
b'use-share-safe.automatic-upgrade-of-mismatching-repositories:quiet',
)
action = None
if (
auto_upgrade_share_source
and requirementsmod.SHARED_REQUIREMENT not in requirements
):
sf_config = ui.configbool(b'format', b'use-share-safe')
sf_local = requirementsmod.SHARESAFE_REQUIREMENT in requirements
if sf_config and not sf_local:
msg = _(
b"automatically upgrading repository to the `share-safe`"
b" feature\n"
)
hint = b"(see `hg help config.format.use-share-safe` for details)\n"
def action():
if not (ui.quiet or auto_upgrade_quiet):
ui.write_err(msg)
ui.write_err(hint)
requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
scmutil.writereporequirements(repo, requirements)
elif sf_local and not sf_config:
msg = _(
b"automatically downgrading repository from the `share-safe`"
b" feature\n"
)
hint = b"(see `hg help config.format.use-share-safe` for details)\n"
def action():
if not (ui.quiet or auto_upgrade_quiet):
ui.write_err(msg)
ui.write_err(hint)
requirements.discard(requirementsmod.SHARESAFE_REQUIREMENT)
scmutil.writereporequirements(repo, requirements)
return action
def get_tracked_hint_action(repo):
"""return an automatic-upgrade action for `tracked-hint` if applicable
If no action is needed, return None, otherwise return a callback to upgrade
or downgrade the repository according the configuration and repository
format.
"""
ui = repo.ui
requirements = set(repo.requirements)
auto_upgrade_tracked_hint = ui.configbool(
b'format',
b'use-dirstate-tracked-hint.automatic-upgrade-of-mismatching-repositories',
)
auto_upgrade_quiet = ui.configbool(
b'format',
b'use-dirstate-tracked-hint.automatic-upgrade-of-mismatching-repositories:quiet',
)
action = None
if auto_upgrade_tracked_hint:
th_config = ui.configbool(b'format', b'use-dirstate-tracked-hint')
th_local = requirementsmod.DIRSTATE_TRACKED_HINT_V1 in requirements
if th_config and not th_local:
msg = _(
b"automatically upgrading repository to the `tracked-hint`"
b" feature\n"
)
hint = b"(see `hg help config.format.use-dirstate-tracked-hint` for details)\n"
def action():
if not (ui.quiet or auto_upgrade_quiet):
ui.write_err(msg)
ui.write_err(hint)
requirements.add(requirementsmod.DIRSTATE_TRACKED_HINT_V1)
op = AutoUpgradeOperation(requirements)
engine.upgrade_tracked_hint(ui, repo, op, add=True)
elif th_local and not th_config:
msg = _(
b"automatically downgrading repository from the `tracked-hint`"
b" feature\n"
)
hint = b"(see `hg help config.format.use-dirstate-tracked-hint` for details)\n"
def action():
if not (ui.quiet or auto_upgrade_quiet):
ui.write_err(msg)
ui.write_err(hint)
requirements.discard(requirementsmod.DIRSTATE_TRACKED_HINT_V1)
op = AutoUpgradeOperation(requirements)
engine.upgrade_tracked_hint(ui, repo, op, add=False)
return action
def get_dirstate_v2_action(repo):
"""return an automatic-upgrade action for `dirstate-v2` if applicable
If no action is needed, return None, otherwise return a callback to upgrade
or downgrade the repository according the configuration and repository
format.
"""
ui = repo.ui
requirements = set(repo.requirements)
auto_upgrade_dv2 = ui.configbool(
b'format',
b'use-dirstate-v2.automatic-upgrade-of-mismatching-repositories',
)
auto_upgrade_dv2_quiet = ui.configbool(
b'format',
b'use-dirstate-v2.automatic-upgrade-of-mismatching-repositories:quiet',
)
action = None
if auto_upgrade_dv2:
d2_config = ui.configbool(b'format', b'use-dirstate-v2')
d2_local = requirementsmod.DIRSTATE_V2_REQUIREMENT in requirements
if d2_config and not d2_local:
msg = _(
b"automatically upgrading repository to the `dirstate-v2`"
b" feature\n"
)
hint = (
b"(see `hg help config.format.use-dirstate-v2` for details)\n"
)
def action():
if not (ui.quiet or auto_upgrade_dv2_quiet):
ui.write_err(msg)
ui.write_err(hint)
requirements.add(requirementsmod.DIRSTATE_V2_REQUIREMENT)
fake_op = AutoUpgradeOperation(requirements)
engine.upgrade_dirstate(repo.ui, repo, fake_op, b'v1', b'v2')
elif d2_local and not d2_config:
msg = _(
b"automatically downgrading repository from the `dirstate-v2`"
b" feature\n"
)
hint = (
b"(see `hg help config.format.use-dirstate-v2` for details)\n"
)
def action():
if not (ui.quiet or auto_upgrade_dv2_quiet):
ui.write_err(msg)
ui.write_err(hint)
requirements.discard(requirementsmod.DIRSTATE_V2_REQUIREMENT)
fake_op = AutoUpgradeOperation(requirements)
engine.upgrade_dirstate(repo.ui, repo, fake_op, b'v2', b'v1')
return action
AUTO_UPGRADE_ACTIONS = [
get_dirstate_v2_action,
get_share_safe_action,
get_tracked_hint_action,
]
def may_auto_upgrade(repo, maker_func):
"""potentially perform auto-upgrade and return the final repository to use
Auto-upgrade are "quick" repository upgrade that might automatically be run
by "any" repository access. See `hg help config.format` for automatic
upgrade documentation.
note: each relevant upgrades are done one after the other for simplicity.
This avoid having repository is partially inconsistent state while
upgrading.
repo: the current repository instance
maker_func: a factory function that can recreate a repository after an upgrade
"""
clear = False
loop = 0
try:
while not clear:
loop += 1
if loop > 100:
# XXX basic protection against infinite loop, make it better.
raise error.ProgrammingError("Too many auto upgrade loops")
clear = True
for get_action in AUTO_UPGRADE_ACTIONS:
action = get_action(repo)
if action is not None:
clear = False
with repo.wlock(wait=False), repo.lock(wait=False):
action = get_action(repo)
if action is not None:
action()
repo = maker_func()
except error.LockError:
# if we cannot get the lock, ignore the auto-upgrade attemps and
# proceed. We might want to make this behavior configurable in the
# future.
pass
return repo