Show More
@@ -1,246 +1,246 b'' | |||||
1 | # upgrade.py - functions for automatic upgrade of Mercurial repository |
|
1 | # upgrade.py - functions for automatic upgrade of Mercurial repository | |
2 | # |
|
2 | # | |
3 | # Copyright (c) 2022-present, Pierre-Yves David |
|
3 | # Copyright (c) 2022-present, Pierre-Yves David | |
4 | # |
|
4 | # | |
5 | # This software may be used and distributed according to the terms of the |
|
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. |
|
6 | # GNU General Public License version 2 or any later version. | |
7 | from ..i18n import _ |
|
7 | from ..i18n import _ | |
8 |
|
8 | |||
9 | from .. import ( |
|
9 | from .. import ( | |
10 | error, |
|
10 | error, | |
11 | requirements as requirementsmod, |
|
11 | requirements as requirementsmod, | |
12 | scmutil, |
|
12 | scmutil, | |
13 | ) |
|
13 | ) | |
14 |
|
14 | |||
15 | from . import ( |
|
15 | from . import ( | |
16 | actions, |
|
16 | actions, | |
17 | engine, |
|
17 | engine, | |
18 | ) |
|
18 | ) | |
19 |
|
19 | |||
20 |
|
20 | |||
21 | class AutoUpgradeOperation(actions.BaseOperation): |
|
21 | class AutoUpgradeOperation(actions.BaseOperation): | |
22 | """A limited Upgrade Operation used to run simple auto upgrade task |
|
22 | """A limited Upgrade Operation used to run simple auto upgrade task | |
23 |
|
23 | |||
24 | (Expand it as needed in the future) |
|
24 | (Expand it as needed in the future) | |
25 | """ |
|
25 | """ | |
26 |
|
26 | |||
27 | def __init__(self, req): |
|
27 | def __init__(self, req): | |
28 | super().__init__( |
|
28 | super().__init__( | |
29 | new_requirements=req, |
|
29 | new_requirements=req, | |
30 | backup_store=False, |
|
30 | backup_store=False, | |
31 | ) |
|
31 | ) | |
32 |
|
32 | |||
33 |
|
33 | |||
34 | def get_share_safe_action(repo): |
|
34 | def get_share_safe_action(repo): | |
35 | """return an automatic-upgrade action for `share-safe` if applicable |
|
35 | """return an automatic-upgrade action for `share-safe` if applicable | |
36 |
|
36 | |||
37 | If no action is needed, return None, otherwise return a callback to upgrade |
|
37 | If no action is needed, return None, otherwise return a callback to upgrade | |
38 | or downgrade the repository according the configuration and repository |
|
38 | or downgrade the repository according the configuration and repository | |
39 | format. |
|
39 | format. | |
40 | """ |
|
40 | """ | |
41 | ui = repo.ui |
|
41 | ui = repo.ui | |
42 | requirements = repo.requirements |
|
42 | requirements = repo.requirements | |
43 | auto_upgrade_share_source = ui.configbool( |
|
43 | auto_upgrade_share_source = ui.configbool( | |
44 | b'format', |
|
44 | b'format', | |
45 | b'use-share-safe.automatic-upgrade-of-mismatching-repositories', |
|
45 | b'use-share-safe.automatic-upgrade-of-mismatching-repositories', | |
46 | ) |
|
46 | ) | |
47 | auto_upgrade_quiet = ui.configbool( |
|
47 | auto_upgrade_quiet = ui.configbool( | |
48 | b'format', |
|
48 | b'format', | |
49 | b'use-share-safe.automatic-upgrade-of-mismatching-repositories:quiet', |
|
49 | b'use-share-safe.automatic-upgrade-of-mismatching-repositories:quiet', | |
50 | ) |
|
50 | ) | |
51 |
|
51 | |||
52 | action = None |
|
52 | action = None | |
53 |
|
53 | |||
54 | if ( |
|
54 | if ( | |
55 | auto_upgrade_share_source |
|
55 | auto_upgrade_share_source | |
56 | and requirementsmod.SHARED_REQUIREMENT not in requirements |
|
56 | and requirementsmod.SHARED_REQUIREMENT not in requirements | |
57 | ): |
|
57 | ): | |
58 | sf_config = ui.configbool(b'format', b'use-share-safe') |
|
58 | sf_config = ui.configbool(b'format', b'use-share-safe') | |
59 | sf_local = requirementsmod.SHARESAFE_REQUIREMENT in requirements |
|
59 | sf_local = requirementsmod.SHARESAFE_REQUIREMENT in requirements | |
60 | if sf_config and not sf_local: |
|
60 | if sf_config and not sf_local: | |
61 | msg = _( |
|
61 | msg = _( | |
62 | b"automatically upgrading repository to the `share-safe`" |
|
62 | b"automatically upgrading repository to the `share-safe`" | |
63 | b" feature\n" |
|
63 | b" feature\n" | |
64 | ) |
|
64 | ) | |
65 | hint = b"(see `hg help config.format.use-share-safe` for details)\n" |
|
65 | hint = b"(see `hg help config.format.use-share-safe` for details)\n" | |
66 |
|
66 | |||
67 | def action(): |
|
67 | def action(): | |
68 | if not (ui.quiet or auto_upgrade_quiet): |
|
68 | if not (ui.quiet or auto_upgrade_quiet): | |
69 | ui.write_err(msg) |
|
69 | ui.write_err(msg) | |
70 | ui.write_err(hint) |
|
70 | ui.write_err(hint) | |
71 | requirements.add(requirementsmod.SHARESAFE_REQUIREMENT) |
|
71 | requirements.add(requirementsmod.SHARESAFE_REQUIREMENT) | |
72 | scmutil.writereporequirements(repo, requirements) |
|
72 | scmutil.writereporequirements(repo, requirements) | |
73 |
|
73 | |||
74 | elif sf_local and not sf_config: |
|
74 | elif sf_local and not sf_config: | |
75 | msg = _( |
|
75 | msg = _( | |
76 | b"automatically downgrading repository from the `share-safe`" |
|
76 | b"automatically downgrading repository from the `share-safe`" | |
77 | b" feature\n" |
|
77 | b" feature\n" | |
78 | ) |
|
78 | ) | |
79 | hint = b"(see `hg help config.format.use-share-safe` for details)\n" |
|
79 | hint = b"(see `hg help config.format.use-share-safe` for details)\n" | |
80 |
|
80 | |||
81 | def action(): |
|
81 | def action(): | |
82 | if not (ui.quiet or auto_upgrade_quiet): |
|
82 | if not (ui.quiet or auto_upgrade_quiet): | |
83 | ui.write_err(msg) |
|
83 | ui.write_err(msg) | |
84 | ui.write_err(hint) |
|
84 | ui.write_err(hint) | |
85 | requirements.discard(requirementsmod.SHARESAFE_REQUIREMENT) |
|
85 | requirements.discard(requirementsmod.SHARESAFE_REQUIREMENT) | |
86 | scmutil.writereporequirements(repo, requirements) |
|
86 | scmutil.writereporequirements(repo, requirements) | |
87 |
|
87 | |||
88 | return action |
|
88 | return action | |
89 |
|
89 | |||
90 |
|
90 | |||
91 | def get_tracked_hint_action(repo): |
|
91 | def get_tracked_hint_action(repo): | |
92 | """return an automatic-upgrade action for `tracked-hint` if applicable |
|
92 | """return an automatic-upgrade action for `tracked-hint` if applicable | |
93 |
|
93 | |||
94 | If no action is needed, return None, otherwise return a callback to upgrade |
|
94 | If no action is needed, return None, otherwise return a callback to upgrade | |
95 | or downgrade the repository according the configuration and repository |
|
95 | or downgrade the repository according the configuration and repository | |
96 | format. |
|
96 | format. | |
97 | """ |
|
97 | """ | |
98 | ui = repo.ui |
|
98 | ui = repo.ui | |
99 | requirements = set(repo.requirements) |
|
99 | requirements = set(repo.requirements) | |
100 | auto_upgrade_tracked_hint = ui.configbool( |
|
100 | auto_upgrade_tracked_hint = ui.configbool( | |
101 | b'format', |
|
101 | b'format', | |
102 | b'use-dirstate-tracked-hint.automatic-upgrade-of-mismatching-repositories', |
|
102 | b'use-dirstate-tracked-hint.automatic-upgrade-of-mismatching-repositories', | |
103 | ) |
|
103 | ) | |
104 |
|
104 | |||
105 | action = None |
|
105 | action = None | |
106 |
|
106 | |||
107 | if auto_upgrade_tracked_hint: |
|
107 | if auto_upgrade_tracked_hint: | |
108 | th_config = ui.configbool(b'format', b'use-dirstate-tracked-hint') |
|
108 | th_config = ui.configbool(b'format', b'use-dirstate-tracked-hint') | |
109 | th_local = requirementsmod.DIRSTATE_TRACKED_HINT_V1 in requirements |
|
109 | th_local = requirementsmod.DIRSTATE_TRACKED_HINT_V1 in requirements | |
110 | if th_config and not th_local: |
|
110 | if th_config and not th_local: | |
111 | msg = _( |
|
111 | msg = _( | |
112 | b"automatically upgrading repository to the `tracked-hint`" |
|
112 | b"automatically upgrading repository to the `tracked-hint`" | |
113 | b" feature\n" |
|
113 | b" feature\n" | |
114 | ) |
|
114 | ) | |
115 | hint = b"(see `hg help config.format.use-dirstate-tracked-hint` for details)\n" |
|
115 | hint = b"(see `hg help config.format.use-dirstate-tracked-hint` for details)\n" | |
116 |
|
116 | |||
117 | def action(): |
|
117 | def action(): | |
118 | if not ui.quiet: |
|
118 | if not ui.quiet: | |
119 | ui.write_err(msg) |
|
119 | ui.write_err(msg) | |
120 | ui.write_err(hint) |
|
120 | ui.write_err(hint) | |
121 | requirements.add(requirementsmod.DIRSTATE_TRACKED_HINT_V1) |
|
121 | requirements.add(requirementsmod.DIRSTATE_TRACKED_HINT_V1) | |
122 | op = AutoUpgradeOperation(requirements) |
|
122 | op = AutoUpgradeOperation(requirements) | |
123 | engine.upgrade_tracked_hint(ui, repo, op, add=True) |
|
123 | engine.upgrade_tracked_hint(ui, repo, op, add=True) | |
124 |
|
124 | |||
125 | elif th_local and not th_config: |
|
125 | elif th_local and not th_config: | |
126 | msg = _( |
|
126 | msg = _( | |
127 | b"automatically downgrading repository from the `tracked-hint`" |
|
127 | b"automatically downgrading repository from the `tracked-hint`" | |
128 | b" feature\n" |
|
128 | b" feature\n" | |
129 | ) |
|
129 | ) | |
130 | hint = b"(see `hg help config.format.use-dirstate-tracked-hint` for details)\n" |
|
130 | hint = b"(see `hg help config.format.use-dirstate-tracked-hint` for details)\n" | |
131 |
|
131 | |||
132 | def action(): |
|
132 | def action(): | |
133 | if not ui.quiet: |
|
133 | if not ui.quiet: | |
134 | ui.write_err(msg) |
|
134 | ui.write_err(msg) | |
135 | ui.write_err(hint) |
|
135 | ui.write_err(hint) | |
136 | requirements.discard(requirementsmod.DIRSTATE_TRACKED_HINT_V1) |
|
136 | requirements.discard(requirementsmod.DIRSTATE_TRACKED_HINT_V1) | |
137 | op = AutoUpgradeOperation(requirements) |
|
137 | op = AutoUpgradeOperation(requirements) | |
138 | engine.upgrade_tracked_hint(ui, repo, op, add=False) |
|
138 | engine.upgrade_tracked_hint(ui, repo, op, add=False) | |
139 |
|
139 | |||
140 | return action |
|
140 | return action | |
141 |
|
141 | |||
142 |
|
142 | |||
143 | def get_dirstate_v2_action(repo): |
|
143 | def get_dirstate_v2_action(repo): | |
144 | """return an automatic-upgrade action for `dirstate-v2` if applicable |
|
144 | """return an automatic-upgrade action for `dirstate-v2` if applicable | |
145 |
|
145 | |||
146 | If no action is needed, return None, otherwise return a callback to upgrade |
|
146 | If no action is needed, return None, otherwise return a callback to upgrade | |
147 | or downgrade the repository according the configuration and repository |
|
147 | or downgrade the repository according the configuration and repository | |
148 | format. |
|
148 | format. | |
149 | """ |
|
149 | """ | |
150 | ui = repo.ui |
|
150 | ui = repo.ui | |
151 | requirements = set(repo.requirements) |
|
151 | requirements = set(repo.requirements) | |
152 |
auto_upgrade_ |
|
152 | auto_upgrade_dv2 = ui.configbool( | |
153 | b'format', |
|
153 | b'format', | |
154 | b'use-dirstate-v2.automatic-upgrade-of-mismatching-repositories', |
|
154 | b'use-dirstate-v2.automatic-upgrade-of-mismatching-repositories', | |
155 | ) |
|
155 | ) | |
156 |
|
156 | |||
157 | action = None |
|
157 | action = None | |
158 |
|
158 | |||
159 |
if auto_upgrade_ |
|
159 | if auto_upgrade_dv2: | |
160 | d2_config = ui.configbool(b'format', b'use-dirstate-v2') |
|
160 | d2_config = ui.configbool(b'format', b'use-dirstate-v2') | |
161 | d2_local = requirementsmod.DIRSTATE_V2_REQUIREMENT in requirements |
|
161 | d2_local = requirementsmod.DIRSTATE_V2_REQUIREMENT in requirements | |
162 | if d2_config and not d2_local: |
|
162 | if d2_config and not d2_local: | |
163 | msg = _( |
|
163 | msg = _( | |
164 | b"automatically upgrading repository to the `dirstate-v2`" |
|
164 | b"automatically upgrading repository to the `dirstate-v2`" | |
165 | b" feature\n" |
|
165 | b" feature\n" | |
166 | ) |
|
166 | ) | |
167 | hint = ( |
|
167 | hint = ( | |
168 | b"(see `hg help config.format.use-dirstate-v2` for details)\n" |
|
168 | b"(see `hg help config.format.use-dirstate-v2` for details)\n" | |
169 | ) |
|
169 | ) | |
170 |
|
170 | |||
171 | def action(): |
|
171 | def action(): | |
172 | if not ui.quiet: |
|
172 | if not ui.quiet: | |
173 | ui.write_err(msg) |
|
173 | ui.write_err(msg) | |
174 | ui.write_err(hint) |
|
174 | ui.write_err(hint) | |
175 | requirements.add(requirementsmod.DIRSTATE_V2_REQUIREMENT) |
|
175 | requirements.add(requirementsmod.DIRSTATE_V2_REQUIREMENT) | |
176 | fake_op = AutoUpgradeOperation(requirements) |
|
176 | fake_op = AutoUpgradeOperation(requirements) | |
177 | engine.upgrade_dirstate(repo.ui, repo, fake_op, b'v1', b'v2') |
|
177 | engine.upgrade_dirstate(repo.ui, repo, fake_op, b'v1', b'v2') | |
178 |
|
178 | |||
179 | elif d2_local and not d2_config: |
|
179 | elif d2_local and not d2_config: | |
180 | msg = _( |
|
180 | msg = _( | |
181 | b"automatically downgrading repository from the `dirstate-v2`" |
|
181 | b"automatically downgrading repository from the `dirstate-v2`" | |
182 | b" feature\n" |
|
182 | b" feature\n" | |
183 | ) |
|
183 | ) | |
184 | hint = ( |
|
184 | hint = ( | |
185 | b"(see `hg help config.format.use-dirstate-v2` for details)\n" |
|
185 | b"(see `hg help config.format.use-dirstate-v2` for details)\n" | |
186 | ) |
|
186 | ) | |
187 |
|
187 | |||
188 | def action(): |
|
188 | def action(): | |
189 | if not ui.quiet: |
|
189 | if not ui.quiet: | |
190 | ui.write_err(msg) |
|
190 | ui.write_err(msg) | |
191 | ui.write_err(hint) |
|
191 | ui.write_err(hint) | |
192 | requirements.discard(requirementsmod.DIRSTATE_V2_REQUIREMENT) |
|
192 | requirements.discard(requirementsmod.DIRSTATE_V2_REQUIREMENT) | |
193 | fake_op = AutoUpgradeOperation(requirements) |
|
193 | fake_op = AutoUpgradeOperation(requirements) | |
194 | engine.upgrade_dirstate(repo.ui, repo, fake_op, b'v2', b'v1') |
|
194 | engine.upgrade_dirstate(repo.ui, repo, fake_op, b'v2', b'v1') | |
195 |
|
195 | |||
196 | return action |
|
196 | return action | |
197 |
|
197 | |||
198 |
|
198 | |||
199 | AUTO_UPGRADE_ACTIONS = [ |
|
199 | AUTO_UPGRADE_ACTIONS = [ | |
200 | get_dirstate_v2_action, |
|
200 | get_dirstate_v2_action, | |
201 | get_share_safe_action, |
|
201 | get_share_safe_action, | |
202 | get_tracked_hint_action, |
|
202 | get_tracked_hint_action, | |
203 | ] |
|
203 | ] | |
204 |
|
204 | |||
205 |
|
205 | |||
206 | def may_auto_upgrade(repo, maker_func): |
|
206 | def may_auto_upgrade(repo, maker_func): | |
207 | """potentially perform auto-upgrade and return the final repository to use |
|
207 | """potentially perform auto-upgrade and return the final repository to use | |
208 |
|
208 | |||
209 | Auto-upgrade are "quick" repository upgrade that might automatically be run |
|
209 | Auto-upgrade are "quick" repository upgrade that might automatically be run | |
210 | by "any" repository access. See `hg help config.format` for automatic |
|
210 | by "any" repository access. See `hg help config.format` for automatic | |
211 | upgrade documentation. |
|
211 | upgrade documentation. | |
212 |
|
212 | |||
213 | note: each relevant upgrades are done one after the other for simplicity. |
|
213 | note: each relevant upgrades are done one after the other for simplicity. | |
214 | This avoid having repository is partially inconsistent state while |
|
214 | This avoid having repository is partially inconsistent state while | |
215 | upgrading. |
|
215 | upgrading. | |
216 |
|
216 | |||
217 | repo: the current repository instance |
|
217 | repo: the current repository instance | |
218 | maker_func: a factory function that can recreate a repository after an upgrade |
|
218 | maker_func: a factory function that can recreate a repository after an upgrade | |
219 | """ |
|
219 | """ | |
220 | clear = False |
|
220 | clear = False | |
221 |
|
221 | |||
222 | loop = 0 |
|
222 | loop = 0 | |
223 |
|
223 | |||
224 | try: |
|
224 | try: | |
225 | while not clear: |
|
225 | while not clear: | |
226 | loop += 1 |
|
226 | loop += 1 | |
227 | if loop > 100: |
|
227 | if loop > 100: | |
228 | # XXX basic protection against infinite loop, make it better. |
|
228 | # XXX basic protection against infinite loop, make it better. | |
229 | raise error.ProgrammingError("Too many auto upgrade loops") |
|
229 | raise error.ProgrammingError("Too many auto upgrade loops") | |
230 | clear = True |
|
230 | clear = True | |
231 | for get_action in AUTO_UPGRADE_ACTIONS: |
|
231 | for get_action in AUTO_UPGRADE_ACTIONS: | |
232 | action = get_action(repo) |
|
232 | action = get_action(repo) | |
233 | if action is not None: |
|
233 | if action is not None: | |
234 | clear = False |
|
234 | clear = False | |
235 | with repo.wlock(wait=False), repo.lock(wait=False): |
|
235 | with repo.wlock(wait=False), repo.lock(wait=False): | |
236 | action = get_action(repo) |
|
236 | action = get_action(repo) | |
237 | if action is not None: |
|
237 | if action is not None: | |
238 | action() |
|
238 | action() | |
239 | repo = maker_func() |
|
239 | repo = maker_func() | |
240 | except error.LockError: |
|
240 | except error.LockError: | |
241 | # if we cannot get the lock, ignore the auto-upgrade attemps and |
|
241 | # if we cannot get the lock, ignore the auto-upgrade attemps and | |
242 | # proceed. We might want to make this behavior configurable in the |
|
242 | # proceed. We might want to make this behavior configurable in the | |
243 | # future. |
|
243 | # future. | |
244 | pass |
|
244 | pass | |
245 |
|
245 | |||
246 | return repo |
|
246 | return repo |
General Comments 0
You need to be logged in to leave comments.
Login now