~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/upgrade.py

  • Committer: Aaron Bentley
  • Date: 2005-10-01 06:48:01 UTC
  • mto: (1185.12.13)
  • mto: This revision was merged to the branch mainline in revision 1419.
  • Revision ID: aaron.bentley@utoronto.ca-20051001064801-7400c2ed0fe26080
Made iter_conflicts a WorkingTree method

Show diffs side-by-side

added added

removed removed

Lines of Context:
68
68
# versions.
69
69
 
70
70
 
71
 
# TODO: Don't create a progress bar here, have it passed by the caller.  
72
 
# At least do it from the UI factory.
73
 
 
74
71
if False:
75
72
    try:
76
73
        import psyco
85
82
import logging
86
83
import shutil
87
84
 
88
 
from bzrlib.branch import Branch, find_branch
89
 
from bzrlib.branch import BZR_BRANCH_FORMAT_5, BZR_BRANCH_FORMAT_6
90
 
import bzrlib.hashcache as hashcache
 
85
from bzrlib.branch import Branch, find_branch, BZR_BRANCH_FORMAT_5
91
86
from bzrlib.revfile import Revfile
92
87
from bzrlib.weave import Weave
93
88
from bzrlib.weavefile import read_weave, write_weave
94
 
from bzrlib.ui import ui_factory
 
89
from bzrlib.progress import ProgressBar
95
90
from bzrlib.atomicfile import AtomicFile
96
91
from bzrlib.xml4 import serializer_v4
97
92
from bzrlib.xml5 import serializer_v5
98
93
from bzrlib.trace import mutter, note, warning, enable_default_logging
99
94
from bzrlib.osutils import sha_strings, sha_string
 
95
from bzrlib.commit import merge_ancestry_lines
100
96
 
101
97
 
102
98
class Convert(object):
114
110
            return
115
111
        note('starting upgrade of %s', os.path.abspath(self.base))
116
112
        self._backup_control_dir()
117
 
        self.pb = ui_factory.progress_bar()
118
 
        if self.old_format == 4:
119
 
            note('starting upgrade from format 4 to 5')
120
 
            self._convert_to_weaves()
121
 
            self._open_branch()
122
 
        if self.old_format == 5:
123
 
            note('starting upgrade from format 5 to 6')
124
 
            self._convert_to_prefixed()
125
 
            self._open_branch()
126
 
        cache = hashcache.HashCache(os.path.abspath(self.base))
127
 
        cache.clear()
128
 
        cache.write()
129
 
        note("finished")
130
 
 
131
 
 
132
 
    def _convert_to_prefixed(self):
133
 
        from bzrlib.store import hash_prefix
134
 
        for store_name in ["weaves", "revision-store"]:
135
 
            note("adding prefixes to %s" % store_name) 
136
 
            store_dir = os.path.join(self.base, ".bzr", store_name)
137
 
            for filename in os.listdir(store_dir):
138
 
                if filename.endswith(".weave") or filename.endswith(".gz"):
139
 
                    file_id = os.path.splitext(filename)[0]
140
 
                else:
141
 
                    file_id = filename
142
 
                prefix_dir = os.path.join(store_dir, hash_prefix(file_id))
143
 
                if not os.path.isdir(prefix_dir):
144
 
                    os.mkdir(prefix_dir)
145
 
                os.rename(os.path.join(store_dir, filename),
146
 
                          os.path.join(prefix_dir, filename))
147
 
        self._set_new_format(BZR_BRANCH_FORMAT_6)
148
 
 
149
 
 
150
 
    def _convert_to_weaves(self):
 
113
        note('starting upgrade')
151
114
        note('note: upgrade may be faster if all store files are ungzipped first')
 
115
        self.pb = ProgressBar()
152
116
        if not os.path.isdir(self.base + '/.bzr/weaves'):
153
117
            os.mkdir(self.base + '/.bzr/weaves')
154
118
        self.inv_weave = Weave('inventory')
 
119
        self.anc_weave = Weave('ancestry')
 
120
        self.ancestries = {}
155
121
        # holds in-memory weaves for all files
156
122
        self.text_weaves = {}
157
123
        os.remove(self.branch.controlfilename('branch-format'))
160
126
        # to_read is a stack holding the revisions we still need to process;
161
127
        # appending to it adds new highest-priority revisions
162
128
        self.known_revisions = set(rev_history)
163
 
        self.to_read = rev_history[-1:]
 
129
        self.to_read = [rev_history[-1]]
164
130
        while self.to_read:
165
131
            rev_id = self.to_read.pop()
166
132
            if (rev_id not in self.revisions
174
140
        self.pb.clear()
175
141
        note('upgraded to weaves:')
176
142
        note('  %6d revisions and inventories' % len(self.revisions))
177
 
        note('  %6d revisions not present' % len(self.absent_revisions))
 
143
        note('  %6d absent revisions removed' % len(self.absent_revisions))
178
144
        note('  %6d texts' % self.text_count)
179
145
        self._write_all_weaves()
180
146
        self._write_all_revs()
 
147
        self._set_new_format()
181
148
        self._cleanup_spare_files()
182
 
        self._set_new_format(BZR_BRANCH_FORMAT_5)
183
149
 
184
150
 
185
151
    def _open_branch(self):
186
152
        self.branch = Branch.open_downlevel(self.base)
187
 
        self.old_format = self.branch._branch_format
188
 
        if self.old_format == 6:
189
 
            note('this branch is in the most current format')
 
153
        if self.branch._branch_format == 5:
 
154
            note('this branch is already in the most current format')
190
155
            return False
191
 
        if self.old_format not in (4, 5):
 
156
        if self.branch._branch_format != 4:
192
157
            raise BzrError("cannot upgrade from branch format %r" %
193
158
                           self.branch._branch_format)
194
159
        return True
195
160
 
196
161
 
197
 
    def _set_new_format(self, format):
198
 
        self.branch.put_controlfile('branch-format', format)
 
162
    def _set_new_format(self):
 
163
        self.branch.put_controlfile('branch-format', BZR_BRANCH_FORMAT_5)
199
164
 
200
165
 
201
166
    def _cleanup_spare_files(self):
229
194
 
230
195
    def _write_all_weaves(self):
231
196
        write_a_weave(self.inv_weave, self.base + '/.bzr/inventory.weave')
 
197
        write_a_weave(self.anc_weave, self.base + '/.bzr/ancestry.weave')
232
198
        i = 0
233
199
        try:
234
200
            for file_id, file_weave in self.text_weaves.items():
263
229
        self.pb.update('loading revision',
264
230
                       len(self.revisions),
265
231
                       len(self.known_revisions))
266
 
        if not self.branch.revision_store.has_id(rev_id):
 
232
        if rev_id not in self.branch.revision_store:
267
233
            self.pb.clear()
268
234
            note('revision {%s} not present in branch; '
269
 
                 'will be converted as a ghost',
 
235
                 'will not be converted',
270
236
                 rev_id)
271
237
            self.absent_revisions.add(rev_id)
272
238
        else:
273
 
            rev_xml = self.branch.revision_store.get(rev_id).read()
 
239
            rev_xml = self.branch.revision_store[rev_id].read()
274
240
            rev = serializer_v4.read_revision_from_string(rev_xml)
275
241
            for parent_id in rev.parent_ids:
276
242
                self.known_revisions.add(parent_id)
280
246
 
281
247
    def _load_old_inventory(self, rev_id):
282
248
        assert rev_id not in self.converted_revs
283
 
        old_inv_xml = self.branch.inventory_store.get(rev_id).read()
 
249
        old_inv_xml = self.branch.inventory_store[rev_id].read()
284
250
        inv = serializer_v4.read_inventory_from_string(old_inv_xml)
285
251
        rev = self.revisions[rev_id]
286
252
        if rev.inventory_sha1:
300
266
        """Convert revision and all referenced objects to new format."""
301
267
        rev = self.revisions[rev_id]
302
268
        inv = self._load_old_inventory(rev_id)
303
 
        present_parents = [p for p in rev.parent_ids
304
 
                           if p not in self.absent_revisions]
305
 
        self._convert_revision_contents(rev, inv, present_parents)
306
 
        self._store_new_weave(rev, inv, present_parents)
 
269
        for parent_id in rev.parent_ids[:]:
 
270
            if parent_id in self.absent_revisions:
 
271
                rev.parent_ids.remove(parent_id)
 
272
                self.pb.clear()
 
273
                note('remove {%s} as parent of {%s}', parent_id, rev_id)
 
274
        self._convert_revision_contents(rev, inv)
 
275
        self._store_new_weave(rev, inv)
 
276
        self._make_rev_ancestry(rev)
307
277
        self.converted_revs.add(rev_id)
308
278
 
309
279
 
310
 
    def _store_new_weave(self, rev, inv, present_parents):
 
280
    def _store_new_weave(self, rev, inv):
311
281
        # the XML is now updated with text versions
312
282
        if __debug__:
313
283
            for file_id in inv:
314
284
                ie = inv[file_id]
315
285
                if ie.kind == 'root_directory':
316
286
                    continue
317
 
                assert hasattr(ie, 'revision'), \
318
 
                    'no revision on {%s} in {%s}' % \
 
287
                assert hasattr(ie, 'name_version'), \
 
288
                    'no name_version on {%s} in {%s}' % \
319
289
                    (file_id, rev.revision_id)
 
290
                if ie.kind == 'file':
 
291
                    assert hasattr(ie, 'text_version')
 
292
 
320
293
        new_inv_xml = serializer_v5.write_inventory_to_string(inv)
321
294
        new_inv_sha1 = sha_string(new_inv_xml)
322
 
        self.inv_weave.add(rev.revision_id, 
323
 
                           present_parents,
 
295
        self.inv_weave.add(rev.revision_id, rev.parent_ids,
324
296
                           new_inv_xml.splitlines(True),
325
297
                           new_inv_sha1)
326
298
        rev.inventory_sha1 = new_inv_sha1
327
299
 
328
 
    def _convert_revision_contents(self, rev, inv, present_parents):
 
300
 
 
301
    def _make_rev_ancestry(self, rev):
 
302
        rev_id = rev.revision_id
 
303
        for parent_id in rev.parent_ids:
 
304
            assert parent_id in self.converted_revs
 
305
        if rev.parent_ids:
 
306
            lines = list(self.anc_weave.mash_iter(rev.parent_ids))
 
307
        else:
 
308
            lines = []
 
309
        lines.append(rev_id + '\n')
 
310
        if __debug__:
 
311
            parent_ancestries = [self.ancestries[p] for p in rev.parent_ids]
 
312
            new_lines = merge_ancestry_lines(rev_id, parent_ancestries)
 
313
            assert set(lines) == set(new_lines)
 
314
            self.ancestries[rev_id] = new_lines
 
315
        self.anc_weave.add(rev_id, rev.parent_ids, lines)
 
316
 
 
317
 
 
318
    def _convert_revision_contents(self, rev, inv):
329
319
        """Convert all the files within a revision.
330
320
 
331
321
        Also upgrade the inventory to refer to the text revision ids."""
332
322
        rev_id = rev.revision_id
333
323
        mutter('converting texts of revision {%s}',
334
324
               rev_id)
335
 
        parent_invs = map(self._load_updated_inventory, present_parents)
 
325
        parent_invs = map(self._load_updated_inventory, rev.parent_ids)
336
326
        for file_id in inv:
337
327
            ie = inv[file_id]
 
328
            self._set_name_version(rev, ie, parent_invs)
 
329
            if ie.kind != 'file':
 
330
                continue
338
331
            self._convert_file_version(rev, ie, parent_invs)
339
332
 
 
333
 
 
334
    def _set_name_version(self, rev, ie, parent_invs):
 
335
        """Set name version for a file.
 
336
 
 
337
        Done in a slightly lazy way: if the file is renamed or in a merge revision
 
338
        it gets a new version, otherwise the same as before.
 
339
        """
 
340
        file_id = ie.file_id
 
341
        if ie.kind == 'root_directory':
 
342
            return
 
343
        if len(parent_invs) != 1:
 
344
            ie.name_version = rev.revision_id
 
345
        else:
 
346
            old_inv = parent_invs[0]
 
347
            if not old_inv.has_id(file_id):
 
348
                ie.name_version = rev.revision_id
 
349
            else:
 
350
                old_ie = old_inv[file_id]
 
351
                if (old_ie.parent_id != ie.parent_id
 
352
                    or old_ie.name != ie.name):
 
353
                    ie.name_version = rev.revision_id
 
354
                else:
 
355
                    ie.name_version = old_ie.name_version
 
356
 
 
357
 
 
358
 
340
359
    def _convert_file_version(self, rev, ie, parent_invs):
341
360
        """Convert one version of one file.
342
361
 
343
362
        The file needs to be added into the weave if it is a merge
344
363
        of >=2 parents or if it's changed from its parent.
345
364
        """
346
 
        if ie.kind == 'root_directory':
347
 
            return
348
365
        file_id = ie.file_id
349
366
        rev_id = rev.revision_id
350
367
        w = self.text_weaves.get(file_id)
351
368
        if w is None:
352
369
            w = Weave(file_id)
353
370
            self.text_weaves[file_id] = w
 
371
        file_parents = []
354
372
        text_changed = False
355
 
        previous_entries = ie.find_previous_heads(parent_invs, w)
356
 
        for old_revision in previous_entries:
357
 
                # if this fails, its a ghost ?
358
 
                assert old_revision in self.converted_revs 
359
 
        self.snapshot_ie(previous_entries, ie, w, rev_id)
360
 
        del ie.text_id
361
 
        assert getattr(ie, 'revision', None) is not None
362
 
 
363
 
    def snapshot_ie(self, previous_revisions, ie, w, rev_id):
364
 
        # TODO: convert this logic, which is ~= snapshot to
365
 
        # a call to:. This needs the path figured out. rather than a work_tree
366
 
        # a v4 revision_tree can be given, or something that looks enough like
367
 
        # one to give the file content to the entry if it needs it.
368
 
        # and we need something that looks like a weave store for snapshot to 
369
 
        # save against.
370
 
        #ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
371
 
        if len(previous_revisions) == 1:
372
 
            previous_ie = previous_revisions.values()[0]
373
 
            if ie._unchanged(previous_ie):
374
 
                ie.revision = previous_ie.revision
375
 
                return
376
 
        parent_indexes = map(w.lookup, previous_revisions)
377
 
        if ie.has_text():
378
 
            file_lines = self.branch.text_store.get(ie.text_id).readlines()
 
373
        for parent_inv in parent_invs:
 
374
            if parent_inv.has_id(file_id):
 
375
                parent_ie = parent_inv[file_id]
 
376
                old_text_version = parent_ie.text_version
 
377
                assert old_text_version in self.converted_revs 
 
378
                if old_text_version not in file_parents:
 
379
                    file_parents.append(old_text_version)
 
380
                if parent_ie.text_sha1 != ie.text_sha1:
 
381
                    text_changed = True
 
382
        if len(file_parents) != 1 or text_changed:
 
383
            file_lines = self.branch.text_store[ie.text_id].readlines()
379
384
            assert sha_strings(file_lines) == ie.text_sha1
380
385
            assert sum(map(len, file_lines)) == ie.text_size
381
 
            w.add(rev_id, parent_indexes, file_lines, ie.text_sha1)
 
386
            w.add(rev_id, file_parents, file_lines, ie.text_sha1)
 
387
            ie.text_version = rev_id
382
388
            self.text_count += 1
 
389
            ##mutter('import text {%s} of {%s}',
 
390
            ##       ie.text_id, file_id)
383
391
        else:
384
 
            w.add(rev_id, parent_indexes, [], None)
385
 
        ie.revision = rev_id
386
 
        ##mutter('import text {%s} of {%s}',
387
 
        ##       ie.text_id, file_id)
 
392
            ##mutter('text of {%s} unchanged from parent', file_id)
 
393
            ie.text_version = file_parents[0]
 
394
        del ie.text_id
 
395
 
 
396
 
388
397
 
389
398
    def _make_order(self):
390
399
        """Return a suitable order for importing revisions.