##// END OF EJS Templates
upgrade: avoid a traceback in case of unrecognized revlog...
marmoute -
r48460:aa229689 default
parent child Browse files
Show More
@@ -1,643 +1,648 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 error,
16 error,
17 filelog,
17 filelog,
18 manifest,
18 manifest,
19 metadata,
19 metadata,
20 pycompat,
20 pycompat,
21 requirements,
21 requirements,
22 scmutil,
22 scmutil,
23 store,
23 store,
24 util,
24 util,
25 vfs as vfsmod,
25 vfs as vfsmod,
26 )
26 )
27 from ..revlogutils import (
27 from ..revlogutils import (
28 constants as revlogconst,
28 constants as revlogconst,
29 flagutil,
29 flagutil,
30 nodemap,
30 nodemap,
31 sidedata as sidedatamod,
31 sidedata as sidedatamod,
32 )
32 )
33 from . import actions as upgrade_actions
33 from . import actions as upgrade_actions
34
34
35
35
36 def get_sidedata_helpers(srcrepo, dstrepo):
36 def get_sidedata_helpers(srcrepo, dstrepo):
37 use_w = srcrepo.ui.configbool(b'experimental', b'worker.repository-upgrade')
37 use_w = srcrepo.ui.configbool(b'experimental', b'worker.repository-upgrade')
38 sequential = pycompat.iswindows or not use_w
38 sequential = pycompat.iswindows or not use_w
39 if not sequential:
39 if not sequential:
40 srcrepo.register_sidedata_computer(
40 srcrepo.register_sidedata_computer(
41 revlogconst.KIND_CHANGELOG,
41 revlogconst.KIND_CHANGELOG,
42 sidedatamod.SD_FILES,
42 sidedatamod.SD_FILES,
43 (sidedatamod.SD_FILES,),
43 (sidedatamod.SD_FILES,),
44 metadata._get_worker_sidedata_adder(srcrepo, dstrepo),
44 metadata._get_worker_sidedata_adder(srcrepo, dstrepo),
45 flagutil.REVIDX_HASCOPIESINFO,
45 flagutil.REVIDX_HASCOPIESINFO,
46 replace=True,
46 replace=True,
47 )
47 )
48 return sidedatamod.get_sidedata_helpers(srcrepo, dstrepo._wanted_sidedata)
48 return sidedatamod.get_sidedata_helpers(srcrepo, dstrepo._wanted_sidedata)
49
49
50
50
51 def _revlogfrompath(repo, rl_type, path):
51 def _revlogfrompath(repo, rl_type, path):
52 """Obtain a revlog from a repo path.
52 """Obtain a revlog from a repo path.
53
53
54 An instance of the appropriate class is returned.
54 An instance of the appropriate class is returned.
55 """
55 """
56 if rl_type & store.FILEFLAGS_CHANGELOG:
56 if rl_type & store.FILEFLAGS_CHANGELOG:
57 return changelog.changelog(repo.svfs)
57 return changelog.changelog(repo.svfs)
58 elif rl_type & store.FILEFLAGS_MANIFESTLOG:
58 elif rl_type & store.FILEFLAGS_MANIFESTLOG:
59 mandir = b''
59 mandir = b''
60 if b'/' in path:
60 if b'/' in path:
61 mandir = path.rsplit(b'/', 1)[0]
61 mandir = path.rsplit(b'/', 1)[0]
62 return manifest.manifestrevlog(
62 return manifest.manifestrevlog(
63 repo.nodeconstants, repo.svfs, tree=mandir
63 repo.nodeconstants, repo.svfs, tree=mandir
64 )
64 )
65 else:
65 else:
66 # drop the extension and the `data/` prefix
66 # drop the extension and the `data/` prefix
67 path = path.rsplit(b'.', 1)[0].split(b'/', 1)[1]
67 path_part = path.rsplit(b'.', 1)[0].split(b'/', 1)
68 if len(path_part) < 2:
69 msg = _('cannot recognize revlog from filename: %s')
70 msg %= path
71 raise error.Abort(msg)
72 path = path_part[1]
68 return filelog.filelog(repo.svfs, path)
73 return filelog.filelog(repo.svfs, path)
69
74
70
75
71 def _copyrevlog(tr, destrepo, oldrl, rl_type, unencodedname):
76 def _copyrevlog(tr, destrepo, oldrl, rl_type, unencodedname):
72 """copy all relevant files for `oldrl` into `destrepo` store
77 """copy all relevant files for `oldrl` into `destrepo` store
73
78
74 Files are copied "as is" without any transformation. The copy is performed
79 Files are copied "as is" without any transformation. The copy is performed
75 without extra checks. Callers are responsible for making sure the copied
80 without extra checks. Callers are responsible for making sure the copied
76 content is compatible with format of the destination repository.
81 content is compatible with format of the destination repository.
77 """
82 """
78 oldrl = getattr(oldrl, '_revlog', oldrl)
83 oldrl = getattr(oldrl, '_revlog', oldrl)
79 newrl = _revlogfrompath(destrepo, rl_type, unencodedname)
84 newrl = _revlogfrompath(destrepo, rl_type, unencodedname)
80 newrl = getattr(newrl, '_revlog', newrl)
85 newrl = getattr(newrl, '_revlog', newrl)
81
86
82 oldvfs = oldrl.opener
87 oldvfs = oldrl.opener
83 newvfs = newrl.opener
88 newvfs = newrl.opener
84 oldindex = oldvfs.join(oldrl._indexfile)
89 oldindex = oldvfs.join(oldrl._indexfile)
85 newindex = newvfs.join(newrl._indexfile)
90 newindex = newvfs.join(newrl._indexfile)
86 olddata = oldvfs.join(oldrl._datafile)
91 olddata = oldvfs.join(oldrl._datafile)
87 newdata = newvfs.join(newrl._datafile)
92 newdata = newvfs.join(newrl._datafile)
88
93
89 with newvfs(newrl._indexfile, b'w'):
94 with newvfs(newrl._indexfile, b'w'):
90 pass # create all the directories
95 pass # create all the directories
91
96
92 util.copyfile(oldindex, newindex)
97 util.copyfile(oldindex, newindex)
93 copydata = oldrl.opener.exists(oldrl._datafile)
98 copydata = oldrl.opener.exists(oldrl._datafile)
94 if copydata:
99 if copydata:
95 util.copyfile(olddata, newdata)
100 util.copyfile(olddata, newdata)
96
101
97 if rl_type & store.FILEFLAGS_FILELOG:
102 if rl_type & store.FILEFLAGS_FILELOG:
98 destrepo.svfs.fncache.add(unencodedname)
103 destrepo.svfs.fncache.add(unencodedname)
99 if copydata:
104 if copydata:
100 destrepo.svfs.fncache.add(unencodedname[:-2] + b'.d')
105 destrepo.svfs.fncache.add(unencodedname[:-2] + b'.d')
101
106
102
107
103 UPGRADE_CHANGELOG = b"changelog"
108 UPGRADE_CHANGELOG = b"changelog"
104 UPGRADE_MANIFEST = b"manifest"
109 UPGRADE_MANIFEST = b"manifest"
105 UPGRADE_FILELOGS = b"all-filelogs"
110 UPGRADE_FILELOGS = b"all-filelogs"
106
111
107 UPGRADE_ALL_REVLOGS = frozenset(
112 UPGRADE_ALL_REVLOGS = frozenset(
108 [UPGRADE_CHANGELOG, UPGRADE_MANIFEST, UPGRADE_FILELOGS]
113 [UPGRADE_CHANGELOG, UPGRADE_MANIFEST, UPGRADE_FILELOGS]
109 )
114 )
110
115
111
116
112 def matchrevlog(revlogfilter, rl_type):
117 def matchrevlog(revlogfilter, rl_type):
113 """check if a revlog is selected for cloning.
118 """check if a revlog is selected for cloning.
114
119
115 In other words, are there any updates which need to be done on revlog
120 In other words, are there any updates which need to be done on revlog
116 or it can be blindly copied.
121 or it can be blindly copied.
117
122
118 The store entry is checked against the passed filter"""
123 The store entry is checked against the passed filter"""
119 if rl_type & store.FILEFLAGS_CHANGELOG:
124 if rl_type & store.FILEFLAGS_CHANGELOG:
120 return UPGRADE_CHANGELOG in revlogfilter
125 return UPGRADE_CHANGELOG in revlogfilter
121 elif rl_type & store.FILEFLAGS_MANIFESTLOG:
126 elif rl_type & store.FILEFLAGS_MANIFESTLOG:
122 return UPGRADE_MANIFEST in revlogfilter
127 return UPGRADE_MANIFEST in revlogfilter
123 assert rl_type & store.FILEFLAGS_FILELOG
128 assert rl_type & store.FILEFLAGS_FILELOG
124 return UPGRADE_FILELOGS in revlogfilter
129 return UPGRADE_FILELOGS in revlogfilter
125
130
126
131
127 def _perform_clone(
132 def _perform_clone(
128 ui,
133 ui,
129 dstrepo,
134 dstrepo,
130 tr,
135 tr,
131 old_revlog,
136 old_revlog,
132 rl_type,
137 rl_type,
133 unencoded,
138 unencoded,
134 upgrade_op,
139 upgrade_op,
135 sidedata_helpers,
140 sidedata_helpers,
136 oncopiedrevision,
141 oncopiedrevision,
137 ):
142 ):
138 """returns the new revlog object created"""
143 """returns the new revlog object created"""
139 newrl = None
144 newrl = None
140 if matchrevlog(upgrade_op.revlogs_to_process, rl_type):
145 if matchrevlog(upgrade_op.revlogs_to_process, rl_type):
141 ui.note(
146 ui.note(
142 _(b'cloning %d revisions from %s\n') % (len(old_revlog), unencoded)
147 _(b'cloning %d revisions from %s\n') % (len(old_revlog), unencoded)
143 )
148 )
144 newrl = _revlogfrompath(dstrepo, rl_type, unencoded)
149 newrl = _revlogfrompath(dstrepo, rl_type, unencoded)
145 old_revlog.clone(
150 old_revlog.clone(
146 tr,
151 tr,
147 newrl,
152 newrl,
148 addrevisioncb=oncopiedrevision,
153 addrevisioncb=oncopiedrevision,
149 deltareuse=upgrade_op.delta_reuse_mode,
154 deltareuse=upgrade_op.delta_reuse_mode,
150 forcedeltabothparents=upgrade_op.force_re_delta_both_parents,
155 forcedeltabothparents=upgrade_op.force_re_delta_both_parents,
151 sidedata_helpers=sidedata_helpers,
156 sidedata_helpers=sidedata_helpers,
152 )
157 )
153 else:
158 else:
154 msg = _(b'blindly copying %s containing %i revisions\n')
159 msg = _(b'blindly copying %s containing %i revisions\n')
155 ui.note(msg % (unencoded, len(old_revlog)))
160 ui.note(msg % (unencoded, len(old_revlog)))
156 _copyrevlog(tr, dstrepo, old_revlog, rl_type, unencoded)
161 _copyrevlog(tr, dstrepo, old_revlog, rl_type, unencoded)
157
162
158 newrl = _revlogfrompath(dstrepo, rl_type, unencoded)
163 newrl = _revlogfrompath(dstrepo, rl_type, unencoded)
159 return newrl
164 return newrl
160
165
161
166
162 def _clonerevlogs(
167 def _clonerevlogs(
163 ui,
168 ui,
164 srcrepo,
169 srcrepo,
165 dstrepo,
170 dstrepo,
166 tr,
171 tr,
167 upgrade_op,
172 upgrade_op,
168 ):
173 ):
169 """Copy revlogs between 2 repos."""
174 """Copy revlogs between 2 repos."""
170 revcount = 0
175 revcount = 0
171 srcsize = 0
176 srcsize = 0
172 srcrawsize = 0
177 srcrawsize = 0
173 dstsize = 0
178 dstsize = 0
174 fcount = 0
179 fcount = 0
175 frevcount = 0
180 frevcount = 0
176 fsrcsize = 0
181 fsrcsize = 0
177 frawsize = 0
182 frawsize = 0
178 fdstsize = 0
183 fdstsize = 0
179 mcount = 0
184 mcount = 0
180 mrevcount = 0
185 mrevcount = 0
181 msrcsize = 0
186 msrcsize = 0
182 mrawsize = 0
187 mrawsize = 0
183 mdstsize = 0
188 mdstsize = 0
184 crevcount = 0
189 crevcount = 0
185 csrcsize = 0
190 csrcsize = 0
186 crawsize = 0
191 crawsize = 0
187 cdstsize = 0
192 cdstsize = 0
188
193
189 alldatafiles = list(srcrepo.store.walk())
194 alldatafiles = list(srcrepo.store.walk())
190 # mapping of data files which needs to be cloned
195 # mapping of data files which needs to be cloned
191 # key is unencoded filename
196 # key is unencoded filename
192 # value is revlog_object_from_srcrepo
197 # value is revlog_object_from_srcrepo
193 manifests = {}
198 manifests = {}
194 changelogs = {}
199 changelogs = {}
195 filelogs = {}
200 filelogs = {}
196
201
197 # Perform a pass to collect metadata. This validates we can open all
202 # Perform a pass to collect metadata. This validates we can open all
198 # source files and allows a unified progress bar to be displayed.
203 # source files and allows a unified progress bar to be displayed.
199 for rl_type, unencoded, encoded, size in alldatafiles:
204 for rl_type, unencoded, encoded, size in alldatafiles:
200 if not rl_type & store.FILEFLAGS_REVLOG_MAIN:
205 if not rl_type & store.FILEFLAGS_REVLOG_MAIN:
201 continue
206 continue
202
207
203 # the store.walk function will wrongly pickup transaction backup and
208 # the store.walk function will wrongly pickup transaction backup and
204 # get confused. As a quick fix for 5.9 release, we ignore those.
209 # get confused. As a quick fix for 5.9 release, we ignore those.
205 # (this is not a module constants because it seems better to keep the
210 # (this is not a module constants because it seems better to keep the
206 # hack together)
211 # hack together)
207 skip_undo = (
212 skip_undo = (
208 b'undo.backup.00changelog.i',
213 b'undo.backup.00changelog.i',
209 b'undo.backup.00manifest.i',
214 b'undo.backup.00manifest.i',
210 )
215 )
211 if unencoded in skip_undo:
216 if unencoded in skip_undo:
212 continue
217 continue
213
218
214 rl = _revlogfrompath(srcrepo, rl_type, unencoded)
219 rl = _revlogfrompath(srcrepo, rl_type, unencoded)
215
220
216 info = rl.storageinfo(
221 info = rl.storageinfo(
217 exclusivefiles=True,
222 exclusivefiles=True,
218 revisionscount=True,
223 revisionscount=True,
219 trackedsize=True,
224 trackedsize=True,
220 storedsize=True,
225 storedsize=True,
221 )
226 )
222
227
223 revcount += info[b'revisionscount'] or 0
228 revcount += info[b'revisionscount'] or 0
224 datasize = info[b'storedsize'] or 0
229 datasize = info[b'storedsize'] or 0
225 rawsize = info[b'trackedsize'] or 0
230 rawsize = info[b'trackedsize'] or 0
226
231
227 srcsize += datasize
232 srcsize += datasize
228 srcrawsize += rawsize
233 srcrawsize += rawsize
229
234
230 # This is for the separate progress bars.
235 # This is for the separate progress bars.
231 if rl_type & store.FILEFLAGS_CHANGELOG:
236 if rl_type & store.FILEFLAGS_CHANGELOG:
232 changelogs[unencoded] = (rl_type, rl)
237 changelogs[unencoded] = (rl_type, rl)
233 crevcount += len(rl)
238 crevcount += len(rl)
234 csrcsize += datasize
239 csrcsize += datasize
235 crawsize += rawsize
240 crawsize += rawsize
236 elif rl_type & store.FILEFLAGS_MANIFESTLOG:
241 elif rl_type & store.FILEFLAGS_MANIFESTLOG:
237 manifests[unencoded] = (rl_type, rl)
242 manifests[unencoded] = (rl_type, rl)
238 mcount += 1
243 mcount += 1
239 mrevcount += len(rl)
244 mrevcount += len(rl)
240 msrcsize += datasize
245 msrcsize += datasize
241 mrawsize += rawsize
246 mrawsize += rawsize
242 elif rl_type & store.FILEFLAGS_FILELOG:
247 elif rl_type & store.FILEFLAGS_FILELOG:
243 filelogs[unencoded] = (rl_type, rl)
248 filelogs[unencoded] = (rl_type, rl)
244 fcount += 1
249 fcount += 1
245 frevcount += len(rl)
250 frevcount += len(rl)
246 fsrcsize += datasize
251 fsrcsize += datasize
247 frawsize += rawsize
252 frawsize += rawsize
248 else:
253 else:
249 error.ProgrammingError(b'unknown revlog type')
254 error.ProgrammingError(b'unknown revlog type')
250
255
251 if not revcount:
256 if not revcount:
252 return
257 return
253
258
254 ui.status(
259 ui.status(
255 _(
260 _(
256 b'migrating %d total revisions (%d in filelogs, %d in manifests, '
261 b'migrating %d total revisions (%d in filelogs, %d in manifests, '
257 b'%d in changelog)\n'
262 b'%d in changelog)\n'
258 )
263 )
259 % (revcount, frevcount, mrevcount, crevcount)
264 % (revcount, frevcount, mrevcount, crevcount)
260 )
265 )
261 ui.status(
266 ui.status(
262 _(b'migrating %s in store; %s tracked data\n')
267 _(b'migrating %s in store; %s tracked data\n')
263 % ((util.bytecount(srcsize), util.bytecount(srcrawsize)))
268 % ((util.bytecount(srcsize), util.bytecount(srcrawsize)))
264 )
269 )
265
270
266 # Used to keep track of progress.
271 # Used to keep track of progress.
267 progress = None
272 progress = None
268
273
269 def oncopiedrevision(rl, rev, node):
274 def oncopiedrevision(rl, rev, node):
270 progress.increment()
275 progress.increment()
271
276
272 sidedata_helpers = get_sidedata_helpers(srcrepo, dstrepo)
277 sidedata_helpers = get_sidedata_helpers(srcrepo, dstrepo)
273
278
274 # Migrating filelogs
279 # Migrating filelogs
275 ui.status(
280 ui.status(
276 _(
281 _(
277 b'migrating %d filelogs containing %d revisions '
282 b'migrating %d filelogs containing %d revisions '
278 b'(%s in store; %s tracked data)\n'
283 b'(%s in store; %s tracked data)\n'
279 )
284 )
280 % (
285 % (
281 fcount,
286 fcount,
282 frevcount,
287 frevcount,
283 util.bytecount(fsrcsize),
288 util.bytecount(fsrcsize),
284 util.bytecount(frawsize),
289 util.bytecount(frawsize),
285 )
290 )
286 )
291 )
287 progress = srcrepo.ui.makeprogress(_(b'file revisions'), total=frevcount)
292 progress = srcrepo.ui.makeprogress(_(b'file revisions'), total=frevcount)
288 for unencoded, (rl_type, oldrl) in sorted(filelogs.items()):
293 for unencoded, (rl_type, oldrl) in sorted(filelogs.items()):
289 newrl = _perform_clone(
294 newrl = _perform_clone(
290 ui,
295 ui,
291 dstrepo,
296 dstrepo,
292 tr,
297 tr,
293 oldrl,
298 oldrl,
294 rl_type,
299 rl_type,
295 unencoded,
300 unencoded,
296 upgrade_op,
301 upgrade_op,
297 sidedata_helpers,
302 sidedata_helpers,
298 oncopiedrevision,
303 oncopiedrevision,
299 )
304 )
300 info = newrl.storageinfo(storedsize=True)
305 info = newrl.storageinfo(storedsize=True)
301 fdstsize += info[b'storedsize'] or 0
306 fdstsize += info[b'storedsize'] or 0
302 ui.status(
307 ui.status(
303 _(
308 _(
304 b'finished migrating %d filelog revisions across %d '
309 b'finished migrating %d filelog revisions across %d '
305 b'filelogs; change in size: %s\n'
310 b'filelogs; change in size: %s\n'
306 )
311 )
307 % (frevcount, fcount, util.bytecount(fdstsize - fsrcsize))
312 % (frevcount, fcount, util.bytecount(fdstsize - fsrcsize))
308 )
313 )
309
314
310 # Migrating manifests
315 # Migrating manifests
311 ui.status(
316 ui.status(
312 _(
317 _(
313 b'migrating %d manifests containing %d revisions '
318 b'migrating %d manifests containing %d revisions '
314 b'(%s in store; %s tracked data)\n'
319 b'(%s in store; %s tracked data)\n'
315 )
320 )
316 % (
321 % (
317 mcount,
322 mcount,
318 mrevcount,
323 mrevcount,
319 util.bytecount(msrcsize),
324 util.bytecount(msrcsize),
320 util.bytecount(mrawsize),
325 util.bytecount(mrawsize),
321 )
326 )
322 )
327 )
323 if progress:
328 if progress:
324 progress.complete()
329 progress.complete()
325 progress = srcrepo.ui.makeprogress(
330 progress = srcrepo.ui.makeprogress(
326 _(b'manifest revisions'), total=mrevcount
331 _(b'manifest revisions'), total=mrevcount
327 )
332 )
328 for unencoded, (rl_type, oldrl) in sorted(manifests.items()):
333 for unencoded, (rl_type, oldrl) in sorted(manifests.items()):
329 newrl = _perform_clone(
334 newrl = _perform_clone(
330 ui,
335 ui,
331 dstrepo,
336 dstrepo,
332 tr,
337 tr,
333 oldrl,
338 oldrl,
334 rl_type,
339 rl_type,
335 unencoded,
340 unencoded,
336 upgrade_op,
341 upgrade_op,
337 sidedata_helpers,
342 sidedata_helpers,
338 oncopiedrevision,
343 oncopiedrevision,
339 )
344 )
340 info = newrl.storageinfo(storedsize=True)
345 info = newrl.storageinfo(storedsize=True)
341 mdstsize += info[b'storedsize'] or 0
346 mdstsize += info[b'storedsize'] or 0
342 ui.status(
347 ui.status(
343 _(
348 _(
344 b'finished migrating %d manifest revisions across %d '
349 b'finished migrating %d manifest revisions across %d '
345 b'manifests; change in size: %s\n'
350 b'manifests; change in size: %s\n'
346 )
351 )
347 % (mrevcount, mcount, util.bytecount(mdstsize - msrcsize))
352 % (mrevcount, mcount, util.bytecount(mdstsize - msrcsize))
348 )
353 )
349
354
350 # Migrating changelog
355 # Migrating changelog
351 ui.status(
356 ui.status(
352 _(
357 _(
353 b'migrating changelog containing %d revisions '
358 b'migrating changelog containing %d revisions '
354 b'(%s in store; %s tracked data)\n'
359 b'(%s in store; %s tracked data)\n'
355 )
360 )
356 % (
361 % (
357 crevcount,
362 crevcount,
358 util.bytecount(csrcsize),
363 util.bytecount(csrcsize),
359 util.bytecount(crawsize),
364 util.bytecount(crawsize),
360 )
365 )
361 )
366 )
362 if progress:
367 if progress:
363 progress.complete()
368 progress.complete()
364 progress = srcrepo.ui.makeprogress(
369 progress = srcrepo.ui.makeprogress(
365 _(b'changelog revisions'), total=crevcount
370 _(b'changelog revisions'), total=crevcount
366 )
371 )
367 for unencoded, (rl_type, oldrl) in sorted(changelogs.items()):
372 for unencoded, (rl_type, oldrl) in sorted(changelogs.items()):
368 newrl = _perform_clone(
373 newrl = _perform_clone(
369 ui,
374 ui,
370 dstrepo,
375 dstrepo,
371 tr,
376 tr,
372 oldrl,
377 oldrl,
373 rl_type,
378 rl_type,
374 unencoded,
379 unencoded,
375 upgrade_op,
380 upgrade_op,
376 sidedata_helpers,
381 sidedata_helpers,
377 oncopiedrevision,
382 oncopiedrevision,
378 )
383 )
379 info = newrl.storageinfo(storedsize=True)
384 info = newrl.storageinfo(storedsize=True)
380 cdstsize += info[b'storedsize'] or 0
385 cdstsize += info[b'storedsize'] or 0
381 progress.complete()
386 progress.complete()
382 ui.status(
387 ui.status(
383 _(
388 _(
384 b'finished migrating %d changelog revisions; change in size: '
389 b'finished migrating %d changelog revisions; change in size: '
385 b'%s\n'
390 b'%s\n'
386 )
391 )
387 % (crevcount, util.bytecount(cdstsize - csrcsize))
392 % (crevcount, util.bytecount(cdstsize - csrcsize))
388 )
393 )
389
394
390 dstsize = fdstsize + mdstsize + cdstsize
395 dstsize = fdstsize + mdstsize + cdstsize
391 ui.status(
396 ui.status(
392 _(
397 _(
393 b'finished migrating %d total revisions; total change in store '
398 b'finished migrating %d total revisions; total change in store '
394 b'size: %s\n'
399 b'size: %s\n'
395 )
400 )
396 % (revcount, util.bytecount(dstsize - srcsize))
401 % (revcount, util.bytecount(dstsize - srcsize))
397 )
402 )
398
403
399
404
400 def _files_to_copy_post_revlog_clone(srcrepo):
405 def _files_to_copy_post_revlog_clone(srcrepo):
401 """yields files which should be copied to destination after revlogs
406 """yields files which should be copied to destination after revlogs
402 are cloned"""
407 are cloned"""
403 for path, kind, st in sorted(srcrepo.store.vfs.readdir(b'', stat=True)):
408 for path, kind, st in sorted(srcrepo.store.vfs.readdir(b'', stat=True)):
404 # don't copy revlogs as they are already cloned
409 # don't copy revlogs as they are already cloned
405 if store.revlog_type(path) is not None:
410 if store.revlog_type(path) is not None:
406 continue
411 continue
407 # Skip transaction related files.
412 # Skip transaction related files.
408 if path.startswith(b'undo'):
413 if path.startswith(b'undo'):
409 continue
414 continue
410 # Only copy regular files.
415 # Only copy regular files.
411 if kind != stat.S_IFREG:
416 if kind != stat.S_IFREG:
412 continue
417 continue
413 # Skip other skipped files.
418 # Skip other skipped files.
414 if path in (b'lock', b'fncache'):
419 if path in (b'lock', b'fncache'):
415 continue
420 continue
416 # TODO: should we skip cache too?
421 # TODO: should we skip cache too?
417
422
418 yield path
423 yield path
419
424
420
425
421 def _replacestores(currentrepo, upgradedrepo, backupvfs, upgrade_op):
426 def _replacestores(currentrepo, upgradedrepo, backupvfs, upgrade_op):
422 """Replace the stores after current repository is upgraded
427 """Replace the stores after current repository is upgraded
423
428
424 Creates a backup of current repository store at backup path
429 Creates a backup of current repository store at backup path
425 Replaces upgraded store files in current repo from upgraded one
430 Replaces upgraded store files in current repo from upgraded one
426
431
427 Arguments:
432 Arguments:
428 currentrepo: repo object of current repository
433 currentrepo: repo object of current repository
429 upgradedrepo: repo object of the upgraded data
434 upgradedrepo: repo object of the upgraded data
430 backupvfs: vfs object for the backup path
435 backupvfs: vfs object for the backup path
431 upgrade_op: upgrade operation object
436 upgrade_op: upgrade operation object
432 to be used to decide what all is upgraded
437 to be used to decide what all is upgraded
433 """
438 """
434 # TODO: don't blindly rename everything in store
439 # TODO: don't blindly rename everything in store
435 # There can be upgrades where store is not touched at all
440 # There can be upgrades where store is not touched at all
436 if upgrade_op.backup_store:
441 if upgrade_op.backup_store:
437 util.rename(currentrepo.spath, backupvfs.join(b'store'))
442 util.rename(currentrepo.spath, backupvfs.join(b'store'))
438 else:
443 else:
439 currentrepo.vfs.rmtree(b'store', forcibly=True)
444 currentrepo.vfs.rmtree(b'store', forcibly=True)
440 util.rename(upgradedrepo.spath, currentrepo.spath)
445 util.rename(upgradedrepo.spath, currentrepo.spath)
441
446
442
447
443 def finishdatamigration(ui, srcrepo, dstrepo, requirements):
448 def finishdatamigration(ui, srcrepo, dstrepo, requirements):
444 """Hook point for extensions to perform additional actions during upgrade.
449 """Hook point for extensions to perform additional actions during upgrade.
445
450
446 This function is called after revlogs and store files have been copied but
451 This function is called after revlogs and store files have been copied but
447 before the new store is swapped into the original location.
452 before the new store is swapped into the original location.
448 """
453 """
449
454
450
455
451 def upgrade(ui, srcrepo, dstrepo, upgrade_op):
456 def upgrade(ui, srcrepo, dstrepo, upgrade_op):
452 """Do the low-level work of upgrading a repository.
457 """Do the low-level work of upgrading a repository.
453
458
454 The upgrade is effectively performed as a copy between a source
459 The upgrade is effectively performed as a copy between a source
455 repository and a temporary destination repository.
460 repository and a temporary destination repository.
456
461
457 The source repository is unmodified for as long as possible so the
462 The source repository is unmodified for as long as possible so the
458 upgrade can abort at any time without causing loss of service for
463 upgrade can abort at any time without causing loss of service for
459 readers and without corrupting the source repository.
464 readers and without corrupting the source repository.
460 """
465 """
461 assert srcrepo.currentwlock()
466 assert srcrepo.currentwlock()
462 assert dstrepo.currentwlock()
467 assert dstrepo.currentwlock()
463 backuppath = None
468 backuppath = None
464 backupvfs = None
469 backupvfs = None
465
470
466 ui.status(
471 ui.status(
467 _(
472 _(
468 b'(it is safe to interrupt this process any time before '
473 b'(it is safe to interrupt this process any time before '
469 b'data migration completes)\n'
474 b'data migration completes)\n'
470 )
475 )
471 )
476 )
472
477
473 if upgrade_actions.dirstatev2 in upgrade_op.upgrade_actions:
478 if upgrade_actions.dirstatev2 in upgrade_op.upgrade_actions:
474 ui.status(_(b'upgrading to dirstate-v2 from v1\n'))
479 ui.status(_(b'upgrading to dirstate-v2 from v1\n'))
475 upgrade_dirstate(ui, srcrepo, upgrade_op, b'v1', b'v2')
480 upgrade_dirstate(ui, srcrepo, upgrade_op, b'v1', b'v2')
476 upgrade_op.upgrade_actions.remove(upgrade_actions.dirstatev2)
481 upgrade_op.upgrade_actions.remove(upgrade_actions.dirstatev2)
477
482
478 if upgrade_actions.dirstatev2 in upgrade_op.removed_actions:
483 if upgrade_actions.dirstatev2 in upgrade_op.removed_actions:
479 ui.status(_(b'downgrading from dirstate-v2 to v1\n'))
484 ui.status(_(b'downgrading from dirstate-v2 to v1\n'))
480 upgrade_dirstate(ui, srcrepo, upgrade_op, b'v2', b'v1')
485 upgrade_dirstate(ui, srcrepo, upgrade_op, b'v2', b'v1')
481 upgrade_op.removed_actions.remove(upgrade_actions.dirstatev2)
486 upgrade_op.removed_actions.remove(upgrade_actions.dirstatev2)
482
487
483 if not (upgrade_op.upgrade_actions or upgrade_op.removed_actions):
488 if not (upgrade_op.upgrade_actions or upgrade_op.removed_actions):
484 return
489 return
485
490
486 if upgrade_op.requirements_only:
491 if upgrade_op.requirements_only:
487 ui.status(_(b'upgrading repository requirements\n'))
492 ui.status(_(b'upgrading repository requirements\n'))
488 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
493 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
489 # if there is only one action and that is persistent nodemap upgrade
494 # if there is only one action and that is persistent nodemap upgrade
490 # directly write the nodemap file and update requirements instead of going
495 # directly write the nodemap file and update requirements instead of going
491 # through the whole cloning process
496 # through the whole cloning process
492 elif (
497 elif (
493 len(upgrade_op.upgrade_actions) == 1
498 len(upgrade_op.upgrade_actions) == 1
494 and b'persistent-nodemap' in upgrade_op.upgrade_actions_names
499 and b'persistent-nodemap' in upgrade_op.upgrade_actions_names
495 and not upgrade_op.removed_actions
500 and not upgrade_op.removed_actions
496 ):
501 ):
497 ui.status(
502 ui.status(
498 _(b'upgrading repository to use persistent nodemap feature\n')
503 _(b'upgrading repository to use persistent nodemap feature\n')
499 )
504 )
500 with srcrepo.transaction(b'upgrade') as tr:
505 with srcrepo.transaction(b'upgrade') as tr:
501 unfi = srcrepo.unfiltered()
506 unfi = srcrepo.unfiltered()
502 cl = unfi.changelog
507 cl = unfi.changelog
503 nodemap.persist_nodemap(tr, cl, force=True)
508 nodemap.persist_nodemap(tr, cl, force=True)
504 # we want to directly operate on the underlying revlog to force
509 # we want to directly operate on the underlying revlog to force
505 # create a nodemap file. This is fine since this is upgrade code
510 # create a nodemap file. This is fine since this is upgrade code
506 # and it heavily relies on repository being revlog based
511 # and it heavily relies on repository being revlog based
507 # hence accessing private attributes can be justified
512 # hence accessing private attributes can be justified
508 nodemap.persist_nodemap(
513 nodemap.persist_nodemap(
509 tr, unfi.manifestlog._rootstore._revlog, force=True
514 tr, unfi.manifestlog._rootstore._revlog, force=True
510 )
515 )
511 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
516 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
512 elif (
517 elif (
513 len(upgrade_op.removed_actions) == 1
518 len(upgrade_op.removed_actions) == 1
514 and [
519 and [
515 x
520 x
516 for x in upgrade_op.removed_actions
521 for x in upgrade_op.removed_actions
517 if x.name == b'persistent-nodemap'
522 if x.name == b'persistent-nodemap'
518 ]
523 ]
519 and not upgrade_op.upgrade_actions
524 and not upgrade_op.upgrade_actions
520 ):
525 ):
521 ui.status(
526 ui.status(
522 _(b'downgrading repository to not use persistent nodemap feature\n')
527 _(b'downgrading repository to not use persistent nodemap feature\n')
523 )
528 )
524 with srcrepo.transaction(b'upgrade') as tr:
529 with srcrepo.transaction(b'upgrade') as tr:
525 unfi = srcrepo.unfiltered()
530 unfi = srcrepo.unfiltered()
526 cl = unfi.changelog
531 cl = unfi.changelog
527 nodemap.delete_nodemap(tr, srcrepo, cl)
532 nodemap.delete_nodemap(tr, srcrepo, cl)
528 # check comment 20 lines above for accessing private attributes
533 # check comment 20 lines above for accessing private attributes
529 nodemap.delete_nodemap(
534 nodemap.delete_nodemap(
530 tr, srcrepo, unfi.manifestlog._rootstore._revlog
535 tr, srcrepo, unfi.manifestlog._rootstore._revlog
531 )
536 )
532 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
537 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
533 else:
538 else:
534 with dstrepo.transaction(b'upgrade') as tr:
539 with dstrepo.transaction(b'upgrade') as tr:
535 _clonerevlogs(
540 _clonerevlogs(
536 ui,
541 ui,
537 srcrepo,
542 srcrepo,
538 dstrepo,
543 dstrepo,
539 tr,
544 tr,
540 upgrade_op,
545 upgrade_op,
541 )
546 )
542
547
543 # Now copy other files in the store directory.
548 # Now copy other files in the store directory.
544 for p in _files_to_copy_post_revlog_clone(srcrepo):
549 for p in _files_to_copy_post_revlog_clone(srcrepo):
545 srcrepo.ui.status(_(b'copying %s\n') % p)
550 srcrepo.ui.status(_(b'copying %s\n') % p)
546 src = srcrepo.store.rawvfs.join(p)
551 src = srcrepo.store.rawvfs.join(p)
547 dst = dstrepo.store.rawvfs.join(p)
552 dst = dstrepo.store.rawvfs.join(p)
548 util.copyfile(src, dst, copystat=True)
553 util.copyfile(src, dst, copystat=True)
549
554
550 finishdatamigration(ui, srcrepo, dstrepo, requirements)
555 finishdatamigration(ui, srcrepo, dstrepo, requirements)
551
556
552 ui.status(_(b'data fully upgraded in a temporary repository\n'))
557 ui.status(_(b'data fully upgraded in a temporary repository\n'))
553
558
554 if upgrade_op.backup_store:
559 if upgrade_op.backup_store:
555 backuppath = pycompat.mkdtemp(
560 backuppath = pycompat.mkdtemp(
556 prefix=b'upgradebackup.', dir=srcrepo.path
561 prefix=b'upgradebackup.', dir=srcrepo.path
557 )
562 )
558 backupvfs = vfsmod.vfs(backuppath)
563 backupvfs = vfsmod.vfs(backuppath)
559
564
560 # Make a backup of requires file first, as it is the first to be modified.
565 # Make a backup of requires file first, as it is the first to be modified.
561 util.copyfile(
566 util.copyfile(
562 srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires')
567 srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires')
563 )
568 )
564
569
565 # We install an arbitrary requirement that clients must not support
570 # We install an arbitrary requirement that clients must not support
566 # as a mechanism to lock out new clients during the data swap. This is
571 # as a mechanism to lock out new clients during the data swap. This is
567 # better than allowing a client to continue while the repository is in
572 # better than allowing a client to continue while the repository is in
568 # an inconsistent state.
573 # an inconsistent state.
569 ui.status(
574 ui.status(
570 _(
575 _(
571 b'marking source repository as being upgraded; clients will be '
576 b'marking source repository as being upgraded; clients will be '
572 b'unable to read from repository\n'
577 b'unable to read from repository\n'
573 )
578 )
574 )
579 )
575 scmutil.writereporequirements(
580 scmutil.writereporequirements(
576 srcrepo, srcrepo.requirements | {b'upgradeinprogress'}
581 srcrepo, srcrepo.requirements | {b'upgradeinprogress'}
577 )
582 )
578
583
579 ui.status(_(b'starting in-place swap of repository data\n'))
584 ui.status(_(b'starting in-place swap of repository data\n'))
580 if upgrade_op.backup_store:
585 if upgrade_op.backup_store:
581 ui.status(
586 ui.status(
582 _(b'replaced files will be backed up at %s\n') % backuppath
587 _(b'replaced files will be backed up at %s\n') % backuppath
583 )
588 )
584
589
585 # Now swap in the new store directory. Doing it as a rename should make
590 # Now swap in the new store directory. Doing it as a rename should make
586 # the operation nearly instantaneous and atomic (at least in well-behaved
591 # the operation nearly instantaneous and atomic (at least in well-behaved
587 # environments).
592 # environments).
588 ui.status(_(b'replacing store...\n'))
593 ui.status(_(b'replacing store...\n'))
589 tstart = util.timer()
594 tstart = util.timer()
590 _replacestores(srcrepo, dstrepo, backupvfs, upgrade_op)
595 _replacestores(srcrepo, dstrepo, backupvfs, upgrade_op)
591 elapsed = util.timer() - tstart
596 elapsed = util.timer() - tstart
592 ui.status(
597 ui.status(
593 _(
598 _(
594 b'store replacement complete; repository was inconsistent for '
599 b'store replacement complete; repository was inconsistent for '
595 b'%0.1fs\n'
600 b'%0.1fs\n'
596 )
601 )
597 % elapsed
602 % elapsed
598 )
603 )
599
604
600 # We first write the requirements file. Any new requirements will lock
605 # We first write the requirements file. Any new requirements will lock
601 # out legacy clients.
606 # out legacy clients.
602 ui.status(
607 ui.status(
603 _(
608 _(
604 b'finalizing requirements file and making repository readable '
609 b'finalizing requirements file and making repository readable '
605 b'again\n'
610 b'again\n'
606 )
611 )
607 )
612 )
608 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
613 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
609
614
610 if upgrade_op.backup_store:
615 if upgrade_op.backup_store:
611 # The lock file from the old store won't be removed because nothing has a
616 # The lock file from the old store won't be removed because nothing has a
612 # reference to its new location. So clean it up manually. Alternatively, we
617 # reference to its new location. So clean it up manually. Alternatively, we
613 # could update srcrepo.svfs and other variables to point to the new
618 # could update srcrepo.svfs and other variables to point to the new
614 # location. This is simpler.
619 # location. This is simpler.
615 assert backupvfs is not None # help pytype
620 assert backupvfs is not None # help pytype
616 backupvfs.unlink(b'store/lock')
621 backupvfs.unlink(b'store/lock')
617
622
618 return backuppath
623 return backuppath
619
624
620
625
621 def upgrade_dirstate(ui, srcrepo, upgrade_op, old, new):
626 def upgrade_dirstate(ui, srcrepo, upgrade_op, old, new):
622 if upgrade_op.backup_store:
627 if upgrade_op.backup_store:
623 backuppath = pycompat.mkdtemp(
628 backuppath = pycompat.mkdtemp(
624 prefix=b'upgradebackup.', dir=srcrepo.path
629 prefix=b'upgradebackup.', dir=srcrepo.path
625 )
630 )
626 ui.status(_(b'replaced files will be backed up at %s\n') % backuppath)
631 ui.status(_(b'replaced files will be backed up at %s\n') % backuppath)
627 backupvfs = vfsmod.vfs(backuppath)
632 backupvfs = vfsmod.vfs(backuppath)
628 util.copyfile(
633 util.copyfile(
629 srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires')
634 srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires')
630 )
635 )
631 util.copyfile(
636 util.copyfile(
632 srcrepo.vfs.join(b'dirstate'), backupvfs.join(b'dirstate')
637 srcrepo.vfs.join(b'dirstate'), backupvfs.join(b'dirstate')
633 )
638 )
634
639
635 assert srcrepo.dirstate._use_dirstate_v2 == (old == b'v2')
640 assert srcrepo.dirstate._use_dirstate_v2 == (old == b'v2')
636 srcrepo.dirstate._map._use_dirstate_tree = True
641 srcrepo.dirstate._map._use_dirstate_tree = True
637 srcrepo.dirstate._map.preload()
642 srcrepo.dirstate._map.preload()
638 srcrepo.dirstate._use_dirstate_v2 = new == b'v2'
643 srcrepo.dirstate._use_dirstate_v2 = new == b'v2'
639 srcrepo.dirstate._map._use_dirstate_v2 = srcrepo.dirstate._use_dirstate_v2
644 srcrepo.dirstate._map._use_dirstate_v2 = srcrepo.dirstate._use_dirstate_v2
640 srcrepo.dirstate._dirty = True
645 srcrepo.dirstate._dirty = True
641 srcrepo.dirstate.write(None)
646 srcrepo.dirstate.write(None)
642
647
643 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
648 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
General Comments 0
You need to be logged in to leave comments. Login now