637 # FUTURE consider adding some optimizations here for certain transitions. |
641 # FUTURE consider adding some optimizations here for certain transitions. |
638 # e.g. adding generaldelta could schedule parent redeltas. |
642 # e.g. adding generaldelta could schedule parent redeltas. |
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 |
645 The upgrade is effectively performed as a copy between a source |
805 The upgrade is effectively performed as a copy between a source |
646 repository and a temporary destination repository. |
806 repository and a temporary destination repository. |
650 readers and without corrupting the source repository. |
810 readers and without corrupting the source repository. |
651 """ |
811 """ |
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) |
659 |
837 |
660 # Make a backup of requires file first, as it is the first to be modified. |
838 # Make a backup of requires file first, as it is the first to be modified. |