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