##// END OF EJS Templates
engine: prevent a function call for each store file...
Pulkit Goyal -
r46846:52abb1af default
parent child Browse files
Show More
@@ -1,538 +1,521 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 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 upgrade_op,
127 upgrade_op,
128 sidedatacompanion,
128 sidedatacompanion,
129 oncopiedrevision,
129 oncopiedrevision,
130 ):
130 ):
131 """ returns the new revlog object created"""
131 """ returns the new revlog object created"""
132 newrl = None
132 newrl = None
133 if matchrevlog(upgrade_op.revlogs_to_process, unencoded):
133 if matchrevlog(upgrade_op.revlogs_to_process, unencoded):
134 ui.note(
134 ui.note(
135 _(b'cloning %d revisions from %s\n') % (len(old_revlog), unencoded)
135 _(b'cloning %d revisions from %s\n') % (len(old_revlog), unencoded)
136 )
136 )
137 newrl = _revlogfrompath(dstrepo, unencoded)
137 newrl = _revlogfrompath(dstrepo, unencoded)
138 old_revlog.clone(
138 old_revlog.clone(
139 tr,
139 tr,
140 newrl,
140 newrl,
141 addrevisioncb=oncopiedrevision,
141 addrevisioncb=oncopiedrevision,
142 deltareuse=upgrade_op.delta_reuse_mode,
142 deltareuse=upgrade_op.delta_reuse_mode,
143 forcedeltabothparents=upgrade_op.force_re_delta_both_parents,
143 forcedeltabothparents=upgrade_op.force_re_delta_both_parents,
144 sidedatacompanion=sidedatacompanion,
144 sidedatacompanion=sidedatacompanion,
145 )
145 )
146 else:
146 else:
147 msg = _(b'blindly copying %s containing %i revisions\n')
147 msg = _(b'blindly copying %s containing %i revisions\n')
148 ui.note(msg % (unencoded, len(old_revlog)))
148 ui.note(msg % (unencoded, len(old_revlog)))
149 _copyrevlog(tr, dstrepo, old_revlog, unencoded)
149 _copyrevlog(tr, dstrepo, old_revlog, unencoded)
150
150
151 newrl = _revlogfrompath(dstrepo, unencoded)
151 newrl = _revlogfrompath(dstrepo, unencoded)
152 return newrl
152 return newrl
153
153
154
154
155 def _clonerevlogs(
155 def _clonerevlogs(
156 ui,
156 ui,
157 srcrepo,
157 srcrepo,
158 dstrepo,
158 dstrepo,
159 tr,
159 tr,
160 upgrade_op,
160 upgrade_op,
161 ):
161 ):
162 """Copy revlogs between 2 repos."""
162 """Copy revlogs between 2 repos."""
163 revcount = 0
163 revcount = 0
164 srcsize = 0
164 srcsize = 0
165 srcrawsize = 0
165 srcrawsize = 0
166 dstsize = 0
166 dstsize = 0
167 fcount = 0
167 fcount = 0
168 frevcount = 0
168 frevcount = 0
169 fsrcsize = 0
169 fsrcsize = 0
170 frawsize = 0
170 frawsize = 0
171 fdstsize = 0
171 fdstsize = 0
172 mcount = 0
172 mcount = 0
173 mrevcount = 0
173 mrevcount = 0
174 msrcsize = 0
174 msrcsize = 0
175 mrawsize = 0
175 mrawsize = 0
176 mdstsize = 0
176 mdstsize = 0
177 crevcount = 0
177 crevcount = 0
178 csrcsize = 0
178 csrcsize = 0
179 crawsize = 0
179 crawsize = 0
180 cdstsize = 0
180 cdstsize = 0
181
181
182 alldatafiles = list(srcrepo.store.walk())
182 alldatafiles = list(srcrepo.store.walk())
183 # mapping of data files which needs to be cloned
183 # mapping of data files which needs to be cloned
184 # key is unencoded filename
184 # key is unencoded filename
185 # value is revlog_object_from_srcrepo
185 # value is revlog_object_from_srcrepo
186 manifests = {}
186 manifests = {}
187 changelogs = {}
187 changelogs = {}
188 filelogs = {}
188 filelogs = {}
189
189
190 # Perform a pass to collect metadata. This validates we can open all
190 # Perform a pass to collect metadata. This validates we can open all
191 # source files and allows a unified progress bar to be displayed.
191 # source files and allows a unified progress bar to be displayed.
192 for unencoded, encoded, size in alldatafiles:
192 for unencoded, encoded, size in alldatafiles:
193 if unencoded.endswith(b'.d'):
193 if unencoded.endswith(b'.d'):
194 continue
194 continue
195
195
196 rl = _revlogfrompath(srcrepo, unencoded)
196 rl = _revlogfrompath(srcrepo, unencoded)
197
197
198 info = rl.storageinfo(
198 info = rl.storageinfo(
199 exclusivefiles=True,
199 exclusivefiles=True,
200 revisionscount=True,
200 revisionscount=True,
201 trackedsize=True,
201 trackedsize=True,
202 storedsize=True,
202 storedsize=True,
203 )
203 )
204
204
205 revcount += info[b'revisionscount'] or 0
205 revcount += info[b'revisionscount'] or 0
206 datasize = info[b'storedsize'] or 0
206 datasize = info[b'storedsize'] or 0
207 rawsize = info[b'trackedsize'] or 0
207 rawsize = info[b'trackedsize'] or 0
208
208
209 srcsize += datasize
209 srcsize += datasize
210 srcrawsize += rawsize
210 srcrawsize += rawsize
211
211
212 # This is for the separate progress bars.
212 # This is for the separate progress bars.
213 if isinstance(rl, changelog.changelog):
213 if isinstance(rl, changelog.changelog):
214 changelogs[unencoded] = rl
214 changelogs[unencoded] = rl
215 crevcount += len(rl)
215 crevcount += len(rl)
216 csrcsize += datasize
216 csrcsize += datasize
217 crawsize += rawsize
217 crawsize += rawsize
218 elif isinstance(rl, manifest.manifestrevlog):
218 elif isinstance(rl, manifest.manifestrevlog):
219 manifests[unencoded] = rl
219 manifests[unencoded] = rl
220 mcount += 1
220 mcount += 1
221 mrevcount += len(rl)
221 mrevcount += len(rl)
222 msrcsize += datasize
222 msrcsize += datasize
223 mrawsize += rawsize
223 mrawsize += rawsize
224 elif isinstance(rl, filelog.filelog):
224 elif isinstance(rl, filelog.filelog):
225 filelogs[unencoded] = rl
225 filelogs[unencoded] = rl
226 fcount += 1
226 fcount += 1
227 frevcount += len(rl)
227 frevcount += len(rl)
228 fsrcsize += datasize
228 fsrcsize += datasize
229 frawsize += rawsize
229 frawsize += rawsize
230 else:
230 else:
231 error.ProgrammingError(b'unknown revlog type')
231 error.ProgrammingError(b'unknown revlog type')
232
232
233 if not revcount:
233 if not revcount:
234 return
234 return
235
235
236 ui.status(
236 ui.status(
237 _(
237 _(
238 b'migrating %d total revisions (%d in filelogs, %d in manifests, '
238 b'migrating %d total revisions (%d in filelogs, %d in manifests, '
239 b'%d in changelog)\n'
239 b'%d in changelog)\n'
240 )
240 )
241 % (revcount, frevcount, mrevcount, crevcount)
241 % (revcount, frevcount, mrevcount, crevcount)
242 )
242 )
243 ui.status(
243 ui.status(
244 _(b'migrating %s in store; %s tracked data\n')
244 _(b'migrating %s in store; %s tracked data\n')
245 % ((util.bytecount(srcsize), util.bytecount(srcrawsize)))
245 % ((util.bytecount(srcsize), util.bytecount(srcrawsize)))
246 )
246 )
247
247
248 # Used to keep track of progress.
248 # Used to keep track of progress.
249 progress = None
249 progress = None
250
250
251 def oncopiedrevision(rl, rev, node):
251 def oncopiedrevision(rl, rev, node):
252 progress.increment()
252 progress.increment()
253
253
254 sidedatacompanion = getsidedatacompanion(srcrepo, dstrepo)
254 sidedatacompanion = getsidedatacompanion(srcrepo, dstrepo)
255
255
256 # Migrating filelogs
256 # Migrating filelogs
257 ui.status(
257 ui.status(
258 _(
258 _(
259 b'migrating %d filelogs containing %d revisions '
259 b'migrating %d filelogs containing %d revisions '
260 b'(%s in store; %s tracked data)\n'
260 b'(%s in store; %s tracked data)\n'
261 )
261 )
262 % (
262 % (
263 fcount,
263 fcount,
264 frevcount,
264 frevcount,
265 util.bytecount(fsrcsize),
265 util.bytecount(fsrcsize),
266 util.bytecount(frawsize),
266 util.bytecount(frawsize),
267 )
267 )
268 )
268 )
269 progress = srcrepo.ui.makeprogress(_(b'file revisions'), total=frevcount)
269 progress = srcrepo.ui.makeprogress(_(b'file revisions'), total=frevcount)
270 for unencoded, oldrl in sorted(filelogs.items()):
270 for unencoded, oldrl in sorted(filelogs.items()):
271 newrl = _perform_clone(
271 newrl = _perform_clone(
272 ui,
272 ui,
273 dstrepo,
273 dstrepo,
274 tr,
274 tr,
275 oldrl,
275 oldrl,
276 unencoded,
276 unencoded,
277 upgrade_op,
277 upgrade_op,
278 sidedatacompanion,
278 sidedatacompanion,
279 oncopiedrevision,
279 oncopiedrevision,
280 )
280 )
281 info = newrl.storageinfo(storedsize=True)
281 info = newrl.storageinfo(storedsize=True)
282 fdstsize += info[b'storedsize'] or 0
282 fdstsize += info[b'storedsize'] or 0
283 ui.status(
283 ui.status(
284 _(
284 _(
285 b'finished migrating %d filelog revisions across %d '
285 b'finished migrating %d filelog revisions across %d '
286 b'filelogs; change in size: %s\n'
286 b'filelogs; change in size: %s\n'
287 )
287 )
288 % (frevcount, fcount, util.bytecount(fdstsize - fsrcsize))
288 % (frevcount, fcount, util.bytecount(fdstsize - fsrcsize))
289 )
289 )
290
290
291 # Migrating manifests
291 # Migrating manifests
292 ui.status(
292 ui.status(
293 _(
293 _(
294 b'migrating %d manifests containing %d revisions '
294 b'migrating %d manifests containing %d revisions '
295 b'(%s in store; %s tracked data)\n'
295 b'(%s in store; %s tracked data)\n'
296 )
296 )
297 % (
297 % (
298 mcount,
298 mcount,
299 mrevcount,
299 mrevcount,
300 util.bytecount(msrcsize),
300 util.bytecount(msrcsize),
301 util.bytecount(mrawsize),
301 util.bytecount(mrawsize),
302 )
302 )
303 )
303 )
304 if progress:
304 if progress:
305 progress.complete()
305 progress.complete()
306 progress = srcrepo.ui.makeprogress(
306 progress = srcrepo.ui.makeprogress(
307 _(b'manifest revisions'), total=mrevcount
307 _(b'manifest revisions'), total=mrevcount
308 )
308 )
309 for unencoded, oldrl in sorted(manifests.items()):
309 for unencoded, oldrl in sorted(manifests.items()):
310 newrl = _perform_clone(
310 newrl = _perform_clone(
311 ui,
311 ui,
312 dstrepo,
312 dstrepo,
313 tr,
313 tr,
314 oldrl,
314 oldrl,
315 unencoded,
315 unencoded,
316 upgrade_op,
316 upgrade_op,
317 sidedatacompanion,
317 sidedatacompanion,
318 oncopiedrevision,
318 oncopiedrevision,
319 )
319 )
320 info = newrl.storageinfo(storedsize=True)
320 info = newrl.storageinfo(storedsize=True)
321 mdstsize += info[b'storedsize'] or 0
321 mdstsize += info[b'storedsize'] or 0
322 ui.status(
322 ui.status(
323 _(
323 _(
324 b'finished migrating %d manifest revisions across %d '
324 b'finished migrating %d manifest revisions across %d '
325 b'manifests; change in size: %s\n'
325 b'manifests; change in size: %s\n'
326 )
326 )
327 % (mrevcount, mcount, util.bytecount(mdstsize - msrcsize))
327 % (mrevcount, mcount, util.bytecount(mdstsize - msrcsize))
328 )
328 )
329
329
330 # Migrating changelog
330 # Migrating changelog
331 ui.status(
331 ui.status(
332 _(
332 _(
333 b'migrating changelog containing %d revisions '
333 b'migrating changelog containing %d revisions '
334 b'(%s in store; %s tracked data)\n'
334 b'(%s in store; %s tracked data)\n'
335 )
335 )
336 % (
336 % (
337 crevcount,
337 crevcount,
338 util.bytecount(csrcsize),
338 util.bytecount(csrcsize),
339 util.bytecount(crawsize),
339 util.bytecount(crawsize),
340 )
340 )
341 )
341 )
342 if progress:
342 if progress:
343 progress.complete()
343 progress.complete()
344 progress = srcrepo.ui.makeprogress(
344 progress = srcrepo.ui.makeprogress(
345 _(b'changelog revisions'), total=crevcount
345 _(b'changelog revisions'), total=crevcount
346 )
346 )
347 for unencoded, oldrl in sorted(changelogs.items()):
347 for unencoded, oldrl in sorted(changelogs.items()):
348 newrl = _perform_clone(
348 newrl = _perform_clone(
349 ui,
349 ui,
350 dstrepo,
350 dstrepo,
351 tr,
351 tr,
352 oldrl,
352 oldrl,
353 unencoded,
353 unencoded,
354 upgrade_op,
354 upgrade_op,
355 sidedatacompanion,
355 sidedatacompanion,
356 oncopiedrevision,
356 oncopiedrevision,
357 )
357 )
358 info = newrl.storageinfo(storedsize=True)
358 info = newrl.storageinfo(storedsize=True)
359 cdstsize += info[b'storedsize'] or 0
359 cdstsize += info[b'storedsize'] or 0
360 progress.complete()
360 progress.complete()
361 ui.status(
361 ui.status(
362 _(
362 _(
363 b'finished migrating %d changelog revisions; change in size: '
363 b'finished migrating %d changelog revisions; change in size: '
364 b'%s\n'
364 b'%s\n'
365 )
365 )
366 % (crevcount, util.bytecount(cdstsize - csrcsize))
366 % (crevcount, util.bytecount(cdstsize - csrcsize))
367 )
367 )
368
368
369 dstsize = fdstsize + mdstsize + cdstsize
369 dstsize = fdstsize + mdstsize + cdstsize
370 ui.status(
370 ui.status(
371 _(
371 _(
372 b'finished migrating %d total revisions; total change in store '
372 b'finished migrating %d total revisions; total change in store '
373 b'size: %s\n'
373 b'size: %s\n'
374 )
374 )
375 % (revcount, util.bytecount(dstsize - srcsize))
375 % (revcount, util.bytecount(dstsize - srcsize))
376 )
376 )
377
377
378
378
379 def _filterstorefile(srcrepo, dstrepo, requirements, path, mode, st):
379 def _files_to_copy_post_revlog_clone(srcrepo):
380 """Determine whether to copy a store file during upgrade.
380 """yields files which should be copied to destination after revlogs
381
381 are cloned"""
382 This function is called when migrating store files from ``srcrepo`` to
382 for path, kind, st in sorted(srcrepo.store.vfs.readdir(b'', stat=True)):
383 ``dstrepo`` as part of upgrading a repository.
383 # don't copy revlogs as they are already cloned
384
384 if path.endswith((b'.i', b'.d', b'.n', b'.nd')):
385 Args:
385 continue
386 srcrepo: repo we are copying from
386 # Skip transaction related files.
387 dstrepo: repo we are copying to
387 if path.startswith(b'undo'):
388 requirements: set of requirements for ``dstrepo``
388 continue
389 path: store file being examined
389 # Only copy regular files.
390 mode: the ``ST_MODE`` file type of ``path``
390 if kind != stat.S_IFREG:
391 st: ``stat`` data structure for ``path``
391 continue
392 # Skip other skipped files.
393 if path in (b'lock', b'fncache'):
394 continue
395 # TODO: should we skip cache too?
392
396
393 Function should return ``True`` if the file is to be copied.
397 yield path
394 """
395 # Skip revlogs.
396 if path.endswith((b'.i', b'.d', b'.n', b'.nd')):
397 return False
398 # Skip transaction related files.
399 if path.startswith(b'undo'):
400 return False
401 # Only copy regular files.
402 if mode != stat.S_IFREG:
403 return False
404 # Skip other skipped files.
405 if path in (b'lock', b'fncache'):
406 return False
407
408 return True
409
398
410
399
411 def _replacestores(currentrepo, upgradedrepo, backupvfs, upgrade_op):
400 def _replacestores(currentrepo, upgradedrepo, backupvfs, upgrade_op):
412 """Replace the stores after current repository is upgraded
401 """Replace the stores after current repository is upgraded
413
402
414 Creates a backup of current repository store at backup path
403 Creates a backup of current repository store at backup path
415 Replaces upgraded store files in current repo from upgraded one
404 Replaces upgraded store files in current repo from upgraded one
416
405
417 Arguments:
406 Arguments:
418 currentrepo: repo object of current repository
407 currentrepo: repo object of current repository
419 upgradedrepo: repo object of the upgraded data
408 upgradedrepo: repo object of the upgraded data
420 backupvfs: vfs object for the backup path
409 backupvfs: vfs object for the backup path
421 upgrade_op: upgrade operation object
410 upgrade_op: upgrade operation object
422 to be used to decide what all is upgraded
411 to be used to decide what all is upgraded
423 """
412 """
424 # TODO: don't blindly rename everything in store
413 # TODO: don't blindly rename everything in store
425 # There can be upgrades where store is not touched at all
414 # There can be upgrades where store is not touched at all
426 util.rename(currentrepo.spath, backupvfs.join(b'store'))
415 util.rename(currentrepo.spath, backupvfs.join(b'store'))
427 util.rename(upgradedrepo.spath, currentrepo.spath)
416 util.rename(upgradedrepo.spath, currentrepo.spath)
428
417
429
418
430 def finishdatamigration(ui, srcrepo, dstrepo, requirements):
419 def finishdatamigration(ui, srcrepo, dstrepo, requirements):
431 """Hook point for extensions to perform additional actions during upgrade.
420 """Hook point for extensions to perform additional actions during upgrade.
432
421
433 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
434 before the new store is swapped into the original location.
423 before the new store is swapped into the original location.
435 """
424 """
436
425
437
426
438 def upgrade(ui, srcrepo, dstrepo, upgrade_op):
427 def upgrade(ui, srcrepo, dstrepo, upgrade_op):
439 """Do the low-level work of upgrading a repository.
428 """Do the low-level work of upgrading a repository.
440
429
441 The upgrade is effectively performed as a copy between a source
430 The upgrade is effectively performed as a copy between a source
442 repository and a temporary destination repository.
431 repository and a temporary destination repository.
443
432
444 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
445 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
446 readers and without corrupting the source repository.
435 readers and without corrupting the source repository.
447 """
436 """
448 assert srcrepo.currentwlock()
437 assert srcrepo.currentwlock()
449 assert dstrepo.currentwlock()
438 assert dstrepo.currentwlock()
450
439
451 ui.status(
440 ui.status(
452 _(
441 _(
453 b'(it is safe to interrupt this process any time before '
442 b'(it is safe to interrupt this process any time before '
454 b'data migration completes)\n'
443 b'data migration completes)\n'
455 )
444 )
456 )
445 )
457
446
458 with dstrepo.transaction(b'upgrade') as tr:
447 with dstrepo.transaction(b'upgrade') as tr:
459 _clonerevlogs(
448 _clonerevlogs(
460 ui,
449 ui,
461 srcrepo,
450 srcrepo,
462 dstrepo,
451 dstrepo,
463 tr,
452 tr,
464 upgrade_op,
453 upgrade_op,
465 )
454 )
466
455
467 # Now copy other files in the store directory.
456 # Now copy other files in the store directory.
468 # The sorted() makes execution deterministic.
457 for p in _files_to_copy_post_revlog_clone(srcrepo):
469 for p, kind, st in sorted(srcrepo.store.vfs.readdir(b'', stat=True)):
470 if not _filterstorefile(
471 srcrepo, dstrepo, upgrade_op.new_requirements, p, kind, st
472 ):
473 continue
474
475 srcrepo.ui.status(_(b'copying %s\n') % p)
458 srcrepo.ui.status(_(b'copying %s\n') % p)
476 src = srcrepo.store.rawvfs.join(p)
459 src = srcrepo.store.rawvfs.join(p)
477 dst = dstrepo.store.rawvfs.join(p)
460 dst = dstrepo.store.rawvfs.join(p)
478 util.copyfile(src, dst, copystat=True)
461 util.copyfile(src, dst, copystat=True)
479
462
480 finishdatamigration(ui, srcrepo, dstrepo, requirements)
463 finishdatamigration(ui, srcrepo, dstrepo, requirements)
481
464
482 ui.status(_(b'data fully upgraded in a temporary repository\n'))
465 ui.status(_(b'data fully upgraded in a temporary repository\n'))
483
466
484 backuppath = pycompat.mkdtemp(prefix=b'upgradebackup.', dir=srcrepo.path)
467 backuppath = pycompat.mkdtemp(prefix=b'upgradebackup.', dir=srcrepo.path)
485 backupvfs = vfsmod.vfs(backuppath)
468 backupvfs = vfsmod.vfs(backuppath)
486
469
487 # Make a backup of requires file first, as it is the first to be modified.
470 # Make a backup of requires file first, as it is the first to be modified.
488 util.copyfile(srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires'))
471 util.copyfile(srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires'))
489
472
490 # We install an arbitrary requirement that clients must not support
473 # We install an arbitrary requirement that clients must not support
491 # as a mechanism to lock out new clients during the data swap. This is
474 # as a mechanism to lock out new clients during the data swap. This is
492 # better than allowing a client to continue while the repository is in
475 # better than allowing a client to continue while the repository is in
493 # an inconsistent state.
476 # an inconsistent state.
494 ui.status(
477 ui.status(
495 _(
478 _(
496 b'marking source repository as being upgraded; clients will be '
479 b'marking source repository as being upgraded; clients will be '
497 b'unable to read from repository\n'
480 b'unable to read from repository\n'
498 )
481 )
499 )
482 )
500 scmutil.writereporequirements(
483 scmutil.writereporequirements(
501 srcrepo, srcrepo.requirements | {b'upgradeinprogress'}
484 srcrepo, srcrepo.requirements | {b'upgradeinprogress'}
502 )
485 )
503
486
504 ui.status(_(b'starting in-place swap of repository data\n'))
487 ui.status(_(b'starting in-place swap of repository data\n'))
505 ui.status(_(b'replaced files will be backed up at %s\n') % backuppath)
488 ui.status(_(b'replaced files will be backed up at %s\n') % backuppath)
506
489
507 # Now swap in the new store directory. Doing it as a rename should make
490 # Now swap in the new store directory. Doing it as a rename should make
508 # the operation nearly instantaneous and atomic (at least in well-behaved
491 # the operation nearly instantaneous and atomic (at least in well-behaved
509 # environments).
492 # environments).
510 ui.status(_(b'replacing store...\n'))
493 ui.status(_(b'replacing store...\n'))
511 tstart = util.timer()
494 tstart = util.timer()
512 _replacestores(srcrepo, dstrepo, backupvfs, upgrade_op)
495 _replacestores(srcrepo, dstrepo, backupvfs, upgrade_op)
513 elapsed = util.timer() - tstart
496 elapsed = util.timer() - tstart
514 ui.status(
497 ui.status(
515 _(
498 _(
516 b'store replacement complete; repository was inconsistent for '
499 b'store replacement complete; repository was inconsistent for '
517 b'%0.1fs\n'
500 b'%0.1fs\n'
518 )
501 )
519 % elapsed
502 % elapsed
520 )
503 )
521
504
522 # We first write the requirements file. Any new requirements will lock
505 # We first write the requirements file. Any new requirements will lock
523 # out legacy clients.
506 # out legacy clients.
524 ui.status(
507 ui.status(
525 _(
508 _(
526 b'finalizing requirements file and making repository readable '
509 b'finalizing requirements file and making repository readable '
527 b'again\n'
510 b'again\n'
528 )
511 )
529 )
512 )
530 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
513 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
531
514
532 # The lock file from the old store won't be removed because nothing has a
515 # The lock file from the old store won't be removed because nothing has a
533 # reference to its new location. So clean it up manually. Alternatively, we
516 # reference to its new location. So clean it up manually. Alternatively, we
534 # could update srcrepo.svfs and other variables to point to the new
517 # could update srcrepo.svfs and other variables to point to the new
535 # location. This is simpler.
518 # location. This is simpler.
536 backupvfs.unlink(b'store/lock')
519 backupvfs.unlink(b'store/lock')
537
520
538 return backuppath
521 return backuppath
General Comments 0
You need to be logged in to leave comments. Login now