##// END OF EJS Templates
repair: begin implementation of in-place upgrading...
Gregory Szorc -
r30777:7de7afd8 default
parent child Browse files
Show More
@@ -10,6 +10,7 b' 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
@@ -19,6 +20,7 b' from . import ('
19 error,
20 error,
20 exchange,
21 exchange,
21 obsolete,
22 obsolete,
23 scmutil,
22 util,
24 util,
23 )
25 )
24
26
@@ -637,6 +639,50 b' def upgradedetermineactions(repo, improv'
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
@@ -771,3 +817,43 b' def upgraderepo(ui, repo, run=False, opt'
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'))
@@ -180,3 +180,76 b' Various sub-optimal detections work'
180 deltas within internal storage will always be recalculated without reusing prior deltas; this will likely make execution run several times slower; this optimization is typically not needed
180 deltas within internal storage will always be recalculated without reusing prior deltas; this will likely make execution run several times slower; this optimization is typically not needed
181
181
182
182
183 $ cd ..
184
185 Upgrading a repository that is already modern essentially no-ops
186
187 $ hg init modern
188 $ hg -R modern debugupgraderepo --run
189 upgrade will perform the following actions:
190
191 requirements
192 preserved: dotencode, fncache, generaldelta, revlogv1, store
193
194 beginning upgrade...
195 repository locked and read-only
196 creating temporary repository to stage migrated data: $TESTTMP/modern/.hg/upgrade.* (glob)
197 marking source repository as being upgraded; clients will be unable to read from repository
198 starting in-place swap of repository data
199 replaced files will be backed up at $TESTTMP/modern/.hg/upgradebackup.* (glob)
200 finalizing requirements file and making repository readable again
201 removing temporary repository $TESTTMP/modern/.hg/upgrade.* (glob)
202 copy of old repository backed up at $TESTTMP/modern/.hg/upgradebackup.* (glob)
203 the old repository will not be deleted; remove it to free up disk space once the upgraded repository is verified
204
205 Upgrading a repository to generaldelta works
206
207 $ hg --config format.usegeneraldelta=false init upgradegd
208 $ cd upgradegd
209 $ touch f0
210 $ hg -q commit -A -m initial
211 $ touch f1
212 $ hg -q commit -A -m 'add f1'
213 $ hg -q up -r 0
214 $ touch f2
215 $ hg -q commit -A -m 'add f2'
216
217 $ hg debugupgraderepo --run
218 upgrade will perform the following actions:
219
220 requirements
221 preserved: dotencode, fncache, revlogv1, store
222 added: generaldelta
223
224 generaldelta
225 repository storage will be able to create optimal deltas; new repository data will be smaller and read times should decrease; interacting with other repositories using this storage model should require less network and CPU resources, making "hg push" and "hg pull" faster
226
227 beginning upgrade...
228 repository locked and read-only
229 creating temporary repository to stage migrated data: $TESTTMP/upgradegd/.hg/upgrade.* (glob)
230 marking source repository as being upgraded; clients will be unable to read from repository
231 starting in-place swap of repository data
232 replaced files will be backed up at $TESTTMP/upgradegd/.hg/upgradebackup.* (glob)
233 finalizing requirements file and making repository readable again
234 removing temporary repository $TESTTMP/upgradegd/.hg/upgrade.* (glob)
235 copy of old repository backed up at $TESTTMP/upgradegd/.hg/upgradebackup.* (glob)
236 the old repository will not be deleted; remove it to free up disk space once the upgraded repository is verified
237
238 Original requirements backed up
239
240 $ cat .hg/upgradebackup.*/requires
241 dotencode
242 fncache
243 revlogv1
244 store
245
246 generaldelta added to original requirements files
247
248 $ cat .hg/requires
249 dotencode
250 fncache
251 generaldelta
252 revlogv1
253 store
254
255 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now