8 |
8 |
9 from __future__ import absolute_import |
9 from __future__ import absolute_import |
10 |
10 |
11 import errno |
11 import errno |
12 import hashlib |
12 import hashlib |
|
13 import tempfile |
13 |
14 |
14 from .i18n import _ |
15 from .i18n import _ |
15 from .node import short |
16 from .node import short |
16 from . import ( |
17 from . import ( |
17 bundle2, |
18 bundle2, |
18 changegroup, |
19 changegroup, |
19 error, |
20 error, |
20 exchange, |
21 exchange, |
21 obsolete, |
22 obsolete, |
|
23 scmutil, |
22 util, |
24 util, |
23 ) |
25 ) |
24 |
26 |
25 def _bundle(repo, bases, heads, node, suffix, compress=True): |
27 def _bundle(repo, bases, heads, node, suffix, compress=True): |
26 """create a bundle with the specified revisions as a backup""" |
28 """create a bundle with the specified revisions as a backup""" |
635 # FUTURE consider adding some optimizations here for certain transitions. |
637 # FUTURE consider adding some optimizations here for certain transitions. |
636 # e.g. adding generaldelta could schedule parent redeltas. |
638 # e.g. adding generaldelta could schedule parent redeltas. |
637 |
639 |
638 return newactions |
640 return newactions |
639 |
641 |
|
642 def _upgraderepo(ui, srcrepo, dstrepo, requirements, actions): |
|
643 """Do the low-level work of upgrading a repository. |
|
644 |
|
645 The upgrade is effectively performed as a copy between a source |
|
646 repository and a temporary destination repository. |
|
647 |
|
648 The source repository is unmodified for as long as possible so the |
|
649 upgrade can abort at any time without causing loss of service for |
|
650 readers and without corrupting the source repository. |
|
651 """ |
|
652 assert srcrepo.currentwlock() |
|
653 assert dstrepo.currentwlock() |
|
654 |
|
655 # TODO copy store |
|
656 |
|
657 backuppath = tempfile.mkdtemp(prefix='upgradebackup.', dir=srcrepo.path) |
|
658 backupvfs = scmutil.vfs(backuppath) |
|
659 |
|
660 # Make a backup of requires file first, as it is the first to be modified. |
|
661 util.copyfile(srcrepo.join('requires'), backupvfs.join('requires')) |
|
662 |
|
663 # We install an arbitrary requirement that clients must not support |
|
664 # as a mechanism to lock out new clients during the data swap. This is |
|
665 # better than allowing a client to continue while the repository is in |
|
666 # an inconsistent state. |
|
667 ui.write(_('marking source repository as being upgraded; clients will be ' |
|
668 'unable to read from repository\n')) |
|
669 scmutil.writerequires(srcrepo.vfs, |
|
670 srcrepo.requirements | set(['upgradeinprogress'])) |
|
671 |
|
672 ui.write(_('starting in-place swap of repository data\n')) |
|
673 ui.write(_('replaced files will be backed up at %s\n') % |
|
674 backuppath) |
|
675 |
|
676 # TODO do the store swap here. |
|
677 |
|
678 # We first write the requirements file. Any new requirements will lock |
|
679 # out legacy clients. |
|
680 ui.write(_('finalizing requirements file and making repository readable ' |
|
681 'again\n')) |
|
682 scmutil.writerequires(srcrepo.vfs, requirements) |
|
683 |
|
684 return backuppath |
|
685 |
640 def upgraderepo(ui, repo, run=False, optimize=None): |
686 def upgraderepo(ui, repo, run=False, optimize=None): |
641 """Upgrade a repository in place.""" |
687 """Upgrade a repository in place.""" |
642 # Avoid cycle: cmdutil -> repair -> localrepo -> cmdutil |
688 # Avoid cycle: cmdutil -> repair -> localrepo -> cmdutil |
643 from . import localrepo |
689 from . import localrepo |
644 |
690 |
769 if unusedoptimize: |
815 if unusedoptimize: |
770 ui.write(_('additional optimizations are available by specifying ' |
816 ui.write(_('additional optimizations are available by specifying ' |
771 '"--optimize <name>":\n\n')) |
817 '"--optimize <name>":\n\n')) |
772 for i in unusedoptimize: |
818 for i in unusedoptimize: |
773 ui.write(_('%s\n %s\n\n') % (i.name, i.description)) |
819 ui.write(_('%s\n %s\n\n') % (i.name, i.description)) |
|
820 return |
|
821 |
|
822 # Else we're in the run=true case. |
|
823 ui.write(_('upgrade will perform the following actions:\n\n')) |
|
824 printrequirements() |
|
825 printupgradeactions() |
|
826 |
|
827 ui.write(_('beginning upgrade...\n')) |
|
828 with repo.wlock(): |
|
829 with repo.lock(): |
|
830 ui.write(_('repository locked and read-only\n')) |
|
831 # Our strategy for upgrading the repository is to create a new, |
|
832 # temporary repository, write data to it, then do a swap of the |
|
833 # data. There are less heavyweight ways to do this, but it is easier |
|
834 # to create a new repo object than to instantiate all the components |
|
835 # (like the store) separately. |
|
836 tmppath = tempfile.mkdtemp(prefix='upgrade.', dir=repo.path) |
|
837 backuppath = None |
|
838 try: |
|
839 ui.write(_('creating temporary repository to stage migrated ' |
|
840 'data: %s\n') % tmppath) |
|
841 dstrepo = localrepo.localrepository(repo.baseui, |
|
842 path=tmppath, |
|
843 create=True) |
|
844 |
|
845 with dstrepo.wlock(): |
|
846 with dstrepo.lock(): |
|
847 backuppath = _upgraderepo(ui, repo, dstrepo, newreqs, |
|
848 actions) |
|
849 |
|
850 finally: |
|
851 ui.write(_('removing temporary repository %s\n') % tmppath) |
|
852 repo.vfs.rmtree(tmppath, forcibly=True) |
|
853 |
|
854 if backuppath: |
|
855 ui.warn(_('copy of old repository backed up at %s\n') % |
|
856 backuppath) |
|
857 ui.warn(_('the old repository will not be deleted; remove ' |
|
858 'it to free up disk space once the upgraded ' |
|
859 'repository is verified\n')) |