##// END OF EJS Templates
upgrade: support upgrade and downgrade from persistent nodemap...
marmoute -
r45356:526d69ee default
parent child Browse files
Show More
@@ -1,144 +1,157 b''
1 Repositories contain a file (``.hg/requires``) containing a list of
1 Repositories contain a file (``.hg/requires``) containing a list of
2 features/capabilities that are *required* for clients to interface
2 features/capabilities that are *required* for clients to interface
3 with the repository. This file has been present in Mercurial since
3 with the repository. This file has been present in Mercurial since
4 version 0.9.2 (released December 2006).
4 version 0.9.2 (released December 2006).
5
5
6 One of the first things clients do when opening a repository is read
6 One of the first things clients do when opening a repository is read
7 ``.hg/requires`` and verify that all listed requirements are supported,
7 ``.hg/requires`` and verify that all listed requirements are supported,
8 aborting if not. Requirements are therefore a strong mechanism to
8 aborting if not. Requirements are therefore a strong mechanism to
9 prevent incompatible clients from reading from unknown repository
9 prevent incompatible clients from reading from unknown repository
10 formats or even corrupting them by writing to them.
10 formats or even corrupting them by writing to them.
11
11
12 Extensions may add requirements. When they do this, clients not running
12 Extensions may add requirements. When they do this, clients not running
13 an extension will be unable to read from repositories.
13 an extension will be unable to read from repositories.
14
14
15 The following sections describe the requirements defined by the
15 The following sections describe the requirements defined by the
16 Mercurial core distribution.
16 Mercurial core distribution.
17
17
18 revlogv1
18 revlogv1
19 ========
19 ========
20
20
21 When present, revlogs are version 1 (RevlogNG). RevlogNG was introduced
21 When present, revlogs are version 1 (RevlogNG). RevlogNG was introduced
22 in 2006. The ``revlogv1`` requirement has been enabled by default
22 in 2006. The ``revlogv1`` requirement has been enabled by default
23 since the ``requires`` file was introduced in Mercurial 0.9.2.
23 since the ``requires`` file was introduced in Mercurial 0.9.2.
24
24
25 If this requirement is not present, version 0 revlogs are assumed.
25 If this requirement is not present, version 0 revlogs are assumed.
26
26
27 store
27 store
28 =====
28 =====
29
29
30 The *store* repository layout should be used.
30 The *store* repository layout should be used.
31
31
32 This requirement has been enabled by default since the ``requires`` file
32 This requirement has been enabled by default since the ``requires`` file
33 was introduced in Mercurial 0.9.2.
33 was introduced in Mercurial 0.9.2.
34
34
35 fncache
35 fncache
36 =======
36 =======
37
37
38 The *fncache* repository layout should be used.
38 The *fncache* repository layout should be used.
39
39
40 The *fncache* layout hash encodes filenames with long paths and
40 The *fncache* layout hash encodes filenames with long paths and
41 encodes reserved filenames.
41 encodes reserved filenames.
42
42
43 This requirement is enabled by default when the *store* requirement is
43 This requirement is enabled by default when the *store* requirement is
44 enabled (which is the default behavior). It was introduced in Mercurial
44 enabled (which is the default behavior). It was introduced in Mercurial
45 1.1 (released December 2008).
45 1.1 (released December 2008).
46
46
47 shared
47 shared
48 ======
48 ======
49
49
50 Denotes that the store for a repository is shared from another location
50 Denotes that the store for a repository is shared from another location
51 (defined by the ``.hg/sharedpath`` file).
51 (defined by the ``.hg/sharedpath`` file).
52
52
53 This requirement is set when a repository is created via :hg:`share`.
53 This requirement is set when a repository is created via :hg:`share`.
54
54
55 The requirement was added in Mercurial 1.3 (released July 2009).
55 The requirement was added in Mercurial 1.3 (released July 2009).
56
56
57 relshared
57 relshared
58 =========
58 =========
59
59
60 Derivative of ``shared``; the location of the store is relative to the
60 Derivative of ``shared``; the location of the store is relative to the
61 store of this repository.
61 store of this repository.
62
62
63 This requirement is set when a repository is created via :hg:`share`
63 This requirement is set when a repository is created via :hg:`share`
64 using the ``--relative`` option.
64 using the ``--relative`` option.
65
65
66 The requirement was added in Mercurial 4.2 (released May 2017).
66 The requirement was added in Mercurial 4.2 (released May 2017).
67
67
68 dotencode
68 dotencode
69 =========
69 =========
70
70
71 The *dotencode* repository layout should be used.
71 The *dotencode* repository layout should be used.
72
72
73 The *dotencode* layout encodes the first period or space in filenames
73 The *dotencode* layout encodes the first period or space in filenames
74 to prevent issues on OS X and Windows.
74 to prevent issues on OS X and Windows.
75
75
76 This requirement is enabled by default when the *store* requirement
76 This requirement is enabled by default when the *store* requirement
77 is enabled (which is the default behavior). It was introduced in
77 is enabled (which is the default behavior). It was introduced in
78 Mercurial 1.7 (released November 2010).
78 Mercurial 1.7 (released November 2010).
79
79
80 parentdelta
80 parentdelta
81 ===========
81 ===========
82
82
83 Denotes a revlog delta encoding format that was experimental and
83 Denotes a revlog delta encoding format that was experimental and
84 replaced by *generaldelta*. It should not be seen in the wild because
84 replaced by *generaldelta*. It should not be seen in the wild because
85 it was never enabled by default.
85 it was never enabled by default.
86
86
87 This requirement was added in Mercurial 1.7 and removed in Mercurial
87 This requirement was added in Mercurial 1.7 and removed in Mercurial
88 1.9.
88 1.9.
89
89
90 generaldelta
90 generaldelta
91 ============
91 ============
92
92
93 Revlogs should be created with the *generaldelta* flag enabled. The
93 Revlogs should be created with the *generaldelta* flag enabled. The
94 generaldelta flag will cause deltas to be encoded against a parent
94 generaldelta flag will cause deltas to be encoded against a parent
95 revision instead of the previous revision in the revlog.
95 revision instead of the previous revision in the revlog.
96
96
97 Support for this requirement was added in Mercurial 1.9 (released
97 Support for this requirement was added in Mercurial 1.9 (released
98 July 2011). The requirement was disabled on new repositories by
98 July 2011). The requirement was disabled on new repositories by
99 default until Mercurial 3.7 (released February 2016).
99 default until Mercurial 3.7 (released February 2016).
100
100
101 manifestv2
101 manifestv2
102 ==========
102 ==========
103
103
104 Denotes that version 2 of manifests are being used.
104 Denotes that version 2 of manifests are being used.
105
105
106 Support for this requirement was added in Mercurial 3.4 (released
106 Support for this requirement was added in Mercurial 3.4 (released
107 May 2015). The new format failed to meet expectations and support
107 May 2015). The new format failed to meet expectations and support
108 for the format and requirement were removed in Mercurial 4.6
108 for the format and requirement were removed in Mercurial 4.6
109 (released May 2018) since the feature never graduated frome experiment
109 (released May 2018) since the feature never graduated frome experiment
110 status.
110 status.
111
111
112 treemanifest
112 treemanifest
113 ============
113 ============
114
114
115 Denotes that tree manifests are being used. Tree manifests are
115 Denotes that tree manifests are being used. Tree manifests are
116 one manifest per directory (as opposed to a single flat manifest).
116 one manifest per directory (as opposed to a single flat manifest).
117
117
118 Support for this requirement was added in Mercurial 3.4 (released
118 Support for this requirement was added in Mercurial 3.4 (released
119 August 2015). The requirement is currently experimental and is
119 August 2015). The requirement is currently experimental and is
120 disabled by default.
120 disabled by default.
121
121
122 exp-sparse
122 exp-sparse
123 ==========
123 ==========
124
124
125 The working directory is sparse (only contains a subset of files).
125 The working directory is sparse (only contains a subset of files).
126
126
127 Support for this requirement was added in Mercurial 4.3 (released
127 Support for this requirement was added in Mercurial 4.3 (released
128 August 2017). This requirement and feature are experimental and may
128 August 2017). This requirement and feature are experimental and may
129 disappear in a future Mercurial release. The requirement will only
129 disappear in a future Mercurial release. The requirement will only
130 be present on repositories that have opted in to a sparse working
130 be present on repositories that have opted in to a sparse working
131 directory.
131 directory.
132
132
133 bookmarksinstore
133 bookmarksinstore
134 ==================
134 ==================
135
135
136 Bookmarks are stored in ``.hg/store/`` instead of directly in ``.hg/``
136 Bookmarks are stored in ``.hg/store/`` instead of directly in ``.hg/``
137 where they used to be stored. The active bookmark is still stored
137 where they used to be stored. The active bookmark is still stored
138 directly in ``.hg/``. This makes them always shared by ``hg share``,
138 directly in ``.hg/``. This makes them always shared by ``hg share``,
139 whether or not ``-B`` was passed.
139 whether or not ``-B`` was passed.
140
140
141 Support for this requirement was added in Mercurial 5.1 (released
141 Support for this requirement was added in Mercurial 5.1 (released
142 August 2019). The requirement will only be present on repositories
142 August 2019). The requirement will only be present on repositories
143 that have opted in to this format (by having
143 that have opted in to this format (by having
144 ``format.bookmarks-in-store=true`` set when they were created).
144 ``format.bookmarks-in-store=true`` set when they were created).
145
146 persistent-nodemap
147 ==================
148
149 The `nodemap` index (mapping nodeid to local revision number) is persisted on
150 disk. This provides speed benefit (if the associated native code is used). The
151 persistent nodemap is only used for two revlogs: the changelog and the
152 manifestlog.
153
154 Support for this requirement was added in Mercurial 5.5 (released August 2020).
155 Note that as of 5.5, only installations compiled with the Rust extension will
156 benefit from a speedup. The other installations will do the necessary work to
157 keep the index up to date, but will suffer a slowdown.
@@ -1,1428 +1,1431 b''
1 # upgrade.py - functions for in place upgrade of Mercurial repository
1 # upgrade.py - functions for in place upgrade of Mercurial repository
2 #
2 #
3 # Copyright (c) 2016-present, Gregory Szorc
3 # Copyright (c) 2016-present, Gregory Szorc
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
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import stat
10 import stat
11
11
12 from .i18n import _
12 from .i18n import _
13 from .pycompat import getattr
13 from .pycompat import getattr
14 from . import (
14 from . import (
15 changelog,
15 changelog,
16 copies,
16 copies,
17 error,
17 error,
18 filelog,
18 filelog,
19 hg,
19 hg,
20 localrepo,
20 localrepo,
21 manifest,
21 manifest,
22 pycompat,
22 pycompat,
23 revlog,
23 revlog,
24 scmutil,
24 scmutil,
25 util,
25 util,
26 vfs as vfsmod,
26 vfs as vfsmod,
27 )
27 )
28
28
29 from .utils import compression
29 from .utils import compression
30
30
31 # list of requirements that request a clone of all revlog if added/removed
31 # list of requirements that request a clone of all revlog if added/removed
32 RECLONES_REQUIREMENTS = {
32 RECLONES_REQUIREMENTS = {
33 b'generaldelta',
33 b'generaldelta',
34 localrepo.SPARSEREVLOG_REQUIREMENT,
34 localrepo.SPARSEREVLOG_REQUIREMENT,
35 }
35 }
36
36
37
37
38 def requiredsourcerequirements(repo):
38 def requiredsourcerequirements(repo):
39 """Obtain requirements required to be present to upgrade a repo.
39 """Obtain requirements required to be present to upgrade a repo.
40
40
41 An upgrade will not be allowed if the repository doesn't have the
41 An upgrade will not be allowed if the repository doesn't have the
42 requirements returned by this function.
42 requirements returned by this function.
43 """
43 """
44 return {
44 return {
45 # Introduced in Mercurial 0.9.2.
45 # Introduced in Mercurial 0.9.2.
46 b'revlogv1',
46 b'revlogv1',
47 # Introduced in Mercurial 0.9.2.
47 # Introduced in Mercurial 0.9.2.
48 b'store',
48 b'store',
49 }
49 }
50
50
51
51
52 def blocksourcerequirements(repo):
52 def blocksourcerequirements(repo):
53 """Obtain requirements that will prevent an upgrade from occurring.
53 """Obtain requirements that will prevent an upgrade from occurring.
54
54
55 An upgrade cannot be performed if the source repository contains a
55 An upgrade cannot be performed if the source repository contains a
56 requirements in the returned set.
56 requirements in the returned set.
57 """
57 """
58 return {
58 return {
59 # The upgrade code does not yet support these experimental features.
59 # The upgrade code does not yet support these experimental features.
60 # This is an artificial limitation.
60 # This is an artificial limitation.
61 b'treemanifest',
61 b'treemanifest',
62 # This was a precursor to generaldelta and was never enabled by default.
62 # This was a precursor to generaldelta and was never enabled by default.
63 # It should (hopefully) not exist in the wild.
63 # It should (hopefully) not exist in the wild.
64 b'parentdelta',
64 b'parentdelta',
65 # Upgrade should operate on the actual store, not the shared link.
65 # Upgrade should operate on the actual store, not the shared link.
66 b'shared',
66 b'shared',
67 }
67 }
68
68
69
69
70 def supportremovedrequirements(repo):
70 def supportremovedrequirements(repo):
71 """Obtain requirements that can be removed during an upgrade.
71 """Obtain requirements that can be removed during an upgrade.
72
72
73 If an upgrade were to create a repository that dropped a requirement,
73 If an upgrade were to create a repository that dropped a requirement,
74 the dropped requirement must appear in the returned set for the upgrade
74 the dropped requirement must appear in the returned set for the upgrade
75 to be allowed.
75 to be allowed.
76 """
76 """
77 supported = {
77 supported = {
78 localrepo.SPARSEREVLOG_REQUIREMENT,
78 localrepo.SPARSEREVLOG_REQUIREMENT,
79 localrepo.SIDEDATA_REQUIREMENT,
79 localrepo.SIDEDATA_REQUIREMENT,
80 localrepo.COPIESSDC_REQUIREMENT,
80 localrepo.COPIESSDC_REQUIREMENT,
81 localrepo.NODEMAP_REQUIREMENT,
81 }
82 }
82 for name in compression.compengines:
83 for name in compression.compengines:
83 engine = compression.compengines[name]
84 engine = compression.compengines[name]
84 if engine.available() and engine.revlogheader():
85 if engine.available() and engine.revlogheader():
85 supported.add(b'exp-compression-%s' % name)
86 supported.add(b'exp-compression-%s' % name)
86 if engine.name() == b'zstd':
87 if engine.name() == b'zstd':
87 supported.add(b'revlog-compression-zstd')
88 supported.add(b'revlog-compression-zstd')
88 return supported
89 return supported
89
90
90
91
91 def supporteddestrequirements(repo):
92 def supporteddestrequirements(repo):
92 """Obtain requirements that upgrade supports in the destination.
93 """Obtain requirements that upgrade supports in the destination.
93
94
94 If the result of the upgrade would create requirements not in this set,
95 If the result of the upgrade would create requirements not in this set,
95 the upgrade is disallowed.
96 the upgrade is disallowed.
96
97
97 Extensions should monkeypatch this to add their custom requirements.
98 Extensions should monkeypatch this to add their custom requirements.
98 """
99 """
99 supported = {
100 supported = {
100 b'dotencode',
101 b'dotencode',
101 b'fncache',
102 b'fncache',
102 b'generaldelta',
103 b'generaldelta',
103 b'revlogv1',
104 b'revlogv1',
104 b'store',
105 b'store',
105 localrepo.SPARSEREVLOG_REQUIREMENT,
106 localrepo.SPARSEREVLOG_REQUIREMENT,
106 localrepo.SIDEDATA_REQUIREMENT,
107 localrepo.SIDEDATA_REQUIREMENT,
107 localrepo.COPIESSDC_REQUIREMENT,
108 localrepo.COPIESSDC_REQUIREMENT,
109 localrepo.NODEMAP_REQUIREMENT,
108 }
110 }
109 for name in compression.compengines:
111 for name in compression.compengines:
110 engine = compression.compengines[name]
112 engine = compression.compengines[name]
111 if engine.available() and engine.revlogheader():
113 if engine.available() and engine.revlogheader():
112 supported.add(b'exp-compression-%s' % name)
114 supported.add(b'exp-compression-%s' % name)
113 if engine.name() == b'zstd':
115 if engine.name() == b'zstd':
114 supported.add(b'revlog-compression-zstd')
116 supported.add(b'revlog-compression-zstd')
115 return supported
117 return supported
116
118
117
119
118 def allowednewrequirements(repo):
120 def allowednewrequirements(repo):
119 """Obtain requirements that can be added to a repository during upgrade.
121 """Obtain requirements that can be added to a repository during upgrade.
120
122
121 This is used to disallow proposed requirements from being added when
123 This is used to disallow proposed requirements from being added when
122 they weren't present before.
124 they weren't present before.
123
125
124 We use a list of allowed requirement additions instead of a list of known
126 We use a list of allowed requirement additions instead of a list of known
125 bad additions because the whitelist approach is safer and will prevent
127 bad additions because the whitelist approach is safer and will prevent
126 future, unknown requirements from accidentally being added.
128 future, unknown requirements from accidentally being added.
127 """
129 """
128 supported = {
130 supported = {
129 b'dotencode',
131 b'dotencode',
130 b'fncache',
132 b'fncache',
131 b'generaldelta',
133 b'generaldelta',
132 localrepo.SPARSEREVLOG_REQUIREMENT,
134 localrepo.SPARSEREVLOG_REQUIREMENT,
133 localrepo.SIDEDATA_REQUIREMENT,
135 localrepo.SIDEDATA_REQUIREMENT,
134 localrepo.COPIESSDC_REQUIREMENT,
136 localrepo.COPIESSDC_REQUIREMENT,
137 localrepo.NODEMAP_REQUIREMENT,
135 }
138 }
136 for name in compression.compengines:
139 for name in compression.compengines:
137 engine = compression.compengines[name]
140 engine = compression.compengines[name]
138 if engine.available() and engine.revlogheader():
141 if engine.available() and engine.revlogheader():
139 supported.add(b'exp-compression-%s' % name)
142 supported.add(b'exp-compression-%s' % name)
140 if engine.name() == b'zstd':
143 if engine.name() == b'zstd':
141 supported.add(b'revlog-compression-zstd')
144 supported.add(b'revlog-compression-zstd')
142 return supported
145 return supported
143
146
144
147
145 def preservedrequirements(repo):
148 def preservedrequirements(repo):
146 return set()
149 return set()
147
150
148
151
149 deficiency = b'deficiency'
152 deficiency = b'deficiency'
150 optimisation = b'optimization'
153 optimisation = b'optimization'
151
154
152
155
153 class improvement(object):
156 class improvement(object):
154 """Represents an improvement that can be made as part of an upgrade.
157 """Represents an improvement that can be made as part of an upgrade.
155
158
156 The following attributes are defined on each instance:
159 The following attributes are defined on each instance:
157
160
158 name
161 name
159 Machine-readable string uniquely identifying this improvement. It
162 Machine-readable string uniquely identifying this improvement. It
160 will be mapped to an action later in the upgrade process.
163 will be mapped to an action later in the upgrade process.
161
164
162 type
165 type
163 Either ``deficiency`` or ``optimisation``. A deficiency is an obvious
166 Either ``deficiency`` or ``optimisation``. A deficiency is an obvious
164 problem. An optimization is an action (sometimes optional) that
167 problem. An optimization is an action (sometimes optional) that
165 can be taken to further improve the state of the repository.
168 can be taken to further improve the state of the repository.
166
169
167 description
170 description
168 Message intended for humans explaining the improvement in more detail,
171 Message intended for humans explaining the improvement in more detail,
169 including the implications of it. For ``deficiency`` types, should be
172 including the implications of it. For ``deficiency`` types, should be
170 worded in the present tense. For ``optimisation`` types, should be
173 worded in the present tense. For ``optimisation`` types, should be
171 worded in the future tense.
174 worded in the future tense.
172
175
173 upgrademessage
176 upgrademessage
174 Message intended for humans explaining what an upgrade addressing this
177 Message intended for humans explaining what an upgrade addressing this
175 issue will do. Should be worded in the future tense.
178 issue will do. Should be worded in the future tense.
176 """
179 """
177
180
178 def __init__(self, name, type, description, upgrademessage):
181 def __init__(self, name, type, description, upgrademessage):
179 self.name = name
182 self.name = name
180 self.type = type
183 self.type = type
181 self.description = description
184 self.description = description
182 self.upgrademessage = upgrademessage
185 self.upgrademessage = upgrademessage
183
186
184 def __eq__(self, other):
187 def __eq__(self, other):
185 if not isinstance(other, improvement):
188 if not isinstance(other, improvement):
186 # This is what python tell use to do
189 # This is what python tell use to do
187 return NotImplemented
190 return NotImplemented
188 return self.name == other.name
191 return self.name == other.name
189
192
190 def __ne__(self, other):
193 def __ne__(self, other):
191 return not (self == other)
194 return not (self == other)
192
195
193 def __hash__(self):
196 def __hash__(self):
194 return hash(self.name)
197 return hash(self.name)
195
198
196
199
197 allformatvariant = []
200 allformatvariant = []
198
201
199
202
200 def registerformatvariant(cls):
203 def registerformatvariant(cls):
201 allformatvariant.append(cls)
204 allformatvariant.append(cls)
202 return cls
205 return cls
203
206
204
207
205 class formatvariant(improvement):
208 class formatvariant(improvement):
206 """an improvement subclass dedicated to repository format"""
209 """an improvement subclass dedicated to repository format"""
207
210
208 type = deficiency
211 type = deficiency
209 ### The following attributes should be defined for each class:
212 ### The following attributes should be defined for each class:
210
213
211 # machine-readable string uniquely identifying this improvement. it will be
214 # machine-readable string uniquely identifying this improvement. it will be
212 # mapped to an action later in the upgrade process.
215 # mapped to an action later in the upgrade process.
213 name = None
216 name = None
214
217
215 # message intended for humans explaining the improvement in more detail,
218 # message intended for humans explaining the improvement in more detail,
216 # including the implications of it ``deficiency`` types, should be worded
219 # including the implications of it ``deficiency`` types, should be worded
217 # in the present tense.
220 # in the present tense.
218 description = None
221 description = None
219
222
220 # message intended for humans explaining what an upgrade addressing this
223 # message intended for humans explaining what an upgrade addressing this
221 # issue will do. should be worded in the future tense.
224 # issue will do. should be worded in the future tense.
222 upgrademessage = None
225 upgrademessage = None
223
226
224 # value of current Mercurial default for new repository
227 # value of current Mercurial default for new repository
225 default = None
228 default = None
226
229
227 def __init__(self):
230 def __init__(self):
228 raise NotImplementedError()
231 raise NotImplementedError()
229
232
230 @staticmethod
233 @staticmethod
231 def fromrepo(repo):
234 def fromrepo(repo):
232 """current value of the variant in the repository"""
235 """current value of the variant in the repository"""
233 raise NotImplementedError()
236 raise NotImplementedError()
234
237
235 @staticmethod
238 @staticmethod
236 def fromconfig(repo):
239 def fromconfig(repo):
237 """current value of the variant in the configuration"""
240 """current value of the variant in the configuration"""
238 raise NotImplementedError()
241 raise NotImplementedError()
239
242
240
243
241 class requirementformatvariant(formatvariant):
244 class requirementformatvariant(formatvariant):
242 """formatvariant based on a 'requirement' name.
245 """formatvariant based on a 'requirement' name.
243
246
244 Many format variant are controlled by a 'requirement'. We define a small
247 Many format variant are controlled by a 'requirement'. We define a small
245 subclass to factor the code.
248 subclass to factor the code.
246 """
249 """
247
250
248 # the requirement that control this format variant
251 # the requirement that control this format variant
249 _requirement = None
252 _requirement = None
250
253
251 @staticmethod
254 @staticmethod
252 def _newreporequirements(ui):
255 def _newreporequirements(ui):
253 return localrepo.newreporequirements(
256 return localrepo.newreporequirements(
254 ui, localrepo.defaultcreateopts(ui)
257 ui, localrepo.defaultcreateopts(ui)
255 )
258 )
256
259
257 @classmethod
260 @classmethod
258 def fromrepo(cls, repo):
261 def fromrepo(cls, repo):
259 assert cls._requirement is not None
262 assert cls._requirement is not None
260 return cls._requirement in repo.requirements
263 return cls._requirement in repo.requirements
261
264
262 @classmethod
265 @classmethod
263 def fromconfig(cls, repo):
266 def fromconfig(cls, repo):
264 assert cls._requirement is not None
267 assert cls._requirement is not None
265 return cls._requirement in cls._newreporequirements(repo.ui)
268 return cls._requirement in cls._newreporequirements(repo.ui)
266
269
267
270
268 @registerformatvariant
271 @registerformatvariant
269 class fncache(requirementformatvariant):
272 class fncache(requirementformatvariant):
270 name = b'fncache'
273 name = b'fncache'
271
274
272 _requirement = b'fncache'
275 _requirement = b'fncache'
273
276
274 default = True
277 default = True
275
278
276 description = _(
279 description = _(
277 b'long and reserved filenames may not work correctly; '
280 b'long and reserved filenames may not work correctly; '
278 b'repository performance is sub-optimal'
281 b'repository performance is sub-optimal'
279 )
282 )
280
283
281 upgrademessage = _(
284 upgrademessage = _(
282 b'repository will be more resilient to storing '
285 b'repository will be more resilient to storing '
283 b'certain paths and performance of certain '
286 b'certain paths and performance of certain '
284 b'operations should be improved'
287 b'operations should be improved'
285 )
288 )
286
289
287
290
288 @registerformatvariant
291 @registerformatvariant
289 class dotencode(requirementformatvariant):
292 class dotencode(requirementformatvariant):
290 name = b'dotencode'
293 name = b'dotencode'
291
294
292 _requirement = b'dotencode'
295 _requirement = b'dotencode'
293
296
294 default = True
297 default = True
295
298
296 description = _(
299 description = _(
297 b'storage of filenames beginning with a period or '
300 b'storage of filenames beginning with a period or '
298 b'space may not work correctly'
301 b'space may not work correctly'
299 )
302 )
300
303
301 upgrademessage = _(
304 upgrademessage = _(
302 b'repository will be better able to store files '
305 b'repository will be better able to store files '
303 b'beginning with a space or period'
306 b'beginning with a space or period'
304 )
307 )
305
308
306
309
307 @registerformatvariant
310 @registerformatvariant
308 class generaldelta(requirementformatvariant):
311 class generaldelta(requirementformatvariant):
309 name = b'generaldelta'
312 name = b'generaldelta'
310
313
311 _requirement = b'generaldelta'
314 _requirement = b'generaldelta'
312
315
313 default = True
316 default = True
314
317
315 description = _(
318 description = _(
316 b'deltas within internal storage are unable to '
319 b'deltas within internal storage are unable to '
317 b'choose optimal revisions; repository is larger and '
320 b'choose optimal revisions; repository is larger and '
318 b'slower than it could be; interaction with other '
321 b'slower than it could be; interaction with other '
319 b'repositories may require extra network and CPU '
322 b'repositories may require extra network and CPU '
320 b'resources, making "hg push" and "hg pull" slower'
323 b'resources, making "hg push" and "hg pull" slower'
321 )
324 )
322
325
323 upgrademessage = _(
326 upgrademessage = _(
324 b'repository storage will be able to create '
327 b'repository storage will be able to create '
325 b'optimal deltas; new repository data will be '
328 b'optimal deltas; new repository data will be '
326 b'smaller and read times should decrease; '
329 b'smaller and read times should decrease; '
327 b'interacting with other repositories using this '
330 b'interacting with other repositories using this '
328 b'storage model should require less network and '
331 b'storage model should require less network and '
329 b'CPU resources, making "hg push" and "hg pull" '
332 b'CPU resources, making "hg push" and "hg pull" '
330 b'faster'
333 b'faster'
331 )
334 )
332
335
333
336
334 @registerformatvariant
337 @registerformatvariant
335 class sparserevlog(requirementformatvariant):
338 class sparserevlog(requirementformatvariant):
336 name = b'sparserevlog'
339 name = b'sparserevlog'
337
340
338 _requirement = localrepo.SPARSEREVLOG_REQUIREMENT
341 _requirement = localrepo.SPARSEREVLOG_REQUIREMENT
339
342
340 default = True
343 default = True
341
344
342 description = _(
345 description = _(
343 b'in order to limit disk reading and memory usage on older '
346 b'in order to limit disk reading and memory usage on older '
344 b'version, the span of a delta chain from its root to its '
347 b'version, the span of a delta chain from its root to its '
345 b'end is limited, whatever the relevant data in this span. '
348 b'end is limited, whatever the relevant data in this span. '
346 b'This can severly limit Mercurial ability to build good '
349 b'This can severly limit Mercurial ability to build good '
347 b'chain of delta resulting is much more storage space being '
350 b'chain of delta resulting is much more storage space being '
348 b'taken and limit reusability of on disk delta during '
351 b'taken and limit reusability of on disk delta during '
349 b'exchange.'
352 b'exchange.'
350 )
353 )
351
354
352 upgrademessage = _(
355 upgrademessage = _(
353 b'Revlog supports delta chain with more unused data '
356 b'Revlog supports delta chain with more unused data '
354 b'between payload. These gaps will be skipped at read '
357 b'between payload. These gaps will be skipped at read '
355 b'time. This allows for better delta chains, making a '
358 b'time. This allows for better delta chains, making a '
356 b'better compression and faster exchange with server.'
359 b'better compression and faster exchange with server.'
357 )
360 )
358
361
359
362
360 @registerformatvariant
363 @registerformatvariant
361 class sidedata(requirementformatvariant):
364 class sidedata(requirementformatvariant):
362 name = b'sidedata'
365 name = b'sidedata'
363
366
364 _requirement = localrepo.SIDEDATA_REQUIREMENT
367 _requirement = localrepo.SIDEDATA_REQUIREMENT
365
368
366 default = False
369 default = False
367
370
368 description = _(
371 description = _(
369 b'Allows storage of extra data alongside a revision, '
372 b'Allows storage of extra data alongside a revision, '
370 b'unlocking various caching options.'
373 b'unlocking various caching options.'
371 )
374 )
372
375
373 upgrademessage = _(b'Allows storage of extra data alongside a revision.')
376 upgrademessage = _(b'Allows storage of extra data alongside a revision.')
374
377
375
378
376 @registerformatvariant
379 @registerformatvariant
377 class persistentnodemap(requirementformatvariant):
380 class persistentnodemap(requirementformatvariant):
378 name = b'persistent-nodemap'
381 name = b'persistent-nodemap'
379
382
380 _requirement = localrepo.NODEMAP_REQUIREMENT
383 _requirement = localrepo.NODEMAP_REQUIREMENT
381
384
382 default = False
385 default = False
383
386
384 description = _(
387 description = _(
385 b'persist the node -> rev mapping on disk to speedup lookup'
388 b'persist the node -> rev mapping on disk to speedup lookup'
386 )
389 )
387
390
388 upgrademessage = _(b'Speedup revision lookup by node id.')
391 upgrademessage = _(b'Speedup revision lookup by node id.')
389
392
390
393
391 @registerformatvariant
394 @registerformatvariant
392 class copiessdc(requirementformatvariant):
395 class copiessdc(requirementformatvariant):
393 name = b'copies-sdc'
396 name = b'copies-sdc'
394
397
395 _requirement = localrepo.COPIESSDC_REQUIREMENT
398 _requirement = localrepo.COPIESSDC_REQUIREMENT
396
399
397 default = False
400 default = False
398
401
399 description = _(b'Stores copies information alongside changesets.')
402 description = _(b'Stores copies information alongside changesets.')
400
403
401 upgrademessage = _(
404 upgrademessage = _(
402 b'Allows to use more efficient algorithm to deal with ' b'copy tracing.'
405 b'Allows to use more efficient algorithm to deal with ' b'copy tracing.'
403 )
406 )
404
407
405
408
406 @registerformatvariant
409 @registerformatvariant
407 class removecldeltachain(formatvariant):
410 class removecldeltachain(formatvariant):
408 name = b'plain-cl-delta'
411 name = b'plain-cl-delta'
409
412
410 default = True
413 default = True
411
414
412 description = _(
415 description = _(
413 b'changelog storage is using deltas instead of '
416 b'changelog storage is using deltas instead of '
414 b'raw entries; changelog reading and any '
417 b'raw entries; changelog reading and any '
415 b'operation relying on changelog data are slower '
418 b'operation relying on changelog data are slower '
416 b'than they could be'
419 b'than they could be'
417 )
420 )
418
421
419 upgrademessage = _(
422 upgrademessage = _(
420 b'changelog storage will be reformated to '
423 b'changelog storage will be reformated to '
421 b'store raw entries; changelog reading will be '
424 b'store raw entries; changelog reading will be '
422 b'faster; changelog size may be reduced'
425 b'faster; changelog size may be reduced'
423 )
426 )
424
427
425 @staticmethod
428 @staticmethod
426 def fromrepo(repo):
429 def fromrepo(repo):
427 # Mercurial 4.0 changed changelogs to not use delta chains. Search for
430 # Mercurial 4.0 changed changelogs to not use delta chains. Search for
428 # changelogs with deltas.
431 # changelogs with deltas.
429 cl = repo.changelog
432 cl = repo.changelog
430 chainbase = cl.chainbase
433 chainbase = cl.chainbase
431 return all(rev == chainbase(rev) for rev in cl)
434 return all(rev == chainbase(rev) for rev in cl)
432
435
433 @staticmethod
436 @staticmethod
434 def fromconfig(repo):
437 def fromconfig(repo):
435 return True
438 return True
436
439
437
440
438 @registerformatvariant
441 @registerformatvariant
439 class compressionengine(formatvariant):
442 class compressionengine(formatvariant):
440 name = b'compression'
443 name = b'compression'
441 default = b'zlib'
444 default = b'zlib'
442
445
443 description = _(
446 description = _(
444 b'Compresion algorithm used to compress data. '
447 b'Compresion algorithm used to compress data. '
445 b'Some engine are faster than other'
448 b'Some engine are faster than other'
446 )
449 )
447
450
448 upgrademessage = _(
451 upgrademessage = _(
449 b'revlog content will be recompressed with the new algorithm.'
452 b'revlog content will be recompressed with the new algorithm.'
450 )
453 )
451
454
452 @classmethod
455 @classmethod
453 def fromrepo(cls, repo):
456 def fromrepo(cls, repo):
454 # we allow multiple compression engine requirement to co-exist because
457 # we allow multiple compression engine requirement to co-exist because
455 # strickly speaking, revlog seems to support mixed compression style.
458 # strickly speaking, revlog seems to support mixed compression style.
456 #
459 #
457 # The compression used for new entries will be "the last one"
460 # The compression used for new entries will be "the last one"
458 compression = b'zlib'
461 compression = b'zlib'
459 for req in repo.requirements:
462 for req in repo.requirements:
460 prefix = req.startswith
463 prefix = req.startswith
461 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
464 if prefix(b'revlog-compression-') or prefix(b'exp-compression-'):
462 compression = req.split(b'-', 2)[2]
465 compression = req.split(b'-', 2)[2]
463 return compression
466 return compression
464
467
465 @classmethod
468 @classmethod
466 def fromconfig(cls, repo):
469 def fromconfig(cls, repo):
467 compengines = repo.ui.configlist(b'format', b'revlog-compression')
470 compengines = repo.ui.configlist(b'format', b'revlog-compression')
468 # return the first valid value as the selection code would do
471 # return the first valid value as the selection code would do
469 for comp in compengines:
472 for comp in compengines:
470 if comp in util.compengines:
473 if comp in util.compengines:
471 return comp
474 return comp
472
475
473 # no valide compression found lets display it all for clarity
476 # no valide compression found lets display it all for clarity
474 return b','.join(compengines)
477 return b','.join(compengines)
475
478
476
479
477 @registerformatvariant
480 @registerformatvariant
478 class compressionlevel(formatvariant):
481 class compressionlevel(formatvariant):
479 name = b'compression-level'
482 name = b'compression-level'
480 default = b'default'
483 default = b'default'
481
484
482 description = _(b'compression level')
485 description = _(b'compression level')
483
486
484 upgrademessage = _(b'revlog content will be recompressed')
487 upgrademessage = _(b'revlog content will be recompressed')
485
488
486 @classmethod
489 @classmethod
487 def fromrepo(cls, repo):
490 def fromrepo(cls, repo):
488 comp = compressionengine.fromrepo(repo)
491 comp = compressionengine.fromrepo(repo)
489 level = None
492 level = None
490 if comp == b'zlib':
493 if comp == b'zlib':
491 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
494 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
492 elif comp == b'zstd':
495 elif comp == b'zstd':
493 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
496 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
494 if level is None:
497 if level is None:
495 return b'default'
498 return b'default'
496 return bytes(level)
499 return bytes(level)
497
500
498 @classmethod
501 @classmethod
499 def fromconfig(cls, repo):
502 def fromconfig(cls, repo):
500 comp = compressionengine.fromconfig(repo)
503 comp = compressionengine.fromconfig(repo)
501 level = None
504 level = None
502 if comp == b'zlib':
505 if comp == b'zlib':
503 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
506 level = repo.ui.configint(b'storage', b'revlog.zlib.level')
504 elif comp == b'zstd':
507 elif comp == b'zstd':
505 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
508 level = repo.ui.configint(b'storage', b'revlog.zstd.level')
506 if level is None:
509 if level is None:
507 return b'default'
510 return b'default'
508 return bytes(level)
511 return bytes(level)
509
512
510
513
511 def finddeficiencies(repo):
514 def finddeficiencies(repo):
512 """returns a list of deficiencies that the repo suffer from"""
515 """returns a list of deficiencies that the repo suffer from"""
513 deficiencies = []
516 deficiencies = []
514
517
515 # We could detect lack of revlogv1 and store here, but they were added
518 # We could detect lack of revlogv1 and store here, but they were added
516 # in 0.9.2 and we don't support upgrading repos without these
519 # in 0.9.2 and we don't support upgrading repos without these
517 # requirements, so let's not bother.
520 # requirements, so let's not bother.
518
521
519 for fv in allformatvariant:
522 for fv in allformatvariant:
520 if not fv.fromrepo(repo):
523 if not fv.fromrepo(repo):
521 deficiencies.append(fv)
524 deficiencies.append(fv)
522
525
523 return deficiencies
526 return deficiencies
524
527
525
528
526 # search without '-' to support older form on newer client.
529 # search without '-' to support older form on newer client.
527 #
530 #
528 # We don't enforce backward compatibility for debug command so this
531 # We don't enforce backward compatibility for debug command so this
529 # might eventually be dropped. However, having to use two different
532 # might eventually be dropped. However, having to use two different
530 # forms in script when comparing result is anoying enough to add
533 # forms in script when comparing result is anoying enough to add
531 # backward compatibility for a while.
534 # backward compatibility for a while.
532 legacy_opts_map = {
535 legacy_opts_map = {
533 b'redeltaparent': b're-delta-parent',
536 b'redeltaparent': b're-delta-parent',
534 b'redeltamultibase': b're-delta-multibase',
537 b'redeltamultibase': b're-delta-multibase',
535 b'redeltaall': b're-delta-all',
538 b'redeltaall': b're-delta-all',
536 b'redeltafulladd': b're-delta-fulladd',
539 b'redeltafulladd': b're-delta-fulladd',
537 }
540 }
538
541
539
542
540 def findoptimizations(repo):
543 def findoptimizations(repo):
541 """Determine optimisation that could be used during upgrade"""
544 """Determine optimisation that could be used during upgrade"""
542 # These are unconditionally added. There is logic later that figures out
545 # These are unconditionally added. There is logic later that figures out
543 # which ones to apply.
546 # which ones to apply.
544 optimizations = []
547 optimizations = []
545
548
546 optimizations.append(
549 optimizations.append(
547 improvement(
550 improvement(
548 name=b're-delta-parent',
551 name=b're-delta-parent',
549 type=optimisation,
552 type=optimisation,
550 description=_(
553 description=_(
551 b'deltas within internal storage will be recalculated to '
554 b'deltas within internal storage will be recalculated to '
552 b'choose an optimal base revision where this was not '
555 b'choose an optimal base revision where this was not '
553 b'already done; the size of the repository may shrink and '
556 b'already done; the size of the repository may shrink and '
554 b'various operations may become faster; the first time '
557 b'various operations may become faster; the first time '
555 b'this optimization is performed could slow down upgrade '
558 b'this optimization is performed could slow down upgrade '
556 b'execution considerably; subsequent invocations should '
559 b'execution considerably; subsequent invocations should '
557 b'not run noticeably slower'
560 b'not run noticeably slower'
558 ),
561 ),
559 upgrademessage=_(
562 upgrademessage=_(
560 b'deltas within internal storage will choose a new '
563 b'deltas within internal storage will choose a new '
561 b'base revision if needed'
564 b'base revision if needed'
562 ),
565 ),
563 )
566 )
564 )
567 )
565
568
566 optimizations.append(
569 optimizations.append(
567 improvement(
570 improvement(
568 name=b're-delta-multibase',
571 name=b're-delta-multibase',
569 type=optimisation,
572 type=optimisation,
570 description=_(
573 description=_(
571 b'deltas within internal storage will be recalculated '
574 b'deltas within internal storage will be recalculated '
572 b'against multiple base revision and the smallest '
575 b'against multiple base revision and the smallest '
573 b'difference will be used; the size of the repository may '
576 b'difference will be used; the size of the repository may '
574 b'shrink significantly when there are many merges; this '
577 b'shrink significantly when there are many merges; this '
575 b'optimization will slow down execution in proportion to '
578 b'optimization will slow down execution in proportion to '
576 b'the number of merges in the repository and the amount '
579 b'the number of merges in the repository and the amount '
577 b'of files in the repository; this slow down should not '
580 b'of files in the repository; this slow down should not '
578 b'be significant unless there are tens of thousands of '
581 b'be significant unless there are tens of thousands of '
579 b'files and thousands of merges'
582 b'files and thousands of merges'
580 ),
583 ),
581 upgrademessage=_(
584 upgrademessage=_(
582 b'deltas within internal storage will choose an '
585 b'deltas within internal storage will choose an '
583 b'optimal delta by computing deltas against multiple '
586 b'optimal delta by computing deltas against multiple '
584 b'parents; may slow down execution time '
587 b'parents; may slow down execution time '
585 b'significantly'
588 b'significantly'
586 ),
589 ),
587 )
590 )
588 )
591 )
589
592
590 optimizations.append(
593 optimizations.append(
591 improvement(
594 improvement(
592 name=b're-delta-all',
595 name=b're-delta-all',
593 type=optimisation,
596 type=optimisation,
594 description=_(
597 description=_(
595 b'deltas within internal storage will always be '
598 b'deltas within internal storage will always be '
596 b'recalculated without reusing prior deltas; this will '
599 b'recalculated without reusing prior deltas; this will '
597 b'likely make execution run several times slower; this '
600 b'likely make execution run several times slower; this '
598 b'optimization is typically not needed'
601 b'optimization is typically not needed'
599 ),
602 ),
600 upgrademessage=_(
603 upgrademessage=_(
601 b'deltas within internal storage will be fully '
604 b'deltas within internal storage will be fully '
602 b'recomputed; this will likely drastically slow down '
605 b'recomputed; this will likely drastically slow down '
603 b'execution time'
606 b'execution time'
604 ),
607 ),
605 )
608 )
606 )
609 )
607
610
608 optimizations.append(
611 optimizations.append(
609 improvement(
612 improvement(
610 name=b're-delta-fulladd',
613 name=b're-delta-fulladd',
611 type=optimisation,
614 type=optimisation,
612 description=_(
615 description=_(
613 b'every revision will be re-added as if it was new '
616 b'every revision will be re-added as if it was new '
614 b'content. It will go through the full storage '
617 b'content. It will go through the full storage '
615 b'mechanism giving extensions a chance to process it '
618 b'mechanism giving extensions a chance to process it '
616 b'(eg. lfs). This is similar to "re-delta-all" but even '
619 b'(eg. lfs). This is similar to "re-delta-all" but even '
617 b'slower since more logic is involved.'
620 b'slower since more logic is involved.'
618 ),
621 ),
619 upgrademessage=_(
622 upgrademessage=_(
620 b'each revision will be added as new content to the '
623 b'each revision will be added as new content to the '
621 b'internal storage; this will likely drastically slow '
624 b'internal storage; this will likely drastically slow '
622 b'down execution time, but some extensions might need '
625 b'down execution time, but some extensions might need '
623 b'it'
626 b'it'
624 ),
627 ),
625 )
628 )
626 )
629 )
627
630
628 return optimizations
631 return optimizations
629
632
630
633
631 def determineactions(repo, deficiencies, sourcereqs, destreqs):
634 def determineactions(repo, deficiencies, sourcereqs, destreqs):
632 """Determine upgrade actions that will be performed.
635 """Determine upgrade actions that will be performed.
633
636
634 Given a list of improvements as returned by ``finddeficiencies`` and
637 Given a list of improvements as returned by ``finddeficiencies`` and
635 ``findoptimizations``, determine the list of upgrade actions that
638 ``findoptimizations``, determine the list of upgrade actions that
636 will be performed.
639 will be performed.
637
640
638 The role of this function is to filter improvements if needed, apply
641 The role of this function is to filter improvements if needed, apply
639 recommended optimizations from the improvements list that make sense,
642 recommended optimizations from the improvements list that make sense,
640 etc.
643 etc.
641
644
642 Returns a list of action names.
645 Returns a list of action names.
643 """
646 """
644 newactions = []
647 newactions = []
645
648
646 for d in deficiencies:
649 for d in deficiencies:
647 name = d._requirement
650 name = d._requirement
648
651
649 # If the action is a requirement that doesn't show up in the
652 # If the action is a requirement that doesn't show up in the
650 # destination requirements, prune the action.
653 # destination requirements, prune the action.
651 if name is not None and name not in destreqs:
654 if name is not None and name not in destreqs:
652 continue
655 continue
653
656
654 newactions.append(d)
657 newactions.append(d)
655
658
656 # FUTURE consider adding some optimizations here for certain transitions.
659 # FUTURE consider adding some optimizations here for certain transitions.
657 # e.g. adding generaldelta could schedule parent redeltas.
660 # e.g. adding generaldelta could schedule parent redeltas.
658
661
659 return newactions
662 return newactions
660
663
661
664
662 def _revlogfrompath(repo, path):
665 def _revlogfrompath(repo, path):
663 """Obtain a revlog from a repo path.
666 """Obtain a revlog from a repo path.
664
667
665 An instance of the appropriate class is returned.
668 An instance of the appropriate class is returned.
666 """
669 """
667 if path == b'00changelog.i':
670 if path == b'00changelog.i':
668 return changelog.changelog(repo.svfs)
671 return changelog.changelog(repo.svfs)
669 elif path.endswith(b'00manifest.i'):
672 elif path.endswith(b'00manifest.i'):
670 mandir = path[: -len(b'00manifest.i')]
673 mandir = path[: -len(b'00manifest.i')]
671 return manifest.manifestrevlog(repo.svfs, tree=mandir)
674 return manifest.manifestrevlog(repo.svfs, tree=mandir)
672 else:
675 else:
673 # reverse of "/".join(("data", path + ".i"))
676 # reverse of "/".join(("data", path + ".i"))
674 return filelog.filelog(repo.svfs, path[5:-2])
677 return filelog.filelog(repo.svfs, path[5:-2])
675
678
676
679
677 def _copyrevlog(tr, destrepo, oldrl, unencodedname):
680 def _copyrevlog(tr, destrepo, oldrl, unencodedname):
678 """copy all relevant files for `oldrl` into `destrepo` store
681 """copy all relevant files for `oldrl` into `destrepo` store
679
682
680 Files are copied "as is" without any transformation. The copy is performed
683 Files are copied "as is" without any transformation. The copy is performed
681 without extra checks. Callers are responsible for making sure the copied
684 without extra checks. Callers are responsible for making sure the copied
682 content is compatible with format of the destination repository.
685 content is compatible with format of the destination repository.
683 """
686 """
684 oldrl = getattr(oldrl, '_revlog', oldrl)
687 oldrl = getattr(oldrl, '_revlog', oldrl)
685 newrl = _revlogfrompath(destrepo, unencodedname)
688 newrl = _revlogfrompath(destrepo, unencodedname)
686 newrl = getattr(newrl, '_revlog', newrl)
689 newrl = getattr(newrl, '_revlog', newrl)
687
690
688 oldvfs = oldrl.opener
691 oldvfs = oldrl.opener
689 newvfs = newrl.opener
692 newvfs = newrl.opener
690 oldindex = oldvfs.join(oldrl.indexfile)
693 oldindex = oldvfs.join(oldrl.indexfile)
691 newindex = newvfs.join(newrl.indexfile)
694 newindex = newvfs.join(newrl.indexfile)
692 olddata = oldvfs.join(oldrl.datafile)
695 olddata = oldvfs.join(oldrl.datafile)
693 newdata = newvfs.join(newrl.datafile)
696 newdata = newvfs.join(newrl.datafile)
694
697
695 with newvfs(newrl.indexfile, b'w'):
698 with newvfs(newrl.indexfile, b'w'):
696 pass # create all the directories
699 pass # create all the directories
697
700
698 util.copyfile(oldindex, newindex)
701 util.copyfile(oldindex, newindex)
699 copydata = oldrl.opener.exists(oldrl.datafile)
702 copydata = oldrl.opener.exists(oldrl.datafile)
700 if copydata:
703 if copydata:
701 util.copyfile(olddata, newdata)
704 util.copyfile(olddata, newdata)
702
705
703 if not (
706 if not (
704 unencodedname.endswith(b'00changelog.i')
707 unencodedname.endswith(b'00changelog.i')
705 or unencodedname.endswith(b'00manifest.i')
708 or unencodedname.endswith(b'00manifest.i')
706 ):
709 ):
707 destrepo.svfs.fncache.add(unencodedname)
710 destrepo.svfs.fncache.add(unencodedname)
708 if copydata:
711 if copydata:
709 destrepo.svfs.fncache.add(unencodedname[:-2] + b'.d')
712 destrepo.svfs.fncache.add(unencodedname[:-2] + b'.d')
710
713
711
714
712 UPGRADE_CHANGELOG = object()
715 UPGRADE_CHANGELOG = object()
713 UPGRADE_MANIFEST = object()
716 UPGRADE_MANIFEST = object()
714 UPGRADE_FILELOG = object()
717 UPGRADE_FILELOG = object()
715
718
716 UPGRADE_ALL_REVLOGS = frozenset(
719 UPGRADE_ALL_REVLOGS = frozenset(
717 [UPGRADE_CHANGELOG, UPGRADE_MANIFEST, UPGRADE_FILELOG]
720 [UPGRADE_CHANGELOG, UPGRADE_MANIFEST, UPGRADE_FILELOG]
718 )
721 )
719
722
720
723
721 def getsidedatacompanion(srcrepo, dstrepo):
724 def getsidedatacompanion(srcrepo, dstrepo):
722 sidedatacompanion = None
725 sidedatacompanion = None
723 removedreqs = srcrepo.requirements - dstrepo.requirements
726 removedreqs = srcrepo.requirements - dstrepo.requirements
724 addedreqs = dstrepo.requirements - srcrepo.requirements
727 addedreqs = dstrepo.requirements - srcrepo.requirements
725 if localrepo.SIDEDATA_REQUIREMENT in removedreqs:
728 if localrepo.SIDEDATA_REQUIREMENT in removedreqs:
726
729
727 def sidedatacompanion(rl, rev):
730 def sidedatacompanion(rl, rev):
728 rl = getattr(rl, '_revlog', rl)
731 rl = getattr(rl, '_revlog', rl)
729 if rl.flags(rev) & revlog.REVIDX_SIDEDATA:
732 if rl.flags(rev) & revlog.REVIDX_SIDEDATA:
730 return True, (), {}
733 return True, (), {}
731 return False, (), {}
734 return False, (), {}
732
735
733 elif localrepo.COPIESSDC_REQUIREMENT in addedreqs:
736 elif localrepo.COPIESSDC_REQUIREMENT in addedreqs:
734 sidedatacompanion = copies.getsidedataadder(srcrepo, dstrepo)
737 sidedatacompanion = copies.getsidedataadder(srcrepo, dstrepo)
735 elif localrepo.COPIESSDC_REQUIREMENT in removedreqs:
738 elif localrepo.COPIESSDC_REQUIREMENT in removedreqs:
736 sidedatacompanion = copies.getsidedataremover(srcrepo, dstrepo)
739 sidedatacompanion = copies.getsidedataremover(srcrepo, dstrepo)
737 return sidedatacompanion
740 return sidedatacompanion
738
741
739
742
740 def matchrevlog(revlogfilter, entry):
743 def matchrevlog(revlogfilter, entry):
741 """check is a revlog is selected for cloning
744 """check is a revlog is selected for cloning
742
745
743 The store entry is checked against the passed filter"""
746 The store entry is checked against the passed filter"""
744 if entry.endswith(b'00changelog.i'):
747 if entry.endswith(b'00changelog.i'):
745 return UPGRADE_CHANGELOG in revlogfilter
748 return UPGRADE_CHANGELOG in revlogfilter
746 elif entry.endswith(b'00manifest.i'):
749 elif entry.endswith(b'00manifest.i'):
747 return UPGRADE_MANIFEST in revlogfilter
750 return UPGRADE_MANIFEST in revlogfilter
748 return UPGRADE_FILELOG in revlogfilter
751 return UPGRADE_FILELOG in revlogfilter
749
752
750
753
751 def _clonerevlogs(
754 def _clonerevlogs(
752 ui,
755 ui,
753 srcrepo,
756 srcrepo,
754 dstrepo,
757 dstrepo,
755 tr,
758 tr,
756 deltareuse,
759 deltareuse,
757 forcedeltabothparents,
760 forcedeltabothparents,
758 revlogs=UPGRADE_ALL_REVLOGS,
761 revlogs=UPGRADE_ALL_REVLOGS,
759 ):
762 ):
760 """Copy revlogs between 2 repos."""
763 """Copy revlogs between 2 repos."""
761 revcount = 0
764 revcount = 0
762 srcsize = 0
765 srcsize = 0
763 srcrawsize = 0
766 srcrawsize = 0
764 dstsize = 0
767 dstsize = 0
765 fcount = 0
768 fcount = 0
766 frevcount = 0
769 frevcount = 0
767 fsrcsize = 0
770 fsrcsize = 0
768 frawsize = 0
771 frawsize = 0
769 fdstsize = 0
772 fdstsize = 0
770 mcount = 0
773 mcount = 0
771 mrevcount = 0
774 mrevcount = 0
772 msrcsize = 0
775 msrcsize = 0
773 mrawsize = 0
776 mrawsize = 0
774 mdstsize = 0
777 mdstsize = 0
775 crevcount = 0
778 crevcount = 0
776 csrcsize = 0
779 csrcsize = 0
777 crawsize = 0
780 crawsize = 0
778 cdstsize = 0
781 cdstsize = 0
779
782
780 alldatafiles = list(srcrepo.store.walk())
783 alldatafiles = list(srcrepo.store.walk())
781
784
782 # Perform a pass to collect metadata. This validates we can open all
785 # Perform a pass to collect metadata. This validates we can open all
783 # source files and allows a unified progress bar to be displayed.
786 # source files and allows a unified progress bar to be displayed.
784 for unencoded, encoded, size in alldatafiles:
787 for unencoded, encoded, size in alldatafiles:
785 if unencoded.endswith(b'.d'):
788 if unencoded.endswith(b'.d'):
786 continue
789 continue
787
790
788 rl = _revlogfrompath(srcrepo, unencoded)
791 rl = _revlogfrompath(srcrepo, unencoded)
789
792
790 info = rl.storageinfo(
793 info = rl.storageinfo(
791 exclusivefiles=True,
794 exclusivefiles=True,
792 revisionscount=True,
795 revisionscount=True,
793 trackedsize=True,
796 trackedsize=True,
794 storedsize=True,
797 storedsize=True,
795 )
798 )
796
799
797 revcount += info[b'revisionscount'] or 0
800 revcount += info[b'revisionscount'] or 0
798 datasize = info[b'storedsize'] or 0
801 datasize = info[b'storedsize'] or 0
799 rawsize = info[b'trackedsize'] or 0
802 rawsize = info[b'trackedsize'] or 0
800
803
801 srcsize += datasize
804 srcsize += datasize
802 srcrawsize += rawsize
805 srcrawsize += rawsize
803
806
804 # This is for the separate progress bars.
807 # This is for the separate progress bars.
805 if isinstance(rl, changelog.changelog):
808 if isinstance(rl, changelog.changelog):
806 crevcount += len(rl)
809 crevcount += len(rl)
807 csrcsize += datasize
810 csrcsize += datasize
808 crawsize += rawsize
811 crawsize += rawsize
809 elif isinstance(rl, manifest.manifestrevlog):
812 elif isinstance(rl, manifest.manifestrevlog):
810 mcount += 1
813 mcount += 1
811 mrevcount += len(rl)
814 mrevcount += len(rl)
812 msrcsize += datasize
815 msrcsize += datasize
813 mrawsize += rawsize
816 mrawsize += rawsize
814 elif isinstance(rl, filelog.filelog):
817 elif isinstance(rl, filelog.filelog):
815 fcount += 1
818 fcount += 1
816 frevcount += len(rl)
819 frevcount += len(rl)
817 fsrcsize += datasize
820 fsrcsize += datasize
818 frawsize += rawsize
821 frawsize += rawsize
819 else:
822 else:
820 error.ProgrammingError(b'unknown revlog type')
823 error.ProgrammingError(b'unknown revlog type')
821
824
822 if not revcount:
825 if not revcount:
823 return
826 return
824
827
825 ui.status(
828 ui.status(
826 _(
829 _(
827 b'migrating %d total revisions (%d in filelogs, %d in manifests, '
830 b'migrating %d total revisions (%d in filelogs, %d in manifests, '
828 b'%d in changelog)\n'
831 b'%d in changelog)\n'
829 )
832 )
830 % (revcount, frevcount, mrevcount, crevcount)
833 % (revcount, frevcount, mrevcount, crevcount)
831 )
834 )
832 ui.status(
835 ui.status(
833 _(b'migrating %s in store; %s tracked data\n')
836 _(b'migrating %s in store; %s tracked data\n')
834 % ((util.bytecount(srcsize), util.bytecount(srcrawsize)))
837 % ((util.bytecount(srcsize), util.bytecount(srcrawsize)))
835 )
838 )
836
839
837 # Used to keep track of progress.
840 # Used to keep track of progress.
838 progress = None
841 progress = None
839
842
840 def oncopiedrevision(rl, rev, node):
843 def oncopiedrevision(rl, rev, node):
841 progress.increment()
844 progress.increment()
842
845
843 sidedatacompanion = getsidedatacompanion(srcrepo, dstrepo)
846 sidedatacompanion = getsidedatacompanion(srcrepo, dstrepo)
844
847
845 # Do the actual copying.
848 # Do the actual copying.
846 # FUTURE this operation can be farmed off to worker processes.
849 # FUTURE this operation can be farmed off to worker processes.
847 seen = set()
850 seen = set()
848 for unencoded, encoded, size in alldatafiles:
851 for unencoded, encoded, size in alldatafiles:
849 if unencoded.endswith(b'.d'):
852 if unencoded.endswith(b'.d'):
850 continue
853 continue
851
854
852 oldrl = _revlogfrompath(srcrepo, unencoded)
855 oldrl = _revlogfrompath(srcrepo, unencoded)
853
856
854 if isinstance(oldrl, changelog.changelog) and b'c' not in seen:
857 if isinstance(oldrl, changelog.changelog) and b'c' not in seen:
855 ui.status(
858 ui.status(
856 _(
859 _(
857 b'finished migrating %d manifest revisions across %d '
860 b'finished migrating %d manifest revisions across %d '
858 b'manifests; change in size: %s\n'
861 b'manifests; change in size: %s\n'
859 )
862 )
860 % (mrevcount, mcount, util.bytecount(mdstsize - msrcsize))
863 % (mrevcount, mcount, util.bytecount(mdstsize - msrcsize))
861 )
864 )
862
865
863 ui.status(
866 ui.status(
864 _(
867 _(
865 b'migrating changelog containing %d revisions '
868 b'migrating changelog containing %d revisions '
866 b'(%s in store; %s tracked data)\n'
869 b'(%s in store; %s tracked data)\n'
867 )
870 )
868 % (
871 % (
869 crevcount,
872 crevcount,
870 util.bytecount(csrcsize),
873 util.bytecount(csrcsize),
871 util.bytecount(crawsize),
874 util.bytecount(crawsize),
872 )
875 )
873 )
876 )
874 seen.add(b'c')
877 seen.add(b'c')
875 progress = srcrepo.ui.makeprogress(
878 progress = srcrepo.ui.makeprogress(
876 _(b'changelog revisions'), total=crevcount
879 _(b'changelog revisions'), total=crevcount
877 )
880 )
878 elif isinstance(oldrl, manifest.manifestrevlog) and b'm' not in seen:
881 elif isinstance(oldrl, manifest.manifestrevlog) and b'm' not in seen:
879 ui.status(
882 ui.status(
880 _(
883 _(
881 b'finished migrating %d filelog revisions across %d '
884 b'finished migrating %d filelog revisions across %d '
882 b'filelogs; change in size: %s\n'
885 b'filelogs; change in size: %s\n'
883 )
886 )
884 % (frevcount, fcount, util.bytecount(fdstsize - fsrcsize))
887 % (frevcount, fcount, util.bytecount(fdstsize - fsrcsize))
885 )
888 )
886
889
887 ui.status(
890 ui.status(
888 _(
891 _(
889 b'migrating %d manifests containing %d revisions '
892 b'migrating %d manifests containing %d revisions '
890 b'(%s in store; %s tracked data)\n'
893 b'(%s in store; %s tracked data)\n'
891 )
894 )
892 % (
895 % (
893 mcount,
896 mcount,
894 mrevcount,
897 mrevcount,
895 util.bytecount(msrcsize),
898 util.bytecount(msrcsize),
896 util.bytecount(mrawsize),
899 util.bytecount(mrawsize),
897 )
900 )
898 )
901 )
899 seen.add(b'm')
902 seen.add(b'm')
900 if progress:
903 if progress:
901 progress.complete()
904 progress.complete()
902 progress = srcrepo.ui.makeprogress(
905 progress = srcrepo.ui.makeprogress(
903 _(b'manifest revisions'), total=mrevcount
906 _(b'manifest revisions'), total=mrevcount
904 )
907 )
905 elif b'f' not in seen:
908 elif b'f' not in seen:
906 ui.status(
909 ui.status(
907 _(
910 _(
908 b'migrating %d filelogs containing %d revisions '
911 b'migrating %d filelogs containing %d revisions '
909 b'(%s in store; %s tracked data)\n'
912 b'(%s in store; %s tracked data)\n'
910 )
913 )
911 % (
914 % (
912 fcount,
915 fcount,
913 frevcount,
916 frevcount,
914 util.bytecount(fsrcsize),
917 util.bytecount(fsrcsize),
915 util.bytecount(frawsize),
918 util.bytecount(frawsize),
916 )
919 )
917 )
920 )
918 seen.add(b'f')
921 seen.add(b'f')
919 if progress:
922 if progress:
920 progress.complete()
923 progress.complete()
921 progress = srcrepo.ui.makeprogress(
924 progress = srcrepo.ui.makeprogress(
922 _(b'file revisions'), total=frevcount
925 _(b'file revisions'), total=frevcount
923 )
926 )
924
927
925 if matchrevlog(revlogs, unencoded):
928 if matchrevlog(revlogs, unencoded):
926 ui.note(
929 ui.note(
927 _(b'cloning %d revisions from %s\n') % (len(oldrl), unencoded)
930 _(b'cloning %d revisions from %s\n') % (len(oldrl), unencoded)
928 )
931 )
929 newrl = _revlogfrompath(dstrepo, unencoded)
932 newrl = _revlogfrompath(dstrepo, unencoded)
930 oldrl.clone(
933 oldrl.clone(
931 tr,
934 tr,
932 newrl,
935 newrl,
933 addrevisioncb=oncopiedrevision,
936 addrevisioncb=oncopiedrevision,
934 deltareuse=deltareuse,
937 deltareuse=deltareuse,
935 forcedeltabothparents=forcedeltabothparents,
938 forcedeltabothparents=forcedeltabothparents,
936 sidedatacompanion=sidedatacompanion,
939 sidedatacompanion=sidedatacompanion,
937 )
940 )
938 else:
941 else:
939 msg = _(b'blindly copying %s containing %i revisions\n')
942 msg = _(b'blindly copying %s containing %i revisions\n')
940 ui.note(msg % (unencoded, len(oldrl)))
943 ui.note(msg % (unencoded, len(oldrl)))
941 _copyrevlog(tr, dstrepo, oldrl, unencoded)
944 _copyrevlog(tr, dstrepo, oldrl, unencoded)
942
945
943 newrl = _revlogfrompath(dstrepo, unencoded)
946 newrl = _revlogfrompath(dstrepo, unencoded)
944
947
945 info = newrl.storageinfo(storedsize=True)
948 info = newrl.storageinfo(storedsize=True)
946 datasize = info[b'storedsize'] or 0
949 datasize = info[b'storedsize'] or 0
947
950
948 dstsize += datasize
951 dstsize += datasize
949
952
950 if isinstance(newrl, changelog.changelog):
953 if isinstance(newrl, changelog.changelog):
951 cdstsize += datasize
954 cdstsize += datasize
952 elif isinstance(newrl, manifest.manifestrevlog):
955 elif isinstance(newrl, manifest.manifestrevlog):
953 mdstsize += datasize
956 mdstsize += datasize
954 else:
957 else:
955 fdstsize += datasize
958 fdstsize += datasize
956
959
957 progress.complete()
960 progress.complete()
958
961
959 ui.status(
962 ui.status(
960 _(
963 _(
961 b'finished migrating %d changelog revisions; change in size: '
964 b'finished migrating %d changelog revisions; change in size: '
962 b'%s\n'
965 b'%s\n'
963 )
966 )
964 % (crevcount, util.bytecount(cdstsize - csrcsize))
967 % (crevcount, util.bytecount(cdstsize - csrcsize))
965 )
968 )
966
969
967 ui.status(
970 ui.status(
968 _(
971 _(
969 b'finished migrating %d total revisions; total change in store '
972 b'finished migrating %d total revisions; total change in store '
970 b'size: %s\n'
973 b'size: %s\n'
971 )
974 )
972 % (revcount, util.bytecount(dstsize - srcsize))
975 % (revcount, util.bytecount(dstsize - srcsize))
973 )
976 )
974
977
975
978
976 def _filterstorefile(srcrepo, dstrepo, requirements, path, mode, st):
979 def _filterstorefile(srcrepo, dstrepo, requirements, path, mode, st):
977 """Determine whether to copy a store file during upgrade.
980 """Determine whether to copy a store file during upgrade.
978
981
979 This function is called when migrating store files from ``srcrepo`` to
982 This function is called when migrating store files from ``srcrepo`` to
980 ``dstrepo`` as part of upgrading a repository.
983 ``dstrepo`` as part of upgrading a repository.
981
984
982 Args:
985 Args:
983 srcrepo: repo we are copying from
986 srcrepo: repo we are copying from
984 dstrepo: repo we are copying to
987 dstrepo: repo we are copying to
985 requirements: set of requirements for ``dstrepo``
988 requirements: set of requirements for ``dstrepo``
986 path: store file being examined
989 path: store file being examined
987 mode: the ``ST_MODE`` file type of ``path``
990 mode: the ``ST_MODE`` file type of ``path``
988 st: ``stat`` data structure for ``path``
991 st: ``stat`` data structure for ``path``
989
992
990 Function should return ``True`` if the file is to be copied.
993 Function should return ``True`` if the file is to be copied.
991 """
994 """
992 # Skip revlogs.
995 # Skip revlogs.
993 if path.endswith((b'.i', b'.d')):
996 if path.endswith((b'.i', b'.d', b'.n', b'.nd')):
994 return False
997 return False
995 # Skip transaction related files.
998 # Skip transaction related files.
996 if path.startswith(b'undo'):
999 if path.startswith(b'undo'):
997 return False
1000 return False
998 # Only copy regular files.
1001 # Only copy regular files.
999 if mode != stat.S_IFREG:
1002 if mode != stat.S_IFREG:
1000 return False
1003 return False
1001 # Skip other skipped files.
1004 # Skip other skipped files.
1002 if path in (b'lock', b'fncache'):
1005 if path in (b'lock', b'fncache'):
1003 return False
1006 return False
1004
1007
1005 return True
1008 return True
1006
1009
1007
1010
1008 def _finishdatamigration(ui, srcrepo, dstrepo, requirements):
1011 def _finishdatamigration(ui, srcrepo, dstrepo, requirements):
1009 """Hook point for extensions to perform additional actions during upgrade.
1012 """Hook point for extensions to perform additional actions during upgrade.
1010
1013
1011 This function is called after revlogs and store files have been copied but
1014 This function is called after revlogs and store files have been copied but
1012 before the new store is swapped into the original location.
1015 before the new store is swapped into the original location.
1013 """
1016 """
1014
1017
1015
1018
1016 def _upgraderepo(
1019 def _upgraderepo(
1017 ui, srcrepo, dstrepo, requirements, actions, revlogs=UPGRADE_ALL_REVLOGS
1020 ui, srcrepo, dstrepo, requirements, actions, revlogs=UPGRADE_ALL_REVLOGS
1018 ):
1021 ):
1019 """Do the low-level work of upgrading a repository.
1022 """Do the low-level work of upgrading a repository.
1020
1023
1021 The upgrade is effectively performed as a copy between a source
1024 The upgrade is effectively performed as a copy between a source
1022 repository and a temporary destination repository.
1025 repository and a temporary destination repository.
1023
1026
1024 The source repository is unmodified for as long as possible so the
1027 The source repository is unmodified for as long as possible so the
1025 upgrade can abort at any time without causing loss of service for
1028 upgrade can abort at any time without causing loss of service for
1026 readers and without corrupting the source repository.
1029 readers and without corrupting the source repository.
1027 """
1030 """
1028 assert srcrepo.currentwlock()
1031 assert srcrepo.currentwlock()
1029 assert dstrepo.currentwlock()
1032 assert dstrepo.currentwlock()
1030
1033
1031 ui.status(
1034 ui.status(
1032 _(
1035 _(
1033 b'(it is safe to interrupt this process any time before '
1036 b'(it is safe to interrupt this process any time before '
1034 b'data migration completes)\n'
1037 b'data migration completes)\n'
1035 )
1038 )
1036 )
1039 )
1037
1040
1038 if b're-delta-all' in actions:
1041 if b're-delta-all' in actions:
1039 deltareuse = revlog.revlog.DELTAREUSENEVER
1042 deltareuse = revlog.revlog.DELTAREUSENEVER
1040 elif b're-delta-parent' in actions:
1043 elif b're-delta-parent' in actions:
1041 deltareuse = revlog.revlog.DELTAREUSESAMEREVS
1044 deltareuse = revlog.revlog.DELTAREUSESAMEREVS
1042 elif b're-delta-multibase' in actions:
1045 elif b're-delta-multibase' in actions:
1043 deltareuse = revlog.revlog.DELTAREUSESAMEREVS
1046 deltareuse = revlog.revlog.DELTAREUSESAMEREVS
1044 elif b're-delta-fulladd' in actions:
1047 elif b're-delta-fulladd' in actions:
1045 deltareuse = revlog.revlog.DELTAREUSEFULLADD
1048 deltareuse = revlog.revlog.DELTAREUSEFULLADD
1046 else:
1049 else:
1047 deltareuse = revlog.revlog.DELTAREUSEALWAYS
1050 deltareuse = revlog.revlog.DELTAREUSEALWAYS
1048
1051
1049 with dstrepo.transaction(b'upgrade') as tr:
1052 with dstrepo.transaction(b'upgrade') as tr:
1050 _clonerevlogs(
1053 _clonerevlogs(
1051 ui,
1054 ui,
1052 srcrepo,
1055 srcrepo,
1053 dstrepo,
1056 dstrepo,
1054 tr,
1057 tr,
1055 deltareuse,
1058 deltareuse,
1056 b're-delta-multibase' in actions,
1059 b're-delta-multibase' in actions,
1057 revlogs=revlogs,
1060 revlogs=revlogs,
1058 )
1061 )
1059
1062
1060 # Now copy other files in the store directory.
1063 # Now copy other files in the store directory.
1061 # The sorted() makes execution deterministic.
1064 # The sorted() makes execution deterministic.
1062 for p, kind, st in sorted(srcrepo.store.vfs.readdir(b'', stat=True)):
1065 for p, kind, st in sorted(srcrepo.store.vfs.readdir(b'', stat=True)):
1063 if not _filterstorefile(srcrepo, dstrepo, requirements, p, kind, st):
1066 if not _filterstorefile(srcrepo, dstrepo, requirements, p, kind, st):
1064 continue
1067 continue
1065
1068
1066 srcrepo.ui.status(_(b'copying %s\n') % p)
1069 srcrepo.ui.status(_(b'copying %s\n') % p)
1067 src = srcrepo.store.rawvfs.join(p)
1070 src = srcrepo.store.rawvfs.join(p)
1068 dst = dstrepo.store.rawvfs.join(p)
1071 dst = dstrepo.store.rawvfs.join(p)
1069 util.copyfile(src, dst, copystat=True)
1072 util.copyfile(src, dst, copystat=True)
1070
1073
1071 _finishdatamigration(ui, srcrepo, dstrepo, requirements)
1074 _finishdatamigration(ui, srcrepo, dstrepo, requirements)
1072
1075
1073 ui.status(_(b'data fully migrated to temporary repository\n'))
1076 ui.status(_(b'data fully migrated to temporary repository\n'))
1074
1077
1075 backuppath = pycompat.mkdtemp(prefix=b'upgradebackup.', dir=srcrepo.path)
1078 backuppath = pycompat.mkdtemp(prefix=b'upgradebackup.', dir=srcrepo.path)
1076 backupvfs = vfsmod.vfs(backuppath)
1079 backupvfs = vfsmod.vfs(backuppath)
1077
1080
1078 # Make a backup of requires file first, as it is the first to be modified.
1081 # Make a backup of requires file first, as it is the first to be modified.
1079 util.copyfile(srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires'))
1082 util.copyfile(srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires'))
1080
1083
1081 # We install an arbitrary requirement that clients must not support
1084 # We install an arbitrary requirement that clients must not support
1082 # as a mechanism to lock out new clients during the data swap. This is
1085 # as a mechanism to lock out new clients during the data swap. This is
1083 # better than allowing a client to continue while the repository is in
1086 # better than allowing a client to continue while the repository is in
1084 # an inconsistent state.
1087 # an inconsistent state.
1085 ui.status(
1088 ui.status(
1086 _(
1089 _(
1087 b'marking source repository as being upgraded; clients will be '
1090 b'marking source repository as being upgraded; clients will be '
1088 b'unable to read from repository\n'
1091 b'unable to read from repository\n'
1089 )
1092 )
1090 )
1093 )
1091 scmutil.writerequires(
1094 scmutil.writerequires(
1092 srcrepo.vfs, srcrepo.requirements | {b'upgradeinprogress'}
1095 srcrepo.vfs, srcrepo.requirements | {b'upgradeinprogress'}
1093 )
1096 )
1094
1097
1095 ui.status(_(b'starting in-place swap of repository data\n'))
1098 ui.status(_(b'starting in-place swap of repository data\n'))
1096 ui.status(_(b'replaced files will be backed up at %s\n') % backuppath)
1099 ui.status(_(b'replaced files will be backed up at %s\n') % backuppath)
1097
1100
1098 # Now swap in the new store directory. Doing it as a rename should make
1101 # Now swap in the new store directory. Doing it as a rename should make
1099 # the operation nearly instantaneous and atomic (at least in well-behaved
1102 # the operation nearly instantaneous and atomic (at least in well-behaved
1100 # environments).
1103 # environments).
1101 ui.status(_(b'replacing store...\n'))
1104 ui.status(_(b'replacing store...\n'))
1102 tstart = util.timer()
1105 tstart = util.timer()
1103 util.rename(srcrepo.spath, backupvfs.join(b'store'))
1106 util.rename(srcrepo.spath, backupvfs.join(b'store'))
1104 util.rename(dstrepo.spath, srcrepo.spath)
1107 util.rename(dstrepo.spath, srcrepo.spath)
1105 elapsed = util.timer() - tstart
1108 elapsed = util.timer() - tstart
1106 ui.status(
1109 ui.status(
1107 _(
1110 _(
1108 b'store replacement complete; repository was inconsistent for '
1111 b'store replacement complete; repository was inconsistent for '
1109 b'%0.1fs\n'
1112 b'%0.1fs\n'
1110 )
1113 )
1111 % elapsed
1114 % elapsed
1112 )
1115 )
1113
1116
1114 # We first write the requirements file. Any new requirements will lock
1117 # We first write the requirements file. Any new requirements will lock
1115 # out legacy clients.
1118 # out legacy clients.
1116 ui.status(
1119 ui.status(
1117 _(
1120 _(
1118 b'finalizing requirements file and making repository readable '
1121 b'finalizing requirements file and making repository readable '
1119 b'again\n'
1122 b'again\n'
1120 )
1123 )
1121 )
1124 )
1122 scmutil.writerequires(srcrepo.vfs, requirements)
1125 scmutil.writerequires(srcrepo.vfs, requirements)
1123
1126
1124 # The lock file from the old store won't be removed because nothing has a
1127 # The lock file from the old store won't be removed because nothing has a
1125 # reference to its new location. So clean it up manually. Alternatively, we
1128 # reference to its new location. So clean it up manually. Alternatively, we
1126 # could update srcrepo.svfs and other variables to point to the new
1129 # could update srcrepo.svfs and other variables to point to the new
1127 # location. This is simpler.
1130 # location. This is simpler.
1128 backupvfs.unlink(b'store/lock')
1131 backupvfs.unlink(b'store/lock')
1129
1132
1130 return backuppath
1133 return backuppath
1131
1134
1132
1135
1133 def upgraderepo(
1136 def upgraderepo(
1134 ui,
1137 ui,
1135 repo,
1138 repo,
1136 run=False,
1139 run=False,
1137 optimize=None,
1140 optimize=None,
1138 backup=True,
1141 backup=True,
1139 manifest=None,
1142 manifest=None,
1140 changelog=None,
1143 changelog=None,
1141 ):
1144 ):
1142 """Upgrade a repository in place."""
1145 """Upgrade a repository in place."""
1143 if optimize is None:
1146 if optimize is None:
1144 optimize = []
1147 optimize = []
1145 optimize = {legacy_opts_map.get(o, o) for o in optimize}
1148 optimize = {legacy_opts_map.get(o, o) for o in optimize}
1146 repo = repo.unfiltered()
1149 repo = repo.unfiltered()
1147
1150
1148 revlogs = set(UPGRADE_ALL_REVLOGS)
1151 revlogs = set(UPGRADE_ALL_REVLOGS)
1149 specentries = ((b'c', changelog), (b'm', manifest))
1152 specentries = ((b'c', changelog), (b'm', manifest))
1150 specified = [(y, x) for (y, x) in specentries if x is not None]
1153 specified = [(y, x) for (y, x) in specentries if x is not None]
1151 if specified:
1154 if specified:
1152 # we have some limitation on revlogs to be recloned
1155 # we have some limitation on revlogs to be recloned
1153 if any(x for y, x in specified):
1156 if any(x for y, x in specified):
1154 revlogs = set()
1157 revlogs = set()
1155 for r, enabled in specified:
1158 for r, enabled in specified:
1156 if enabled:
1159 if enabled:
1157 if r == b'c':
1160 if r == b'c':
1158 revlogs.add(UPGRADE_CHANGELOG)
1161 revlogs.add(UPGRADE_CHANGELOG)
1159 elif r == b'm':
1162 elif r == b'm':
1160 revlogs.add(UPGRADE_MANIFEST)
1163 revlogs.add(UPGRADE_MANIFEST)
1161 else:
1164 else:
1162 # none are enabled
1165 # none are enabled
1163 for r, __ in specified:
1166 for r, __ in specified:
1164 if r == b'c':
1167 if r == b'c':
1165 revlogs.discard(UPGRADE_CHANGELOG)
1168 revlogs.discard(UPGRADE_CHANGELOG)
1166 elif r == b'm':
1169 elif r == b'm':
1167 revlogs.discard(UPGRADE_MANIFEST)
1170 revlogs.discard(UPGRADE_MANIFEST)
1168
1171
1169 # Ensure the repository can be upgraded.
1172 # Ensure the repository can be upgraded.
1170 missingreqs = requiredsourcerequirements(repo) - repo.requirements
1173 missingreqs = requiredsourcerequirements(repo) - repo.requirements
1171 if missingreqs:
1174 if missingreqs:
1172 raise error.Abort(
1175 raise error.Abort(
1173 _(b'cannot upgrade repository; requirement missing: %s')
1176 _(b'cannot upgrade repository; requirement missing: %s')
1174 % _(b', ').join(sorted(missingreqs))
1177 % _(b', ').join(sorted(missingreqs))
1175 )
1178 )
1176
1179
1177 blockedreqs = blocksourcerequirements(repo) & repo.requirements
1180 blockedreqs = blocksourcerequirements(repo) & repo.requirements
1178 if blockedreqs:
1181 if blockedreqs:
1179 raise error.Abort(
1182 raise error.Abort(
1180 _(
1183 _(
1181 b'cannot upgrade repository; unsupported source '
1184 b'cannot upgrade repository; unsupported source '
1182 b'requirement: %s'
1185 b'requirement: %s'
1183 )
1186 )
1184 % _(b', ').join(sorted(blockedreqs))
1187 % _(b', ').join(sorted(blockedreqs))
1185 )
1188 )
1186
1189
1187 # FUTURE there is potentially a need to control the wanted requirements via
1190 # FUTURE there is potentially a need to control the wanted requirements via
1188 # command arguments or via an extension hook point.
1191 # command arguments or via an extension hook point.
1189 newreqs = localrepo.newreporequirements(
1192 newreqs = localrepo.newreporequirements(
1190 repo.ui, localrepo.defaultcreateopts(repo.ui)
1193 repo.ui, localrepo.defaultcreateopts(repo.ui)
1191 )
1194 )
1192 newreqs.update(preservedrequirements(repo))
1195 newreqs.update(preservedrequirements(repo))
1193
1196
1194 noremovereqs = (
1197 noremovereqs = (
1195 repo.requirements - newreqs - supportremovedrequirements(repo)
1198 repo.requirements - newreqs - supportremovedrequirements(repo)
1196 )
1199 )
1197 if noremovereqs:
1200 if noremovereqs:
1198 raise error.Abort(
1201 raise error.Abort(
1199 _(
1202 _(
1200 b'cannot upgrade repository; requirement would be '
1203 b'cannot upgrade repository; requirement would be '
1201 b'removed: %s'
1204 b'removed: %s'
1202 )
1205 )
1203 % _(b', ').join(sorted(noremovereqs))
1206 % _(b', ').join(sorted(noremovereqs))
1204 )
1207 )
1205
1208
1206 noaddreqs = newreqs - repo.requirements - allowednewrequirements(repo)
1209 noaddreqs = newreqs - repo.requirements - allowednewrequirements(repo)
1207 if noaddreqs:
1210 if noaddreqs:
1208 raise error.Abort(
1211 raise error.Abort(
1209 _(
1212 _(
1210 b'cannot upgrade repository; do not support adding '
1213 b'cannot upgrade repository; do not support adding '
1211 b'requirement: %s'
1214 b'requirement: %s'
1212 )
1215 )
1213 % _(b', ').join(sorted(noaddreqs))
1216 % _(b', ').join(sorted(noaddreqs))
1214 )
1217 )
1215
1218
1216 unsupportedreqs = newreqs - supporteddestrequirements(repo)
1219 unsupportedreqs = newreqs - supporteddestrequirements(repo)
1217 if unsupportedreqs:
1220 if unsupportedreqs:
1218 raise error.Abort(
1221 raise error.Abort(
1219 _(
1222 _(
1220 b'cannot upgrade repository; do not support '
1223 b'cannot upgrade repository; do not support '
1221 b'destination requirement: %s'
1224 b'destination requirement: %s'
1222 )
1225 )
1223 % _(b', ').join(sorted(unsupportedreqs))
1226 % _(b', ').join(sorted(unsupportedreqs))
1224 )
1227 )
1225
1228
1226 # Find and validate all improvements that can be made.
1229 # Find and validate all improvements that can be made.
1227 alloptimizations = findoptimizations(repo)
1230 alloptimizations = findoptimizations(repo)
1228
1231
1229 # Apply and Validate arguments.
1232 # Apply and Validate arguments.
1230 optimizations = []
1233 optimizations = []
1231 for o in alloptimizations:
1234 for o in alloptimizations:
1232 if o.name in optimize:
1235 if o.name in optimize:
1233 optimizations.append(o)
1236 optimizations.append(o)
1234 optimize.discard(o.name)
1237 optimize.discard(o.name)
1235
1238
1236 if optimize: # anything left is unknown
1239 if optimize: # anything left is unknown
1237 raise error.Abort(
1240 raise error.Abort(
1238 _(b'unknown optimization action requested: %s')
1241 _(b'unknown optimization action requested: %s')
1239 % b', '.join(sorted(optimize)),
1242 % b', '.join(sorted(optimize)),
1240 hint=_(b'run without arguments to see valid optimizations'),
1243 hint=_(b'run without arguments to see valid optimizations'),
1241 )
1244 )
1242
1245
1243 deficiencies = finddeficiencies(repo)
1246 deficiencies = finddeficiencies(repo)
1244 actions = determineactions(repo, deficiencies, repo.requirements, newreqs)
1247 actions = determineactions(repo, deficiencies, repo.requirements, newreqs)
1245 actions.extend(
1248 actions.extend(
1246 o
1249 o
1247 for o in sorted(optimizations)
1250 for o in sorted(optimizations)
1248 # determineactions could have added optimisation
1251 # determineactions could have added optimisation
1249 if o not in actions
1252 if o not in actions
1250 )
1253 )
1251
1254
1252 removedreqs = repo.requirements - newreqs
1255 removedreqs = repo.requirements - newreqs
1253 addedreqs = newreqs - repo.requirements
1256 addedreqs = newreqs - repo.requirements
1254
1257
1255 if revlogs != UPGRADE_ALL_REVLOGS:
1258 if revlogs != UPGRADE_ALL_REVLOGS:
1256 incompatible = RECLONES_REQUIREMENTS & (removedreqs | addedreqs)
1259 incompatible = RECLONES_REQUIREMENTS & (removedreqs | addedreqs)
1257 if incompatible:
1260 if incompatible:
1258 msg = _(
1261 msg = _(
1259 b'ignoring revlogs selection flags, format requirements '
1262 b'ignoring revlogs selection flags, format requirements '
1260 b'change: %s\n'
1263 b'change: %s\n'
1261 )
1264 )
1262 ui.warn(msg % b', '.join(sorted(incompatible)))
1265 ui.warn(msg % b', '.join(sorted(incompatible)))
1263 revlogs = UPGRADE_ALL_REVLOGS
1266 revlogs = UPGRADE_ALL_REVLOGS
1264
1267
1265 def write_labeled(l, label):
1268 def write_labeled(l, label):
1266 first = True
1269 first = True
1267 for r in sorted(l):
1270 for r in sorted(l):
1268 if not first:
1271 if not first:
1269 ui.write(b', ')
1272 ui.write(b', ')
1270 ui.write(r, label=label)
1273 ui.write(r, label=label)
1271 first = False
1274 first = False
1272
1275
1273 def printrequirements():
1276 def printrequirements():
1274 ui.write(_(b'requirements\n'))
1277 ui.write(_(b'requirements\n'))
1275 ui.write(_(b' preserved: '))
1278 ui.write(_(b' preserved: '))
1276 write_labeled(
1279 write_labeled(
1277 newreqs & repo.requirements, "upgrade-repo.requirement.preserved"
1280 newreqs & repo.requirements, "upgrade-repo.requirement.preserved"
1278 )
1281 )
1279 ui.write((b'\n'))
1282 ui.write((b'\n'))
1280 removed = repo.requirements - newreqs
1283 removed = repo.requirements - newreqs
1281 if repo.requirements - newreqs:
1284 if repo.requirements - newreqs:
1282 ui.write(_(b' removed: '))
1285 ui.write(_(b' removed: '))
1283 write_labeled(removed, "upgrade-repo.requirement.removed")
1286 write_labeled(removed, "upgrade-repo.requirement.removed")
1284 ui.write((b'\n'))
1287 ui.write((b'\n'))
1285 added = newreqs - repo.requirements
1288 added = newreqs - repo.requirements
1286 if added:
1289 if added:
1287 ui.write(_(b' added: '))
1290 ui.write(_(b' added: '))
1288 write_labeled(added, "upgrade-repo.requirement.added")
1291 write_labeled(added, "upgrade-repo.requirement.added")
1289 ui.write((b'\n'))
1292 ui.write((b'\n'))
1290 ui.write(b'\n')
1293 ui.write(b'\n')
1291
1294
1292 def printoptimisations():
1295 def printoptimisations():
1293 optimisations = [a for a in actions if a.type == optimisation]
1296 optimisations = [a for a in actions if a.type == optimisation]
1294 optimisations.sort(key=lambda a: a.name)
1297 optimisations.sort(key=lambda a: a.name)
1295 if optimisations:
1298 if optimisations:
1296 ui.write(_(b'optimisations: '))
1299 ui.write(_(b'optimisations: '))
1297 write_labeled(
1300 write_labeled(
1298 [a.name for a in optimisations],
1301 [a.name for a in optimisations],
1299 "upgrade-repo.optimisation.performed",
1302 "upgrade-repo.optimisation.performed",
1300 )
1303 )
1301 ui.write(b'\n\n')
1304 ui.write(b'\n\n')
1302
1305
1303 def printupgradeactions():
1306 def printupgradeactions():
1304 for a in actions:
1307 for a in actions:
1305 ui.status(b'%s\n %s\n\n' % (a.name, a.upgrademessage))
1308 ui.status(b'%s\n %s\n\n' % (a.name, a.upgrademessage))
1306
1309
1307 if not run:
1310 if not run:
1308 fromconfig = []
1311 fromconfig = []
1309 onlydefault = []
1312 onlydefault = []
1310
1313
1311 for d in deficiencies:
1314 for d in deficiencies:
1312 if d.fromconfig(repo):
1315 if d.fromconfig(repo):
1313 fromconfig.append(d)
1316 fromconfig.append(d)
1314 elif d.default:
1317 elif d.default:
1315 onlydefault.append(d)
1318 onlydefault.append(d)
1316
1319
1317 if fromconfig or onlydefault:
1320 if fromconfig or onlydefault:
1318
1321
1319 if fromconfig:
1322 if fromconfig:
1320 ui.status(
1323 ui.status(
1321 _(
1324 _(
1322 b'repository lacks features recommended by '
1325 b'repository lacks features recommended by '
1323 b'current config options:\n\n'
1326 b'current config options:\n\n'
1324 )
1327 )
1325 )
1328 )
1326 for i in fromconfig:
1329 for i in fromconfig:
1327 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
1330 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
1328
1331
1329 if onlydefault:
1332 if onlydefault:
1330 ui.status(
1333 ui.status(
1331 _(
1334 _(
1332 b'repository lacks features used by the default '
1335 b'repository lacks features used by the default '
1333 b'config options:\n\n'
1336 b'config options:\n\n'
1334 )
1337 )
1335 )
1338 )
1336 for i in onlydefault:
1339 for i in onlydefault:
1337 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
1340 ui.status(b'%s\n %s\n\n' % (i.name, i.description))
1338
1341
1339 ui.status(b'\n')
1342 ui.status(b'\n')
1340 else:
1343 else:
1341 ui.status(
1344 ui.status(
1342 _(
1345 _(
1343 b'(no feature deficiencies found in existing '
1346 b'(no feature deficiencies found in existing '
1344 b'repository)\n'
1347 b'repository)\n'
1345 )
1348 )
1346 )
1349 )
1347
1350
1348 ui.status(
1351 ui.status(
1349 _(
1352 _(
1350 b'performing an upgrade with "--run" will make the following '
1353 b'performing an upgrade with "--run" will make the following '
1351 b'changes:\n\n'
1354 b'changes:\n\n'
1352 )
1355 )
1353 )
1356 )
1354
1357
1355 printrequirements()
1358 printrequirements()
1356 printoptimisations()
1359 printoptimisations()
1357 printupgradeactions()
1360 printupgradeactions()
1358
1361
1359 unusedoptimize = [i for i in alloptimizations if i not in actions]
1362 unusedoptimize = [i for i in alloptimizations if i not in actions]
1360
1363
1361 if unusedoptimize:
1364 if unusedoptimize:
1362 ui.status(
1365 ui.status(
1363 _(
1366 _(
1364 b'additional optimizations are available by specifying '
1367 b'additional optimizations are available by specifying '
1365 b'"--optimize <name>":\n\n'
1368 b'"--optimize <name>":\n\n'
1366 )
1369 )
1367 )
1370 )
1368 for i in unusedoptimize:
1371 for i in unusedoptimize:
1369 ui.status(_(b'%s\n %s\n\n') % (i.name, i.description))
1372 ui.status(_(b'%s\n %s\n\n') % (i.name, i.description))
1370 return
1373 return
1371
1374
1372 # Else we're in the run=true case.
1375 # Else we're in the run=true case.
1373 ui.write(_(b'upgrade will perform the following actions:\n\n'))
1376 ui.write(_(b'upgrade will perform the following actions:\n\n'))
1374 printrequirements()
1377 printrequirements()
1375 printoptimisations()
1378 printoptimisations()
1376 printupgradeactions()
1379 printupgradeactions()
1377
1380
1378 upgradeactions = [a.name for a in actions]
1381 upgradeactions = [a.name for a in actions]
1379
1382
1380 ui.status(_(b'beginning upgrade...\n'))
1383 ui.status(_(b'beginning upgrade...\n'))
1381 with repo.wlock(), repo.lock():
1384 with repo.wlock(), repo.lock():
1382 ui.status(_(b'repository locked and read-only\n'))
1385 ui.status(_(b'repository locked and read-only\n'))
1383 # Our strategy for upgrading the repository is to create a new,
1386 # Our strategy for upgrading the repository is to create a new,
1384 # temporary repository, write data to it, then do a swap of the
1387 # temporary repository, write data to it, then do a swap of the
1385 # data. There are less heavyweight ways to do this, but it is easier
1388 # data. There are less heavyweight ways to do this, but it is easier
1386 # to create a new repo object than to instantiate all the components
1389 # to create a new repo object than to instantiate all the components
1387 # (like the store) separately.
1390 # (like the store) separately.
1388 tmppath = pycompat.mkdtemp(prefix=b'upgrade.', dir=repo.path)
1391 tmppath = pycompat.mkdtemp(prefix=b'upgrade.', dir=repo.path)
1389 backuppath = None
1392 backuppath = None
1390 try:
1393 try:
1391 ui.status(
1394 ui.status(
1392 _(
1395 _(
1393 b'creating temporary repository to stage migrated '
1396 b'creating temporary repository to stage migrated '
1394 b'data: %s\n'
1397 b'data: %s\n'
1395 )
1398 )
1396 % tmppath
1399 % tmppath
1397 )
1400 )
1398
1401
1399 # clone ui without using ui.copy because repo.ui is protected
1402 # clone ui without using ui.copy because repo.ui is protected
1400 repoui = repo.ui.__class__(repo.ui)
1403 repoui = repo.ui.__class__(repo.ui)
1401 dstrepo = hg.repository(repoui, path=tmppath, create=True)
1404 dstrepo = hg.repository(repoui, path=tmppath, create=True)
1402
1405
1403 with dstrepo.wlock(), dstrepo.lock():
1406 with dstrepo.wlock(), dstrepo.lock():
1404 backuppath = _upgraderepo(
1407 backuppath = _upgraderepo(
1405 ui, repo, dstrepo, newreqs, upgradeactions, revlogs=revlogs
1408 ui, repo, dstrepo, newreqs, upgradeactions, revlogs=revlogs
1406 )
1409 )
1407 if not (backup or backuppath is None):
1410 if not (backup or backuppath is None):
1408 ui.status(
1411 ui.status(
1409 _(b'removing old repository content%s\n') % backuppath
1412 _(b'removing old repository content%s\n') % backuppath
1410 )
1413 )
1411 repo.vfs.rmtree(backuppath, forcibly=True)
1414 repo.vfs.rmtree(backuppath, forcibly=True)
1412 backuppath = None
1415 backuppath = None
1413
1416
1414 finally:
1417 finally:
1415 ui.status(_(b'removing temporary repository %s\n') % tmppath)
1418 ui.status(_(b'removing temporary repository %s\n') % tmppath)
1416 repo.vfs.rmtree(tmppath, forcibly=True)
1419 repo.vfs.rmtree(tmppath, forcibly=True)
1417
1420
1418 if backuppath and not ui.quiet:
1421 if backuppath and not ui.quiet:
1419 ui.warn(
1422 ui.warn(
1420 _(b'copy of old repository backed up at %s\n') % backuppath
1423 _(b'copy of old repository backed up at %s\n') % backuppath
1421 )
1424 )
1422 ui.warn(
1425 ui.warn(
1423 _(
1426 _(
1424 b'the old repository will not be deleted; remove '
1427 b'the old repository will not be deleted; remove '
1425 b'it to free up disk space once the upgraded '
1428 b'it to free up disk space once the upgraded '
1426 b'repository is verified\n'
1429 b'repository is verified\n'
1427 )
1430 )
1428 )
1431 )
@@ -1,441 +1,537 b''
1 ===================================
1 ===================================
2 Test the persistent on-disk nodemap
2 Test the persistent on-disk nodemap
3 ===================================
3 ===================================
4
4
5 $ cat << EOF >> $HGRCPATH
5 $ cat << EOF >> $HGRCPATH
6 > [format]
6 > [format]
7 > use-persistent-nodemap=yes
7 > use-persistent-nodemap=yes
8 > [devel]
8 > [devel]
9 > persistent-nodemap=yes
9 > persistent-nodemap=yes
10 > EOF
10 > EOF
11 $ hg init test-repo
11 $ hg init test-repo
12 $ cd test-repo
12 $ cd test-repo
13 $ hg debugformat
13 $ hg debugformat
14 format-variant repo
14 format-variant repo
15 fncache: yes
15 fncache: yes
16 dotencode: yes
16 dotencode: yes
17 generaldelta: yes
17 generaldelta: yes
18 sparserevlog: yes
18 sparserevlog: yes
19 sidedata: no
19 sidedata: no
20 persistent-nodemap: yes
20 persistent-nodemap: yes
21 copies-sdc: no
21 copies-sdc: no
22 plain-cl-delta: yes
22 plain-cl-delta: yes
23 compression: zlib
23 compression: zlib
24 compression-level: default
24 compression-level: default
25 $ hg debugbuilddag .+5000 --new-file --config "storage.revlog.nodemap.mode=warn"
25 $ hg debugbuilddag .+5000 --new-file --config "storage.revlog.nodemap.mode=warn"
26 persistent nodemap in strict mode without efficient method (no-rust no-pure !)
26 persistent nodemap in strict mode without efficient method (no-rust no-pure !)
27 persistent nodemap in strict mode without efficient method (no-rust no-pure !)
27 persistent nodemap in strict mode without efficient method (no-rust no-pure !)
28 $ hg debugnodemap --metadata
28 $ hg debugnodemap --metadata
29 uid: ???????????????? (glob)
29 uid: ???????????????? (glob)
30 tip-rev: 5000
30 tip-rev: 5000
31 tip-node: 6b02b8c7b96654c25e86ba69eda198d7e6ad8b3c
31 tip-node: 6b02b8c7b96654c25e86ba69eda198d7e6ad8b3c
32 data-length: 121088
32 data-length: 121088
33 data-unused: 0
33 data-unused: 0
34 data-unused: 0.000%
34 data-unused: 0.000%
35 $ f --size .hg/store/00changelog.n
35 $ f --size .hg/store/00changelog.n
36 .hg/store/00changelog.n: size=70
36 .hg/store/00changelog.n: size=70
37
37
38 Simple lookup works
38 Simple lookup works
39
39
40 $ ANYNODE=`hg log --template '{node|short}\n' --rev tip`
40 $ ANYNODE=`hg log --template '{node|short}\n' --rev tip`
41 $ hg log -r "$ANYNODE" --template '{rev}\n'
41 $ hg log -r "$ANYNODE" --template '{rev}\n'
42 5000
42 5000
43
43
44
44
45 #if rust
45 #if rust
46
46
47 $ f --sha256 .hg/store/00changelog-*.nd
47 $ f --sha256 .hg/store/00changelog-*.nd
48 .hg/store/00changelog-????????????????.nd: sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd (glob)
48 .hg/store/00changelog-????????????????.nd: sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd (glob)
49
49
50 $ f --sha256 .hg/store/00manifest-*.nd
50 $ f --sha256 .hg/store/00manifest-*.nd
51 .hg/store/00manifest-????????????????.nd: sha256=97117b1c064ea2f86664a124589e47db0e254e8d34739b5c5cc5bf31c9da2b51 (glob)
51 .hg/store/00manifest-????????????????.nd: sha256=97117b1c064ea2f86664a124589e47db0e254e8d34739b5c5cc5bf31c9da2b51 (glob)
52 $ hg debugnodemap --dump-new | f --sha256 --size
52 $ hg debugnodemap --dump-new | f --sha256 --size
53 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
53 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
54 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
54 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
55 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
55 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
56 0000: 00 00 00 91 00 00 00 20 00 00 00 bb 00 00 00 e7 |....... ........|
56 0000: 00 00 00 91 00 00 00 20 00 00 00 bb 00 00 00 e7 |....... ........|
57 0010: 00 00 00 66 00 00 00 a1 00 00 01 13 00 00 01 22 |...f..........."|
57 0010: 00 00 00 66 00 00 00 a1 00 00 01 13 00 00 01 22 |...f..........."|
58 0020: 00 00 00 23 00 00 00 fc 00 00 00 ba 00 00 00 5e |...#...........^|
58 0020: 00 00 00 23 00 00 00 fc 00 00 00 ba 00 00 00 5e |...#...........^|
59 0030: 00 00 00 df 00 00 01 4e 00 00 01 65 00 00 00 ab |.......N...e....|
59 0030: 00 00 00 df 00 00 01 4e 00 00 01 65 00 00 00 ab |.......N...e....|
60 0040: 00 00 00 a9 00 00 00 95 00 00 00 73 00 00 00 38 |...........s...8|
60 0040: 00 00 00 a9 00 00 00 95 00 00 00 73 00 00 00 38 |...........s...8|
61 0050: 00 00 00 cc 00 00 00 92 00 00 00 90 00 00 00 69 |...............i|
61 0050: 00 00 00 cc 00 00 00 92 00 00 00 90 00 00 00 69 |...............i|
62 0060: 00 00 00 ec 00 00 00 8d 00 00 01 4f 00 00 00 12 |...........O....|
62 0060: 00 00 00 ec 00 00 00 8d 00 00 01 4f 00 00 00 12 |...........O....|
63 0070: 00 00 02 0c 00 00 00 77 00 00 00 9c 00 00 00 8f |.......w........|
63 0070: 00 00 02 0c 00 00 00 77 00 00 00 9c 00 00 00 8f |.......w........|
64 0080: 00 00 00 d5 00 00 00 6b 00 00 00 48 00 00 00 b3 |.......k...H....|
64 0080: 00 00 00 d5 00 00 00 6b 00 00 00 48 00 00 00 b3 |.......k...H....|
65 0090: 00 00 00 e5 00 00 00 b5 00 00 00 8e 00 00 00 ad |................|
65 0090: 00 00 00 e5 00 00 00 b5 00 00 00 8e 00 00 00 ad |................|
66 00a0: 00 00 00 7b 00 00 00 7c 00 00 00 0b 00 00 00 2b |...{...|.......+|
66 00a0: 00 00 00 7b 00 00 00 7c 00 00 00 0b 00 00 00 2b |...{...|.......+|
67 00b0: 00 00 00 c6 00 00 00 1e 00 00 01 08 00 00 00 11 |................|
67 00b0: 00 00 00 c6 00 00 00 1e 00 00 01 08 00 00 00 11 |................|
68 00c0: 00 00 01 30 00 00 00 26 00 00 01 9c 00 00 00 35 |...0...&.......5|
68 00c0: 00 00 01 30 00 00 00 26 00 00 01 9c 00 00 00 35 |...0...&.......5|
69 00d0: 00 00 00 b8 00 00 01 31 00 00 00 2c 00 00 00 55 |.......1...,...U|
69 00d0: 00 00 00 b8 00 00 01 31 00 00 00 2c 00 00 00 55 |.......1...,...U|
70 00e0: 00 00 00 8a 00 00 00 9a 00 00 00 0c 00 00 01 1e |................|
70 00e0: 00 00 00 8a 00 00 00 9a 00 00 00 0c 00 00 01 1e |................|
71 00f0: 00 00 00 a4 00 00 00 83 00 00 00 c9 00 00 00 8c |................|
71 00f0: 00 00 00 a4 00 00 00 83 00 00 00 c9 00 00 00 8c |................|
72
72
73
73
74 #else
74 #else
75
75
76 $ f --sha256 .hg/store/00changelog-*.nd
76 $ f --sha256 .hg/store/00changelog-*.nd
77 .hg/store/00changelog-????????????????.nd: sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79 (glob)
77 .hg/store/00changelog-????????????????.nd: sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79 (glob)
78 $ hg debugnodemap --dump-new | f --sha256 --size
78 $ hg debugnodemap --dump-new | f --sha256 --size
79 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
79 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
80 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
80 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
81 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
81 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
82 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
82 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
83 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
83 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
84 0020: ff ff ff ff ff ff f5 06 ff ff ff ff ff ff f3 e7 |................|
84 0020: ff ff ff ff ff ff f5 06 ff ff ff ff ff ff f3 e7 |................|
85 0030: ff ff ef ca ff ff ff ff ff ff ff ff ff ff ff ff |................|
85 0030: ff ff ef ca ff ff ff ff ff ff ff ff ff ff ff ff |................|
86 0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
86 0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
87 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ed 08 |................|
87 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ed 08 |................|
88 0060: ff ff ed 66 ff ff ff ff ff ff ff ff ff ff ff ff |...f............|
88 0060: ff ff ed 66 ff ff ff ff ff ff ff ff ff ff ff ff |...f............|
89 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
89 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
90 0080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
90 0080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
91 0090: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f6 ed |................|
91 0090: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f6 ed |................|
92 00a0: ff ff ff ff ff ff fe 61 ff ff ff ff ff ff ff ff |.......a........|
92 00a0: ff ff ff ff ff ff fe 61 ff ff ff ff ff ff ff ff |.......a........|
93 00b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
93 00b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
94 00c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
94 00c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
95 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
95 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
96 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f1 02 |................|
96 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f1 02 |................|
97 00f0: ff ff ff ff ff ff ed 1b ff ff ff ff ff ff ff ff |................|
97 00f0: ff ff ff ff ff ff ed 1b ff ff ff ff ff ff ff ff |................|
98
98
99 #endif
99 #endif
100
100
101 $ hg debugnodemap --check
101 $ hg debugnodemap --check
102 revision in index: 5001
102 revision in index: 5001
103 revision in nodemap: 5001
103 revision in nodemap: 5001
104
104
105 add a new commit
105 add a new commit
106
106
107 $ hg up
107 $ hg up
108 5001 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 5001 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 $ echo foo > foo
109 $ echo foo > foo
110 $ hg add foo
110 $ hg add foo
111
111
112 #if no-pure no-rust
112 #if no-pure no-rust
113
113
114 $ hg ci -m 'foo' --config "storage.revlog.nodemap.mode=strict"
114 $ hg ci -m 'foo' --config "storage.revlog.nodemap.mode=strict"
115 transaction abort!
115 transaction abort!
116 rollback completed
116 rollback completed
117 abort: persistent nodemap in strict mode without efficient method
117 abort: persistent nodemap in strict mode without efficient method
118 [255]
118 [255]
119
119
120 #endif
120 #endif
121
121
122 $ hg ci -m 'foo'
122 $ hg ci -m 'foo'
123
123
124 #if no-pure no-rust
124 #if no-pure no-rust
125 $ hg debugnodemap --metadata
125 $ hg debugnodemap --metadata
126 uid: ???????????????? (glob)
126 uid: ???????????????? (glob)
127 tip-rev: 5001
127 tip-rev: 5001
128 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
128 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
129 data-length: 121088
129 data-length: 121088
130 data-unused: 0
130 data-unused: 0
131 data-unused: 0.000%
131 data-unused: 0.000%
132 #else
132 #else
133 $ hg debugnodemap --metadata
133 $ hg debugnodemap --metadata
134 uid: ???????????????? (glob)
134 uid: ???????????????? (glob)
135 tip-rev: 5001
135 tip-rev: 5001
136 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
136 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
137 data-length: 121344
137 data-length: 121344
138 data-unused: 256
138 data-unused: 256
139 data-unused: 0.211%
139 data-unused: 0.211%
140 #endif
140 #endif
141
141
142 $ f --size .hg/store/00changelog.n
142 $ f --size .hg/store/00changelog.n
143 .hg/store/00changelog.n: size=70
143 .hg/store/00changelog.n: size=70
144
144
145 (The pure code use the debug code that perform incremental update, the C code reencode from scratch)
145 (The pure code use the debug code that perform incremental update, the C code reencode from scratch)
146
146
147 #if pure
147 #if pure
148 $ f --sha256 .hg/store/00changelog-*.nd --size
148 $ f --sha256 .hg/store/00changelog-*.nd --size
149 .hg/store/00changelog-????????????????.nd: size=121344, sha256=cce54c5da5bde3ad72a4938673ed4064c86231b9c64376b082b163fdb20f8f66 (glob)
149 .hg/store/00changelog-????????????????.nd: size=121344, sha256=cce54c5da5bde3ad72a4938673ed4064c86231b9c64376b082b163fdb20f8f66 (glob)
150 #endif
150 #endif
151
151
152 #if rust
152 #if rust
153 $ f --sha256 .hg/store/00changelog-*.nd --size
153 $ f --sha256 .hg/store/00changelog-*.nd --size
154 .hg/store/00changelog-????????????????.nd: size=121344, sha256=952b042fcf614ceb37b542b1b723e04f18f83efe99bee4e0f5ccd232ef470e58 (glob)
154 .hg/store/00changelog-????????????????.nd: size=121344, sha256=952b042fcf614ceb37b542b1b723e04f18f83efe99bee4e0f5ccd232ef470e58 (glob)
155 #endif
155 #endif
156
156
157 #if no-pure no-rust
157 #if no-pure no-rust
158 $ f --sha256 .hg/store/00changelog-*.nd --size
158 $ f --sha256 .hg/store/00changelog-*.nd --size
159 .hg/store/00changelog-????????????????.nd: size=121088, sha256=df7c06a035b96cb28c7287d349d603baef43240be7736fe34eea419a49702e17 (glob)
159 .hg/store/00changelog-????????????????.nd: size=121088, sha256=df7c06a035b96cb28c7287d349d603baef43240be7736fe34eea419a49702e17 (glob)
160 #endif
160 #endif
161
161
162 $ hg debugnodemap --check
162 $ hg debugnodemap --check
163 revision in index: 5002
163 revision in index: 5002
164 revision in nodemap: 5002
164 revision in nodemap: 5002
165
165
166 Test code path without mmap
166 Test code path without mmap
167 ---------------------------
167 ---------------------------
168
168
169 $ echo bar > bar
169 $ echo bar > bar
170 $ hg add bar
170 $ hg add bar
171 $ hg ci -m 'bar' --config storage.revlog.nodemap.mmap=no
171 $ hg ci -m 'bar' --config storage.revlog.nodemap.mmap=no
172
172
173 $ hg debugnodemap --check --config storage.revlog.nodemap.mmap=yes
173 $ hg debugnodemap --check --config storage.revlog.nodemap.mmap=yes
174 revision in index: 5003
174 revision in index: 5003
175 revision in nodemap: 5003
175 revision in nodemap: 5003
176 $ hg debugnodemap --check --config storage.revlog.nodemap.mmap=no
176 $ hg debugnodemap --check --config storage.revlog.nodemap.mmap=no
177 revision in index: 5003
177 revision in index: 5003
178 revision in nodemap: 5003
178 revision in nodemap: 5003
179
179
180
180
181 #if pure
181 #if pure
182 $ hg debugnodemap --metadata
182 $ hg debugnodemap --metadata
183 uid: ???????????????? (glob)
183 uid: ???????????????? (glob)
184 tip-rev: 5002
184 tip-rev: 5002
185 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
185 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
186 data-length: 121600
186 data-length: 121600
187 data-unused: 512
187 data-unused: 512
188 data-unused: 0.421%
188 data-unused: 0.421%
189 $ f --sha256 .hg/store/00changelog-*.nd --size
189 $ f --sha256 .hg/store/00changelog-*.nd --size
190 .hg/store/00changelog-????????????????.nd: size=121600, sha256=def52503d049ccb823974af313a98a935319ba61f40f3aa06a8be4d35c215054 (glob)
190 .hg/store/00changelog-????????????????.nd: size=121600, sha256=def52503d049ccb823974af313a98a935319ba61f40f3aa06a8be4d35c215054 (glob)
191 #endif
191 #endif
192 #if rust
192 #if rust
193 $ hg debugnodemap --metadata
193 $ hg debugnodemap --metadata
194 uid: ???????????????? (glob)
194 uid: ???????????????? (glob)
195 tip-rev: 5002
195 tip-rev: 5002
196 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
196 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
197 data-length: 121600
197 data-length: 121600
198 data-unused: 512
198 data-unused: 512
199 data-unused: 0.421%
199 data-unused: 0.421%
200 $ f --sha256 .hg/store/00changelog-*.nd --size
200 $ f --sha256 .hg/store/00changelog-*.nd --size
201 .hg/store/00changelog-????????????????.nd: size=121600, sha256=dacf5b5f1d4585fee7527d0e67cad5b1ba0930e6a0928f650f779aefb04ce3fb (glob)
201 .hg/store/00changelog-????????????????.nd: size=121600, sha256=dacf5b5f1d4585fee7527d0e67cad5b1ba0930e6a0928f650f779aefb04ce3fb (glob)
202 #endif
202 #endif
203 #if no-pure no-rust
203 #if no-pure no-rust
204 $ hg debugnodemap --metadata
204 $ hg debugnodemap --metadata
205 uid: ???????????????? (glob)
205 uid: ???????????????? (glob)
206 tip-rev: 5002
206 tip-rev: 5002
207 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
207 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
208 data-length: 121088
208 data-length: 121088
209 data-unused: 0
209 data-unused: 0
210 data-unused: 0.000%
210 data-unused: 0.000%
211 $ f --sha256 .hg/store/00changelog-*.nd --size
211 $ f --sha256 .hg/store/00changelog-*.nd --size
212 .hg/store/00changelog-????????????????.nd: size=121088, sha256=59fcede3e3cc587755916ceed29e3c33748cd1aa7d2f91828ac83e7979d935e8 (glob)
212 .hg/store/00changelog-????????????????.nd: size=121088, sha256=59fcede3e3cc587755916ceed29e3c33748cd1aa7d2f91828ac83e7979d935e8 (glob)
213 #endif
213 #endif
214
214
215 Test force warming the cache
215 Test force warming the cache
216
216
217 $ rm .hg/store/00changelog.n
217 $ rm .hg/store/00changelog.n
218 $ hg debugnodemap --metadata
218 $ hg debugnodemap --metadata
219 $ hg debugupdatecache
219 $ hg debugupdatecache
220 #if pure
220 #if pure
221 $ hg debugnodemap --metadata
221 $ hg debugnodemap --metadata
222 uid: ???????????????? (glob)
222 uid: ???????????????? (glob)
223 tip-rev: 5002
223 tip-rev: 5002
224 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
224 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
225 data-length: 121088
225 data-length: 121088
226 data-unused: 0
226 data-unused: 0
227 data-unused: 0.000%
227 data-unused: 0.000%
228 #else
228 #else
229 $ hg debugnodemap --metadata
229 $ hg debugnodemap --metadata
230 uid: ???????????????? (glob)
230 uid: ???????????????? (glob)
231 tip-rev: 5002
231 tip-rev: 5002
232 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
232 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
233 data-length: 121088
233 data-length: 121088
234 data-unused: 0
234 data-unused: 0
235 data-unused: 0.000%
235 data-unused: 0.000%
236 #endif
236 #endif
237
237
238 Check out of sync nodemap
238 Check out of sync nodemap
239 =========================
239 =========================
240
240
241 First copy old data on the side.
241 First copy old data on the side.
242
242
243 $ mkdir ../tmp-copies
243 $ mkdir ../tmp-copies
244 $ cp .hg/store/00changelog-????????????????.nd .hg/store/00changelog.n ../tmp-copies
244 $ cp .hg/store/00changelog-????????????????.nd .hg/store/00changelog.n ../tmp-copies
245
245
246 Nodemap lagging behind
246 Nodemap lagging behind
247 ----------------------
247 ----------------------
248
248
249 make a new commit
249 make a new commit
250
250
251 $ echo bar2 > bar
251 $ echo bar2 > bar
252 $ hg ci -m 'bar2'
252 $ hg ci -m 'bar2'
253 $ NODE=`hg log -r tip -T '{node}\n'`
253 $ NODE=`hg log -r tip -T '{node}\n'`
254 $ hg log -r "$NODE" -T '{rev}\n'
254 $ hg log -r "$NODE" -T '{rev}\n'
255 5003
255 5003
256
256
257 If the nodemap is lagging behind, it can catch up fine
257 If the nodemap is lagging behind, it can catch up fine
258
258
259 $ hg debugnodemap --metadata
259 $ hg debugnodemap --metadata
260 uid: ???????????????? (glob)
260 uid: ???????????????? (glob)
261 tip-rev: 5003
261 tip-rev: 5003
262 tip-node: c9329770f979ade2d16912267c38ba5f82fd37b3
262 tip-node: c9329770f979ade2d16912267c38ba5f82fd37b3
263 data-length: 121344 (pure !)
263 data-length: 121344 (pure !)
264 data-length: 121344 (rust !)
264 data-length: 121344 (rust !)
265 data-length: 121152 (no-rust no-pure !)
265 data-length: 121152 (no-rust no-pure !)
266 data-unused: 192 (pure !)
266 data-unused: 192 (pure !)
267 data-unused: 192 (rust !)
267 data-unused: 192 (rust !)
268 data-unused: 0 (no-rust no-pure !)
268 data-unused: 0 (no-rust no-pure !)
269 data-unused: 0.158% (pure !)
269 data-unused: 0.158% (pure !)
270 data-unused: 0.158% (rust !)
270 data-unused: 0.158% (rust !)
271 data-unused: 0.000% (no-rust no-pure !)
271 data-unused: 0.000% (no-rust no-pure !)
272 $ cp -f ../tmp-copies/* .hg/store/
272 $ cp -f ../tmp-copies/* .hg/store/
273 $ hg debugnodemap --metadata
273 $ hg debugnodemap --metadata
274 uid: ???????????????? (glob)
274 uid: ???????????????? (glob)
275 tip-rev: 5002
275 tip-rev: 5002
276 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
276 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
277 data-length: 121088
277 data-length: 121088
278 data-unused: 0
278 data-unused: 0
279 data-unused: 0.000%
279 data-unused: 0.000%
280 $ hg log -r "$NODE" -T '{rev}\n'
280 $ hg log -r "$NODE" -T '{rev}\n'
281 5003
281 5003
282
282
283 changelog altered
283 changelog altered
284 -----------------
284 -----------------
285
285
286 If the nodemap is not gated behind a requirements, an unaware client can alter
286 If the nodemap is not gated behind a requirements, an unaware client can alter
287 the repository so the revlog used to generate the nodemap is not longer
287 the repository so the revlog used to generate the nodemap is not longer
288 compatible with the persistent nodemap. We need to detect that.
288 compatible with the persistent nodemap. We need to detect that.
289
289
290 $ hg up "$NODE~5"
290 $ hg up "$NODE~5"
291 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
291 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
292 $ echo bar > babar
292 $ echo bar > babar
293 $ hg add babar
293 $ hg add babar
294 $ hg ci -m 'babar'
294 $ hg ci -m 'babar'
295 created new head
295 created new head
296 $ OTHERNODE=`hg log -r tip -T '{node}\n'`
296 $ OTHERNODE=`hg log -r tip -T '{node}\n'`
297 $ hg log -r "$OTHERNODE" -T '{rev}\n'
297 $ hg log -r "$OTHERNODE" -T '{rev}\n'
298 5004
298 5004
299
299
300 $ hg --config extensions.strip= strip --rev "$NODE~1" --no-backup
300 $ hg --config extensions.strip= strip --rev "$NODE~1" --no-backup
301
301
302 the nodemap should detect the changelog have been tampered with and recover.
302 the nodemap should detect the changelog have been tampered with and recover.
303
303
304 $ hg debugnodemap --metadata
304 $ hg debugnodemap --metadata
305 uid: ???????????????? (glob)
305 uid: ???????????????? (glob)
306 tip-rev: 5002
306 tip-rev: 5002
307 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
307 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
308 data-length: 121536 (pure !)
308 data-length: 121536 (pure !)
309 data-length: 121088 (rust !)
309 data-length: 121088 (rust !)
310 data-length: 121088 (no-pure no-rust !)
310 data-length: 121088 (no-pure no-rust !)
311 data-unused: 448 (pure !)
311 data-unused: 448 (pure !)
312 data-unused: 0 (rust !)
312 data-unused: 0 (rust !)
313 data-unused: 0 (no-pure no-rust !)
313 data-unused: 0 (no-pure no-rust !)
314 data-unused: 0.000% (rust !)
314 data-unused: 0.000% (rust !)
315 data-unused: 0.369% (pure !)
315 data-unused: 0.369% (pure !)
316 data-unused: 0.000% (no-pure no-rust !)
316 data-unused: 0.000% (no-pure no-rust !)
317
317
318 $ cp -f ../tmp-copies/* .hg/store/
318 $ cp -f ../tmp-copies/* .hg/store/
319 $ hg debugnodemap --metadata
319 $ hg debugnodemap --metadata
320 uid: ???????????????? (glob)
320 uid: ???????????????? (glob)
321 tip-rev: 5002
321 tip-rev: 5002
322 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
322 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
323 data-length: 121088
323 data-length: 121088
324 data-unused: 0
324 data-unused: 0
325 data-unused: 0.000%
325 data-unused: 0.000%
326 $ hg log -r "$OTHERNODE" -T '{rev}\n'
326 $ hg log -r "$OTHERNODE" -T '{rev}\n'
327 5002
327 5002
328
328
329 Check transaction related property
329 Check transaction related property
330 ==================================
330 ==================================
331
331
332 An up to date nodemap should be available to shell hooks,
332 An up to date nodemap should be available to shell hooks,
333
333
334 $ echo dsljfl > a
334 $ echo dsljfl > a
335 $ hg add a
335 $ hg add a
336 $ hg ci -m a
336 $ hg ci -m a
337 $ hg debugnodemap --metadata
337 $ hg debugnodemap --metadata
338 uid: ???????????????? (glob)
338 uid: ???????????????? (glob)
339 tip-rev: 5003
339 tip-rev: 5003
340 tip-node: a52c5079765b5865d97b993b303a18740113bbb2
340 tip-node: a52c5079765b5865d97b993b303a18740113bbb2
341 data-length: 121088
341 data-length: 121088
342 data-unused: 0
342 data-unused: 0
343 data-unused: 0.000%
343 data-unused: 0.000%
344 $ echo babar2 > babar
344 $ echo babar2 > babar
345 $ hg ci -m 'babar2' --config "hooks.pretxnclose.nodemap-test=hg debugnodemap --metadata"
345 $ hg ci -m 'babar2' --config "hooks.pretxnclose.nodemap-test=hg debugnodemap --metadata"
346 uid: ???????????????? (glob)
346 uid: ???????????????? (glob)
347 tip-rev: 5004
347 tip-rev: 5004
348 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
348 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
349 data-length: 121280 (pure !)
349 data-length: 121280 (pure !)
350 data-length: 121280 (rust !)
350 data-length: 121280 (rust !)
351 data-length: 121088 (no-pure no-rust !)
351 data-length: 121088 (no-pure no-rust !)
352 data-unused: 192 (pure !)
352 data-unused: 192 (pure !)
353 data-unused: 192 (rust !)
353 data-unused: 192 (rust !)
354 data-unused: 0 (no-pure no-rust !)
354 data-unused: 0 (no-pure no-rust !)
355 data-unused: 0.158% (pure !)
355 data-unused: 0.158% (pure !)
356 data-unused: 0.158% (rust !)
356 data-unused: 0.158% (rust !)
357 data-unused: 0.000% (no-pure no-rust !)
357 data-unused: 0.000% (no-pure no-rust !)
358 $ hg debugnodemap --metadata
358 $ hg debugnodemap --metadata
359 uid: ???????????????? (glob)
359 uid: ???????????????? (glob)
360 tip-rev: 5004
360 tip-rev: 5004
361 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
361 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
362 data-length: 121280 (pure !)
362 data-length: 121280 (pure !)
363 data-length: 121280 (rust !)
363 data-length: 121280 (rust !)
364 data-length: 121088 (no-pure no-rust !)
364 data-length: 121088 (no-pure no-rust !)
365 data-unused: 192 (pure !)
365 data-unused: 192 (pure !)
366 data-unused: 192 (rust !)
366 data-unused: 192 (rust !)
367 data-unused: 0 (no-pure no-rust !)
367 data-unused: 0 (no-pure no-rust !)
368 data-unused: 0.158% (pure !)
368 data-unused: 0.158% (pure !)
369 data-unused: 0.158% (rust !)
369 data-unused: 0.158% (rust !)
370 data-unused: 0.000% (no-pure no-rust !)
370 data-unused: 0.000% (no-pure no-rust !)
371
371
372 Another process does not see the pending nodemap content during run.
372 Another process does not see the pending nodemap content during run.
373
373
374 $ PATH=$RUNTESTDIR/testlib/:$PATH
374 $ PATH=$RUNTESTDIR/testlib/:$PATH
375 $ echo qpoasp > a
375 $ echo qpoasp > a
376 $ hg ci -m a2 \
376 $ hg ci -m a2 \
377 > --config "hooks.pretxnclose=wait-on-file 20 sync-repo-read sync-txn-pending" \
377 > --config "hooks.pretxnclose=wait-on-file 20 sync-repo-read sync-txn-pending" \
378 > --config "hooks.txnclose=touch sync-txn-close" > output.txt 2>&1 &
378 > --config "hooks.txnclose=touch sync-txn-close" > output.txt 2>&1 &
379
379
380 (read the repository while the commit transaction is pending)
380 (read the repository while the commit transaction is pending)
381
381
382 $ wait-on-file 20 sync-txn-pending && \
382 $ wait-on-file 20 sync-txn-pending && \
383 > hg debugnodemap --metadata && \
383 > hg debugnodemap --metadata && \
384 > wait-on-file 20 sync-txn-close sync-repo-read
384 > wait-on-file 20 sync-txn-close sync-repo-read
385 uid: ???????????????? (glob)
385 uid: ???????????????? (glob)
386 tip-rev: 5004
386 tip-rev: 5004
387 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
387 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
388 data-length: 121280 (pure !)
388 data-length: 121280 (pure !)
389 data-length: 121280 (rust !)
389 data-length: 121280 (rust !)
390 data-length: 121088 (no-pure no-rust !)
390 data-length: 121088 (no-pure no-rust !)
391 data-unused: 192 (pure !)
391 data-unused: 192 (pure !)
392 data-unused: 192 (rust !)
392 data-unused: 192 (rust !)
393 data-unused: 0 (no-pure no-rust !)
393 data-unused: 0 (no-pure no-rust !)
394 data-unused: 0.158% (pure !)
394 data-unused: 0.158% (pure !)
395 data-unused: 0.158% (rust !)
395 data-unused: 0.158% (rust !)
396 data-unused: 0.000% (no-pure no-rust !)
396 data-unused: 0.000% (no-pure no-rust !)
397 $ hg debugnodemap --metadata
397 $ hg debugnodemap --metadata
398 uid: ???????????????? (glob)
398 uid: ???????????????? (glob)
399 tip-rev: 5005
399 tip-rev: 5005
400 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
400 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
401 data-length: 121536 (pure !)
401 data-length: 121536 (pure !)
402 data-length: 121536 (rust !)
402 data-length: 121536 (rust !)
403 data-length: 121088 (no-pure no-rust !)
403 data-length: 121088 (no-pure no-rust !)
404 data-unused: 448 (pure !)
404 data-unused: 448 (pure !)
405 data-unused: 448 (rust !)
405 data-unused: 448 (rust !)
406 data-unused: 0 (no-pure no-rust !)
406 data-unused: 0 (no-pure no-rust !)
407 data-unused: 0.369% (pure !)
407 data-unused: 0.369% (pure !)
408 data-unused: 0.369% (rust !)
408 data-unused: 0.369% (rust !)
409 data-unused: 0.000% (no-pure no-rust !)
409 data-unused: 0.000% (no-pure no-rust !)
410
410
411 $ cat output.txt
411 $ cat output.txt
412
412
413 Check that a failing transaction will properly revert the data
413 Check that a failing transaction will properly revert the data
414
414
415 $ echo plakfe > a
415 $ echo plakfe > a
416 $ f --size --sha256 .hg/store/00changelog-*.nd
416 $ f --size --sha256 .hg/store/00changelog-*.nd
417 .hg/store/00changelog-????????????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
417 .hg/store/00changelog-????????????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
418 .hg/store/00changelog-????????????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
418 .hg/store/00changelog-????????????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
419 .hg/store/00changelog-????????????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
419 .hg/store/00changelog-????????????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
420 $ hg ci -m a3 --config "extensions.abort=$RUNTESTDIR/testlib/crash_transaction_late.py"
420 $ hg ci -m a3 --config "extensions.abort=$RUNTESTDIR/testlib/crash_transaction_late.py"
421 transaction abort!
421 transaction abort!
422 rollback completed
422 rollback completed
423 abort: This is a late abort
423 abort: This is a late abort
424 [255]
424 [255]
425 $ hg debugnodemap --metadata
425 $ hg debugnodemap --metadata
426 uid: ???????????????? (glob)
426 uid: ???????????????? (glob)
427 tip-rev: 5005
427 tip-rev: 5005
428 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
428 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
429 data-length: 121536 (pure !)
429 data-length: 121536 (pure !)
430 data-length: 121536 (rust !)
430 data-length: 121536 (rust !)
431 data-length: 121088 (no-pure no-rust !)
431 data-length: 121088 (no-pure no-rust !)
432 data-unused: 448 (pure !)
432 data-unused: 448 (pure !)
433 data-unused: 448 (rust !)
433 data-unused: 448 (rust !)
434 data-unused: 0 (no-pure no-rust !)
434 data-unused: 0 (no-pure no-rust !)
435 data-unused: 0.369% (pure !)
435 data-unused: 0.369% (pure !)
436 data-unused: 0.369% (rust !)
436 data-unused: 0.369% (rust !)
437 data-unused: 0.000% (no-pure no-rust !)
437 data-unused: 0.000% (no-pure no-rust !)
438 $ f --size --sha256 .hg/store/00changelog-*.nd
438 $ f --size --sha256 .hg/store/00changelog-*.nd
439 .hg/store/00changelog-????????????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
439 .hg/store/00changelog-????????????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
440 .hg/store/00changelog-????????????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
440 .hg/store/00changelog-????????????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
441 .hg/store/00changelog-????????????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
441 .hg/store/00changelog-????????????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
442
443 Test upgrade / downgrade
444 ========================
445
446 downgrading
447
448 $ cat << EOF >> .hg/hgrc
449 > [format]
450 > use-persistent-nodemap=no
451 > EOF
452 $ hg debugformat -v
453 format-variant repo config default
454 fncache: yes yes yes
455 dotencode: yes yes yes
456 generaldelta: yes yes yes
457 sparserevlog: yes yes yes
458 sidedata: no no no
459 persistent-nodemap: yes no no
460 copies-sdc: no no no
461 plain-cl-delta: yes yes yes
462 compression: zlib zlib zlib
463 compression-level: default default default
464 $ hg debugupgraderepo --run --no-backup --quiet
465 upgrade will perform the following actions:
466
467 requirements
468 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store
469 removed: persistent-nodemap
470
471 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
472 [1]
473 $ hg debugnodemap --metadata
474
475
476 upgrading
477
478 $ cat << EOF >> .hg/hgrc
479 > [format]
480 > use-persistent-nodemap=yes
481 > EOF
482 $ hg debugformat -v
483 format-variant repo config default
484 fncache: yes yes yes
485 dotencode: yes yes yes
486 generaldelta: yes yes yes
487 sparserevlog: yes yes yes
488 sidedata: no no no
489 persistent-nodemap: no yes no
490 copies-sdc: no no no
491 plain-cl-delta: yes yes yes
492 compression: zlib zlib zlib
493 compression-level: default default default
494 $ hg debugupgraderepo --run --no-backup --quiet
495 upgrade will perform the following actions:
496
497 requirements
498 preserved: dotencode, fncache, generaldelta, revlogv1, sparserevlog, store
499 added: persistent-nodemap
500
501 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
502 00changelog-*.nd (glob)
503 00changelog.n
504 00manifest-*.nd (glob)
505 00manifest.n
506
507 $ hg debugnodemap --metadata
508 uid: * (glob)
509 tip-rev: 5005
510 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
511 data-length: 121088
512 data-unused: 0
513 data-unused: 0.000%
514
515 Running unrelated upgrade
516
517 $ hg debugupgraderepo --run --no-backup --quiet --optimize re-delta-all
518 upgrade will perform the following actions:
519
520 requirements
521 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, sparserevlog, store
522
523 optimisations: re-delta-all
524
525 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
526 00changelog-*.nd (glob)
527 00changelog.n
528 00manifest-*.nd (glob)
529 00manifest.n
530
531 $ hg debugnodemap --metadata
532 uid: * (glob)
533 tip-rev: 5005
534 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
535 data-length: 121088
536 data-unused: 0
537 data-unused: 0.000%
General Comments 0
You need to be logged in to leave comments. Login now