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