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