##// END OF EJS Templates
auto-upgrade: introduce a way to auto-upgrade to/from share-safe...
marmoute -
r50087:2ab79873 default
parent child Browse files
Show More
@@ -0,0 +1,107 b''
1 # upgrade.py - functions for automatic upgrade of Mercurial repository
2 #
3 # Copyright (c) 2022-present, Pierre-Yves David
4 #
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
7 from ..i18n import _
8
9 from .. import (
10 error,
11 requirements as requirementsmod,
12 scmutil,
13 )
14
15
16 def get_share_safe_action(repo):
17 """return an automatic-upgrade action for `share-safe` if applicable
18
19 If no action is needed, return None, otherwise return a callback to upgrade
20 or downgrade the repository according the configuration and repository
21 format.
22 """
23 ui = repo.ui
24 requirements = repo.requirements
25 auto_upgrade_share_source = ui.configbool(
26 b'format',
27 b'use-share-safe.automatic-upgrade-of-mismatching-repositories',
28 )
29
30 action = None
31
32 if (
33 auto_upgrade_share_source
34 and requirementsmod.SHARED_REQUIREMENT not in requirements
35 ):
36 sf_config = ui.configbool(b'format', b'use-share-safe')
37 sf_local = requirementsmod.SHARESAFE_REQUIREMENT in requirements
38 if sf_config and not sf_local:
39 msg = _(
40 b"automatically upgrading repository to the `share-safe`"
41 b" feature\n"
42 )
43 hint = b"(see `hg help config.format.use-share-safe` for details)\n"
44
45 def action():
46 if not ui.quiet:
47 ui.write_err(msg)
48 ui.write_err(hint)
49 requirements.add(requirementsmod.SHARESAFE_REQUIREMENT)
50 scmutil.writereporequirements(repo, requirements)
51
52 elif sf_local and not sf_config:
53 msg = _(
54 b"automatically downgrading repository from the `share-safe`"
55 b" feature\n"
56 )
57 hint = b"(see `hg help config.format.use-share-safe` for details)\n"
58
59 def action():
60 if not ui.quiet:
61 ui.write_err(msg)
62 ui.write_err(hint)
63 requirements.discard(requirementsmod.SHARESAFE_REQUIREMENT)
64 scmutil.writereporequirements(repo, requirements)
65
66 return action
67
68
69 AUTO_UPGRADE_ACTIONS = [
70 get_share_safe_action,
71 ]
72
73
74 def may_auto_upgrade(repo, maker_func):
75 """potentially perform auto-upgrade and return the final repository to use
76
77 Auto-upgrade are "quick" repository upgrade that might automatically be run
78 by "any" repository access. See `hg help config.format` for automatic
79 upgrade documentation.
80
81 note: each relevant upgrades are done one after the other for simplicity.
82 This avoid having repository is partially inconsistent state while
83 upgrading.
84
85 repo: the current repository instance
86 maker_func: a factory function that can recreate a repository after an upgrade
87 """
88 clear = False
89
90 loop = 0
91
92 while not clear:
93 loop += 1
94 if loop > 100:
95 # XXX basic protection against infinite loop, make it better.
96 raise error.ProgrammingError("Too many auto upgrade loops")
97 clear = True
98 for get_action in AUTO_UPGRADE_ACTIONS:
99 action = get_action(repo)
100 if action is not None:
101 clear = False
102 with repo.wlock(wait=False), repo.lock(wait=False):
103 action = get_action(repo)
104 if action is not None:
105 action()
106 repo = maker_func()
107 return repo
@@ -1386,6 +1386,12 b' coreconfigitem('
1386 )
1386 )
1387 coreconfigitem(
1387 coreconfigitem(
1388 b'format',
1388 b'format',
1389 b'use-share-safe.automatic-upgrade-of-mismatching-repositories',
1390 default=False,
1391 experimental=True,
1392 )
1393 coreconfigitem(
1394 b'format',
1389 b'internal-phase',
1395 b'internal-phase',
1390 default=False,
1396 default=False,
1391 experimental=True,
1397 experimental=True,
@@ -1032,6 +1032,24 b' https://www.mercurial-scm.org/wiki/Missi'
1032
1032
1033 Enabled by default in Mercurial 6.1.
1033 Enabled by default in Mercurial 6.1.
1034
1034
1035 ``use-share-safe.automatic-upgrade-of-mismatching-repositories``
1036 When enabled, an automatic upgrade will be triggered when a repository format
1037 does not match its `use-share-safe` config.
1038
1039 This is an advanced behavior that most users will not need. We recommend you
1040 don't use this unless you are a seasoned administrator of a Mercurial install
1041 base.
1042
1043 Automatic upgrade means that any process accessing the repository will
1044 upgrade the repository format to use `share-safe`. This only triggers if a
1045 change is needed. This also applies to operation that would have been
1046 read-only (like hg status).
1047
1048 This configuration will apply for moves in any direction, either adding the
1049 `share-safe` format if `format.use-share-safe=yes` or removing the
1050 `share-safe` requirement if `format.use-share-safe=no`. So we recommend
1051 setting both this value and `format.use-share-safe` at the same time.
1052
1035 ``usestore``
1053 ``usestore``
1036 Enable or disable the "store" repository format which improves
1054 Enable or disable the "store" repository format which improves
1037 compatibility with systems that fold case or otherwise mangle
1055 compatibility with systems that fold case or otherwise mangle
@@ -3516,11 +3516,20 b' def undoname(fn):'
3516
3516
3517
3517
3518 def instance(ui, path, create, intents=None, createopts=None):
3518 def instance(ui, path, create, intents=None, createopts=None):
3519
3520 # prevent cyclic import localrepo -> upgrade -> localrepo
3521 from . import upgrade
3522
3519 localpath = urlutil.urllocalpath(path)
3523 localpath = urlutil.urllocalpath(path)
3520 if create:
3524 if create:
3521 createrepository(ui, localpath, createopts=createopts)
3525 createrepository(ui, localpath, createopts=createopts)
3522
3526
3523 return makelocalrepository(ui, localpath, intents=intents)
3527 def repo_maker():
3528 return makelocalrepository(ui, localpath, intents=intents)
3529
3530 repo = repo_maker()
3531 repo = upgrade.may_auto_upgrade(repo, repo_maker)
3532 return repo
3524
3533
3525
3534
3526 def islocal(path):
3535 def islocal(path):
@@ -19,6 +19,7 b' from . import ('
19
19
20 from .upgrade_utils import (
20 from .upgrade_utils import (
21 actions as upgrade_actions,
21 actions as upgrade_actions,
22 auto_upgrade,
22 engine as upgrade_engine,
23 engine as upgrade_engine,
23 )
24 )
24
25
@@ -26,6 +27,7 b' from .utils import ('
26 stringutil,
27 stringutil,
27 )
28 )
28
29
30 may_auto_upgrade = auto_upgrade.may_auto_upgrade
29 allformatvariant = upgrade_actions.allformatvariant
31 allformatvariant = upgrade_actions.allformatvariant
30
32
31
33
@@ -7,10 +7,10 b' use clap::Arg;'
7 use clap::ArgMatches;
7 use clap::ArgMatches;
8 use format_bytes::{format_bytes, join};
8 use format_bytes::{format_bytes, join};
9 use hg::config::{Config, ConfigSource};
9 use hg::config::{Config, ConfigSource};
10 use hg::exit_codes;
11 use hg::repo::{Repo, RepoError};
10 use hg::repo::{Repo, RepoError};
12 use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes};
11 use hg::utils::files::{get_bytes_from_os_str, get_path_from_bytes};
13 use hg::utils::SliceExt;
12 use hg::utils::SliceExt;
13 use hg::{exit_codes, requirements};
14 use std::collections::HashSet;
14 use std::collections::HashSet;
15 use std::ffi::OsString;
15 use std::ffi::OsString;
16 use std::os::unix::prelude::CommandExt;
16 use std::os::unix::prelude::CommandExt;
@@ -724,6 +724,50 b' fn check_extensions(config: &Config) -> '
724 }
724 }
725 }
725 }
726
726
727 /// Array of tuples of (auto upgrade conf, feature conf, local requirement)
728 const AUTO_UPGRADES: &[((&str, &str), (&str, &str), &str)] = &[
729 (
730 ("format", "use-share-safe.automatic-upgrade-of-mismatching-repositories"),
731 ("format", "use-share-safe"),
732 requirements::SHARESAFE_REQUIREMENT,
733 ),
734 ];
735
736 /// Mercurial allows users to automatically upgrade their repository.
737 /// `rhg` does not have the ability to upgrade yet, so fallback if an upgrade
738 /// is needed.
739 fn check_auto_upgrade(
740 config: &Config,
741 reqs: &HashSet<String>,
742 ) -> Result<(), CommandError> {
743 for (upgrade_conf, feature_conf, local_req) in AUTO_UPGRADES.iter() {
744 let auto_upgrade = config
745 .get_bool(upgrade_conf.0.as_bytes(), upgrade_conf.1.as_bytes())?;
746
747 if auto_upgrade {
748 let want_it = config.get_bool(
749 feature_conf.0.as_bytes(),
750 feature_conf.1.as_bytes(),
751 )?;
752 let have_it = reqs.contains(*local_req);
753
754 let action = match (want_it, have_it) {
755 (true, false) => Some("upgrade"),
756 (false, true) => Some("downgrade"),
757 _ => None,
758 };
759 if let Some(action) = action {
760 let message = format!(
761 "automatic {} {}.{}",
762 action, upgrade_conf.0, upgrade_conf.1
763 );
764 return Err(CommandError::unsupported(message));
765 }
766 }
767 }
768 Ok(())
769 }
770
727 fn check_unsupported(
771 fn check_unsupported(
728 config: &Config,
772 config: &Config,
729 repo: Result<&Repo, &NoRepoInCwdError>,
773 repo: Result<&Repo, &NoRepoInCwdError>,
@@ -740,6 +784,7 b' fn check_unsupported('
740 if repo.has_subrepos()? {
784 if repo.has_subrepos()? {
741 Err(CommandError::unsupported("sub-repositories"))?
785 Err(CommandError::unsupported("sub-repositories"))?
742 }
786 }
787 check_auto_upgrade(config, repo.requirements())?;
743 }
788 }
744
789
745 if config.has_non_empty_section(b"encode") {
790 if config.has_non_empty_section(b"encode") {
@@ -1606,6 +1606,8 b' Separate sections from subsections'
1606
1606
1607 "use-share-safe"
1607 "use-share-safe"
1608
1608
1609 "use-share-safe.automatic-upgrade-of-mismatching-repositories"
1610
1609 "usestore"
1611 "usestore"
1610
1612
1611 "sparse-revlog"
1613 "sparse-revlog"
General Comments 0
You need to be logged in to leave comments. Login now