~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/upgrade.py

[merge] land Robert's branch-formats branch

Show diffs side-by-side

added added

removed removed

Lines of Context:
65
65
# the moment saves us having to worry about when files need new
66
66
# versions.
67
67
 
68
 
 
 
68
from cStringIO import StringIO
69
69
import os
70
70
import tempfile
71
71
import sys
72
 
import shutil
 
72
from stat import *
73
73
 
 
74
import bzrlib
74
75
from bzrlib.branch import Branch
75
76
from bzrlib.branch import BZR_BRANCH_FORMAT_5, BZR_BRANCH_FORMAT_6
 
77
from bzrlib.branch import BzrBranchFormat, BzrBranchFormat4, BzrBranchFormat5, BzrBranchFormat6
 
78
from bzrlib.errors import NoSuchFile, UpgradeReadonly
76
79
import bzrlib.hashcache as hashcache
 
80
from bzrlib.lockable_files import LockableFiles
 
81
from bzrlib.osutils import sha_strings, sha_string, pathjoin, abspath
 
82
from bzrlib.ui import ui_factory
 
83
from bzrlib.store.text import TextStore
 
84
from bzrlib.store.weave import WeaveStore
 
85
from bzrlib.trace import mutter, note, warning
 
86
from bzrlib.transactions import PassThroughTransaction
 
87
from bzrlib.transport import get_transport
 
88
from bzrlib.transport.local import LocalTransport
77
89
from bzrlib.weave import Weave
78
90
from bzrlib.weavefile import read_weave, write_weave
79
 
from bzrlib.ui import ui_factory
80
 
from bzrlib.atomicfile import AtomicFile
81
91
from bzrlib.xml4 import serializer_v4
82
92
from bzrlib.xml5 import serializer_v5
83
 
from bzrlib.trace import mutter, note, warning
84
 
from bzrlib.osutils import sha_strings, sha_string, pathjoin, abspath
85
 
 
86
 
 
87
 
# TODO: jam 20060108 Create a new branch format, and as part of upgrade
88
 
#       make sure that ancestry.weave is deleted (it is never used, but
89
 
#       used to be created)
90
93
 
91
94
 
92
95
class Convert(object):
93
 
    def __init__(self, base_dir):
94
 
        self.base = base_dir
 
96
 
 
97
    def __init__(self, transport):
 
98
        self.base = transport.base
95
99
        self.converted_revs = set()
96
100
        self.absent_revisions = set()
97
101
        self.text_count = 0
98
102
        self.revisions = {}
99
 
        self.convert()
100
 
 
 
103
        self.transport = transport
 
104
        if self.transport.is_readonly():
 
105
            raise UpgradeReadonly
 
106
        self.control_files = LockableFiles(transport.clone(bzrlib.BZRDIR), 'branch-lock')
 
107
        # Lock the branch (soon to be meta dir) to prevent anyone racing with us
 
108
        # This is currently windows incompatible, it will deadlock. When the upgrade
 
109
        # logic becomes format specific, then we can have the format know how to pass this
 
110
        # on. Also note that we probably have an 'upgrade meta' which upgrades the constituent
 
111
        # parts.
 
112
        print "FIXME: control files reuse" 
 
113
        self.control_files.lock_write()
 
114
        try:
 
115
            self.convert()
 
116
        finally:
 
117
            self.control_files.unlock()
101
118
 
102
119
    def convert(self):
103
120
        if not self._open_branch():
104
121
            return
105
 
        note('starting upgrade of %s', os.path.abspath(self.base))
 
122
        note('starting upgrade of %s', self.base)
106
123
        self._backup_control_dir()
107
124
        self.pb = ui_factory.progress_bar()
108
 
        if self.old_format == 4:
 
125
        if isinstance(self.old_format, BzrBranchFormat4):
109
126
            note('starting upgrade from format 4 to 5')
110
 
            self.branch.lock_write()
111
 
            try:
112
 
                self._convert_to_weaves()
113
 
            finally:
114
 
                self.branch.unlock()
115
 
            self._open_branch()
116
 
        if self.old_format == 5:
 
127
            self._convert_to_weaves()
 
128
        if isinstance(self.old_format, BzrBranchFormat5):
117
129
            note('starting upgrade from format 5 to 6')
118
 
            self.branch.lock_write()
119
 
            try:
120
 
                self._convert_to_prefixed()
121
 
            finally:
122
 
                self.branch.unlock()
123
 
            self._open_branch()
124
 
        cache = hashcache.HashCache(abspath(self.base))
125
 
        cache.clear()
126
 
        cache.write()
 
130
            self._convert_to_prefixed()
 
131
        if isinstance(self.transport, LocalTransport):
 
132
            cache = hashcache.HashCache(abspath(self.base))
 
133
            cache.clear()
 
134
            cache.write()
127
135
        note("finished")
128
136
 
129
 
 
130
137
    def _convert_to_prefixed(self):
131
138
        from bzrlib.store import hash_prefix
 
139
        bzr_transport = self.transport.clone('.bzr')
 
140
        bzr_transport.delete('branch-format')
132
141
        for store_name in ["weaves", "revision-store"]:
133
142
            note("adding prefixes to %s" % store_name) 
134
 
            store_dir = pathjoin(self.base, ".bzr", store_name)
135
 
            for filename in os.listdir(store_dir):
136
 
                if filename.endswith(".weave") or filename.endswith(".gz"):
 
143
            store_transport = bzr_transport.clone(store_name)
 
144
            for filename in store_transport.list_dir('.'):
 
145
                if (filename.endswith(".weave") or
 
146
                    filename.endswith(".gz") or
 
147
                    filename.endswith(".sig")):
137
148
                    file_id = os.path.splitext(filename)[0]
138
149
                else:
139
150
                    file_id = filename
140
 
                prefix_dir = pathjoin(store_dir, hash_prefix(file_id))
141
 
                if not os.path.isdir(prefix_dir):
142
 
                    os.mkdir(prefix_dir)
143
 
                os.rename(pathjoin(store_dir, filename),
144
 
                          pathjoin(prefix_dir, filename))
 
151
                prefix_dir = hash_prefix(file_id)
 
152
                # FIXME keep track of the dirs made RBC 20060121
 
153
                try:
 
154
                    store_transport.move(filename, prefix_dir + '/' + filename)
 
155
                except NoSuchFile: # catches missing dirs strangely enough
 
156
                    store_transport.mkdir(prefix_dir)
 
157
                    store_transport.move(filename, prefix_dir + '/' + filename)
145
158
        self._set_new_format(BZR_BRANCH_FORMAT_6)
146
 
 
 
159
        self.branch = BzrBranchFormat6().open(self.transport)
 
160
        self.old_format = self.branch._branch_format
147
161
 
148
162
    def _convert_to_weaves(self):
149
163
        note('note: upgrade may be faster if all store files are ungzipped first')
150
 
        if not os.path.isdir(self.base + '/.bzr/weaves'):
151
 
            os.mkdir(self.base + '/.bzr/weaves')
 
164
        bzr_transport = self.transport.clone('.bzr')
 
165
        try:
 
166
            # TODO permissions
 
167
            stat = bzr_transport.stat('weaves')
 
168
            if not S_ISDIR(stat.st_mode):
 
169
                bzr_transport.delete('weaves')
 
170
                bzr_transport.mkdir('weaves')
 
171
        except NoSuchFile:
 
172
            bzr_transport.mkdir('weaves')
152
173
        self.inv_weave = Weave('inventory')
153
174
        # holds in-memory weaves for all files
154
175
        self.text_weaves = {}
155
 
        os.remove(self.branch.control_files.controlfilename('branch-format'))
 
176
        bzr_transport.delete('branch-format')
156
177
        self._convert_working_inv()
157
178
        rev_history = self.branch.revision_history()
158
179
        # to_read is a stack holding the revisions we still need to process;
170
191
            self.pb.update('converting revision', i, len(to_import))
171
192
            self._convert_one_rev(rev_id)
172
193
        self.pb.clear()
 
194
        self._write_all_weaves()
 
195
        self._write_all_revs()
173
196
        note('upgraded to weaves:')
174
197
        note('  %6d revisions and inventories' % len(self.revisions))
175
198
        note('  %6d revisions not present' % len(self.absent_revisions))
176
199
        note('  %6d texts' % self.text_count)
177
 
        self._write_all_weaves()
178
 
        self._write_all_revs()
179
 
        self._cleanup_spare_files()
 
200
        self._cleanup_spare_files_after_format4()
180
201
        self._set_new_format(BZR_BRANCH_FORMAT_5)
181
 
 
 
202
        self.branch = BzrBranchFormat5().open(self.transport)
 
203
        self.old_format = self.branch._branch_format
182
204
 
183
205
    def _open_branch(self):
184
 
        self.branch = Branch.open_downlevel(self.base)
185
 
        self.old_format = self.branch._branch_format
186
 
        if self.old_format == 6:
187
 
            note('this branch is in the most current format')
 
206
        self.old_format = BzrBranchFormat.find_format(self.transport)
 
207
        self.branch = self.old_format.open(self.transport)
 
208
        if isinstance(self.old_format, BzrBranchFormat6):
 
209
            note('this branch is in the most current format (%s)', self.old_format)
188
210
            return False
189
 
        if self.old_format not in (4, 5):
190
 
            raise BzrError("cannot upgrade from branch format %r" %
 
211
        if (not isinstance(self.old_format, BzrBranchFormat4) and
 
212
            not isinstance(self.old_format, BzrBranchFormat5)):
 
213
            raise BzrError("cannot upgrade from branch format %s" %
191
214
                           self.branch._branch_format)
192
215
        return True
193
216
 
194
217
    def _set_new_format(self, format):
195
218
        self.branch.control_files.put_utf8('branch-format', format)
196
219
 
197
 
    def _cleanup_spare_files(self):
 
220
    def _cleanup_spare_files_after_format4(self):
 
221
        transport = self.transport.clone('.bzr')
 
222
        print "FIXME working tree upgrade foo."
198
223
        for n in 'merged-patches', 'pending-merged-patches':
199
 
            p = self.branch.control_files.controlfilename(n)
200
 
            if not os.path.exists(p):
201
 
                continue
202
 
            ## assert os.path.getsize(p) == 0
203
 
            os.remove(p)
204
 
        shutil.rmtree(self.base + '/.bzr/inventory-store')
205
 
        shutil.rmtree(self.base + '/.bzr/text-store')
 
224
            try:
 
225
                ## assert os.path.getsize(p) == 0
 
226
                transport.delete(n)
 
227
            except NoSuchFile:
 
228
                pass
 
229
        transport.delete_tree('inventory-store')
 
230
        transport.delete_tree('text-store')
206
231
 
207
232
    def _backup_control_dir(self):
208
 
        orig = self.base + '/.bzr'
209
 
        backup = orig + '.backup'
210
233
        note('making backup of tree history')
211
 
        shutil.copytree(orig, backup)
212
 
        note('%s has been backed up to %s', orig, backup)
 
234
        self.transport.copy_tree('.bzr', '.bzr.backup')
 
235
        note('%s.bzr has been backed up to %s.bzr.backup',
 
236
             self.transport.base,
 
237
             self.transport.base)
213
238
        note('if conversion fails, you can move this directory back to .bzr')
214
239
        note('if it succeeds, you can remove this directory if you wish')
215
240
 
217
242
        branch = self.branch
218
243
        inv = serializer_v4.read_inventory(branch.control_files.get('inventory'))
219
244
        new_inv_xml = serializer_v5.write_inventory_to_string(inv)
 
245
        print "fixme inventory is a working tree change."
220
246
        branch.control_files.put('inventory', new_inv_xml)
221
247
 
222
248
    def _write_all_weaves(self):
223
 
        write_a_weave(self.inv_weave, self.base + '/.bzr/inventory.weave')
 
249
        bzr_transport = self.transport.clone('.bzr')
 
250
        controlweaves = WeaveStore(bzr_transport, prefixed=False)
 
251
        weave_transport = bzr_transport.clone('weaves')
 
252
        weaves = WeaveStore(weave_transport, prefixed=False)
 
253
        transaction = PassThroughTransaction()
 
254
 
 
255
        controlweaves.put_weave('inventory', self.inv_weave, transaction)
224
256
        i = 0
225
257
        try:
226
258
            for file_id, file_weave in self.text_weaves.items():
227
259
                self.pb.update('writing weave', i, len(self.text_weaves))
228
 
                write_a_weave(file_weave, self.base + '/.bzr/weaves/%s.weave' % file_id)
 
260
                weaves.put_weave(file_id, file_weave, transaction)
229
261
                i += 1
230
262
        finally:
231
263
            self.pb.clear()
232
264
 
233
 
 
234
265
    def _write_all_revs(self):
235
266
        """Write all revisions out in new form."""
236
 
        shutil.rmtree(self.base + '/.bzr/revision-store')
237
 
        os.mkdir(self.base + '/.bzr/revision-store')
 
267
        transport = self.transport.clone('.bzr')
 
268
        transport.delete_tree('revision-store')
 
269
        transport.mkdir('revision-store')
 
270
        revision_transport = transport.clone('revision-store')
 
271
        # TODO permissions
 
272
        revision_store = TextStore(revision_transport,
 
273
                                   prefixed=False,
 
274
                                   compressed=True)
238
275
        try:
239
276
            for i, rev_id in enumerate(self.converted_revs):
240
277
                self.pb.update('write revision', i, len(self.converted_revs))
241
 
                f = file(self.base + '/.bzr/revision-store/%s' % rev_id, 'wb')
242
 
                try:
243
 
                    serializer_v5.write_revision(self.revisions[rev_id], f)
244
 
                finally:
245
 
                    f.close()
 
278
                rev_tmp = StringIO()
 
279
                serializer_v5.write_revision(self.revisions[rev_id], rev_tmp)
 
280
                rev_tmp.seek(0)
 
281
                revision_store.add(rev_tmp, rev_id)
246
282
        finally:
247
283
            self.pb.clear()
248
284
 
402
438
        return o
403
439
 
404
440
 
405
 
def write_a_weave(weave, filename):
406
 
    inv_wf = file(filename, 'wb')
407
 
    try:
408
 
        write_weave(weave, inv_wf)
409
 
    finally:
410
 
        inv_wf.close()
411
 
 
412
 
 
413
 
def upgrade(base_dir):
414
 
    Convert(base_dir)
 
441
def upgrade(url):
 
442
    t = get_transport(url)
 
443
    Convert(t)