##// END OF EJS Templates
upgrade: move the revlog selection code lower down the chain...
upgrade: move the revlog selection code lower down the chain We about about to make revlog section smarter. Moving the code around will make the next changesets clearer. Differential Revision: https://phab.mercurial-scm.org/D11867

File last commit:

r49273:6e045497 default
r49273:6e045497 default
Show More
upgrade.py
361 lines | 12.3 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: 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
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: move the revlog selection code lower down the chain...
r49273 # check if we need to touch revlog and if so, which ones
revlogs = set(upgrade_engine.UPGRADE_ALL_REVLOGS)
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()
for upgrade, enabled in specified:
if enabled:
revlogs.add(upgrade)
else:
# none are enabled
for upgrade, __ in specified:
revlogs.discard(upgrade)
# check the consistency of the revlog selection with the planned action
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,
Pulkit Goyal
upgrade: don't create store backup if `--no-backup` is passed...
r47092 backup,
upgrade: start moving the "to be happening" data in a dedicated object...
r46673 )
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 )
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(
Pulkit Goyal
share: rename share-safe warning config...
r47051 ui,
hgvfs,
storevfs,
current_requirements,
mismatch_config,
mismatch_warn,
Pulkit Goyal
share: rework config options to be much clearer and easier...
r47050 ):
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:
share-share: have the hint issue more consistently and point to the right doc...
r47077 hint = _(
"see `hg help config.format.use-share-safe` for more information"
)
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')
share-share: have the hint issue more consistently and point to the right doc...
r47077 % stringutil.forcebytestr(e.strerror),
hint=hint,
Pulkit Goyal
sharesafe: introduce config to disallow outdated shares if upgrade fails...
r46856 )
Pulkit Goyal
share: rename share-safe warning config...
r47051 elif mismatch_warn:
Pulkit Goyal
sharesafe: make warning about outdated share configurable...
r46855 ui.warn(
_(b'failed to upgrade share, got error: %s\n')
share-share: have the hint issue more consistently and point to the right doc...
r47077 % stringutil.forcebytestr(e.strerror),
hint=hint,
Pulkit Goyal
sharesafe: make warning about outdated share configurable...
r46855 )
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
share: rename share-safe warning config...
r47051 mismatch_warn,
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:
share-share: have the hint issue more consistently and point to the right doc...
r47077 hint = _(
"see `hg help config.format.use-share-safe` for more information"
)
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')
share-share: have the hint issue more consistently and point to the right doc...
r47077 % stringutil.forcebytestr(e.strerror),
hint=hint,
Pulkit Goyal
share: rework config options to be much clearer and easier...
r47050 )
Pulkit Goyal
share: rename share-safe warning config...
r47051 elif mismatch_warn:
ui.warn(
_(b'failed to downgrade share, got error: %s\n')
share-share: have the hint issue more consistently and point to the right doc...
r47077 % stringutil.forcebytestr(e.strerror),
hint=hint,
Pulkit Goyal
share: rename share-safe warning config...
r47051 )
Pulkit Goyal
sharesafe: add functionality to automatically downgrade shares...
r46853 finally:
if wlock:
wlock.release()