~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.

Show diffs side-by-side

added added

removed removed

Lines of Context:
408
408
        return self._get_delta(ie, basis_inv, path), True
409
409
 
410
410
    def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
411
 
        versionedfile = self.repository.weave_store.get_weave_or_empty(
412
 
            file_id, self.repository.get_transaction())
413
 
        # Don't change this to add_lines - add_lines_with_ghosts is cheaper
414
 
        # than add_lines, and allows committing when a parent is ghosted for
415
 
        # some reason.
416
411
        # Note: as we read the content directly from the tree, we know its not
417
412
        # been turned into unicode or badly split - but a broken tree
418
413
        # implementation could give us bad output from readlines() so this is
419
414
        # not a guarantee of safety. What would be better is always checking
420
415
        # the content during test suite execution. RBC 20070912
421
 
        return versionedfile.add_lines_with_ghosts(
422
 
            self._new_revision_id, parents, new_lines,
 
416
        parent_keys = tuple((file_id, parent) for parent in parents)
 
417
        return self.repository.texts.add_lines(
 
418
            (file_id, self._new_revision_id), parent_keys, new_lines,
423
419
            nostore_sha=nostore_sha, random_id=self.random_revid,
424
420
            check_content=False)[0:2]
425
421
 
453
449
    The Repository builds on top of Stores and a Transport, which respectively 
454
450
    describe the disk data format and the way of accessing the (possibly 
455
451
    remote) disk.
 
452
 
 
453
    :ivar revisions: A bzrlib.versionedfile.VersionedFiles instance containing
 
454
        the serialised revisions for the repository. This can be used to obtain
 
455
        revision graph information or to access raw serialised revisions.
 
456
        The result of trying to insert data into the repository via this store
 
457
        is undefined: it should be considered read-only except for implementors
 
458
        of repositories.
456
459
    """
457
460
 
458
461
    # What class to use for a CommitBuilder. Often its simpler to change this
510
513
        if inv.root is None:
511
514
            raise AssertionError()
512
515
        inv_lines = self._serialise_inventory_to_lines(inv)
513
 
        inv_vf = self.get_inventory_weave()
514
 
        return self._inventory_add_lines(inv_vf, revision_id, parents,
 
516
        return self._inventory_add_lines(revision_id, parents,
515
517
            inv_lines, check_content=False)
516
518
 
517
 
    def _inventory_add_lines(self, inv_vf, revision_id, parents, lines,
 
519
    def _inventory_add_lines(self, revision_id, parents, lines,
518
520
        check_content=True):
519
521
        """Store lines in inv_vf and return the sha1 of the inventory."""
520
 
        final_parents = []
521
 
        for parent in parents:
522
 
            if parent in inv_vf:
523
 
                final_parents.append(parent)
524
 
        return inv_vf.add_lines(revision_id, final_parents, lines,
 
522
        parents = [(parent,) for parent in parents]
 
523
        return self.inventories.add_lines((revision_id,), parents, lines,
525
524
            check_content=check_content)[0]
526
525
 
527
526
    def add_revision(self, revision_id, rev, inv=None, config=None):
544
543
            plaintext = Testament(rev, inv).as_short_text()
545
544
            self.store_revision_signature(
546
545
                gpg.GPGStrategy(config), plaintext, revision_id)
547
 
        inventory_vf = self.get_inventory_weave()
548
 
        if not revision_id in inventory_vf:
 
546
        # check inventory present
 
547
        if not self.inventories.get_parent_map([(revision_id,)]):
549
548
            if inv is None:
550
549
                raise errors.WeaveRevisionNotPresent(revision_id,
551
 
                                                     inventory_vf)
 
550
                                                     self.inventories)
552
551
            else:
553
552
                # yes, this is not suitable for adding with ghosts.
554
553
                rev.inventory_sha1 = self.add_inventory(revision_id, inv,
555
554
                                                        rev.parent_ids)
556
555
        else:
557
 
            rev.inventory_sha1 = inventory_vf.get_sha1s([revision_id])[0]
558
 
        self._revision_store.add_revision(rev, self.get_transaction())
 
556
            rev.inventory_sha1 = self.inventories.get_sha1s([(revision_id,)])[0]
 
557
        self._add_revision(rev)
559
558
 
560
 
    def _add_revision_text(self, revision_id, text):
561
 
        revision = self._revision_store._serializer.read_revision_from_string(
562
 
            text)
563
 
        self._revision_store._add_revision(revision, StringIO(text),
564
 
                                           self.get_transaction())
 
559
    def _add_revision(self, revision):
 
560
        text = self._serializer.write_revision_to_string(revision)
 
561
        key = (revision.revision_id,)
 
562
        parents = tuple((parent,) for parent in revision.parent_ids)
 
563
        self.revisions.add_lines(key, parents, osutils.split_lines(text))
565
564
 
566
565
    def all_revision_ids(self):
567
566
        """Returns a list of all the revision ids in the repository. 
607
606
        """Construct the current default format repository in a_bzrdir."""
608
607
        return RepositoryFormat.get_default_format().initialize(a_bzrdir)
609
608
 
610
 
    def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
 
609
    def __init__(self, _format, a_bzrdir, control_files):
611
610
        """instantiate a Repository.
612
611
 
613
612
        :param _format: The format of the repository on disk.
614
613
        :param a_bzrdir: The BzrDir of the repository.
 
614
        :param revisions: The revisions store for the repository.
615
615
 
616
616
        In the future we will have a single api for all stores for
617
617
        getting file texts, inventories and revisions, then
622
622
        # the following are part of the public API for Repository:
623
623
        self.bzrdir = a_bzrdir
624
624
        self.control_files = control_files
625
 
        self._revision_store = _revision_store
626
 
        # backwards compatibility
627
 
        self.weave_store = text_store
 
625
        self._transport = control_files._transport
628
626
        # for tests
629
627
        self._reconcile_does_inventory_gc = True
630
628
        self._reconcile_fixes_text_parents = False
631
629
        self._reconcile_backsup_inventory = True
632
630
        # not right yet - should be more semantically clear ? 
633
631
        # 
634
 
        self.control_store = control_store
635
 
        self.control_weaves = control_store
636
632
        # TODO: make sure to construct the right store classes, etc, depending
637
633
        # on whether escaping is required.
638
634
        self._warn_if_deprecated()
762
758
                last_revision.timezone)
763
759
 
764
760
        # now gather global repository information
 
761
        # XXX: This is available for many repos regardless of listability.
765
762
        if self.bzrdir.root_transport.listable():
766
 
            c, t = self._revision_store.total_size(self.get_transaction())
767
 
            result['revisions'] = c
768
 
            result['size'] = t
 
763
            # XXX: do we want to __define len__() ?
 
764
            result['revisions'] = len(self.revisions.keys())
 
765
            # result['size'] = t
769
766
        return result
770
767
 
771
768
    def find_branches(self, using=False):
811
808
                branches.extend(repository.find_branches())
812
809
        return branches
813
810
 
814
 
    def get_data_stream(self, revision_ids):
815
 
        raise NotImplementedError(self.get_data_stream)
816
 
 
817
 
    def get_data_stream_for_search(self, search_result):
818
 
        """Get a data stream that can be inserted to a repository.
819
 
 
820
 
        :param search_result: A bzrlib.graph.SearchResult selecting the
821
 
            revisions to get.
822
 
        :return: A data stream that can be inserted into a repository using
823
 
            insert_data_stream.
824
 
        """
825
 
        raise NotImplementedError(self.get_data_stream_for_search)
826
 
 
827
 
    def insert_data_stream(self, stream):
828
 
        """XXX What does this really do? 
829
 
        
830
 
        Is it a substitute for fetch? 
831
 
        Should it manage its own write group ?
832
 
        """
833
 
        for item_key, bytes in stream:
834
 
            if item_key[0] == 'file':
835
 
                (file_id,) = item_key[1:]
836
 
                knit = self.weave_store.get_weave_or_empty(
837
 
                    file_id, self.get_transaction())
838
 
            elif item_key == ('inventory',):
839
 
                knit = self.get_inventory_weave()
840
 
            elif item_key == ('revisions',):
841
 
                knit = self._revision_store.get_revision_file(
842
 
                    self.get_transaction())
843
 
            elif item_key == ('signatures',):
844
 
                knit = self._revision_store.get_signature_file(
845
 
                    self.get_transaction())
846
 
            else:
847
 
                raise errors.RepositoryDataStreamError(
848
 
                    "Unrecognised data stream key '%s'" % (item_key,))
849
 
            decoded_list = bencode.bdecode(bytes)
850
 
            format = decoded_list.pop(0)
851
 
            data_list = []
852
 
            knit_bytes = ''
853
 
            for version, options, parents, some_bytes in decoded_list:
854
 
                data_list.append((version, options, len(some_bytes), parents))
855
 
                knit_bytes += some_bytes
856
 
            buffer = StringIO(knit_bytes)
857
 
            def reader_func(count):
858
 
                if count is None:
859
 
                    return buffer.read()
860
 
                else:
861
 
                    return buffer.read(count)
862
 
            knit.insert_data_stream(
863
 
                (format, data_list, reader_func))
864
 
 
865
811
    @needs_read_lock
866
812
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
867
813
        """Return the revision ids that other has that this does not.
1057
1003
        """True if this repository has a copy of the revision."""
1058
1004
        return revision_id in self.has_revisions((revision_id,))
1059
1005
 
 
1006
    @needs_read_lock
1060
1007
    def has_revisions(self, revision_ids):
1061
1008
        """Probe to find out the presence of multiple revisions.
1062
1009
 
1063
1010
        :param revision_ids: An iterable of revision_ids.
1064
1011
        :return: A set of the revision_ids that were present.
1065
1012
        """
1066
 
        raise NotImplementedError(self.has_revisions)
1067
 
 
1068
 
        return self._revision_store.has_revision_id(revision_id,
1069
 
                                                    self.get_transaction())
 
1013
        parent_map = self.revisions.get_parent_map(
 
1014
            [(rev_id,) for rev_id in revision_ids])
 
1015
        result = set()
 
1016
        if _mod_revision.NULL_REVISION in revision_ids:
 
1017
            result.add(_mod_revision.NULL_REVISION)
 
1018
        result.update([key[0] for key in parent_map])
 
1019
        return result
1070
1020
 
1071
1021
    @needs_read_lock
1072
1022
    def get_revision(self, revision_id):
1095
1045
        for rev_id in revision_ids:
1096
1046
            if not rev_id or not isinstance(rev_id, basestring):
1097
1047
                raise errors.InvalidRevisionId(revision_id=rev_id, branch=self)
1098
 
        revs = self._revision_store.get_revisions(revision_ids,
1099
 
                                                  self.get_transaction())
1100
 
        return revs
 
1048
        keys = [(key,) for key in revision_ids]
 
1049
        stream = self.revisions.get_record_stream(keys, 'unordered', True)
 
1050
        revs = {}
 
1051
        for record in stream:
 
1052
            if record.storage_kind == 'absent':
 
1053
                raise errors.NoSuchRevision(self, record.key[0])
 
1054
            text = record.get_bytes_as('fulltext')
 
1055
            rev = self._serializer.read_revision_from_string(text)
 
1056
            revs[record.key[0]] = rev
 
1057
        return [revs[revid] for revid in revision_ids]
1101
1058
 
1102
1059
    @needs_read_lock
1103
1060
    def get_revision_xml(self, revision_id):
1107
1064
        rev = self.get_revision(revision_id)
1108
1065
        rev_tmp = StringIO()
1109
1066
        # the current serializer..
1110
 
        self._revision_store._serializer.write_revision(rev, rev_tmp)
 
1067
        self._serializer.write_revision(rev, rev_tmp)
1111
1068
        rev_tmp.seek(0)
1112
1069
        return rev_tmp.getvalue()
1113
1070
 
1148
1105
 
1149
1106
    @needs_write_lock
1150
1107
    def add_signature_text(self, revision_id, signature):
1151
 
        self._revision_store.add_revision_signature_text(revision_id,
1152
 
                                                         signature,
1153
 
                                                         self.get_transaction())
 
1108
        self.signatures.add_lines((revision_id,), (),
 
1109
            osutils.split_lines(signature))
1154
1110
 
1155
1111
    def find_text_key_references(self):
1156
1112
        """Find the text key references within the repository.
1163
1119
            revision_id that they contain. The inventory texts from all present
1164
1120
            revision ids are assessed to generate this report.
1165
1121
        """
1166
 
        revision_ids = self.all_revision_ids()
1167
 
        w = self.get_inventory_weave()
 
1122
        revision_keys = self.revisions.keys()
 
1123
        w = self.inventories
1168
1124
        pb = ui.ui_factory.nested_progress_bar()
1169
1125
        try:
1170
1126
            return self._find_text_key_references_from_xml_inventory_lines(
1171
 
                w.iter_lines_added_or_present_in_versions(revision_ids, pb=pb))
 
1127
                w.iter_lines_added_or_present_in_keys(revision_keys, pb=pb))
1172
1128
        finally:
1173
1129
            pb.finished()
1174
1130
 
1211
1167
        search = self._file_ids_altered_regex.search
1212
1168
        unescape = _unescape_xml
1213
1169
        setdefault = result.setdefault
1214
 
        for line, version_id in line_iterator:
 
1170
        for line, line_key in line_iterator:
1215
1171
            match = search(line)
1216
1172
            if match is None:
1217
1173
                continue
1248
1204
 
1249
1205
            key = (file_id, revision_id)
1250
1206
            setdefault(key, False)
1251
 
            if revision_id == version_id:
 
1207
            if revision_id == line_key[-1]:
1252
1208
                result[key] = True
1253
1209
        return result
1254
1210
 
1269
1225
        """
1270
1226
        result = {}
1271
1227
        setdefault = result.setdefault
1272
 
        for file_id, revision_id in \
 
1228
        for key in \
1273
1229
            self._find_text_key_references_from_xml_inventory_lines(
1274
1230
                line_iterator).iterkeys():
1275
1231
            # once data is all ensured-consistent; then this is
1276
1232
            # if revision_id == version_id
1277
 
            if revision_id in revision_ids:
1278
 
                setdefault(file_id, set()).add(revision_id)
 
1233
            if key[-1:] in revision_ids:
 
1234
                setdefault(key[0], set()).add(key[-1])
1279
1235
        return result
1280
1236
 
1281
1237
    def fileids_altered_by_revision_ids(self, revision_ids, _inv_weave=None):
1288
1244
        revision_ids. Each altered file-ids has the exact revision_ids that
1289
1245
        altered it listed explicitly.
1290
1246
        """
1291
 
        selected_revision_ids = set(revision_ids)
1292
 
        w = _inv_weave or self.get_inventory_weave()
 
1247
        selected_keys = set((revid,) for revid in revision_ids)
 
1248
        w = _inv_weave or self.inventories
1293
1249
        pb = ui.ui_factory.nested_progress_bar()
1294
1250
        try:
1295
1251
            return self._find_file_ids_from_xml_inventory_lines(
1296
 
                w.iter_lines_added_or_present_in_versions(
1297
 
                    selected_revision_ids, pb=pb),
1298
 
                selected_revision_ids)
 
1252
                w.iter_lines_added_or_present_in_keys(
 
1253
                    selected_keys, pb=pb),
 
1254
                selected_keys)
1299
1255
        finally:
1300
1256
            pb.finished()
1301
1257
 
1312
1268
 
1313
1269
        bytes_iterator is an iterable of bytestrings for the file.  The
1314
1270
        kind of iterable and length of the bytestrings are unspecified, but for
1315
 
        this implementation, it is a list of lines produced by
1316
 
        VersionedFile.get_lines().
 
1271
        this implementation, it is a list of bytes produced by
 
1272
        VersionedFile.get_record_stream().
1317
1273
 
1318
1274
        :param desired_files: a list of (file_id, revision_id, identifier)
1319
1275
            triples
1320
1276
        """
1321
1277
        transaction = self.get_transaction()
 
1278
        text_keys = {}
1322
1279
        for file_id, revision_id, callable_data in desired_files:
1323
 
            try:
1324
 
                weave = self.weave_store.get_weave(file_id, transaction)
1325
 
            except errors.NoSuchFile:
1326
 
                raise errors.NoSuchIdInRepository(self, file_id)
1327
 
            yield callable_data, weave.get_lines(revision_id)
 
1280
            text_keys[(file_id, revision_id)] = callable_data
 
1281
        for record in self.texts.get_record_stream(text_keys, 'unordered', True):
 
1282
            if record.storage_kind == 'absent':
 
1283
                raise errors.RevisionNotPresent(record.key, self)
 
1284
            yield text_keys[record.key], record.get_bytes_as('fulltext')
1328
1285
 
1329
1286
    def _generate_text_key_index(self, text_key_references=None,
1330
1287
        ancestors=None):
1447
1404
        # maybe this generator should explicitly have the contract that it
1448
1405
        # should not be iterated until the previously yielded item has been
1449
1406
        # processed?
1450
 
        inv_w = self.get_inventory_weave()
 
1407
        inv_w = self.inventories
1451
1408
 
1452
1409
        # file ids that changed
1453
1410
        file_ids = self.fileids_altered_by_revision_ids(revision_ids, inv_w)
1481
1438
        yield ("revisions", None, revision_ids)
1482
1439
 
1483
1440
    @needs_read_lock
1484
 
    def get_inventory_weave(self):
1485
 
        return self.control_weaves.get_weave('inventory',
1486
 
            self.get_transaction())
1487
 
 
1488
 
    @needs_read_lock
1489
1441
    def get_inventory(self, revision_id):
1490
1442
        """Get Inventory object by revision id."""
1491
1443
        return self.iter_inventories([revision_id]).next()
1506
1458
 
1507
1459
    def _iter_inventories(self, revision_ids):
1508
1460
        """single-document based inventory iteration."""
1509
 
        texts = self.get_inventory_weave().get_texts(revision_ids)
1510
 
        for text, revision_id in zip(texts, revision_ids):
 
1461
        for text, revision_id in self._iter_inventory_xmls(revision_ids):
1511
1462
            yield self.deserialise_inventory(revision_id, text)
1512
1463
 
 
1464
    def _iter_inventory_xmls(self, revision_ids):
 
1465
        keys = [(revision_id,) for revision_id in revision_ids]
 
1466
        stream = self.inventories.get_record_stream(keys, 'unordered', True)
 
1467
        texts = {}
 
1468
        for record in stream:
 
1469
            if record.storage_kind != 'absent':
 
1470
                texts[record.key] = record.get_bytes_as('fulltext')
 
1471
            else:
 
1472
                raise errors.NoSuchRevision(self, record.key)
 
1473
        for key in keys:
 
1474
            yield texts[key], key[-1]
 
1475
 
1513
1476
    def deserialise_inventory(self, revision_id, xml):
1514
1477
        """Transform the xml into an inventory object. 
1515
1478
 
1534
1497
    @needs_read_lock
1535
1498
    def get_inventory_xml(self, revision_id):
1536
1499
        """Get inventory XML as a file object."""
 
1500
        texts = self._iter_inventory_xmls([revision_id])
1537
1501
        try:
1538
 
            iw = self.get_inventory_weave()
1539
 
            return iw.get_text(revision_id)
1540
 
        except IndexError:
 
1502
            text, revision_id = texts.next()
 
1503
        except StopIteration:
1541
1504
            raise errors.HistoryMissing(self, 'inventory', revision_id)
 
1505
        return text
1542
1506
 
1543
1507
    @needs_read_lock
1544
1508
    def get_inventory_sha1(self, revision_id):
1645
1609
            return [None]
1646
1610
        if not self.has_revision(revision_id):
1647
1611
            raise errors.NoSuchRevision(self, revision_id)
1648
 
        w = self.get_inventory_weave()
1649
 
        candidates = w.get_ancestry(revision_id, topo_sorted)
1650
 
        return [None] + candidates # self._eliminate_revisions_not_present(candidates)
 
1612
        graph = self.get_graph()
 
1613
        keys = set()
 
1614
        search = graph._make_breadth_first_searcher([revision_id])
 
1615
        while True:
 
1616
            try:
 
1617
                found, ghosts = search.next_with_ghosts()
 
1618
            except StopIteration:
 
1619
                break
 
1620
            keys.update(found)
 
1621
        if _mod_revision.NULL_REVISION in keys:
 
1622
            keys.remove(_mod_revision.NULL_REVISION)
 
1623
        if topo_sorted:
 
1624
            parent_map = graph.get_parent_map(keys)
 
1625
            keys = tsort.topo_sort(parent_map)
 
1626
        return [None] + list(keys)
1651
1627
 
1652
1628
    def pack(self):
1653
1629
        """Compress the data within the repository.
1683
1659
 
1684
1660
    @deprecated_method(symbol_versioning.one_five)
1685
1661
    def revision_parents(self, revision_id):
1686
 
        return self.get_inventory_weave().parent_names(revision_id)
 
1662
        return self.get_revision(revision_id).parent_ids
1687
1663
 
1688
1664
    @deprecated_method(symbol_versioning.one_one)
1689
1665
    def get_parents(self, revision_ids):
1763
1739
    @needs_read_lock
1764
1740
    def has_signature_for_revision_id(self, revision_id):
1765
1741
        """Query for a revision signature for revision_id in the repository."""
1766
 
        return self._revision_store.has_signature(revision_id,
1767
 
                                                  self.get_transaction())
 
1742
        if not self.has_revision(revision_id):
 
1743
            raise errors.NoSuchRevision(self, revision_id)
 
1744
        sig_present = (1 == len(
 
1745
            self.signatures.get_parent_map([(revision_id,)])))
 
1746
        return sig_present
1768
1747
 
1769
1748
    @needs_read_lock
1770
1749
    def get_signature_text(self, revision_id):
1771
1750
        """Return the text for a signature."""
1772
 
        return self._revision_store.get_signature_text(revision_id,
1773
 
                                                       self.get_transaction())
 
1751
        stream = self.signatures.get_record_stream([(revision_id,)],
 
1752
            'unordered', True)
 
1753
        record = stream.next()
 
1754
        if record.storage_kind == 'absent':
 
1755
            raise errors.NoSuchRevision(self, revision_id)
 
1756
        return record.get_bytes_as('fulltext')
1774
1757
 
1775
1758
    @needs_read_lock
1776
1759
    def check(self, revision_ids=None):
1903
1886
        path, root = entries.next()
1904
1887
        if root.revision != rev.revision_id:
1905
1888
            raise errors.IncompatibleRevision(repr(repository))
 
1889
    text_keys = {}
 
1890
    for path, ie in entries:
 
1891
        text_keys[(ie.file_id, ie.revision)] = ie
 
1892
    text_parent_map = repository.texts.get_parent_map(text_keys)
 
1893
    missing_texts = set(text_keys) - set(text_parent_map)
1906
1894
    # Add the texts that are not already present
1907
 
    for path, ie in entries:
1908
 
        w = repository.weave_store.get_weave_or_empty(ie.file_id,
1909
 
                repository.get_transaction())
1910
 
        if ie.revision not in w:
1911
 
            text_parents = []
1912
 
            # FIXME: TODO: The following loop *may* be overlapping/duplicate
1913
 
            # with InventoryEntry.find_previous_heads(). if it is, then there
1914
 
            # is a latent bug here where the parents may have ancestors of each
1915
 
            # other. RBC, AB
1916
 
            for revision, tree in parent_trees.iteritems():
1917
 
                if ie.file_id not in tree:
1918
 
                    continue
1919
 
                parent_id = tree.inventory[ie.file_id].revision
1920
 
                if parent_id in text_parents:
1921
 
                    continue
1922
 
                text_parents.append(parent_id)
1923
 
                    
1924
 
            vfile = repository.weave_store.get_weave_or_empty(ie.file_id, 
1925
 
                repository.get_transaction())
1926
 
            lines = revision_tree.get_file(ie.file_id).readlines()
1927
 
            vfile.add_lines(rev.revision_id, text_parents, lines)
 
1895
    for text_key in missing_texts:
 
1896
        ie = text_keys[text_key]
 
1897
        text_parents = []
 
1898
        # FIXME: TODO: The following loop overlaps/duplicates that done by
 
1899
        # commit to determine parents. There is a latent/real bug here where
 
1900
        # the parents inserted are not those commit would do - in particular
 
1901
        # they are not filtered by heads(). RBC, AB
 
1902
        for revision, tree in parent_trees.iteritems():
 
1903
            if ie.file_id not in tree:
 
1904
                continue
 
1905
            parent_id = tree.inventory[ie.file_id].revision
 
1906
            if parent_id in text_parents:
 
1907
                continue
 
1908
            text_parents.append((ie.file_id, parent_id))
 
1909
        lines = revision_tree.get_file(ie.file_id).readlines()
 
1910
        repository.texts.add_lines(text_key, text_parents, lines)
1928
1911
    try:
1929
1912
        # install the inventory
1930
1913
        repository.add_inventory(rev.revision_id, inv, present_parents)
1938
1921
class MetaDirRepository(Repository):
1939
1922
    """Repositories in the new meta-dir layout."""
1940
1923
 
1941
 
    def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
1942
 
        super(MetaDirRepository, self).__init__(_format,
1943
 
                                                a_bzrdir,
1944
 
                                                control_files,
1945
 
                                                _revision_store,
1946
 
                                                control_store,
1947
 
                                                text_store)
 
1924
    def __init__(self, _format, a_bzrdir, control_files):
 
1925
        super(MetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
1948
1926
        dir_mode = self.control_files._dir_mode
1949
1927
        file_mode = self.control_files._file_mode
1950
1928
 
1979
1957
class MetaDirVersionedFileRepository(MetaDirRepository):
1980
1958
    """Repositories in a meta-dir, that work via versioned file objects."""
1981
1959
 
1982
 
    def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
 
1960
    def __init__(self, _format, a_bzrdir, control_files):
1983
1961
        super(MetaDirVersionedFileRepository, self).__init__(_format, a_bzrdir,
1984
 
            control_files, _revision_store, control_store, text_store)
1985
 
        _revision_store.get_scope = self.get_transaction
1986
 
        control_store.get_scope = self.get_transaction
1987
 
        text_store.get_scope = self.get_transaction
 
1962
            control_files)
1988
1963
 
1989
1964
 
1990
1965
class RepositoryFormatRegistry(registry.Registry):
2086
2061
        from bzrlib import bzrdir
2087
2062
        return bzrdir.format_registry.make_bzrdir('default').repository_format
2088
2063
 
2089
 
    def _get_control_store(self, repo_transport, control_files):
2090
 
        """Return the control store for this repository."""
2091
 
        raise NotImplementedError(self._get_control_store)
2092
 
 
2093
2064
    def get_format_string(self):
2094
2065
        """Return the ASCII format string that identifies this format.
2095
2066
        
2102
2073
        """Return the short description for this format."""
2103
2074
        raise NotImplementedError(self.get_format_description)
2104
2075
 
2105
 
    def _get_revision_store(self, repo_transport, control_files):
2106
 
        """Return the revision store object for this a_bzrdir."""
2107
 
        raise NotImplementedError(self._get_revision_store)
2108
 
 
2109
 
    def _get_text_rev_store(self,
2110
 
                            transport,
2111
 
                            control_files,
2112
 
                            name,
2113
 
                            compressed=True,
2114
 
                            prefixed=False,
2115
 
                            serializer=None):
2116
 
        """Common logic for getting a revision store for a repository.
2117
 
        
2118
 
        see self._get_revision_store for the subclass-overridable method to 
2119
 
        get the store for a repository.
2120
 
        """
2121
 
        from bzrlib.store.revision.text import TextRevisionStore
2122
 
        dir_mode = control_files._dir_mode
2123
 
        file_mode = control_files._file_mode
2124
 
        text_store = TextStore(transport.clone(name),
2125
 
                              prefixed=prefixed,
2126
 
                              compressed=compressed,
2127
 
                              dir_mode=dir_mode,
2128
 
                              file_mode=file_mode)
2129
 
        _revision_store = TextRevisionStore(text_store, serializer)
2130
 
        return _revision_store
2131
 
 
2132
2076
    # TODO: this shouldn't be in the base class, it's specific to things that
2133
2077
    # use weaves or knits -- mbp 20070207
2134
2078
    def _get_versioned_file_store(self,
2509
2453
        if self.source.control_files._transport.listable():
2510
2454
            pb = ui.ui_factory.nested_progress_bar()
2511
2455
            try:
2512
 
                self.target.weave_store.copy_all_ids(
2513
 
                    self.source.weave_store,
2514
 
                    pb=pb,
2515
 
                    from_transaction=self.source.get_transaction(),
2516
 
                    to_transaction=self.target.get_transaction())
 
2456
                self.target.texts.insert_record_stream(
 
2457
                    self.source.texts.get_record_stream(
 
2458
                        self.source.texts.keys(), 'topological', False))
2517
2459
                pb.update('copying inventory', 0, 1)
2518
 
                self.target.control_weaves.copy_multi(
2519
 
                    self.source.control_weaves, ['inventory'],
2520
 
                    from_transaction=self.source.get_transaction(),
2521
 
                    to_transaction=self.target.get_transaction())
2522
 
                self.target._revision_store.text_store.copy_all_ids(
2523
 
                    self.source._revision_store.text_store,
2524
 
                    pb=pb)
 
2460
                self.target.inventories.insert_record_stream(
 
2461
                    self.source.inventories.get_record_stream(
 
2462
                        self.source.inventories.keys(), 'topological', False))
 
2463
                self.target.signatures.insert_record_stream(
 
2464
                    self.source.signatures.get_record_stream(
 
2465
                        self.source.signatures.keys(),
 
2466
                        'unordered', True))
 
2467
                self.target.revisions.insert_record_stream(
 
2468
                    self.source.revisions.get_record_stream(
 
2469
                        self.source.revisions.keys(),
 
2470
                        'topological', True))
2525
2471
            finally:
2526
2472
                pb.finished()
2527
2473
        else:
2894
2840
        return len(revision_ids), 0
2895
2841
 
2896
2842
 
2897
 
class InterRemoteToOther(InterRepository):
2898
 
 
2899
 
    def __init__(self, source, target):
2900
 
        InterRepository.__init__(self, source, target)
2901
 
        self._real_inter = None
2902
 
 
2903
 
    @staticmethod
2904
 
    def is_compatible(source, target):
2905
 
        if not isinstance(source, remote.RemoteRepository):
2906
 
            return False
2907
 
        # Is source's model compatible with target's model?
2908
 
        source._ensure_real()
2909
 
        real_source = source._real_repository
2910
 
        if isinstance(real_source, remote.RemoteRepository):
2911
 
            raise NotImplementedError(
2912
 
                "We don't support remote repos backed by remote repos yet.")
2913
 
        return InterRepository._same_model(real_source, target)
2914
 
 
2915
 
    @needs_write_lock
2916
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2917
 
        """See InterRepository.fetch()."""
2918
 
        from bzrlib.fetch import RemoteToOtherFetcher
2919
 
        mutter("Using fetch logic to copy between %s(remote) and %s(%s)",
2920
 
               self.source, self.target, self.target._format)
2921
 
        # TODO: jam 20070210 This should be an assert, not a translate
2922
 
        revision_id = osutils.safe_revision_id(revision_id)
2923
 
        f = RemoteToOtherFetcher(to_repository=self.target,
2924
 
                                 from_repository=self.source,
2925
 
                                 last_revision=revision_id,
2926
 
                                 pb=pb, find_ghosts=find_ghosts)
2927
 
        return f.count_copied, f.failed_revisions
2928
 
 
2929
 
    @classmethod
2930
 
    def _get_repo_format_to_test(self):
2931
 
        return None
2932
 
 
2933
 
 
2934
2843
class InterOtherToRemote(InterRepository):
2935
2844
 
2936
2845
    def __init__(self, source, target):
2970
2879
InterRepository.register_optimiser(InterModel1and2)
2971
2880
InterRepository.register_optimiser(InterKnit1and2)
2972
2881
InterRepository.register_optimiser(InterPackRepo)
2973
 
InterRepository.register_optimiser(InterRemoteToOther)
2974
2882
InterRepository.register_optimiser(InterOtherToRemote)
2975
2883
 
2976
2884
 
3062
2970
        self.repository = repository
3063
2971
        self.text_index = self.repository._generate_text_key_index()
3064
2972
    
3065
 
    def calculate_file_version_parents(self, revision_id, file_id):
 
2973
    def calculate_file_version_parents(self, text_key):
3066
2974
        """Calculate the correct parents for a file version according to
3067
2975
        the inventories.
3068
2976
        """
3069
 
        parent_keys = self.text_index[(file_id, revision_id)]
 
2977
        parent_keys = self.text_index[text_key]
3070
2978
        if parent_keys == [_mod_revision.NULL_REVISION]:
3071
2979
            return ()
3072
 
        # strip the file_id, for the weave api
3073
 
        return tuple([revision_id for file_id, revision_id in parent_keys])
 
2980
        return tuple(parent_keys)
3074
2981
 
3075
 
    def check_file_version_parents(self, weave, file_id):
 
2982
    def check_file_version_parents(self, texts, progress_bar=None):
3076
2983
        """Check the parents stored in a versioned file are correct.
3077
2984
 
3078
2985
        It also detects file versions that are not referenced by their
3086
2993
            file, but not used by the corresponding inventory.
3087
2994
        """
3088
2995
        wrong_parents = {}
3089
 
        unused_versions = set()
3090
 
        versions = weave.versions()
3091
 
        parent_map = weave.get_parent_map(versions)
3092
 
        for num, revision_id in enumerate(versions):
 
2996
        self.file_ids = set([file_id for file_id, _ in
 
2997
            self.text_index.iterkeys()])
 
2998
        # text keys is now grouped by file_id
 
2999
        n_weaves = len(self.file_ids)
 
3000
        files_in_revisions = {}
 
3001
        revisions_of_files = {}
 
3002
        n_versions = len(self.text_index)
 
3003
        progress_bar.update('loading text store', 0, n_versions)
 
3004
        parent_map = self.repository.texts.get_parent_map(self.text_index)
 
3005
        # On unlistable transports this could well be empty/error...
 
3006
        text_keys = self.repository.texts.keys()
 
3007
        unused_keys = frozenset(text_keys) - set(self.text_index)
 
3008
        for num, key in enumerate(self.text_index.iterkeys()):
 
3009
            if progress_bar is not None:
 
3010
                progress_bar.update('checking text graph', num, n_versions)
 
3011
            correct_parents = self.calculate_file_version_parents(key)
3093
3012
            try:
3094
 
                correct_parents = self.calculate_file_version_parents(
3095
 
                    revision_id, file_id)
3096
 
            except KeyError:
3097
 
                # The version is not part of the used keys.
3098
 
                unused_versions.add(revision_id)
3099
 
            else:
3100
 
                try:
3101
 
                    knit_parents = tuple(parent_map[revision_id])
3102
 
                except errors.RevisionNotPresent:
3103
 
                    knit_parents = None
3104
 
                if correct_parents != knit_parents:
3105
 
                    wrong_parents[revision_id] = (knit_parents, correct_parents)
3106
 
        return wrong_parents, unused_versions
 
3013
                knit_parents = parent_map[key]
 
3014
            except errors.RevisionNotPresent:
 
3015
                # Missing text!
 
3016
                knit_parents = None
 
3017
            if correct_parents != knit_parents:
 
3018
                wrong_parents[key] = (knit_parents, correct_parents)
 
3019
        return wrong_parents, unused_keys
3107
3020
 
3108
3021
 
3109
3022
def _old_get_graph(repository, revision_id):