/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/repository.py

  • Committer: Aaron Bentley
  • Date: 2008-02-24 16:42:13 UTC
  • mfrom: (3234 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3235.
  • Revision ID: aaron@aaronbentley.com-20080224164213-eza1lzru5bwuwmmj
Merge with bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
495
495
        attempted.
496
496
        """
497
497
 
498
 
    @needs_write_lock
499
498
    def add_inventory(self, revision_id, inv, parents):
500
499
        """Add the inventory inv to the repository as revision_id.
501
500
        
502
501
        :param parents: The revision ids of the parents that revision_id
503
502
                        is known to have and are in the repository already.
504
503
 
505
 
        returns the sha1 of the serialized inventory.
 
504
        :returns: The validator(which is a sha1 digest, though what is sha'd is
 
505
            repository format specific) of the serialized inventory.
506
506
        """
507
507
        assert self.is_in_write_group()
508
508
        _mod_revision.check_not_reserved_id(revision_id)
525
525
        return inv_vf.add_lines(revision_id, final_parents, lines,
526
526
            check_content=check_content)[0]
527
527
 
528
 
    @needs_write_lock
529
528
    def add_revision(self, revision_id, rev, inv=None, config=None):
530
529
        """Add rev to the revision store as revision_id.
531
530
 
766
765
            result['size'] = t
767
766
        return result
768
767
 
 
768
    def find_branches(self, using=False):
 
769
        """Find branches underneath this repository.
 
770
 
 
771
        This will include branches inside other branches.
 
772
 
 
773
        :param using: If True, list only branches using this repository.
 
774
        """
 
775
        if using and not self.is_shared():
 
776
            try:
 
777
                return [self.bzrdir.open_branch()]
 
778
            except errors.NotBranchError:
 
779
                return []
 
780
        class Evaluator(object):
 
781
 
 
782
            def __init__(self):
 
783
                self.first_call = True
 
784
 
 
785
            def __call__(self, bzrdir):
 
786
                # On the first call, the parameter is always the bzrdir
 
787
                # containing the current repo.
 
788
                if not self.first_call:
 
789
                    try:
 
790
                        repository = bzrdir.open_repository()
 
791
                    except errors.NoRepositoryPresent:
 
792
                        pass
 
793
                    else:
 
794
                        return False, (None, repository)
 
795
                self.first_call = False
 
796
                try:
 
797
                    value = (bzrdir.open_branch(), None)
 
798
                except errors.NotBranchError:
 
799
                    value = (None, None)
 
800
                return True, value
 
801
 
 
802
        branches = []
 
803
        for branch, repository in bzrdir.BzrDir.find_bzrdirs(
 
804
                self.bzrdir.root_transport, evaluate=Evaluator()):
 
805
            if branch is not None:
 
806
                branches.append(branch)
 
807
            if not using and repository is not None:
 
808
                branches.extend(repository.find_branches())
 
809
        return branches
 
810
 
769
811
    def get_data_stream(self, revision_ids):
770
812
        raise NotImplementedError(self.get_data_stream)
771
813
 
 
814
    def get_data_stream_for_search(self, search_result):
 
815
        """Get a data stream that can be inserted to a repository.
 
816
 
 
817
        :param search_result: A bzrlib.graph.SearchResult selecting the
 
818
            revisions to get.
 
819
        :return: A data stream that can be inserted into a repository using
 
820
            insert_data_stream.
 
821
        """
 
822
        raise NotImplementedError(self.get_data_stream_for_search)
 
823
 
772
824
    def insert_data_stream(self, stream):
773
825
        """XXX What does this really do? 
774
826
        
789
841
                knit = self._revision_store.get_signature_file(
790
842
                    self.get_transaction())
791
843
            else:
792
 
                raise RepositoryDataStreamError(
 
844
                raise errors.RepositoryDataStreamError(
793
845
                    "Unrecognised data stream key '%s'" % (item_key,))
794
846
            decoded_list = bencode.bdecode(bytes)
795
847
            format = decoded_list.pop(0)
798
850
            for version, options, parents, some_bytes in decoded_list:
799
851
                data_list.append((version, options, len(some_bytes), parents))
800
852
                knit_bytes += some_bytes
 
853
            buffer = StringIO(knit_bytes)
 
854
            def reader_func(count):
 
855
                if count is None:
 
856
                    return buffer.read()
 
857
                else:
 
858
                    return buffer.read(count)
801
859
            knit.insert_data_stream(
802
 
                (format, data_list, StringIO(knit_bytes).read))
803
 
 
 
860
                (format, data_list, reader_func))
 
861
 
 
862
    @needs_read_lock
 
863
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
 
864
        """Return the revision ids that other has that this does not.
 
865
        
 
866
        These are returned in topological order.
 
867
 
 
868
        revision_id: only return revision ids included by revision_id.
 
869
        """
 
870
        return InterRepository.get(other, self).search_missing_revision_ids(
 
871
            revision_id, find_ghosts)
 
872
 
 
873
    @deprecated_method(symbol_versioning.one_two)
804
874
    @needs_read_lock
805
875
    def missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
806
876
        """Return the revision ids that other has that this does not.
809
879
 
810
880
        revision_id: only return revision ids included by revision_id.
811
881
        """
812
 
        return InterRepository.get(other, self).missing_revision_ids(
813
 
            revision_id, find_ghosts)
 
882
        keys =  self.search_missing_revision_ids(
 
883
            other, revision_id, find_ghosts).get_keys()
 
884
        other.lock_read()
 
885
        try:
 
886
            parents = other.get_graph().get_parent_map(keys)
 
887
        finally:
 
888
            other.unlock()
 
889
        return tsort.topo_sort(parents)
814
890
 
815
891
    @staticmethod
816
892
    def open(base):
976
1052
    @needs_read_lock
977
1053
    def has_revision(self, revision_id):
978
1054
        """True if this repository has a copy of the revision."""
979
 
        if 'evil' in debug.debug_flags:
980
 
            mutter_callsite(3, "has_revision is a LBYL symptom.")
 
1055
        return revision_id in self.has_revisions((revision_id,))
 
1056
 
 
1057
    def has_revisions(self, revision_ids):
 
1058
        """Probe to find out the presence of multiple revisions.
 
1059
 
 
1060
        :param revision_ids: An iterable of revision_ids.
 
1061
        :return: A set of the revision_ids that were present.
 
1062
        """
 
1063
        raise NotImplementedError(self.has_revisions)
 
1064
 
981
1065
        return self._revision_store.has_revision_id(revision_id,
982
1066
                                                    self.get_transaction())
983
1067
 
1407
1491
 
1408
1492
    @needs_read_lock
1409
1493
    def get_inventory(self, revision_id):
1410
 
        """Get Inventory object by hash."""
1411
 
        return self.deserialise_inventory(
1412
 
            revision_id, self.get_inventory_xml(revision_id))
 
1494
        """Get Inventory object by revision id."""
 
1495
        return self.iter_inventories([revision_id]).next()
 
1496
 
 
1497
    def iter_inventories(self, revision_ids):
 
1498
        """Get many inventories by revision_ids.
 
1499
 
 
1500
        This will buffer some or all of the texts used in constructing the
 
1501
        inventories in memory, but will only parse a single inventory at a
 
1502
        time.
 
1503
 
 
1504
        :return: An iterator of inventories.
 
1505
        """
 
1506
        assert None not in revision_ids
 
1507
        assert _mod_revision.NULL_REVISION not in revision_ids
 
1508
        return self._iter_inventories(revision_ids)
 
1509
 
 
1510
    def _iter_inventories(self, revision_ids):
 
1511
        """single-document based inventory iteration."""
 
1512
        texts = self.get_inventory_weave().get_texts(revision_ids)
 
1513
        for text, revision_id in zip(texts, revision_ids):
 
1514
            yield self.deserialise_inventory(revision_id, text)
1413
1515
 
1414
1516
    def deserialise_inventory(self, revision_id, xml):
1415
1517
        """Transform the xml into an inventory object. 
1417
1519
        :param revision_id: The expected revision id of the inventory.
1418
1520
        :param xml: A serialised inventory.
1419
1521
        """
1420
 
        return self._serializer.read_inventory_from_string(xml, revision_id)
 
1522
        result = self._serializer.read_inventory_from_string(xml, revision_id)
 
1523
        if result.revision_id != revision_id:
 
1524
            raise AssertionError('revision id mismatch %s != %s' % (
 
1525
                result.revision_id, revision_id))
 
1526
        return result
1421
1527
 
1422
1528
    def serialise_inventory(self, inv):
1423
1529
        return self._serializer.write_inventory_to_string(inv)
1583
1689
        """Return Tree for a revision on this branch.
1584
1690
 
1585
1691
        `revision_id` may not be None or 'null:'"""
1586
 
        assert None not in revision_ids
1587
 
        assert _mod_revision.NULL_REVISION not in revision_ids
1588
 
        texts = self.get_inventory_weave().get_texts(revision_ids)
1589
 
        for text, revision_id in zip(texts, revision_ids):
1590
 
            inv = self.deserialise_inventory(revision_id, text)
1591
 
            yield RevisionTree(self, inv, revision_id)
 
1692
        inventories = self.iter_inventories(revision_ids)
 
1693
        for inv in inventories:
 
1694
            yield RevisionTree(self, inv, inv.revision_id)
1592
1695
 
1593
1696
    @needs_read_lock
1594
1697
    def get_ancestry(self, revision_id, topo_sorted=True):
1654
1757
        parent_map = {}
1655
1758
        for revision_id in keys:
1656
1759
            if revision_id == _mod_revision.NULL_REVISION:
1657
 
                parent_map[revision_id] = []
 
1760
                parent_map[revision_id] = ()
1658
1761
            else:
1659
1762
                try:
1660
 
                    parent_ids = self.get_revision(revision_id).parent_ids
 
1763
                    parent_id_list = self.get_revision(revision_id).parent_ids
1661
1764
                except errors.NoSuchRevision:
1662
1765
                    pass
1663
1766
                else:
1664
 
                    if len(parent_ids) == 0:
1665
 
                        parent_ids = [_mod_revision.NULL_REVISION]
 
1767
                    if len(parent_id_list) == 0:
 
1768
                        parent_ids = (_mod_revision.NULL_REVISION,)
 
1769
                    else:
 
1770
                        parent_ids = tuple(parent_id_list)
1666
1771
                    parent_map[revision_id] = parent_ids
1667
1772
        return parent_map
1668
1773
 
1673
1778
        """Return the graph walker for this repository format"""
1674
1779
        parents_provider = self._make_parents_provider()
1675
1780
        if (other_repository is not None and
1676
 
            other_repository.bzrdir.transport.base !=
1677
 
            self.bzrdir.transport.base):
 
1781
            not self.has_same_location(other_repository)):
1678
1782
            parents_provider = graph._StackedParentsProvider(
1679
1783
                [parents_provider, other_repository._make_parents_provider()])
1680
1784
        return graph.Graph(parents_provider)
1683
1787
        """Return an object suitable for checking versioned files."""
1684
1788
        return _VersionedFileChecker(self)
1685
1789
 
 
1790
    def revision_ids_to_search_result(self, result_set):
 
1791
        """Convert a set of revision ids to a graph SearchResult."""
 
1792
        result_parents = set()
 
1793
        for parents in self.get_graph().get_parent_map(
 
1794
            result_set).itervalues():
 
1795
            result_parents.update(parents)
 
1796
        included_keys = result_set.intersection(result_parents)
 
1797
        start_keys = result_set.difference(included_keys)
 
1798
        exclude_keys = result_parents.difference(result_set)
 
1799
        result = graph.SearchResult(start_keys, exclude_keys,
 
1800
            len(result_set), result_set)
 
1801
        return result
 
1802
 
1686
1803
    @needs_write_lock
1687
1804
    def set_make_working_trees(self, new_value):
1688
1805
        """Set the policy flag for making working trees when creating branches.
1769
1886
        depend on the revision index being consistent.
1770
1887
        """
1771
1888
        raise NotImplementedError(self.revision_graph_can_have_wrong_parents)
1772
 
        
 
1889
 
 
1890
 
1773
1891
# remove these delegates a while after bzr 0.15
1774
1892
def __make_delegated(name, from_module):
1775
1893
    def _deprecated_repository_forwarder():
1809
1927
    install_revisions(repository, [(rev, revision_tree, None)])
1810
1928
 
1811
1929
 
1812
 
def install_revisions(repository, iterable):
 
1930
def install_revisions(repository, iterable, num_revisions=None, pb=None):
1813
1931
    """Install all revision data into a repository.
1814
1932
 
1815
1933
    Accepts an iterable of revision, tree, signature tuples.  The signature
1817
1935
    """
1818
1936
    repository.start_write_group()
1819
1937
    try:
1820
 
        for revision, revision_tree, signature in iterable:
 
1938
        for n, (revision, revision_tree, signature) in enumerate(iterable):
1821
1939
            _install_revision(repository, revision, revision_tree, signature)
 
1940
            if pb is not None:
 
1941
                pb.update('Transferring revisions', n + 1, num_revisions)
1822
1942
    except:
1823
1943
        repository.abort_write_group()
1824
1944
        raise
1970
2090
    # Set to True or False in derived classes. True indicates that the format
1971
2091
    # supports ghosts gracefully.
1972
2092
    supports_ghosts = None
 
2093
    # Can this repository be given external locations to lookup additional
 
2094
    # data. Set to True or False in derived classes.
 
2095
    supports_external_lookups = None
1973
2096
 
1974
2097
    def __str__(self):
1975
2098
        return "<%s>" % self.__class__.__name__
2114
2237
 
2115
2238
    rich_root_data = False
2116
2239
    supports_tree_reference = False
 
2240
    supports_external_lookups = False
2117
2241
    _matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2118
2242
 
2119
2243
    def __init__(self):
2178
2302
 
2179
2303
# Pack-based formats. There is one format for pre-subtrees, and one for
2180
2304
# post-subtrees to allow ease of testing.
2181
 
# NOTE: These are experimental in 0.92.
 
2305
# NOTE: These are experimental in 0.92. Stable in 1.0 and above
2182
2306
format_registry.register_lazy(
2183
2307
    'Bazaar pack repository format 1 (needs bzr 0.92)\n',
2184
2308
    'bzrlib.repofmt.pack_repo',
2194
2318
    'bzrlib.repofmt.pack_repo',
2195
2319
    'RepositoryFormatKnitPack4',
2196
2320
    )
 
2321
# Development formats. 
 
2322
# 1.2->1.3
 
2323
# development 0 - stub to introduce development versioning scheme.
 
2324
format_registry.register_lazy(
 
2325
    "Bazaar development format 0 (needs bzr.dev from before 1.3)\n",
 
2326
    'bzrlib.repofmt.pack_repo',
 
2327
    'RepositoryFormatPackDevelopment0',
 
2328
    )
 
2329
format_registry.register_lazy(
 
2330
    ("Bazaar development format 0 with subtree support "
 
2331
        "(needs bzr.dev from before 1.3)\n"),
 
2332
    'bzrlib.repofmt.pack_repo',
 
2333
    'RepositoryFormatPackDevelopment0Subtree',
 
2334
    )
 
2335
# 1.3->1.4 go below here
2197
2336
 
2198
2337
 
2199
2338
class InterRepository(InterObject):
2228
2367
        (copied, failures).
2229
2368
        """
2230
2369
        raise NotImplementedError(self.fetch)
 
2370
 
 
2371
    def _walk_to_common_revisions(self, revision_ids):
 
2372
        """Walk out from revision_ids in source to revisions target has.
 
2373
 
 
2374
        :param revision_ids: The start point for the search.
 
2375
        :return: A set of revision ids.
 
2376
        """
 
2377
        graph = self.source.get_graph()
 
2378
        missing_revs = set()
 
2379
        # ensure we don't pay silly lookup costs.
 
2380
        revision_ids = frozenset(revision_ids)
 
2381
        searcher = graph._make_breadth_first_searcher(revision_ids)
 
2382
        null_set = frozenset([_mod_revision.NULL_REVISION])
 
2383
        while True:
 
2384
            try:
 
2385
                next_revs, ghosts = searcher.next_with_ghosts()
 
2386
            except StopIteration:
 
2387
                break
 
2388
            if revision_ids.intersection(ghosts):
 
2389
                absent_ids = set(revision_ids.intersection(ghosts))
 
2390
                # If all absent_ids are present in target, no error is needed.
 
2391
                absent_ids.difference_update(
 
2392
                    self.target.has_revisions(absent_ids))
 
2393
                if absent_ids:
 
2394
                    raise errors.NoSuchRevision(self.source, absent_ids.pop())
 
2395
            # we don't care about other ghosts as we can't fetch them and
 
2396
            # haven't been asked to.
 
2397
            next_revs = set(next_revs)
 
2398
            # we always have NULL_REVISION present.
 
2399
            have_revs = self.target.has_revisions(next_revs).union(null_set)
 
2400
            missing_revs.update(next_revs - have_revs)
 
2401
            searcher.stop_searching_any(have_revs)
 
2402
        return searcher.get_result()
2231
2403
   
 
2404
    @deprecated_method(symbol_versioning.one_two)
2232
2405
    @needs_read_lock
2233
2406
    def missing_revision_ids(self, revision_id=None, find_ghosts=True):
2234
2407
        """Return the revision ids that source has that target does not.
2237
2410
 
2238
2411
        :param revision_id: only return revision ids included by this
2239
2412
                            revision_id.
2240
 
        """
 
2413
        :param find_ghosts: If True find missing revisions in deep history
 
2414
            rather than just finding the surface difference.
 
2415
        """
 
2416
        return list(self.search_missing_revision_ids(
 
2417
            revision_id, find_ghosts).get_keys())
 
2418
 
 
2419
    @needs_read_lock
 
2420
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
 
2421
        """Return the revision ids that source has that target does not.
 
2422
        
 
2423
        :param revision_id: only return revision ids included by this
 
2424
                            revision_id.
 
2425
        :param find_ghosts: If True find missing revisions in deep history
 
2426
            rather than just finding the surface difference.
 
2427
        :return: A bzrlib.graph.SearchResult.
 
2428
        """
 
2429
        # stop searching at found target revisions.
 
2430
        if not find_ghosts and revision_id is not None:
 
2431
            return self._walk_to_common_revisions([revision_id])
2241
2432
        # generic, possibly worst case, slow code path.
2242
2433
        target_ids = set(self.target.all_revision_ids())
2243
2434
        if revision_id is not None:
2247
2438
        else:
2248
2439
            source_ids = self.source.all_revision_ids()
2249
2440
        result_set = set(source_ids).difference(target_ids)
2250
 
        # this may look like a no-op: its not. It preserves the ordering
2251
 
        # other_ids had while only returning the members from other_ids
2252
 
        # that we've decided we need.
2253
 
        return [rev_id for rev_id in source_ids if rev_id in result_set]
 
2441
        return self.source.revision_ids_to_search_result(result_set)
2254
2442
 
2255
2443
    @staticmethod
2256
2444
    def _same_model(source, target):
2315
2503
        f = GenericRepoFetcher(to_repository=self.target,
2316
2504
                               from_repository=self.source,
2317
2505
                               last_revision=revision_id,
2318
 
                               pb=pb)
 
2506
                               pb=pb, find_ghosts=find_ghosts)
2319
2507
        return f.count_copied, f.failed_revisions
2320
2508
 
2321
2509
 
2393
2581
        f = GenericRepoFetcher(to_repository=self.target,
2394
2582
                               from_repository=self.source,
2395
2583
                               last_revision=revision_id,
2396
 
                               pb=pb)
 
2584
                               pb=pb, find_ghosts=find_ghosts)
2397
2585
        return f.count_copied, f.failed_revisions
2398
2586
 
2399
2587
    @needs_read_lock
2400
 
    def missing_revision_ids(self, revision_id=None, find_ghosts=True):
 
2588
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
2401
2589
        """See InterRepository.missing_revision_ids()."""
2402
2590
        # we want all revisions to satisfy revision_id in source.
2403
2591
        # but we don't want to stat every file here and there.
2423
2611
        # we do not have a revision as that would be pointless.
2424
2612
        target_ids = set(self.target._all_possible_ids())
2425
2613
        possibly_present_revisions = target_ids.intersection(source_ids_set)
2426
 
        actually_present_revisions = set(self.target._eliminate_revisions_not_present(possibly_present_revisions))
 
2614
        actually_present_revisions = set(
 
2615
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
2427
2616
        required_revisions = source_ids_set.difference(actually_present_revisions)
2428
 
        required_topo_revisions = [rev_id for rev_id in source_ids if rev_id in required_revisions]
2429
2617
        if revision_id is not None:
2430
2618
            # we used get_ancestry to determine source_ids then we are assured all
2431
2619
            # revisions referenced are present as they are installed in topological order.
2432
2620
            # and the tip revision was validated by get_ancestry.
2433
 
            return required_topo_revisions
 
2621
            result_set = required_revisions
2434
2622
        else:
2435
2623
            # if we just grabbed the possibly available ids, then 
2436
2624
            # we only have an estimate of whats available and need to validate
2437
2625
            # that against the revision records.
2438
 
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
 
2626
            result_set = set(
 
2627
                self.source._eliminate_revisions_not_present(required_revisions))
 
2628
        return self.source.revision_ids_to_search_result(result_set)
2439
2629
 
2440
2630
 
2441
2631
class InterKnitRepo(InterSameDataRepository):
2471
2661
        f = KnitRepoFetcher(to_repository=self.target,
2472
2662
                            from_repository=self.source,
2473
2663
                            last_revision=revision_id,
2474
 
                            pb=pb)
 
2664
                            pb=pb, find_ghosts=find_ghosts)
2475
2665
        return f.count_copied, f.failed_revisions
2476
2666
 
2477
2667
    @needs_read_lock
2478
 
    def missing_revision_ids(self, revision_id=None, find_ghosts=True):
 
2668
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
2479
2669
        """See InterRepository.missing_revision_ids()."""
2480
2670
        if revision_id is not None:
2481
2671
            source_ids = self.source.get_ancestry(revision_id)
2490
2680
        # we do not have a revision as that would be pointless.
2491
2681
        target_ids = set(self.target.all_revision_ids())
2492
2682
        possibly_present_revisions = target_ids.intersection(source_ids_set)
2493
 
        actually_present_revisions = set(self.target._eliminate_revisions_not_present(possibly_present_revisions))
 
2683
        actually_present_revisions = set(
 
2684
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
2494
2685
        required_revisions = source_ids_set.difference(actually_present_revisions)
2495
 
        required_topo_revisions = [rev_id for rev_id in source_ids if rev_id in required_revisions]
2496
2686
        if revision_id is not None:
2497
2687
            # we used get_ancestry to determine source_ids then we are assured all
2498
2688
            # revisions referenced are present as they are installed in topological order.
2499
2689
            # and the tip revision was validated by get_ancestry.
2500
 
            return required_topo_revisions
 
2690
            result_set = required_revisions
2501
2691
        else:
2502
2692
            # if we just grabbed the possibly available ids, then 
2503
2693
            # we only have an estimate of whats available and need to validate
2504
2694
            # that against the revision records.
2505
 
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
 
2695
            result_set = set(
 
2696
                self.source._eliminate_revisions_not_present(required_revisions))
 
2697
        return self.source.revision_ids_to_search_result(result_set)
2506
2698
 
2507
2699
 
2508
2700
class InterPackRepo(InterSameDataRepository):
2555
2747
            return (0, [])
2556
2748
        else:
2557
2749
            try:
2558
 
                revision_ids = self.missing_revision_ids(revision_id,
2559
 
                    find_ghosts=find_ghosts)
 
2750
                revision_ids = self.search_missing_revision_ids(revision_id,
 
2751
                    find_ghosts=find_ghosts).get_keys()
2560
2752
            except errors.NoSuchRevision:
2561
2753
                raise errors.InstallFailed([revision_id])
2562
2754
        packs = self.source._pack_collection.all_packs()
2573
2765
            return (0, [])
2574
2766
 
2575
2767
    @needs_read_lock
2576
 
    def missing_revision_ids(self, revision_id=None, find_ghosts=True):
 
2768
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
2577
2769
        """See InterRepository.missing_revision_ids().
2578
2770
        
2579
 
        :param find_ghosts: Find ghosts throughough the ancestry of
 
2771
        :param find_ghosts: Find ghosts throughout the ancestry of
2580
2772
            revision_id.
2581
2773
        """
2582
2774
        if not find_ghosts and revision_id is not None:
2583
 
            graph = self.source.get_graph()
2584
 
            missing_revs = set()
2585
 
            searcher = graph._make_breadth_first_searcher([revision_id])
2586
 
            target_index = \
2587
 
                self.target._pack_collection.revision_index.combined_index
2588
 
            null_set = frozenset([_mod_revision.NULL_REVISION])
2589
 
            while True:
2590
 
                try:
2591
 
                    next_revs = set(searcher.next())
2592
 
                except StopIteration:
2593
 
                    break
2594
 
                next_revs.difference_update(null_set)
2595
 
                target_keys = [(key,) for key in next_revs]
2596
 
                have_revs = frozenset(node[1][0] for node in
2597
 
                    target_index.iter_entries(target_keys))
2598
 
                missing_revs.update(next_revs - have_revs)
2599
 
                searcher.stop_searching_any(have_revs)
2600
 
            if next_revs - have_revs == set([revision_id]):
2601
 
                # we saw the start rev itself, but no parents from it (or
2602
 
                # next_revs would have been updated to e.g. set(). We remove
2603
 
                # have_revs because if we found revision_id locally we
2604
 
                # stop_searching at the first time around.
2605
 
                raise errors.NoSuchRevision(self.source, revision_id)
2606
 
            return missing_revs
 
2775
            return self._walk_to_common_revisions([revision_id])
2607
2776
        elif revision_id is not None:
2608
2777
            source_ids = self.source.get_ancestry(revision_id)
2609
2778
            assert source_ids[0] is None
2615
2784
        # have in target, but don't try to check for existence where we know
2616
2785
        # we do not have a revision as that would be pointless.
2617
2786
        target_ids = set(self.target.all_revision_ids())
2618
 
        return [r for r in source_ids if (r not in target_ids)]
 
2787
        result_set = set(source_ids).difference(target_ids)
 
2788
        return self.source.revision_ids_to_search_result(result_set)
2619
2789
 
2620
2790
 
2621
2791
class InterModel1and2(InterRepository):
2638
2808
        f = Model1toKnit2Fetcher(to_repository=self.target,
2639
2809
                                 from_repository=self.source,
2640
2810
                                 last_revision=revision_id,
2641
 
                                 pb=pb)
 
2811
                                 pb=pb, find_ghosts=find_ghosts)
2642
2812
        return f.count_copied, f.failed_revisions
2643
2813
 
2644
2814
    @needs_write_lock
2675
2845
        try:
2676
2846
            from bzrlib.repofmt.knitrepo import (RepositoryFormatKnit1,
2677
2847
                RepositoryFormatKnit3)
2678
 
            from bzrlib.repofmt.pack_repo import (RepositoryFormatKnitPack1,
2679
 
                RepositoryFormatKnitPack3)
2680
 
            return (isinstance(source._format,
2681
 
                    (RepositoryFormatKnit1, RepositoryFormatKnitPack1)) and
2682
 
                isinstance(target._format,
2683
 
                    (RepositoryFormatKnit3, RepositoryFormatKnitPack3))
2684
 
                )
 
2848
            from bzrlib.repofmt.pack_repo import (
 
2849
                RepositoryFormatKnitPack1,
 
2850
                RepositoryFormatKnitPack3,
 
2851
                RepositoryFormatPackDevelopment0,
 
2852
                RepositoryFormatPackDevelopment0Subtree,
 
2853
                )
 
2854
            nosubtrees = (
 
2855
                RepositoryFormatKnit1,
 
2856
                RepositoryFormatKnitPack1,
 
2857
                RepositoryFormatPackDevelopment0,
 
2858
                )
 
2859
            subtrees = (
 
2860
                RepositoryFormatKnit3,
 
2861
                RepositoryFormatKnitPack3,
 
2862
                RepositoryFormatPackDevelopment0Subtree,
 
2863
                )
 
2864
            return (isinstance(source._format, nosubtrees) and
 
2865
                isinstance(target._format, subtrees))
2685
2866
        except AttributeError:
2686
2867
            return False
2687
2868
 
2695
2876
        f = Knit1to2Fetcher(to_repository=self.target,
2696
2877
                            from_repository=self.source,
2697
2878
                            last_revision=revision_id,
2698
 
                            pb=pb)
 
2879
                            pb=pb, find_ghosts=find_ghosts)
2699
2880
        return f.count_copied, f.failed_revisions
2700
2881
 
2701
2882
 
2720
2901
    @needs_write_lock
2721
2902
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2722
2903
        """See InterRepository.fetch()."""
2723
 
        revision_ids = self.target.missing_revision_ids(self.source,
2724
 
                                                        revision_id)
 
2904
        revision_ids = self.target.search_missing_revision_ids(self.source,
 
2905
            revision_id, find_ghosts=find_ghosts).get_keys()
 
2906
        revision_ids = tsort.topo_sort(
 
2907
            self.source.get_graph().get_parent_map(revision_ids))
2725
2908
        def revisions_iterator():
2726
2909
            for current_revision_id in revision_ids:
2727
2910
                revision = self.source.get_revision(current_revision_id)
2732
2915
                except errors.NoSuchRevision:
2733
2916
                    signature = None
2734
2917
                yield revision, tree, signature
2735
 
        install_revisions(self.target, revisions_iterator())
 
2918
        if pb is None:
 
2919
            my_pb = ui.ui_factory.nested_progress_bar()
 
2920
            pb = my_pb
 
2921
        else:
 
2922
            my_pb = None
 
2923
        try:
 
2924
            install_revisions(self.target, revisions_iterator(),
 
2925
                              len(revision_ids), pb)
 
2926
        finally:
 
2927
            if my_pb is not None:
 
2928
                my_pb.finished()
2736
2929
        return len(revision_ids), 0
2737
2930
 
2738
2931
 
2746
2939
    def is_compatible(source, target):
2747
2940
        if not isinstance(source, remote.RemoteRepository):
2748
2941
            return False
 
2942
        # Is source's model compatible with target's model?
2749
2943
        source._ensure_real()
2750
2944
        real_source = source._real_repository
2751
 
        # Is source's model compatible with target's model, and are they the
2752
 
        # same format?  Currently we can only optimise fetching from an
2753
 
        # identical model & format repo.
2754
2945
        assert not isinstance(real_source, remote.RemoteRepository), (
2755
2946
            "We don't support remote repos backed by remote repos yet.")
2756
 
        return real_source._format == target._format
 
2947
        return InterRepository._same_model(real_source, target)
2757
2948
 
2758
2949
    @needs_write_lock
2759
2950
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2766
2957
        f = RemoteToOtherFetcher(to_repository=self.target,
2767
2958
                                 from_repository=self.source,
2768
2959
                                 last_revision=revision_id,
2769
 
                                 pb=pb)
 
2960
                                 pb=pb, find_ghosts=find_ghosts)
2770
2961
        return f.count_copied, f.failed_revisions
2771
2962
 
2772
2963
    @classmethod
2798
2989
 
2799
2990
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2800
2991
        self._ensure_real_inter()
2801
 
        self._real_inter.fetch(revision_id=revision_id, pb=pb)
 
2992
        self._real_inter.fetch(revision_id=revision_id, pb=pb,
 
2993
            find_ghosts=find_ghosts)
2802
2994
 
2803
2995
    @classmethod
2804
2996
    def _get_repo_format_to_test(self):