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