~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bundle/serializer/v4.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-11-04 18:51:39 UTC
  • mfrom: (2961.1.1 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20071104185139-kaio3sneodg2kp71
Authentication ring implementation (read-only)

Show diffs side-by-side

added added

removed removed

Lines of Context:
107
107
    @staticmethod
108
108
    def encode_name(content_kind, revision_id, file_id=None):
109
109
        """Encode semantic ids as a container name"""
110
 
        if content_kind not in ('revision', 'file', 'inventory', 'signature',
111
 
                'info'):
112
 
            raise ValueError(content_kind)
 
110
        assert content_kind in ('revision', 'file', 'inventory', 'signature',
 
111
                                'info')
 
112
 
113
113
        if content_kind == 'file':
114
 
            if file_id is None:
115
 
                raise AssertionError()
 
114
            assert file_id is not None
116
115
        else:
117
 
            if file_id is not None:
118
 
                raise AssertionError()
 
116
            assert file_id is None
119
117
        if content_kind == 'info':
120
 
            if revision_id is not None:
121
 
                raise AssertionError()
122
 
        elif revision_id is None:
123
 
            raise AssertionError()
 
118
            assert revision_id is None
 
119
        else:
 
120
            assert revision_id is not None
124
121
        names = [n.replace('/', '//') for n in
125
122
                 (content_kind, revision_id, file_id) if n is not None]
126
123
        return '/'.join(names)
163
160
            source_file = iterablefile.IterableFile(self.iter_decode(fileobj))
164
161
        else:
165
162
            source_file = StringIO(bz2.decompress(fileobj.read()))
166
 
        self._container_file = source_file
 
163
        self._container = pack.ContainerReader(source_file)
167
164
 
168
165
    @staticmethod
169
166
    def iter_decode(fileobj):
170
167
        """Iterate through decoded fragments of the file"""
171
168
        decompressor = bz2.BZ2Decompressor()
172
169
        for line in fileobj:
173
 
            try:
174
 
                yield decompressor.decompress(line)
175
 
            except EOFError:
176
 
                return
 
170
            yield decompressor.decompress(line)
177
171
 
178
172
    @staticmethod
179
173
    def decode_name(name):
205
199
        :return: a generator of (bytes, metadata, content_kind, revision_id,
206
200
            file_id)
207
201
        """
208
 
        iterator = pack.iter_records_from_file(self._container_file)
209
 
        for names, bytes in iterator:
 
202
        iterator = self._container.iter_records()
 
203
        for names, meta_bytes in iterator:
210
204
            if len(names) != 1:
211
205
                raise errors.BadBundle('Record has %d names instead of 1'
212
206
                                       % len(names))
213
 
            metadata = bencode.bdecode(bytes)
 
207
            metadata = bencode.bdecode(meta_bytes(None))
214
208
            if metadata['storage_kind'] == 'header':
215
209
                bytes = None
216
210
            else:
217
211
                _unused, bytes = iterator.next()
 
212
                bytes = bytes(None)
218
213
            yield (bytes, metadata) + self.decode_name(names[0][0])
219
214
 
220
215
 
278
273
            revision_ids = set(repository.get_ancestry(target,
279
274
                                                       topo_sorted=False))
280
275
            self.revision_ids = revision_ids.difference(self.base_ancestry)
281
 
        self.revision_keys = set([(revid,) for revid in self.revision_ids])
282
276
 
283
277
    def do_write(self):
284
278
        """Write all data to the bundle"""
285
 
        self.repository.lock_read()
286
 
        try:
287
 
            self.bundle.begin()
288
 
            self.write_info()
289
 
            self.write_files()
290
 
            self.write_revisions()
291
 
            self.bundle.end()
292
 
        finally:
293
 
            self.repository.unlock()
 
279
        self.bundle.begin()
 
280
        self.write_info()
 
281
        self.write_files()
 
282
        self.write_revisions()
 
283
        self.bundle.end()
294
284
        return self.revision_ids
295
285
 
296
286
    def write_info(self):
301
291
        self.bundle.add_info_record(serializer=serializer_format,
302
292
                                    supports_rich_root=supports_rich_root)
303
293
 
 
294
    def iter_file_revisions(self):
 
295
        """Iterate through all relevant revisions of all files.
 
296
 
 
297
        This is the correct implementation, but is not compatible with bzr.dev,
 
298
        because certain old revisions were not converted correctly, and have
 
299
        the wrong "revision" marker in inventories.
 
300
        """
 
301
        transaction = self.repository.get_transaction()
 
302
        altered = self.repository.fileids_altered_by_revision_ids(
 
303
            self.revision_ids)
 
304
        for file_id, file_revision_ids in altered.iteritems():
 
305
            vf = self.repository.weave_store.get_weave(file_id, transaction)
 
306
            yield vf, file_id, file_revision_ids
 
307
 
 
308
    def iter_file_revisions_aggressive(self):
 
309
        """Iterate through all relevant revisions of all files.
 
310
 
 
311
        This uses the standard iter_file_revisions to determine what revisions
 
312
        are referred to by inventories, but then uses the versionedfile to
 
313
        determine what the build-dependencies of each required revision.
 
314
 
 
315
        All build dependencies which are not ancestors of the base revision
 
316
        are emitted.
 
317
        """
 
318
        for vf, file_id, file_revision_ids in self.iter_file_revisions():
 
319
            new_revision_ids = set()
 
320
            pending = list(file_revision_ids)
 
321
            while len(pending) > 0:
 
322
                revision_id = pending.pop()
 
323
                if revision_id in new_revision_ids:
 
324
                    continue
 
325
                if revision_id in self.base_ancestry:
 
326
                    continue
 
327
                new_revision_ids.add(revision_id)
 
328
                pending.extend(vf.get_parents(revision_id))
 
329
            yield vf, file_id, new_revision_ids
 
330
 
304
331
    def write_files(self):
305
332
        """Write bundle records for all revisions of all files"""
306
 
        text_keys = []
307
 
        altered_fileids = self.repository.fileids_altered_by_revision_ids(
308
 
                self.revision_ids)
309
 
        for file_id, revision_ids in altered_fileids.iteritems():
310
 
            for revision_id in revision_ids:
311
 
                text_keys.append((file_id, revision_id))
312
 
        self._add_mp_records_keys('file', self.repository.texts, text_keys)
 
333
        for vf, file_id, revision_ids in self.iter_file_revisions():
 
334
            self.add_mp_records('file', file_id, vf, revision_ids)
313
335
 
314
336
    def write_revisions(self):
315
337
        """Write bundle records for all revisions and signatures"""
316
 
        inv_vf = self.repository.inventories
317
 
        revision_order = [key[-1] for key in multiparent.topo_iter_keys(inv_vf,
318
 
            self.revision_keys)]
 
338
        inv_vf = self.repository.get_inventory_weave()
 
339
        revision_order = list(multiparent.topo_iter(inv_vf, self.revision_ids))
319
340
        if self.target is not None and self.target in self.revision_ids:
320
341
            revision_order.remove(self.target)
321
342
            revision_order.append(self.target)
322
 
        self._add_mp_records_keys('inventory', inv_vf, [(revid,) for revid in revision_order])
323
 
        parent_map = self.repository.get_parent_map(revision_order)
324
 
        for revision_id in revision_order:
325
 
            parents = parent_map.get(revision_id, None)
 
343
        self.add_mp_records('inventory', None, inv_vf, revision_order)
 
344
        parents_list = self.repository.get_parents(revision_order)
 
345
        for parents, revision_id in zip(parents_list, revision_order):
326
346
            revision_text = self.repository.get_revision_xml(revision_id)
327
347
            self.bundle.add_fulltext_record(revision_text, parents,
328
348
                                       'revision', revision_id)
348
368
                base = parents[0]
349
369
        return base, target
350
370
 
351
 
    def _add_mp_records_keys(self, repo_kind, vf, keys):
 
371
    def add_mp_records(self, repo_kind, file_id, vf, revision_ids):
352
372
        """Add multi-parent diff records to a bundle"""
353
 
        ordered_keys = list(multiparent.topo_iter_keys(vf, keys))
354
 
        mpdiffs = vf.make_mpdiffs(ordered_keys)
355
 
        sha1s = vf.get_sha1s(ordered_keys)
356
 
        parent_map = vf.get_parent_map(ordered_keys)
357
 
        for mpdiff, item_key, in zip(mpdiffs, ordered_keys):
358
 
            sha1 = sha1s[item_key]
359
 
            parents = [key[-1] for key in parent_map[item_key]]
 
373
        revision_ids = list(multiparent.topo_iter(vf, revision_ids))
 
374
        mpdiffs = vf.make_mpdiffs(revision_ids)
 
375
        sha1s = vf.get_sha1s(revision_ids)
 
376
        for mpdiff, revision_id, sha1, in zip(mpdiffs, revision_ids, sha1s):
 
377
            parents = vf.get_parents(revision_id)
360
378
            text = ''.join(mpdiff.to_patch())
361
 
            # Infer file id records as appropriate.
362
 
            if len(item_key) == 2:
363
 
                file_id = item_key[0]
364
 
            else:
365
 
                file_id = None
366
379
            self.bundle.add_multiparent_record(text, sha1, parents, repo_kind,
367
 
                                               item_key[-1], file_id)
 
380
                                               revision_id, file_id)
368
381
 
369
382
 
370
383
class BundleInfoV4(object):
477
490
        for bytes, metadata, repo_kind, revision_id, file_id in\
478
491
            self._container.iter_records():
479
492
            if repo_kind == 'info':
480
 
                if self._info is not None:
481
 
                    raise AssertionError()
 
493
                assert self._info is None
482
494
                self._handle_info(metadata)
483
 
            if (pending_file_records and
484
 
                (repo_kind, file_id) != ('file', current_file)):
485
 
                # Flush the data for a single file - prevents memory
486
 
                # spiking due to buffering all files in memory.
487
 
                self._install_mp_records_keys(self._repository.texts,
488
 
                    pending_file_records)
 
495
            if (repo_kind, file_id) != ('file', current_file):
 
496
                if len(pending_file_records) > 0:
 
497
                    self._install_mp_records(current_versionedfile,
 
498
                                             pending_file_records)
489
499
                current_file = None
490
 
                del pending_file_records[:]
 
500
                current_versionedfile = None
 
501
                pending_file_records = []
491
502
            if len(pending_inventory_records) > 0 and repo_kind != 'inventory':
492
 
                self._install_inventory_records(pending_inventory_records)
 
503
                self._install_inventory_records(inventory_vf,
 
504
                                                pending_inventory_records)
493
505
                pending_inventory_records = []
494
506
            if repo_kind == 'inventory':
495
 
                pending_inventory_records.append(((revision_id,), metadata, bytes))
 
507
                if inventory_vf is None:
 
508
                    inventory_vf = self._repository.get_inventory_weave()
 
509
                if revision_id not in inventory_vf:
 
510
                    pending_inventory_records.append((revision_id, metadata,
 
511
                                                      bytes))
496
512
            if repo_kind == 'revision':
497
513
                target_revision = revision_id
498
514
                self._install_revision(revision_id, metadata, bytes)
500
516
                self._install_signature(revision_id, metadata, bytes)
501
517
            if repo_kind == 'file':
502
518
                current_file = file_id
503
 
                pending_file_records.append(((file_id, revision_id), metadata, bytes))
504
 
        self._install_mp_records_keys(self._repository.texts, pending_file_records)
 
519
                if current_versionedfile is None:
 
520
                    current_versionedfile = \
 
521
                        self._repository.weave_store.get_weave_or_empty(
 
522
                        file_id, self._repository.get_transaction())
 
523
                    pending_file_records = []
 
524
                if revision_id in current_versionedfile:
 
525
                    continue
 
526
                pending_file_records.append((revision_id, metadata, bytes))
 
527
        self._install_mp_records(current_versionedfile, pending_file_records)
505
528
        return target_revision
506
529
 
507
530
    def _handle_info(self, info):
522
545
                      records if r not in versionedfile]
523
546
        versionedfile.add_mpdiffs(vf_records)
524
547
 
525
 
    def _install_mp_records_keys(self, versionedfile, records):
526
 
        d_func = multiparent.MultiParent.from_patch
527
 
        vf_records = []
528
 
        for key, meta, text in records:
529
 
            # Adapt to tuple interface: A length two key is a file_id,
530
 
            # revision_id pair, a length 1 key is a
531
 
            # revision/signature/inventory. We need to do this because
532
 
            # the metadata extraction from the bundle has not yet been updated
533
 
            # to use the consistent tuple interface itself.
534
 
            if len(key) == 2:
535
 
                prefix = key[:1]
536
 
            else:
537
 
                prefix = ()
538
 
            parents = [prefix + (parent,) for parent in meta['parents']]
539
 
            vf_records.append((key, parents, meta['sha1'], d_func(text)))
540
 
        versionedfile.add_mpdiffs(vf_records)
541
 
 
542
 
    def _install_inventory_records(self, records):
 
548
    def _install_inventory_records(self, vf, records):
543
549
        if self._info['serializer'] == self._repository._serializer.format_num:
544
 
            return self._install_mp_records_keys(self._repository.inventories,
545
 
                records)
546
 
        for key, metadata, bytes in records:
547
 
            revision_id = key[-1]
 
550
            return self._install_mp_records(vf, records)
 
551
        for revision_id, metadata, bytes in records:
548
552
            parent_ids = metadata['parents']
549
553
            parents = [self._repository.get_inventory(p)
550
554
                       for p in parent_ids]
567
571
    def _handle_root(self, target_inv, parent_ids):
568
572
        revision_id = target_inv.revision_id
569
573
        if self.update_root:
570
 
            text_key = (target_inv.root.file_id, revision_id)
571
 
            parent_keys = [(target_inv.root.file_id, parent) for
572
 
                parent in parent_ids]
573
 
            self._repository.texts.add_lines(text_key, parent_keys, [])
 
574
            target_inv.root.revision = revision_id
 
575
            store = self._repository.weave_store
 
576
            transaction = self._repository.get_transaction()
 
577
            vf = store.get_weave_or_empty(target_inv.root.file_id, transaction)
 
578
            vf.add_lines(revision_id, parent_ids, [])
574
579
        elif not self._repository.supports_rich_root():
575
580
            if target_inv.root.revision != revision_id:
576
581
                raise errors.IncompatibleRevision(repr(self._repository))
577
582
 
 
583
 
578
584
    def _install_revision(self, revision_id, metadata, text):
579
585
        if self._repository.has_revision(revision_id):
580
586
            return
581
 
        revision = self._source_serializer.read_revision_from_string(text)
582
 
        self._repository.add_revision(revision.revision_id, revision)
 
587
        self._repository._add_revision_text(revision_id, text)
583
588
 
584
589
    def _install_signature(self, revision_id, metadata, text):
585
590
        transaction = self._repository.get_transaction()
586
 
        if self._repository.has_signature_for_revision_id(revision_id):
 
591
        if self._repository._revision_store.has_signature(revision_id,
 
592
                                                          transaction):
587
593
            return
588
 
        self._repository.add_signature_text(revision_id, text)
 
594
        self._repository._revision_store.add_revision_signature_text(
 
595
            revision_id, text, transaction)