~toddy/bzr/bzr.i18n

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: Tobias Toedter
  • Date: 2008-02-10 08:01:48 UTC
  • mfrom: (2438.1.783 +trunk)
  • Revision ID: t.toedter@gmx.net-20080210080148-bg5rh61oq2zk2xw3
Merge trunk

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
 
773
772
 
774
773
        :param using: If True, list only branches using this repository.
775
774
        """
776
 
 
 
775
        if using and not self.is_shared():
 
776
            try:
 
777
                return [self.bzrdir.open_branch()]
 
778
            except errors.NotBranchError:
 
779
                return []
777
780
        class Evaluator(object):
778
781
 
779
782
            def __init__(self):
808
811
    def get_data_stream(self, revision_ids):
809
812
        raise NotImplementedError(self.get_data_stream)
810
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
 
811
824
    def insert_data_stream(self, stream):
812
825
        """XXX What does this really do? 
813
826
        
828
841
                knit = self._revision_store.get_signature_file(
829
842
                    self.get_transaction())
830
843
            else:
831
 
                raise RepositoryDataStreamError(
 
844
                raise errors.RepositoryDataStreamError(
832
845
                    "Unrecognised data stream key '%s'" % (item_key,))
833
846
            decoded_list = bencode.bdecode(bytes)
834
847
            format = decoded_list.pop(0)
837
850
            for version, options, parents, some_bytes in decoded_list:
838
851
                data_list.append((version, options, len(some_bytes), parents))
839
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)
840
859
            knit.insert_data_stream(
841
 
                (format, data_list, StringIO(knit_bytes).read))
842
 
 
 
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)
843
874
    @needs_read_lock
844
875
    def missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
845
876
        """Return the revision ids that other has that this does not.
848
879
 
849
880
        revision_id: only return revision ids included by revision_id.
850
881
        """
851
 
        return InterRepository.get(other, self).missing_revision_ids(
852
 
            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)
853
890
 
854
891
    @staticmethod
855
892
    def open(base):
1015
1052
    @needs_read_lock
1016
1053
    def has_revision(self, revision_id):
1017
1054
        """True if this repository has a copy of the revision."""
1018
 
        if 'evil' in debug.debug_flags:
1019
 
            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
 
1020
1065
        return self._revision_store.has_revision_id(revision_id,
1021
1066
                                                    self.get_transaction())
1022
1067
 
1446
1491
 
1447
1492
    @needs_read_lock
1448
1493
    def get_inventory(self, revision_id):
1449
 
        """Get Inventory object by hash."""
1450
 
        return self.deserialise_inventory(
1451
 
            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)
1452
1515
 
1453
1516
    def deserialise_inventory(self, revision_id, xml):
1454
1517
        """Transform the xml into an inventory object. 
1456
1519
        :param revision_id: The expected revision id of the inventory.
1457
1520
        :param xml: A serialised inventory.
1458
1521
        """
1459
 
        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
1460
1527
 
1461
1528
    def serialise_inventory(self, inv):
1462
1529
        return self._serializer.write_inventory_to_string(inv)
1622
1689
        """Return Tree for a revision on this branch.
1623
1690
 
1624
1691
        `revision_id` may not be None or 'null:'"""
1625
 
        assert None not in revision_ids
1626
 
        assert _mod_revision.NULL_REVISION not in revision_ids
1627
 
        texts = self.get_inventory_weave().get_texts(revision_ids)
1628
 
        for text, revision_id in zip(texts, revision_ids):
1629
 
            inv = self.deserialise_inventory(revision_id, text)
1630
 
            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)
1631
1695
 
1632
1696
    @needs_read_lock
1633
1697
    def get_ancestry(self, revision_id, topo_sorted=True):
1693
1757
        parent_map = {}
1694
1758
        for revision_id in keys:
1695
1759
            if revision_id == _mod_revision.NULL_REVISION:
1696
 
                parent_map[revision_id] = []
 
1760
                parent_map[revision_id] = ()
1697
1761
            else:
1698
1762
                try:
1699
 
                    parent_ids = self.get_revision(revision_id).parent_ids
 
1763
                    parent_id_list = self.get_revision(revision_id).parent_ids
1700
1764
                except errors.NoSuchRevision:
1701
1765
                    pass
1702
1766
                else:
1703
 
                    if len(parent_ids) == 0:
1704
 
                        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)
1705
1771
                    parent_map[revision_id] = parent_ids
1706
1772
        return parent_map
1707
1773
 
1712
1778
        """Return the graph walker for this repository format"""
1713
1779
        parents_provider = self._make_parents_provider()
1714
1780
        if (other_repository is not None and
1715
 
            other_repository.bzrdir.transport.base !=
1716
 
            self.bzrdir.transport.base):
 
1781
            not self.has_same_location(other_repository)):
1717
1782
            parents_provider = graph._StackedParentsProvider(
1718
1783
                [parents_provider, other_repository._make_parents_provider()])
1719
1784
        return graph.Graph(parents_provider)
1722
1787
        """Return an object suitable for checking versioned files."""
1723
1788
        return _VersionedFileChecker(self)
1724
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
 
1725
1803
    @needs_write_lock
1726
1804
    def set_make_working_trees(self, new_value):
1727
1805
        """Set the policy flag for making working trees when creating branches.
1808
1886
        depend on the revision index being consistent.
1809
1887
        """
1810
1888
        raise NotImplementedError(self.revision_graph_can_have_wrong_parents)
1811
 
        
 
1889
 
 
1890
 
1812
1891
# remove these delegates a while after bzr 0.15
1813
1892
def __make_delegated(name, from_module):
1814
1893
    def _deprecated_repository_forwarder():
1848
1927
    install_revisions(repository, [(rev, revision_tree, None)])
1849
1928
 
1850
1929
 
1851
 
def install_revisions(repository, iterable):
 
1930
def install_revisions(repository, iterable, num_revisions=None, pb=None):
1852
1931
    """Install all revision data into a repository.
1853
1932
 
1854
1933
    Accepts an iterable of revision, tree, signature tuples.  The signature
1856
1935
    """
1857
1936
    repository.start_write_group()
1858
1937
    try:
1859
 
        for revision, revision_tree, signature in iterable:
 
1938
        for n, (revision, revision_tree, signature) in enumerate(iterable):
1860
1939
            _install_revision(repository, revision, revision_tree, signature)
 
1940
            if pb is not None:
 
1941
                pb.update('Transferring revisions', n + 1, num_revisions)
1861
1942
    except:
1862
1943
        repository.abort_write_group()
1863
1944
        raise
2267
2348
        (copied, failures).
2268
2349
        """
2269
2350
        raise NotImplementedError(self.fetch)
 
2351
 
 
2352
    def _walk_to_common_revisions(self, revision_ids):
 
2353
        """Walk out from revision_ids in source to revisions target has.
 
2354
 
 
2355
        :param revision_ids: The start point for the search.
 
2356
        :return: A set of revision ids.
 
2357
        """
 
2358
        graph = self.source.get_graph()
 
2359
        missing_revs = set()
 
2360
        # ensure we don't pay silly lookup costs.
 
2361
        revision_ids = frozenset(revision_ids)
 
2362
        searcher = graph._make_breadth_first_searcher(revision_ids)
 
2363
        null_set = frozenset([_mod_revision.NULL_REVISION])
 
2364
        while True:
 
2365
            try:
 
2366
                next_revs, ghosts = searcher.next_with_ghosts()
 
2367
            except StopIteration:
 
2368
                break
 
2369
            if revision_ids.intersection(ghosts):
 
2370
                absent_ids = set(revision_ids.intersection(ghosts))
 
2371
                # If all absent_ids are present in target, no error is needed.
 
2372
                absent_ids.difference_update(
 
2373
                    self.target.has_revisions(absent_ids))
 
2374
                if absent_ids:
 
2375
                    raise errors.NoSuchRevision(self.source, absent_ids.pop())
 
2376
            # we don't care about other ghosts as we can't fetch them and
 
2377
            # haven't been asked to.
 
2378
            next_revs = set(next_revs)
 
2379
            # we always have NULL_REVISION present.
 
2380
            have_revs = self.target.has_revisions(next_revs).union(null_set)
 
2381
            missing_revs.update(next_revs - have_revs)
 
2382
            searcher.stop_searching_any(have_revs)
 
2383
        return searcher.get_result()
2270
2384
   
 
2385
    @deprecated_method(symbol_versioning.one_two)
2271
2386
    @needs_read_lock
2272
2387
    def missing_revision_ids(self, revision_id=None, find_ghosts=True):
2273
2388
        """Return the revision ids that source has that target does not.
2276
2391
 
2277
2392
        :param revision_id: only return revision ids included by this
2278
2393
                            revision_id.
2279
 
        """
 
2394
        :param find_ghosts: If True find missing revisions in deep history
 
2395
            rather than just finding the surface difference.
 
2396
        """
 
2397
        return list(self.search_missing_revision_ids(
 
2398
            revision_id, find_ghosts).get_keys())
 
2399
 
 
2400
    @needs_read_lock
 
2401
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
 
2402
        """Return the revision ids that source has that target does not.
 
2403
        
 
2404
        :param revision_id: only return revision ids included by this
 
2405
                            revision_id.
 
2406
        :param find_ghosts: If True find missing revisions in deep history
 
2407
            rather than just finding the surface difference.
 
2408
        :return: A bzrlib.graph.SearchResult.
 
2409
        """
 
2410
        # stop searching at found target revisions.
 
2411
        if not find_ghosts and revision_id is not None:
 
2412
            return self._walk_to_common_revisions([revision_id])
2280
2413
        # generic, possibly worst case, slow code path.
2281
2414
        target_ids = set(self.target.all_revision_ids())
2282
2415
        if revision_id is not None:
2286
2419
        else:
2287
2420
            source_ids = self.source.all_revision_ids()
2288
2421
        result_set = set(source_ids).difference(target_ids)
2289
 
        # this may look like a no-op: its not. It preserves the ordering
2290
 
        # other_ids had while only returning the members from other_ids
2291
 
        # that we've decided we need.
2292
 
        return [rev_id for rev_id in source_ids if rev_id in result_set]
 
2422
        return self.source.revision_ids_to_search_result(result_set)
2293
2423
 
2294
2424
    @staticmethod
2295
2425
    def _same_model(source, target):
2354
2484
        f = GenericRepoFetcher(to_repository=self.target,
2355
2485
                               from_repository=self.source,
2356
2486
                               last_revision=revision_id,
2357
 
                               pb=pb)
 
2487
                               pb=pb, find_ghosts=find_ghosts)
2358
2488
        return f.count_copied, f.failed_revisions
2359
2489
 
2360
2490
 
2432
2562
        f = GenericRepoFetcher(to_repository=self.target,
2433
2563
                               from_repository=self.source,
2434
2564
                               last_revision=revision_id,
2435
 
                               pb=pb)
 
2565
                               pb=pb, find_ghosts=find_ghosts)
2436
2566
        return f.count_copied, f.failed_revisions
2437
2567
 
2438
2568
    @needs_read_lock
2439
 
    def missing_revision_ids(self, revision_id=None, find_ghosts=True):
 
2569
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
2440
2570
        """See InterRepository.missing_revision_ids()."""
2441
2571
        # we want all revisions to satisfy revision_id in source.
2442
2572
        # but we don't want to stat every file here and there.
2462
2592
        # we do not have a revision as that would be pointless.
2463
2593
        target_ids = set(self.target._all_possible_ids())
2464
2594
        possibly_present_revisions = target_ids.intersection(source_ids_set)
2465
 
        actually_present_revisions = set(self.target._eliminate_revisions_not_present(possibly_present_revisions))
 
2595
        actually_present_revisions = set(
 
2596
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
2466
2597
        required_revisions = source_ids_set.difference(actually_present_revisions)
2467
 
        required_topo_revisions = [rev_id for rev_id in source_ids if rev_id in required_revisions]
2468
2598
        if revision_id is not None:
2469
2599
            # we used get_ancestry to determine source_ids then we are assured all
2470
2600
            # revisions referenced are present as they are installed in topological order.
2471
2601
            # and the tip revision was validated by get_ancestry.
2472
 
            return required_topo_revisions
 
2602
            result_set = required_revisions
2473
2603
        else:
2474
2604
            # if we just grabbed the possibly available ids, then 
2475
2605
            # we only have an estimate of whats available and need to validate
2476
2606
            # that against the revision records.
2477
 
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
 
2607
            result_set = set(
 
2608
                self.source._eliminate_revisions_not_present(required_revisions))
 
2609
        return self.source.revision_ids_to_search_result(result_set)
2478
2610
 
2479
2611
 
2480
2612
class InterKnitRepo(InterSameDataRepository):
2510
2642
        f = KnitRepoFetcher(to_repository=self.target,
2511
2643
                            from_repository=self.source,
2512
2644
                            last_revision=revision_id,
2513
 
                            pb=pb)
 
2645
                            pb=pb, find_ghosts=find_ghosts)
2514
2646
        return f.count_copied, f.failed_revisions
2515
2647
 
2516
2648
    @needs_read_lock
2517
 
    def missing_revision_ids(self, revision_id=None, find_ghosts=True):
 
2649
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
2518
2650
        """See InterRepository.missing_revision_ids()."""
2519
2651
        if revision_id is not None:
2520
2652
            source_ids = self.source.get_ancestry(revision_id)
2529
2661
        # we do not have a revision as that would be pointless.
2530
2662
        target_ids = set(self.target.all_revision_ids())
2531
2663
        possibly_present_revisions = target_ids.intersection(source_ids_set)
2532
 
        actually_present_revisions = set(self.target._eliminate_revisions_not_present(possibly_present_revisions))
 
2664
        actually_present_revisions = set(
 
2665
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
2533
2666
        required_revisions = source_ids_set.difference(actually_present_revisions)
2534
 
        required_topo_revisions = [rev_id for rev_id in source_ids if rev_id in required_revisions]
2535
2667
        if revision_id is not None:
2536
2668
            # we used get_ancestry to determine source_ids then we are assured all
2537
2669
            # revisions referenced are present as they are installed in topological order.
2538
2670
            # and the tip revision was validated by get_ancestry.
2539
 
            return required_topo_revisions
 
2671
            result_set = required_revisions
2540
2672
        else:
2541
2673
            # if we just grabbed the possibly available ids, then 
2542
2674
            # we only have an estimate of whats available and need to validate
2543
2675
            # that against the revision records.
2544
 
            return self.source._eliminate_revisions_not_present(required_topo_revisions)
 
2676
            result_set = set(
 
2677
                self.source._eliminate_revisions_not_present(required_revisions))
 
2678
        return self.source.revision_ids_to_search_result(result_set)
2545
2679
 
2546
2680
 
2547
2681
class InterPackRepo(InterSameDataRepository):
2594
2728
            return (0, [])
2595
2729
        else:
2596
2730
            try:
2597
 
                revision_ids = self.missing_revision_ids(revision_id,
2598
 
                    find_ghosts=find_ghosts)
 
2731
                revision_ids = self.search_missing_revision_ids(revision_id,
 
2732
                    find_ghosts=find_ghosts).get_keys()
2599
2733
            except errors.NoSuchRevision:
2600
2734
                raise errors.InstallFailed([revision_id])
2601
2735
        packs = self.source._pack_collection.all_packs()
2612
2746
            return (0, [])
2613
2747
 
2614
2748
    @needs_read_lock
2615
 
    def missing_revision_ids(self, revision_id=None, find_ghosts=True):
 
2749
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
2616
2750
        """See InterRepository.missing_revision_ids().
2617
2751
        
2618
 
        :param find_ghosts: Find ghosts throughough the ancestry of
 
2752
        :param find_ghosts: Find ghosts throughout the ancestry of
2619
2753
            revision_id.
2620
2754
        """
2621
2755
        if not find_ghosts and revision_id is not None:
2622
 
            graph = self.source.get_graph()
2623
 
            missing_revs = set()
2624
 
            searcher = graph._make_breadth_first_searcher([revision_id])
2625
 
            target_index = \
2626
 
                self.target._pack_collection.revision_index.combined_index
2627
 
            null_set = frozenset([_mod_revision.NULL_REVISION])
2628
 
            while True:
2629
 
                try:
2630
 
                    next_revs = set(searcher.next())
2631
 
                except StopIteration:
2632
 
                    break
2633
 
                next_revs.difference_update(null_set)
2634
 
                target_keys = [(key,) for key in next_revs]
2635
 
                have_revs = frozenset(node[1][0] for node in
2636
 
                    target_index.iter_entries(target_keys))
2637
 
                missing_revs.update(next_revs - have_revs)
2638
 
                searcher.stop_searching_any(have_revs)
2639
 
            if next_revs - have_revs == set([revision_id]):
2640
 
                # we saw the start rev itself, but no parents from it (or
2641
 
                # next_revs would have been updated to e.g. set(). We remove
2642
 
                # have_revs because if we found revision_id locally we
2643
 
                # stop_searching at the first time around.
2644
 
                raise errors.NoSuchRevision(self.source, revision_id)
2645
 
            return missing_revs
 
2756
            return self._walk_to_common_revisions([revision_id])
2646
2757
        elif revision_id is not None:
2647
2758
            source_ids = self.source.get_ancestry(revision_id)
2648
2759
            assert source_ids[0] is None
2654
2765
        # have in target, but don't try to check for existence where we know
2655
2766
        # we do not have a revision as that would be pointless.
2656
2767
        target_ids = set(self.target.all_revision_ids())
2657
 
        return [r for r in source_ids if (r not in target_ids)]
 
2768
        result_set = set(source_ids).difference(target_ids)
 
2769
        return self.source.revision_ids_to_search_result(result_set)
2658
2770
 
2659
2771
 
2660
2772
class InterModel1and2(InterRepository):
2677
2789
        f = Model1toKnit2Fetcher(to_repository=self.target,
2678
2790
                                 from_repository=self.source,
2679
2791
                                 last_revision=revision_id,
2680
 
                                 pb=pb)
 
2792
                                 pb=pb, find_ghosts=find_ghosts)
2681
2793
        return f.count_copied, f.failed_revisions
2682
2794
 
2683
2795
    @needs_write_lock
2734
2846
        f = Knit1to2Fetcher(to_repository=self.target,
2735
2847
                            from_repository=self.source,
2736
2848
                            last_revision=revision_id,
2737
 
                            pb=pb)
 
2849
                            pb=pb, find_ghosts=find_ghosts)
2738
2850
        return f.count_copied, f.failed_revisions
2739
2851
 
2740
2852
 
2759
2871
    @needs_write_lock
2760
2872
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2761
2873
        """See InterRepository.fetch()."""
2762
 
        revision_ids = self.target.missing_revision_ids(self.source,
2763
 
                                                        revision_id)
 
2874
        revision_ids = self.target.search_missing_revision_ids(self.source,
 
2875
            revision_id, find_ghosts=find_ghosts).get_keys()
 
2876
        revision_ids = tsort.topo_sort(
 
2877
            self.source.get_graph().get_parent_map(revision_ids))
2764
2878
        def revisions_iterator():
2765
2879
            for current_revision_id in revision_ids:
2766
2880
                revision = self.source.get_revision(current_revision_id)
2771
2885
                except errors.NoSuchRevision:
2772
2886
                    signature = None
2773
2887
                yield revision, tree, signature
2774
 
        install_revisions(self.target, revisions_iterator())
 
2888
        if pb is None:
 
2889
            my_pb = ui.ui_factory.nested_progress_bar()
 
2890
            pb = my_pb
 
2891
        else:
 
2892
            my_pb = None
 
2893
        try:
 
2894
            install_revisions(self.target, revisions_iterator(),
 
2895
                              len(revision_ids), pb)
 
2896
        finally:
 
2897
            if my_pb is not None:
 
2898
                my_pb.finished()
2775
2899
        return len(revision_ids), 0
2776
2900
 
2777
2901
 
2785
2909
    def is_compatible(source, target):
2786
2910
        if not isinstance(source, remote.RemoteRepository):
2787
2911
            return False
 
2912
        # Is source's model compatible with target's model?
2788
2913
        source._ensure_real()
2789
2914
        real_source = source._real_repository
2790
 
        # Is source's model compatible with target's model, and are they the
2791
 
        # same format?  Currently we can only optimise fetching from an
2792
 
        # identical model & format repo.
2793
2915
        assert not isinstance(real_source, remote.RemoteRepository), (
2794
2916
            "We don't support remote repos backed by remote repos yet.")
2795
 
        return real_source._format == target._format
 
2917
        return InterRepository._same_model(real_source, target)
2796
2918
 
2797
2919
    @needs_write_lock
2798
2920
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2805
2927
        f = RemoteToOtherFetcher(to_repository=self.target,
2806
2928
                                 from_repository=self.source,
2807
2929
                                 last_revision=revision_id,
2808
 
                                 pb=pb)
 
2930
                                 pb=pb, find_ghosts=find_ghosts)
2809
2931
        return f.count_copied, f.failed_revisions
2810
2932
 
2811
2933
    @classmethod
2837
2959
 
2838
2960
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2839
2961
        self._ensure_real_inter()
2840
 
        self._real_inter.fetch(revision_id=revision_id, pb=pb)
 
2962
        self._real_inter.fetch(revision_id=revision_id, pb=pb,
 
2963
            find_ghosts=find_ghosts)
2841
2964
 
2842
2965
    @classmethod
2843
2966
    def _get_repo_format_to_test(self):