Show More
@@ -11,15 +11,19 b' from __future__ import absolute_import' | |||||
11 | import errno |
|
11 | import errno | |
12 | import hashlib |
|
12 | import hashlib | |
13 | import tempfile |
|
13 | import tempfile | |
|
14 | import time | |||
14 |
|
15 | |||
15 | from .i18n import _ |
|
16 | from .i18n import _ | |
16 | from .node import short |
|
17 | from .node import short | |
17 | from . import ( |
|
18 | from . import ( | |
18 | bundle2, |
|
19 | bundle2, | |
19 | changegroup, |
|
20 | changegroup, | |
|
21 | changelog, | |||
20 | error, |
|
22 | error, | |
21 | exchange, |
|
23 | exchange, | |
|
24 | manifest, | |||
22 | obsolete, |
|
25 | obsolete, | |
|
26 | revlog, | |||
23 | scmutil, |
|
27 | scmutil, | |
24 | util, |
|
28 | util, | |
25 | ) |
|
29 | ) | |
@@ -639,6 +643,162 b' def upgradedetermineactions(repo, improv' | |||||
639 |
|
643 | |||
640 | return newactions |
|
644 | return newactions | |
641 |
|
645 | |||
|
646 | def _revlogfrompath(repo, path): | |||
|
647 | """Obtain a revlog from a repo path. | |||
|
648 | ||||
|
649 | An instance of the appropriate class is returned. | |||
|
650 | """ | |||
|
651 | if path == '00changelog.i': | |||
|
652 | return changelog.changelog(repo.svfs) | |||
|
653 | elif path.endswith('00manifest.i'): | |||
|
654 | mandir = path[:-len('00manifest.i')] | |||
|
655 | return manifest.manifestrevlog(repo.svfs, dir=mandir) | |||
|
656 | else: | |||
|
657 | # Filelogs don't do anything special with settings. So we can use a | |||
|
658 | # vanilla revlog. | |||
|
659 | return revlog.revlog(repo.svfs, path) | |||
|
660 | ||||
|
661 | def _copyrevlogs(ui, srcrepo, dstrepo, tr, deltareuse, aggressivemergedeltas): | |||
|
662 | """Copy revlogs between 2 repos.""" | |||
|
663 | revcount = 0 | |||
|
664 | srcsize = 0 | |||
|
665 | srcrawsize = 0 | |||
|
666 | dstsize = 0 | |||
|
667 | fcount = 0 | |||
|
668 | frevcount = 0 | |||
|
669 | fsrcsize = 0 | |||
|
670 | frawsize = 0 | |||
|
671 | fdstsize = 0 | |||
|
672 | mcount = 0 | |||
|
673 | mrevcount = 0 | |||
|
674 | msrcsize = 0 | |||
|
675 | mrawsize = 0 | |||
|
676 | mdstsize = 0 | |||
|
677 | crevcount = 0 | |||
|
678 | csrcsize = 0 | |||
|
679 | crawsize = 0 | |||
|
680 | cdstsize = 0 | |||
|
681 | ||||
|
682 | # Perform a pass to collect metadata. This validates we can open all | |||
|
683 | # source files and allows a unified progress bar to be displayed. | |||
|
684 | for unencoded, encoded, size in srcrepo.store.walk(): | |||
|
685 | if unencoded.endswith('.d'): | |||
|
686 | continue | |||
|
687 | ||||
|
688 | rl = _revlogfrompath(srcrepo, unencoded) | |||
|
689 | revcount += len(rl) | |||
|
690 | ||||
|
691 | datasize = 0 | |||
|
692 | rawsize = 0 | |||
|
693 | idx = rl.index | |||
|
694 | for rev in rl: | |||
|
695 | e = idx[rev] | |||
|
696 | datasize += e[1] | |||
|
697 | rawsize += e[2] | |||
|
698 | ||||
|
699 | srcsize += datasize | |||
|
700 | srcrawsize += rawsize | |||
|
701 | ||||
|
702 | # This is for the separate progress bars. | |||
|
703 | if isinstance(rl, changelog.changelog): | |||
|
704 | crevcount += len(rl) | |||
|
705 | csrcsize += datasize | |||
|
706 | crawsize += rawsize | |||
|
707 | elif isinstance(rl, manifest.manifestrevlog): | |||
|
708 | mcount += 1 | |||
|
709 | mrevcount += len(rl) | |||
|
710 | msrcsize += datasize | |||
|
711 | mrawsize += rawsize | |||
|
712 | elif isinstance(rl, revlog.revlog): | |||
|
713 | fcount += 1 | |||
|
714 | frevcount += len(rl) | |||
|
715 | fsrcsize += datasize | |||
|
716 | frawsize += rawsize | |||
|
717 | ||||
|
718 | if not revcount: | |||
|
719 | return | |||
|
720 | ||||
|
721 | ui.write(_('migrating %d total revisions (%d in filelogs, %d in manifests, ' | |||
|
722 | '%d in changelog)\n') % | |||
|
723 | (revcount, frevcount, mrevcount, crevcount)) | |||
|
724 | ui.write(_('migrating %s in store; %s tracked data\n') % ( | |||
|
725 | (util.bytecount(srcsize), util.bytecount(srcrawsize)))) | |||
|
726 | ||||
|
727 | # Used to keep track of progress. | |||
|
728 | progress = [] | |||
|
729 | def oncopiedrevision(rl, rev, node): | |||
|
730 | progress[1] += 1 | |||
|
731 | srcrepo.ui.progress(progress[0], progress[1], total=progress[2]) | |||
|
732 | ||||
|
733 | # Do the actual copying. | |||
|
734 | # FUTURE this operation can be farmed off to worker processes. | |||
|
735 | seen = set() | |||
|
736 | for unencoded, encoded, size in srcrepo.store.walk(): | |||
|
737 | if unencoded.endswith('.d'): | |||
|
738 | continue | |||
|
739 | ||||
|
740 | oldrl = _revlogfrompath(srcrepo, unencoded) | |||
|
741 | newrl = _revlogfrompath(dstrepo, unencoded) | |||
|
742 | ||||
|
743 | if isinstance(oldrl, changelog.changelog) and 'c' not in seen: | |||
|
744 | ui.write(_('finished migrating %d manifest revisions across %d ' | |||
|
745 | 'manifests; change in size: %s\n') % | |||
|
746 | (mrevcount, mcount, util.bytecount(mdstsize - msrcsize))) | |||
|
747 | ||||
|
748 | ui.write(_('migrating changelog containing %d revisions ' | |||
|
749 | '(%s in store; %s tracked data)\n') % | |||
|
750 | (crevcount, util.bytecount(csrcsize), | |||
|
751 | util.bytecount(crawsize))) | |||
|
752 | seen.add('c') | |||
|
753 | progress[:] = [_('changelog revisions'), 0, crevcount] | |||
|
754 | elif isinstance(oldrl, manifest.manifestrevlog) and 'm' not in seen: | |||
|
755 | ui.write(_('finished migrating %d filelog revisions across %d ' | |||
|
756 | 'filelogs; change in size: %s\n') % | |||
|
757 | (frevcount, fcount, util.bytecount(fdstsize - fsrcsize))) | |||
|
758 | ||||
|
759 | ui.write(_('migrating %d manifests containing %d revisions ' | |||
|
760 | '(%s in store; %s tracked data)\n') % | |||
|
761 | (mcount, mrevcount, util.bytecount(msrcsize), | |||
|
762 | util.bytecount(mrawsize))) | |||
|
763 | seen.add('m') | |||
|
764 | progress[:] = [_('manifest revisions'), 0, mrevcount] | |||
|
765 | elif 'f' not in seen: | |||
|
766 | ui.write(_('migrating %d filelogs containing %d revisions ' | |||
|
767 | '(%s in store; %s tracked data)\n') % | |||
|
768 | (fcount, frevcount, util.bytecount(fsrcsize), | |||
|
769 | util.bytecount(frawsize))) | |||
|
770 | seen.add('f') | |||
|
771 | progress[:] = [_('file revisions'), 0, frevcount] | |||
|
772 | ||||
|
773 | ui.progress(progress[0], progress[1], total=progress[2]) | |||
|
774 | ||||
|
775 | ui.note(_('cloning %d revisions from %s\n') % (len(oldrl), unencoded)) | |||
|
776 | oldrl.clone(tr, newrl, addrevisioncb=oncopiedrevision, | |||
|
777 | deltareuse=deltareuse, | |||
|
778 | aggressivemergedeltas=aggressivemergedeltas) | |||
|
779 | ||||
|
780 | datasize = 0 | |||
|
781 | idx = newrl.index | |||
|
782 | for rev in newrl: | |||
|
783 | datasize += idx[rev][1] | |||
|
784 | ||||
|
785 | dstsize += datasize | |||
|
786 | ||||
|
787 | if isinstance(newrl, changelog.changelog): | |||
|
788 | cdstsize += datasize | |||
|
789 | elif isinstance(newrl, manifest.manifestrevlog): | |||
|
790 | mdstsize += datasize | |||
|
791 | else: | |||
|
792 | fdstsize += datasize | |||
|
793 | ||||
|
794 | ui.progress(progress[0], None) | |||
|
795 | ||||
|
796 | ui.write(_('finished migrating %d changelog revisions; change in size: ' | |||
|
797 | '%s\n') % (crevcount, util.bytecount(cdstsize - csrcsize))) | |||
|
798 | ||||
|
799 | ui.write(_('finished migrating %d total revisions; total change in store ' | |||
|
800 | 'size: %s\n') % (revcount, util.bytecount(dstsize - srcsize))) | |||
|
801 | ||||
642 | def _upgraderepo(ui, srcrepo, dstrepo, requirements, actions): |
|
802 | def _upgraderepo(ui, srcrepo, dstrepo, requirements, actions): | |
643 | """Do the low-level work of upgrading a repository. |
|
803 | """Do the low-level work of upgrading a repository. | |
644 |
|
804 | |||
@@ -652,7 +812,25 b' def _upgraderepo(ui, srcrepo, dstrepo, r' | |||||
652 | assert srcrepo.currentwlock() |
|
812 | assert srcrepo.currentwlock() | |
653 | assert dstrepo.currentwlock() |
|
813 | assert dstrepo.currentwlock() | |
654 |
|
814 | |||
655 | # TODO copy store |
|
815 | ui.write(_('(it is safe to interrupt this process any time before ' | |
|
816 | 'data migration completes)\n')) | |||
|
817 | ||||
|
818 | if 'redeltaall' in actions: | |||
|
819 | deltareuse = revlog.revlog.DELTAREUSENEVER | |||
|
820 | elif 'redeltaparent' in actions: | |||
|
821 | deltareuse = revlog.revlog.DELTAREUSESAMEREVS | |||
|
822 | elif 'redeltamultibase' in actions: | |||
|
823 | deltareuse = revlog.revlog.DELTAREUSESAMEREVS | |||
|
824 | else: | |||
|
825 | deltareuse = revlog.revlog.DELTAREUSEALWAYS | |||
|
826 | ||||
|
827 | with dstrepo.transaction('upgrade') as tr: | |||
|
828 | _copyrevlogs(ui, srcrepo, dstrepo, tr, deltareuse, | |||
|
829 | 'redeltamultibase' in actions) | |||
|
830 | ||||
|
831 | # TODO copy non-revlog store files | |||
|
832 | ||||
|
833 | ui.write(_('data fully migrated to temporary repository\n')) | |||
656 |
|
834 | |||
657 | backuppath = tempfile.mkdtemp(prefix='upgradebackup.', dir=srcrepo.path) |
|
835 | backuppath = tempfile.mkdtemp(prefix='upgradebackup.', dir=srcrepo.path) | |
658 | backupvfs = scmutil.vfs(backuppath) |
|
836 | backupvfs = scmutil.vfs(backuppath) | |
@@ -673,7 +851,16 b' def _upgraderepo(ui, srcrepo, dstrepo, r' | |||||
673 | ui.write(_('replaced files will be backed up at %s\n') % |
|
851 | ui.write(_('replaced files will be backed up at %s\n') % | |
674 | backuppath) |
|
852 | backuppath) | |
675 |
|
853 | |||
676 | # TODO do the store swap here. |
|
854 | # Now swap in the new store directory. Doing it as a rename should make | |
|
855 | # the operation nearly instantaneous and atomic (at least in well-behaved | |||
|
856 | # environments). | |||
|
857 | ui.write(_('replacing store...\n')) | |||
|
858 | tstart = time.time() | |||
|
859 | util.rename(srcrepo.spath, backupvfs.join('store')) | |||
|
860 | util.rename(dstrepo.spath, srcrepo.spath) | |||
|
861 | elapsed = time.time() - tstart | |||
|
862 | ui.write(_('store replacement complete; repository was inconsistent for ' | |||
|
863 | '%0.1fs\n') % elapsed) | |||
677 |
|
864 | |||
678 | # We first write the requirements file. Any new requirements will lock |
|
865 | # We first write the requirements file. Any new requirements will lock | |
679 | # out legacy clients. |
|
866 | # out legacy clients. |
@@ -194,9 +194,13 b' Upgrading a repository that is already m' | |||||
194 | beginning upgrade... |
|
194 | beginning upgrade... | |
195 | repository locked and read-only |
|
195 | repository locked and read-only | |
196 | creating temporary repository to stage migrated data: $TESTTMP/modern/.hg/upgrade.* (glob) |
|
196 | creating temporary repository to stage migrated data: $TESTTMP/modern/.hg/upgrade.* (glob) | |
|
197 | (it is safe to interrupt this process any time before data migration completes) | |||
|
198 | data fully migrated to temporary repository | |||
197 | marking source repository as being upgraded; clients will be unable to read from repository |
|
199 | marking source repository as being upgraded; clients will be unable to read from repository | |
198 | starting in-place swap of repository data |
|
200 | starting in-place swap of repository data | |
199 | replaced files will be backed up at $TESTTMP/modern/.hg/upgradebackup.* (glob) |
|
201 | replaced files will be backed up at $TESTTMP/modern/.hg/upgradebackup.* (glob) | |
|
202 | replacing store... | |||
|
203 | store replacement complete; repository was inconsistent for *s (glob) | |||
200 | finalizing requirements file and making repository readable again |
|
204 | finalizing requirements file and making repository readable again | |
201 | removing temporary repository $TESTTMP/modern/.hg/upgrade.* (glob) |
|
205 | removing temporary repository $TESTTMP/modern/.hg/upgrade.* (glob) | |
202 | copy of old repository backed up at $TESTTMP/modern/.hg/upgradebackup.* (glob) |
|
206 | copy of old repository backed up at $TESTTMP/modern/.hg/upgradebackup.* (glob) | |
@@ -227,9 +231,22 b' Upgrading a repository to generaldelta w' | |||||
227 | beginning upgrade... |
|
231 | beginning upgrade... | |
228 | repository locked and read-only |
|
232 | repository locked and read-only | |
229 | creating temporary repository to stage migrated data: $TESTTMP/upgradegd/.hg/upgrade.* (glob) |
|
233 | creating temporary repository to stage migrated data: $TESTTMP/upgradegd/.hg/upgrade.* (glob) | |
|
234 | (it is safe to interrupt this process any time before data migration completes) | |||
|
235 | migrating 9 total revisions (3 in filelogs, 3 in manifests, 3 in changelog) | |||
|
236 | migrating 341 bytes in store; 401 bytes tracked data | |||
|
237 | migrating 3 filelogs containing 3 revisions (0 bytes in store; 0 bytes tracked data) | |||
|
238 | finished migrating 3 filelog revisions across 3 filelogs; change in size: 0 bytes | |||
|
239 | migrating 1 manifests containing 3 revisions (157 bytes in store; 220 bytes tracked data) | |||
|
240 | finished migrating 3 manifest revisions across 1 manifests; change in size: 0 bytes | |||
|
241 | migrating changelog containing 3 revisions (184 bytes in store; 181 bytes tracked data) | |||
|
242 | finished migrating 3 changelog revisions; change in size: 0 bytes | |||
|
243 | finished migrating 9 total revisions; total change in store size: 0 bytes | |||
|
244 | data fully migrated to temporary repository | |||
230 | marking source repository as being upgraded; clients will be unable to read from repository |
|
245 | marking source repository as being upgraded; clients will be unable to read from repository | |
231 | starting in-place swap of repository data |
|
246 | starting in-place swap of repository data | |
232 | replaced files will be backed up at $TESTTMP/upgradegd/.hg/upgradebackup.* (glob) |
|
247 | replaced files will be backed up at $TESTTMP/upgradegd/.hg/upgradebackup.* (glob) | |
|
248 | replacing store... | |||
|
249 | store replacement complete; repository was inconsistent for *s (glob) | |||
233 | finalizing requirements file and making repository readable again |
|
250 | finalizing requirements file and making repository readable again | |
234 | removing temporary repository $TESTTMP/upgradegd/.hg/upgrade.* (glob) |
|
251 | removing temporary repository $TESTTMP/upgradegd/.hg/upgrade.* (glob) | |
235 | copy of old repository backed up at $TESTTMP/upgradegd/.hg/upgradebackup.* (glob) |
|
252 | copy of old repository backed up at $TESTTMP/upgradegd/.hg/upgradebackup.* (glob) | |
@@ -252,4 +269,43 b' generaldelta added to original requireme' | |||||
252 | revlogv1 |
|
269 | revlogv1 | |
253 | store |
|
270 | store | |
254 |
|
271 | |||
|
272 | store directory has files we expect | |||
|
273 | ||||
|
274 | $ ls .hg/store | |||
|
275 | 00changelog.i | |||
|
276 | 00manifest.i | |||
|
277 | data | |||
|
278 | fncache | |||
|
279 | undo | |||
|
280 | undo.backupfiles | |||
|
281 | undo.phaseroots | |||
|
282 | ||||
|
283 | manifest should be generaldelta | |||
|
284 | ||||
|
285 | $ hg debugrevlog -m | grep flags | |||
|
286 | flags : inline, generaldelta | |||
|
287 | ||||
|
288 | verify should be happy | |||
|
289 | ||||
|
290 | $ hg verify | |||
|
291 | checking changesets | |||
|
292 | checking manifests | |||
|
293 | crosschecking files in changesets and manifests | |||
|
294 | checking files | |||
|
295 | 3 files, 3 changesets, 3 total revisions | |||
|
296 | ||||
|
297 | old store should be backed up | |||
|
298 | ||||
|
299 | $ ls .hg/upgradebackup.*/store | |||
|
300 | 00changelog.i | |||
|
301 | 00manifest.i | |||
|
302 | data | |||
|
303 | fncache | |||
|
304 | lock | |||
|
305 | phaseroots | |||
|
306 | undo | |||
|
307 | undo.backup.fncache | |||
|
308 | undo.backupfiles | |||
|
309 | undo.phaseroots | |||
|
310 | ||||
255 | $ cd .. |
|
311 | $ cd .. |
General Comments 0
You need to be logged in to leave comments.
Login now