~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/vf_repository.py

  • Committer: Jelmer Vernooij
  • Date: 2011-05-15 17:58:19 UTC
  • mto: This revision was merged to the branch mainline in revision 5887.
  • Revision ID: jelmer@samba.org-20110515175819-0wakfnxgxvzfryl1
Move interrepository implementation to vf_repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
32
32
    revision as _mod_revision,
33
33
    serializer as _mod_serializer,
34
34
    static_tuple,
 
35
    symbol_versioning,
35
36
    tsort,
36
37
    ui,
37
38
    versionedfile,
2464
2465
        return wrong_parents, unused_keys
2465
2466
 
2466
2467
 
2467
 
class InterDifferingSerializer(InterRepository):
 
2468
class InterVersionedFileRepository(InterRepository):
 
2469
 
 
2470
    _walk_to_common_revisions_batch_size = 50
 
2471
 
 
2472
    @needs_write_lock
 
2473
    def fetch(self, revision_id=None, find_ghosts=False,
 
2474
            fetch_spec=None):
 
2475
        """Fetch the content required to construct revision_id.
 
2476
 
 
2477
        The content is copied from self.source to self.target.
 
2478
 
 
2479
        :param revision_id: if None all content is copied, if NULL_REVISION no
 
2480
                            content is copied.
 
2481
        :return: None.
 
2482
        """
 
2483
        ui.ui_factory.warn_experimental_format_fetch(self)
 
2484
        from bzrlib.fetch import RepoFetcher
 
2485
        # See <https://launchpad.net/bugs/456077> asking for a warning here
 
2486
        if self.source._format.network_name() != self.target._format.network_name():
 
2487
            ui.ui_factory.show_user_warning('cross_format_fetch',
 
2488
                from_format=self.source._format,
 
2489
                to_format=self.target._format)
 
2490
        f = RepoFetcher(to_repository=self.target,
 
2491
                               from_repository=self.source,
 
2492
                               last_revision=revision_id,
 
2493
                               fetch_spec=fetch_spec,
 
2494
                               find_ghosts=find_ghosts)
 
2495
 
 
2496
    def _walk_to_common_revisions(self, revision_ids, if_present_ids=None):
 
2497
        """Walk out from revision_ids in source to revisions target has.
 
2498
 
 
2499
        :param revision_ids: The start point for the search.
 
2500
        :return: A set of revision ids.
 
2501
        """
 
2502
        target_graph = self.target.get_graph()
 
2503
        revision_ids = frozenset(revision_ids)
 
2504
        if if_present_ids:
 
2505
            all_wanted_revs = revision_ids.union(if_present_ids)
 
2506
        else:
 
2507
            all_wanted_revs = revision_ids
 
2508
        missing_revs = set()
 
2509
        source_graph = self.source.get_graph()
 
2510
        # ensure we don't pay silly lookup costs.
 
2511
        searcher = source_graph._make_breadth_first_searcher(all_wanted_revs)
 
2512
        null_set = frozenset([_mod_revision.NULL_REVISION])
 
2513
        searcher_exhausted = False
 
2514
        while True:
 
2515
            next_revs = set()
 
2516
            ghosts = set()
 
2517
            # Iterate the searcher until we have enough next_revs
 
2518
            while len(next_revs) < self._walk_to_common_revisions_batch_size:
 
2519
                try:
 
2520
                    next_revs_part, ghosts_part = searcher.next_with_ghosts()
 
2521
                    next_revs.update(next_revs_part)
 
2522
                    ghosts.update(ghosts_part)
 
2523
                except StopIteration:
 
2524
                    searcher_exhausted = True
 
2525
                    break
 
2526
            # If there are ghosts in the source graph, and the caller asked for
 
2527
            # them, make sure that they are present in the target.
 
2528
            # We don't care about other ghosts as we can't fetch them and
 
2529
            # haven't been asked to.
 
2530
            ghosts_to_check = set(revision_ids.intersection(ghosts))
 
2531
            revs_to_get = set(next_revs).union(ghosts_to_check)
 
2532
            if revs_to_get:
 
2533
                have_revs = set(target_graph.get_parent_map(revs_to_get))
 
2534
                # we always have NULL_REVISION present.
 
2535
                have_revs = have_revs.union(null_set)
 
2536
                # Check if the target is missing any ghosts we need.
 
2537
                ghosts_to_check.difference_update(have_revs)
 
2538
                if ghosts_to_check:
 
2539
                    # One of the caller's revision_ids is a ghost in both the
 
2540
                    # source and the target.
 
2541
                    raise errors.NoSuchRevision(
 
2542
                        self.source, ghosts_to_check.pop())
 
2543
                missing_revs.update(next_revs - have_revs)
 
2544
                # Because we may have walked past the original stop point, make
 
2545
                # sure everything is stopped
 
2546
                stop_revs = searcher.find_seen_ancestors(have_revs)
 
2547
                searcher.stop_searching_any(stop_revs)
 
2548
            if searcher_exhausted:
 
2549
                break
 
2550
        return searcher.get_result()
 
2551
 
 
2552
    @needs_read_lock
 
2553
    def search_missing_revision_ids(self,
 
2554
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
 
2555
            find_ghosts=True, revision_ids=None, if_present_ids=None):
 
2556
        """Return the revision ids that source has that target does not.
 
2557
 
 
2558
        :param revision_id: only return revision ids included by this
 
2559
            revision_id.
 
2560
        :param revision_ids: return revision ids included by these
 
2561
            revision_ids.  NoSuchRevision will be raised if any of these
 
2562
            revisions are not present.
 
2563
        :param if_present_ids: like revision_ids, but will not cause
 
2564
            NoSuchRevision if any of these are absent, instead they will simply
 
2565
            not be in the result.  This is useful for e.g. finding revisions
 
2566
            to fetch for tags, which may reference absent revisions.
 
2567
        :param find_ghosts: If True find missing revisions in deep history
 
2568
            rather than just finding the surface difference.
 
2569
        :return: A bzrlib.graph.SearchResult.
 
2570
        """
 
2571
        if symbol_versioning.deprecated_passed(revision_id):
 
2572
            symbol_versioning.warn(
 
2573
                'search_missing_revision_ids(revision_id=...) was '
 
2574
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
 
2575
                DeprecationWarning, stacklevel=2)
 
2576
            if revision_ids is not None:
 
2577
                raise AssertionError(
 
2578
                    'revision_ids is mutually exclusive with revision_id')
 
2579
            if revision_id is not None:
 
2580
                revision_ids = [revision_id]
 
2581
        del revision_id
 
2582
        # stop searching at found target revisions.
 
2583
        if not find_ghosts and (revision_ids is not None or if_present_ids is
 
2584
                not None):
 
2585
            return self._walk_to_common_revisions(revision_ids,
 
2586
                    if_present_ids=if_present_ids)
 
2587
        # generic, possibly worst case, slow code path.
 
2588
        target_ids = set(self.target.all_revision_ids())
 
2589
        source_ids = self._present_source_revisions_for(
 
2590
            revision_ids, if_present_ids)
 
2591
        result_set = set(source_ids).difference(target_ids)
 
2592
        return self.source.revision_ids_to_search_result(result_set)
 
2593
 
 
2594
    def _present_source_revisions_for(self, revision_ids, if_present_ids=None):
 
2595
        """Returns set of all revisions in ancestry of revision_ids present in
 
2596
        the source repo.
 
2597
 
 
2598
        :param revision_ids: if None, all revisions in source are returned.
 
2599
        :param if_present_ids: like revision_ids, but if any/all of these are
 
2600
            absent no error is raised.
 
2601
        """
 
2602
        if revision_ids is not None or if_present_ids is not None:
 
2603
            # First, ensure all specified revisions exist.  Callers expect
 
2604
            # NoSuchRevision when they pass absent revision_ids here.
 
2605
            if revision_ids is None:
 
2606
                revision_ids = set()
 
2607
            if if_present_ids is None:
 
2608
                if_present_ids = set()
 
2609
            revision_ids = set(revision_ids)
 
2610
            if_present_ids = set(if_present_ids)
 
2611
            all_wanted_ids = revision_ids.union(if_present_ids)
 
2612
            graph = self.source.get_graph()
 
2613
            present_revs = set(graph.get_parent_map(all_wanted_ids))
 
2614
            missing = revision_ids.difference(present_revs)
 
2615
            if missing:
 
2616
                raise errors.NoSuchRevision(self.source, missing.pop())
 
2617
            found_ids = all_wanted_ids.intersection(present_revs)
 
2618
            source_ids = [rev_id for (rev_id, parents) in
 
2619
                          graph.iter_ancestry(found_ids)
 
2620
                          if rev_id != _mod_revision.NULL_REVISION
 
2621
                          and parents is not None]
 
2622
        else:
 
2623
            source_ids = self.source.all_revision_ids()
 
2624
        return set(source_ids)
 
2625
 
 
2626
    @classmethod
 
2627
    def _get_repo_format_to_test(self):
 
2628
        return None
 
2629
 
 
2630
    @classmethod
 
2631
    def is_compatible(cls, source, target):
 
2632
        # The default implementation is compatible with everything
 
2633
        return (source._format.supports_full_versioned_files and
 
2634
                target._format.supports_full_versioned_files)
 
2635
 
 
2636
 
 
2637
class InterDifferingSerializer(InterVersionedFileRepository):
2468
2638
 
2469
2639
    @classmethod
2470
2640
    def _get_repo_format_to_test(self):
2806
2976
        return basis_id, basis_tree
2807
2977
 
2808
2978
 
2809
 
class InterSameDataRepository(InterRepository):
 
2979
class InterSameDataRepository(InterVersionedFileRepository):
2810
2980
    """Code for converting between repositories that represent the same data.
2811
2981
 
2812
2982
    Data format and model must match for this to work.
2830
3000
            target._format.supports_full_versioned_files)
2831
3001
 
2832
3002
 
 
3003
InterRepository.register_optimiser(InterVersionedFileRepository)
2833
3004
InterRepository.register_optimiser(InterDifferingSerializer)
2834
3005
InterRepository.register_optimiser(InterSameDataRepository)
2835
3006