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