/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/pack_repo.py

  • Committer: Aaron Bentley
  • Date: 2008-12-03 04:23:21 UTC
  • mfrom: (3878 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3892.
  • Revision ID: aaron@aaronbentley.com-20081203042321-kr5k4mdhmdvl3553
Merge with trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
172
172
        """The text index is the name + .tix."""
173
173
        return self.index_name('text', name)
174
174
 
175
 
    def _external_compression_parents_of_texts(self):
176
 
        keys = set()
177
 
        refs = set()
178
 
        for node in self.text_index.iter_all_entries():
179
 
            keys.add(node[1])
180
 
            refs.update(node[3][1])
181
 
        return refs - keys
182
 
 
183
175
 
184
176
class ExistingPack(Pack):
185
177
    """An in memory proxy for an existing .pack and its disk indices."""
222
214
        'signature': ('.six', 3),
223
215
        }
224
216
 
225
 
    def __init__(self, upload_transport, index_transport, pack_transport,
226
 
        upload_suffix='', file_mode=None, index_builder_class=None,
227
 
        index_class=None):
 
217
    def __init__(self, pack_collection, upload_suffix='', file_mode=None):
228
218
        """Create a NewPack instance.
229
219
 
230
 
        :param upload_transport: A writable transport for the pack to be
231
 
            incrementally uploaded to.
232
 
        :param index_transport: A writable transport for the pack's indices to
233
 
            be written to when the pack is finished.
234
 
        :param pack_transport: A writable transport for the pack to be renamed
235
 
            to when the upload is complete. This *must* be the same as
236
 
            upload_transport.clone('../packs').
 
220
        :param pack_collection: A PackCollection into which this is being inserted.
237
221
        :param upload_suffix: An optional suffix to be given to any temporary
238
222
            files created during the pack creation. e.g '.autopack'
239
 
        :param file_mode: An optional file mode to create the new files with.
240
 
        :param index_builder_class: Required keyword parameter - the class of
241
 
            index builder to use.
242
 
        :param index_class: Required keyword parameter - the class of index
243
 
            object to use.
 
223
        :param file_mode: Unix permissions for newly created file.
244
224
        """
245
225
        # The relative locations of the packs are constrained, but all are
246
226
        # passed in because the caller has them, so as to avoid object churn.
 
227
        index_builder_class = pack_collection._index_builder_class
247
228
        Pack.__init__(self,
248
229
            # Revisions: parents list, no text compression.
249
230
            index_builder_class(reference_lists=1),
259
240
            # listing.
260
241
            index_builder_class(reference_lists=0),
261
242
            )
 
243
        self._pack_collection = pack_collection
262
244
        # When we make readonly indices, we need this.
263
 
        self.index_class = index_class
 
245
        self.index_class = pack_collection._index_class
264
246
        # where should the new pack be opened
265
 
        self.upload_transport = upload_transport
 
247
        self.upload_transport = pack_collection._upload_transport
266
248
        # where are indices written out to
267
 
        self.index_transport = index_transport
 
249
        self.index_transport = pack_collection._index_transport
268
250
        # where is the pack renamed to when it is finished?
269
 
        self.pack_transport = pack_transport
 
251
        self.pack_transport = pack_collection._pack_transport
270
252
        # What file mode to upload the pack and indices with.
271
253
        self._file_mode = file_mode
272
254
        # tracks the content written to the .pack file.
334
316
        else:
335
317
            raise AssertionError(self._state)
336
318
 
 
319
    def _check_references(self):
 
320
        """Make sure our external references are present.
 
321
        
 
322
        Packs are allowed to have deltas whose base is not in the pack, but it
 
323
        must be present somewhere in this collection.  It is not allowed to
 
324
        have deltas based on a fallback repository. 
 
325
        (See <https://bugs.launchpad.net/bzr/+bug/288751>)
 
326
        """
 
327
        missing_items = {}
 
328
        for (index_name, external_refs, index) in [
 
329
            ('texts',
 
330
                self.text_index._external_references(),
 
331
                self._pack_collection.text_index.combined_index),
 
332
            ('inventories',
 
333
                self.inventory_index._external_references(),
 
334
                self._pack_collection.inventory_index.combined_index),
 
335
            ]:
 
336
            missing = external_refs.difference(
 
337
                k for (idx, k, v, r) in 
 
338
                index.iter_entries(external_refs))
 
339
            if missing:
 
340
                missing_items[index_name] = sorted(list(missing))
 
341
        if missing_items:
 
342
            from pprint import pformat
 
343
            raise errors.BzrCheckError(
 
344
                "Newly created pack file %r has delta references to "
 
345
                "items not in its repository:\n%s"
 
346
                % (self, pformat(missing_items)))
 
347
 
337
348
    def data_inserted(self):
338
349
        """True if data has been added to this pack."""
339
350
        return bool(self.get_revision_count() or
356
367
        if self._buffer[1]:
357
368
            self._write_data('', flush=True)
358
369
        self.name = self._hash.hexdigest()
 
370
        self._check_references()
359
371
        # write indices
360
372
        # XXX: It'd be better to write them all to temporary names, then
361
373
        # rename them all into place, so that the window when only some are
462
474
        self._reload_func = reload_func
463
475
        self.index_to_pack = {}
464
476
        self.combined_index = CombinedGraphIndex([], reload_func=reload_func)
465
 
        self.data_access = _DirectPackAccess(self.index_to_pack)
 
477
        self.data_access = _DirectPackAccess(self.index_to_pack,
 
478
                                             reload_func=reload_func)
466
479
        self.add_callback = None
467
480
 
468
481
    def replace_indices(self, index_to_pack, indices):
568
581
    def _extra_init(self):
569
582
        """A template hook to allow extending the constructor trivially."""
570
583
 
 
584
    def _pack_map_and_index_list(self, index_attribute):
 
585
        """Convert a list of packs to an index pack map and index list.
 
586
 
 
587
        :param index_attribute: The attribute that the desired index is found
 
588
            on.
 
589
        :return: A tuple (map, list) where map contains the dict from
 
590
            index:pack_tuple, and list contains the indices in the preferred
 
591
            access order.
 
592
        """
 
593
        indices = []
 
594
        pack_map = {}
 
595
        for pack_obj in self.packs:
 
596
            index = getattr(pack_obj, index_attribute)
 
597
            indices.append(index)
 
598
            pack_map[index] = pack_obj
 
599
        return pack_map, indices
 
600
 
 
601
    def _index_contents(self, indices, key_filter=None):
 
602
        """Get an iterable of the index contents from a pack_map.
 
603
 
 
604
        :param indices: The list of indices to query
 
605
        :param key_filter: An optional filter to limit the keys returned.
 
606
        """
 
607
        all_index = CombinedGraphIndex(indices)
 
608
        if key_filter is None:
 
609
            return all_index.iter_all_entries()
 
610
        else:
 
611
            return all_index.iter_entries(key_filter)
 
612
 
571
613
    def pack(self, pb=None):
572
614
        """Create a new pack by reading data from other packs.
573
615
 
609
651
 
610
652
    def open_pack(self):
611
653
        """Open a pack for the pack we are creating."""
612
 
        return NewPack(self._pack_collection._upload_transport,
613
 
            self._pack_collection._index_transport,
614
 
            self._pack_collection._pack_transport, upload_suffix=self.suffix,
615
 
            file_mode=self._pack_collection.repo.bzrdir._get_file_mode(),
616
 
            index_builder_class=self._pack_collection._index_builder_class,
617
 
            index_class=self._pack_collection._index_class)
 
654
        return NewPack(self._pack_collection, upload_suffix=self.suffix,
 
655
                file_mode=self._pack_collection.repo.bzrdir._get_file_mode())
 
656
 
 
657
    def _update_pack_order(self, entries, index_to_pack_map):
 
658
        """Determine how we want our packs to be ordered.
 
659
 
 
660
        This changes the sort order of the self.packs list so that packs unused
 
661
        by 'entries' will be at the end of the list, so that future requests
 
662
        can avoid probing them.  Used packs will be at the front of the
 
663
        self.packs list, in the order of their first use in 'entries'.
 
664
 
 
665
        :param entries: A list of (index, ...) tuples
 
666
        :param index_to_pack_map: A mapping from index objects to pack objects.
 
667
        """
 
668
        packs = []
 
669
        seen_indexes = set()
 
670
        for entry in entries:
 
671
            index = entry[0]
 
672
            if index not in seen_indexes:
 
673
                packs.append(index_to_pack_map[index])
 
674
                seen_indexes.add(index)
 
675
        if len(packs) == len(self.packs):
 
676
            if 'pack' in debug.debug_flags:
 
677
                mutter('Not changing pack list, all packs used.')
 
678
            return
 
679
        seen_packs = set(packs)
 
680
        for pack in self.packs:
 
681
            if pack not in seen_packs:
 
682
                packs.append(pack)
 
683
                seen_packs.add(pack)
 
684
        if 'pack' in debug.debug_flags:
 
685
            old_names = [p.access_tuple()[1] for p in self.packs]
 
686
            new_names = [p.access_tuple()[1] for p in packs]
 
687
            mutter('Reordering packs\nfrom: %s\n  to: %s',
 
688
                   old_names, new_names)
 
689
        self.packs = packs
618
690
 
619
691
    def _copy_revision_texts(self):
620
692
        """Copy revision data to the new pack."""
624
696
        else:
625
697
            revision_keys = None
626
698
        # select revision keys
627
 
        revision_index_map = self._pack_collection._packs_list_to_pack_map_and_index_list(
628
 
            self.packs, 'revision_index')[0]
629
 
        revision_nodes = self._pack_collection._index_contents(revision_index_map, revision_keys)
 
699
        revision_index_map, revision_indices = self._pack_map_and_index_list(
 
700
            'revision_index')
 
701
        revision_nodes = self._index_contents(revision_indices, revision_keys)
 
702
        revision_nodes = list(revision_nodes)
 
703
        self._update_pack_order(revision_nodes, revision_index_map)
630
704
        # copy revision keys and adjust values
631
705
        self.pb.update("Copying revision texts", 1)
632
706
        total_items, readv_group_iter = self._revision_node_readv(revision_nodes)
652
726
        # querying for keys here could introduce a bug where an inventory item
653
727
        # is missed, so do not change it to query separately without cross
654
728
        # checking like the text key check below.
655
 
        inventory_index_map = self._pack_collection._packs_list_to_pack_map_and_index_list(
656
 
            self.packs, 'inventory_index')[0]
657
 
        inv_nodes = self._pack_collection._index_contents(inventory_index_map, inv_keys)
 
729
        inventory_index_map, inventory_indices = self._pack_map_and_index_list(
 
730
            'inventory_index')
 
731
        inv_nodes = self._index_contents(inventory_indices, inv_keys)
658
732
        # copy inventory keys and adjust values
659
733
        # XXX: Should be a helper function to allow different inv representation
660
734
        # at this point.
704
778
            self.new_pack.text_index, readv_group_iter, total_items))
705
779
        self._log_copied_texts()
706
780
 
707
 
    def _check_references(self):
708
 
        """Make sure our external refereneces are present."""
709
 
        external_refs = self.new_pack._external_compression_parents_of_texts()
710
 
        if external_refs:
711
 
            index = self._pack_collection.text_index.combined_index
712
 
            found_items = list(index.iter_entries(external_refs))
713
 
            if len(found_items) != len(external_refs):
714
 
                found_keys = set(k for idx, k, refs, value in found_items)
715
 
                missing_items = external_refs - found_keys
716
 
                missing_file_id, missing_revision_id = missing_items.pop()
717
 
                raise errors.RevisionNotPresent(missing_revision_id,
718
 
                                                missing_file_id)
719
 
 
720
781
    def _create_pack_from_packs(self):
721
782
        self.pb.update("Opening pack", 0, 5)
722
783
        self.new_pack = self.open_pack()
740
801
        self._copy_text_texts()
741
802
        # select signature keys
742
803
        signature_filter = self._revision_keys # same keyspace
743
 
        signature_index_map = self._pack_collection._packs_list_to_pack_map_and_index_list(
744
 
            self.packs, 'signature_index')[0]
745
 
        signature_nodes = self._pack_collection._index_contents(signature_index_map,
 
804
        signature_index_map, signature_indices = self._pack_map_and_index_list(
 
805
            'signature_index')
 
806
        signature_nodes = self._index_contents(signature_indices,
746
807
            signature_filter)
747
808
        # copy signature keys and adjust values
748
809
        self.pb.update("Copying signature texts", 4)
753
814
                time.ctime(), self._pack_collection._upload_transport.base, new_pack.random_name,
754
815
                new_pack.signature_index.key_count(),
755
816
                time.time() - new_pack.start_time)
756
 
        self._check_references()
 
817
        new_pack._check_references()
757
818
        if not self._use_pack(new_pack):
758
819
            new_pack.abort()
759
820
            return None
798
859
            # linear scan up the pack
799
860
            pack_readv_requests.sort()
800
861
            # copy the data
801
 
            transport, path = index_map[index]
 
862
            pack_obj = index_map[index]
 
863
            transport, path = pack_obj.access_tuple()
802
864
            reader = pack.make_readv_reader(transport, path,
803
865
                [offset[0:2] for offset in pack_readv_requests])
804
866
            for (names, read_func), (_1, _2, (key, eol_flag)) in \
842
904
        pb.update("Copied record", record_index, total_items)
843
905
        for index, readv_vector, node_vector in readv_group_iter:
844
906
            # copy the data
845
 
            transport, path = index_map[index]
 
907
            pack_obj = index_map[index]
 
908
            transport, path = pack_obj.access_tuple()
846
909
            reader = pack.make_readv_reader(transport, path, readv_vector)
847
910
            for (names, read_func), (key, eol_flag, references) in \
848
911
                izip(reader.iter_records(), node_vector):
866
929
                record_index += 1
867
930
 
868
931
    def _get_text_nodes(self):
869
 
        text_index_map = self._pack_collection._packs_list_to_pack_map_and_index_list(
870
 
            self.packs, 'text_index')[0]
871
 
        return text_index_map, self._pack_collection._index_contents(text_index_map,
 
932
        text_index_map, text_indices = self._pack_map_and_index_list(
 
933
            'text_index')
 
934
        return text_index_map, self._index_contents(text_indices,
872
935
            self._text_filter)
873
936
 
874
937
    def _least_readv_node_readv(self, nodes):
1109
1172
            output_texts.add_lines(key, parent_keys, text_lines,
1110
1173
                random_id=True, check_content=False)
1111
1174
        # 5) check that nothing inserted has a reference outside the keyspace.
1112
 
        missing_text_keys = self.new_pack._external_compression_parents_of_texts()
 
1175
        missing_text_keys = self.new_pack.text_index._external_references()
1113
1176
        if missing_text_keys:
1114
 
            raise errors.BzrError('Reference to missing compression parents %r'
 
1177
            raise errors.BzrCheckError('Reference to missing compression parents %r'
1115
1178
                % (missing_text_keys,))
1116
1179
        self._log_copied_texts()
1117
1180
 
1219
1282
            return False
1220
1283
        # XXX: the following may want to be a class, to pack with a given
1221
1284
        # policy.
1222
 
        mutter('Auto-packing repository %s, which has %d pack files, '
1223
 
            'containing %d revisions into %d packs.', self, total_packs,
1224
 
            total_revisions, self._max_pack_count(total_revisions))
1225
1285
        # determine which packs need changing
1226
1286
        pack_distribution = self.pack_distribution(total_revisions)
1227
1287
        existing_packs = []
1242
1302
            existing_packs.append((revision_count, pack))
1243
1303
        pack_operations = self.plan_autopack_combinations(
1244
1304
            existing_packs, pack_distribution)
 
1305
        num_new_packs = len(pack_operations)
 
1306
        num_old_packs = sum([len(po[1]) for po in pack_operations])
 
1307
        num_revs_affected = sum([po[0] for po in pack_operations])
 
1308
        mutter('Auto-packing repository %s, which has %d pack files, '
 
1309
            'containing %d revisions. Packing %d files into %d affecting %d'
 
1310
            ' revisions', self, total_packs, total_revisions, num_old_packs,
 
1311
            num_new_packs, num_revs_affected)
1245
1312
        self._execute_pack_operations(pack_operations)
1246
1313
        return True
1247
1314
 
1515
1582
        self._packs_by_name = {}
1516
1583
        self._packs_at_load = None
1517
1584
 
1518
 
    def _make_index_map(self, index_suffix):
1519
 
        """Return information on existing indices.
1520
 
 
1521
 
        :param suffix: Index suffix added to pack name.
1522
 
 
1523
 
        :returns: (pack_map, indices) where indices is a list of GraphIndex 
1524
 
        objects, and pack_map is a mapping from those objects to the 
1525
 
        pack tuple they describe.
1526
 
        """
1527
 
        # TODO: stop using this; it creates new indices unnecessarily.
1528
 
        self.ensure_loaded()
1529
 
        suffix_map = {'.rix': 'revision_index',
1530
 
            '.six': 'signature_index',
1531
 
            '.iix': 'inventory_index',
1532
 
            '.tix': 'text_index',
1533
 
        }
1534
 
        return self._packs_list_to_pack_map_and_index_list(self.all_packs(),
1535
 
            suffix_map[index_suffix])
1536
 
 
1537
 
    def _packs_list_to_pack_map_and_index_list(self, packs, index_attribute):
1538
 
        """Convert a list of packs to an index pack map and index list.
1539
 
 
1540
 
        :param packs: The packs list to process.
1541
 
        :param index_attribute: The attribute that the desired index is found
1542
 
            on.
1543
 
        :return: A tuple (map, list) where map contains the dict from
1544
 
            index:pack_tuple, and lsit contains the indices in the same order
1545
 
            as the packs list.
1546
 
        """
1547
 
        indices = []
1548
 
        pack_map = {}
1549
 
        for pack in packs:
1550
 
            index = getattr(pack, index_attribute)
1551
 
            indices.append(index)
1552
 
            pack_map[index] = (pack.pack_transport, pack.file_name())
1553
 
        return pack_map, indices
1554
 
 
1555
 
    def _index_contents(self, pack_map, key_filter=None):
1556
 
        """Get an iterable of the index contents from a pack_map.
1557
 
 
1558
 
        :param pack_map: A map from indices to pack details.
1559
 
        :param key_filter: An optional filter to limit the
1560
 
            keys returned.
1561
 
        """
1562
 
        indices = [index for index in pack_map.iterkeys()]
1563
 
        all_index = CombinedGraphIndex(indices)
1564
 
        if key_filter is None:
1565
 
            return all_index.iter_all_entries()
1566
 
        else:
1567
 
            return all_index.iter_entries(key_filter)
1568
 
 
1569
1585
    def _unlock_names(self):
1570
1586
        """Release the mutex around the pack-names index."""
1571
1587
        self.repo.control_files.unlock()
1708
1724
        # Do not permit preparation for writing if we're not in a 'write lock'.
1709
1725
        if not self.repo.is_write_locked():
1710
1726
            raise errors.NotWriteLocked(self)
1711
 
        self._new_pack = NewPack(self._upload_transport, self._index_transport,
1712
 
            self._pack_transport, upload_suffix='.pack',
1713
 
            file_mode=self.repo.bzrdir._get_file_mode(),
1714
 
            index_builder_class=self._index_builder_class,
1715
 
            index_class=self._index_class)
 
1727
        self._new_pack = NewPack(self, upload_suffix='.pack',
 
1728
            file_mode=self.repo.bzrdir._get_file_mode())
1716
1729
        # allow writing: queue writes to a new index
1717
1730
        self.revision_index.add_writable_index(self._new_pack.revision_index,
1718
1731
            self._new_pack)
1735
1748
            try:
1736
1749
                self._new_pack.abort()
1737
1750
            finally:
 
1751
                # XXX: If we aborted while in the middle of finishing the write
 
1752
                # group, _remove_pack_indices can fail because the indexes are
 
1753
                # already gone.  If they're not there we shouldn't fail in this
 
1754
                # case.  -- mbp 20081113
1738
1755
                self._remove_pack_indices(self._new_pack)
1739
1756
                self._new_pack = None
1740
1757
        self.repo._text_knit = None
2296
2313
        return xml7.serializer_v7
2297
2314
 
2298
2315
    def _get_matching_bzrdir(self):
2299
 
        return bzrdir.format_registry.make_bzrdir(
 
2316
        matching = bzrdir.format_registry.make_bzrdir(
2300
2317
            '1.6.1-rich-root')
 
2318
        matching.repository_format = self
 
2319
        return matching
2301
2320
 
2302
2321
    def _ignore_setting_bzrdir(self, format):
2303
2322
        pass