##// END OF EJS Templates
engine: refactor code to replace stores in separate function...
Pulkit Goyal -
r46837:1ca7865c default
parent child Browse files
Show More
@@ -1,520 +1,538 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 _filterstorefile(srcrepo, dstrepo, requirements, path, mode, st):
380 """Determine whether to copy a store file during upgrade.
380 """Determine whether to copy a store file during upgrade.
381
381
382 This function is called when migrating store files from ``srcrepo`` to
382 This function is called when migrating store files from ``srcrepo`` to
383 ``dstrepo`` as part of upgrading a repository.
383 ``dstrepo`` as part of upgrading a repository.
384
384
385 Args:
385 Args:
386 srcrepo: repo we are copying from
386 srcrepo: repo we are copying from
387 dstrepo: repo we are copying to
387 dstrepo: repo we are copying to
388 requirements: set of requirements for ``dstrepo``
388 requirements: set of requirements for ``dstrepo``
389 path: store file being examined
389 path: store file being examined
390 mode: the ``ST_MODE`` file type of ``path``
390 mode: the ``ST_MODE`` file type of ``path``
391 st: ``stat`` data structure for ``path``
391 st: ``stat`` data structure for ``path``
392
392
393 Function should return ``True`` if the file is to be copied.
393 Function should return ``True`` if the file is to be copied.
394 """
394 """
395 # Skip revlogs.
395 # Skip revlogs.
396 if path.endswith((b'.i', b'.d', b'.n', b'.nd')):
396 if path.endswith((b'.i', b'.d', b'.n', b'.nd')):
397 return False
397 return False
398 # Skip transaction related files.
398 # Skip transaction related files.
399 if path.startswith(b'undo'):
399 if path.startswith(b'undo'):
400 return False
400 return False
401 # Only copy regular files.
401 # Only copy regular files.
402 if mode != stat.S_IFREG:
402 if mode != stat.S_IFREG:
403 return False
403 return False
404 # Skip other skipped files.
404 # Skip other skipped files.
405 if path in (b'lock', b'fncache'):
405 if path in (b'lock', b'fncache'):
406 return False
406 return False
407
407
408 return True
408 return True
409
409
410
410
411 def _replacestores(currentrepo, upgradedrepo, backupvfs, upgrade_op):
412 """Replace the stores after current repository is upgraded
413
414 Creates a backup of current repository store at backup path
415 Replaces upgraded store files in current repo from upgraded one
416
417 Arguments:
418 currentrepo: repo object of current repository
419 upgradedrepo: repo object of the upgraded data
420 backupvfs: vfs object for the backup path
421 upgrade_op: upgrade operation object
422 to be used to decide what all is upgraded
423 """
424 # TODO: don't blindly rename everything in store
425 # There can be upgrades where store is not touched at all
426 util.rename(currentrepo.spath, backupvfs.join(b'store'))
427 util.rename(upgradedrepo.spath, currentrepo.spath)
428
429
411 def finishdatamigration(ui, srcrepo, dstrepo, requirements):
430 def finishdatamigration(ui, srcrepo, dstrepo, requirements):
412 """Hook point for extensions to perform additional actions during upgrade.
431 """Hook point for extensions to perform additional actions during upgrade.
413
432
414 This function is called after revlogs and store files have been copied but
433 This function is called after revlogs and store files have been copied but
415 before the new store is swapped into the original location.
434 before the new store is swapped into the original location.
416 """
435 """
417
436
418
437
419 def upgrade(ui, srcrepo, dstrepo, upgrade_op):
438 def upgrade(ui, srcrepo, dstrepo, upgrade_op):
420 """Do the low-level work of upgrading a repository.
439 """Do the low-level work of upgrading a repository.
421
440
422 The upgrade is effectively performed as a copy between a source
441 The upgrade is effectively performed as a copy between a source
423 repository and a temporary destination repository.
442 repository and a temporary destination repository.
424
443
425 The source repository is unmodified for as long as possible so the
444 The source repository is unmodified for as long as possible so the
426 upgrade can abort at any time without causing loss of service for
445 upgrade can abort at any time without causing loss of service for
427 readers and without corrupting the source repository.
446 readers and without corrupting the source repository.
428 """
447 """
429 assert srcrepo.currentwlock()
448 assert srcrepo.currentwlock()
430 assert dstrepo.currentwlock()
449 assert dstrepo.currentwlock()
431
450
432 ui.status(
451 ui.status(
433 _(
452 _(
434 b'(it is safe to interrupt this process any time before '
453 b'(it is safe to interrupt this process any time before '
435 b'data migration completes)\n'
454 b'data migration completes)\n'
436 )
455 )
437 )
456 )
438
457
439 with dstrepo.transaction(b'upgrade') as tr:
458 with dstrepo.transaction(b'upgrade') as tr:
440 _clonerevlogs(
459 _clonerevlogs(
441 ui,
460 ui,
442 srcrepo,
461 srcrepo,
443 dstrepo,
462 dstrepo,
444 tr,
463 tr,
445 upgrade_op,
464 upgrade_op,
446 )
465 )
447
466
448 # Now copy other files in the store directory.
467 # Now copy other files in the store directory.
449 # The sorted() makes execution deterministic.
468 # The sorted() makes execution deterministic.
450 for p, kind, st in sorted(srcrepo.store.vfs.readdir(b'', stat=True)):
469 for p, kind, st in sorted(srcrepo.store.vfs.readdir(b'', stat=True)):
451 if not _filterstorefile(
470 if not _filterstorefile(
452 srcrepo, dstrepo, upgrade_op.new_requirements, p, kind, st
471 srcrepo, dstrepo, upgrade_op.new_requirements, p, kind, st
453 ):
472 ):
454 continue
473 continue
455
474
456 srcrepo.ui.status(_(b'copying %s\n') % p)
475 srcrepo.ui.status(_(b'copying %s\n') % p)
457 src = srcrepo.store.rawvfs.join(p)
476 src = srcrepo.store.rawvfs.join(p)
458 dst = dstrepo.store.rawvfs.join(p)
477 dst = dstrepo.store.rawvfs.join(p)
459 util.copyfile(src, dst, copystat=True)
478 util.copyfile(src, dst, copystat=True)
460
479
461 finishdatamigration(ui, srcrepo, dstrepo, requirements)
480 finishdatamigration(ui, srcrepo, dstrepo, requirements)
462
481
463 ui.status(_(b'data fully migrated to temporary repository\n'))
482 ui.status(_(b'data fully migrated to temporary repository\n'))
464
483
465 backuppath = pycompat.mkdtemp(prefix=b'upgradebackup.', dir=srcrepo.path)
484 backuppath = pycompat.mkdtemp(prefix=b'upgradebackup.', dir=srcrepo.path)
466 backupvfs = vfsmod.vfs(backuppath)
485 backupvfs = vfsmod.vfs(backuppath)
467
486
468 # Make a backup of requires file first, as it is the first to be modified.
487 # Make a backup of requires file first, as it is the first to be modified.
469 util.copyfile(srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires'))
488 util.copyfile(srcrepo.vfs.join(b'requires'), backupvfs.join(b'requires'))
470
489
471 # We install an arbitrary requirement that clients must not support
490 # We install an arbitrary requirement that clients must not support
472 # as a mechanism to lock out new clients during the data swap. This is
491 # as a mechanism to lock out new clients during the data swap. This is
473 # better than allowing a client to continue while the repository is in
492 # better than allowing a client to continue while the repository is in
474 # an inconsistent state.
493 # an inconsistent state.
475 ui.status(
494 ui.status(
476 _(
495 _(
477 b'marking source repository as being upgraded; clients will be '
496 b'marking source repository as being upgraded; clients will be '
478 b'unable to read from repository\n'
497 b'unable to read from repository\n'
479 )
498 )
480 )
499 )
481 scmutil.writereporequirements(
500 scmutil.writereporequirements(
482 srcrepo, srcrepo.requirements | {b'upgradeinprogress'}
501 srcrepo, srcrepo.requirements | {b'upgradeinprogress'}
483 )
502 )
484
503
485 ui.status(_(b'starting in-place swap of repository data\n'))
504 ui.status(_(b'starting in-place swap of repository data\n'))
486 ui.status(_(b'replaced files will be backed up at %s\n') % backuppath)
505 ui.status(_(b'replaced files will be backed up at %s\n') % backuppath)
487
506
488 # Now swap in the new store directory. Doing it as a rename should make
507 # Now swap in the new store directory. Doing it as a rename should make
489 # the operation nearly instantaneous and atomic (at least in well-behaved
508 # the operation nearly instantaneous and atomic (at least in well-behaved
490 # environments).
509 # environments).
491 ui.status(_(b'replacing store...\n'))
510 ui.status(_(b'replacing store...\n'))
492 tstart = util.timer()
511 tstart = util.timer()
493 util.rename(srcrepo.spath, backupvfs.join(b'store'))
512 _replacestores(srcrepo, dstrepo, backupvfs, upgrade_op)
494 util.rename(dstrepo.spath, srcrepo.spath)
495 elapsed = util.timer() - tstart
513 elapsed = util.timer() - tstart
496 ui.status(
514 ui.status(
497 _(
515 _(
498 b'store replacement complete; repository was inconsistent for '
516 b'store replacement complete; repository was inconsistent for '
499 b'%0.1fs\n'
517 b'%0.1fs\n'
500 )
518 )
501 % elapsed
519 % elapsed
502 )
520 )
503
521
504 # We first write the requirements file. Any new requirements will lock
522 # We first write the requirements file. Any new requirements will lock
505 # out legacy clients.
523 # out legacy clients.
506 ui.status(
524 ui.status(
507 _(
525 _(
508 b'finalizing requirements file and making repository readable '
526 b'finalizing requirements file and making repository readable '
509 b'again\n'
527 b'again\n'
510 )
528 )
511 )
529 )
512 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
530 scmutil.writereporequirements(srcrepo, upgrade_op.new_requirements)
513
531
514 # The lock file from the old store won't be removed because nothing has a
532 # The lock file from the old store won't be removed because nothing has a
515 # reference to its new location. So clean it up manually. Alternatively, we
533 # reference to its new location. So clean it up manually. Alternatively, we
516 # could update srcrepo.svfs and other variables to point to the new
534 # could update srcrepo.svfs and other variables to point to the new
517 # location. This is simpler.
535 # location. This is simpler.
518 backupvfs.unlink(b'store/lock')
536 backupvfs.unlink(b'store/lock')
519
537
520 return backuppath
538 return backuppath
General Comments 0
You need to be logged in to leave comments. Login now