2446
2485
return wrong_parents, unused_keys
2449
class InterDifferingSerializer(InterRepository):
2488
class InterVersionedFileRepository(InterRepository):
2490
_walk_to_common_revisions_batch_size = 50
2493
def fetch(self, revision_id=None, find_ghosts=False,
2495
"""Fetch the content required to construct revision_id.
2497
The content is copied from self.source to self.target.
2499
:param revision_id: if None all content is copied, if NULL_REVISION no
2503
if self.target._format.experimental:
2504
ui.ui_factory.show_user_warning('experimental_format_fetch',
2505
from_format=self.source._format,
2506
to_format=self.target._format)
2507
from bzrlib.fetch import RepoFetcher
2508
# See <https://launchpad.net/bugs/456077> asking for a warning here
2509
if self.source._format.network_name() != self.target._format.network_name():
2510
ui.ui_factory.show_user_warning('cross_format_fetch',
2511
from_format=self.source._format,
2512
to_format=self.target._format)
2513
f = RepoFetcher(to_repository=self.target,
2514
from_repository=self.source,
2515
last_revision=revision_id,
2516
fetch_spec=fetch_spec,
2517
find_ghosts=find_ghosts)
2519
def _walk_to_common_revisions(self, revision_ids, if_present_ids=None):
2520
"""Walk out from revision_ids in source to revisions target has.
2522
:param revision_ids: The start point for the search.
2523
:return: A set of revision ids.
2525
target_graph = self.target.get_graph()
2526
revision_ids = frozenset(revision_ids)
2528
all_wanted_revs = revision_ids.union(if_present_ids)
2530
all_wanted_revs = revision_ids
2531
missing_revs = set()
2532
source_graph = self.source.get_graph()
2533
# ensure we don't pay silly lookup costs.
2534
searcher = source_graph._make_breadth_first_searcher(all_wanted_revs)
2535
null_set = frozenset([_mod_revision.NULL_REVISION])
2536
searcher_exhausted = False
2540
# Iterate the searcher until we have enough next_revs
2541
while len(next_revs) < self._walk_to_common_revisions_batch_size:
2543
next_revs_part, ghosts_part = searcher.next_with_ghosts()
2544
next_revs.update(next_revs_part)
2545
ghosts.update(ghosts_part)
2546
except StopIteration:
2547
searcher_exhausted = True
2549
# If there are ghosts in the source graph, and the caller asked for
2550
# them, make sure that they are present in the target.
2551
# We don't care about other ghosts as we can't fetch them and
2552
# haven't been asked to.
2553
ghosts_to_check = set(revision_ids.intersection(ghosts))
2554
revs_to_get = set(next_revs).union(ghosts_to_check)
2556
have_revs = set(target_graph.get_parent_map(revs_to_get))
2557
# we always have NULL_REVISION present.
2558
have_revs = have_revs.union(null_set)
2559
# Check if the target is missing any ghosts we need.
2560
ghosts_to_check.difference_update(have_revs)
2562
# One of the caller's revision_ids is a ghost in both the
2563
# source and the target.
2564
raise errors.NoSuchRevision(
2565
self.source, ghosts_to_check.pop())
2566
missing_revs.update(next_revs - have_revs)
2567
# Because we may have walked past the original stop point, make
2568
# sure everything is stopped
2569
stop_revs = searcher.find_seen_ancestors(have_revs)
2570
searcher.stop_searching_any(stop_revs)
2571
if searcher_exhausted:
2573
return searcher.get_result()
2576
def search_missing_revision_ids(self,
2577
revision_id=symbol_versioning.DEPRECATED_PARAMETER,
2578
find_ghosts=True, revision_ids=None, if_present_ids=None,
2580
"""Return the revision ids that source has that target does not.
2582
:param revision_id: only return revision ids included by this
2584
:param revision_ids: return revision ids included by these
2585
revision_ids. NoSuchRevision will be raised if any of these
2586
revisions are not present.
2587
:param if_present_ids: like revision_ids, but will not cause
2588
NoSuchRevision if any of these are absent, instead they will simply
2589
not be in the result. This is useful for e.g. finding revisions
2590
to fetch for tags, which may reference absent revisions.
2591
:param find_ghosts: If True find missing revisions in deep history
2592
rather than just finding the surface difference.
2593
:return: A bzrlib.graph.SearchResult.
2595
if symbol_versioning.deprecated_passed(revision_id):
2596
symbol_versioning.warn(
2597
'search_missing_revision_ids(revision_id=...) was '
2598
'deprecated in 2.4. Use revision_ids=[...] instead.',
2599
DeprecationWarning, stacklevel=2)
2600
if revision_ids is not None:
2601
raise AssertionError(
2602
'revision_ids is mutually exclusive with revision_id')
2603
if revision_id is not None:
2604
revision_ids = [revision_id]
2606
# stop searching at found target revisions.
2607
if not find_ghosts and (revision_ids is not None or if_present_ids is
2609
result = self._walk_to_common_revisions(revision_ids,
2610
if_present_ids=if_present_ids)
2613
result_set = result.get_keys()
2615
# generic, possibly worst case, slow code path.
2616
target_ids = set(self.target.all_revision_ids())
2617
source_ids = self._present_source_revisions_for(
2618
revision_ids, if_present_ids)
2619
result_set = set(source_ids).difference(target_ids)
2620
if limit is not None:
2621
topo_ordered = self.source.get_graph().iter_topo_order(result_set)
2622
result_set = set(itertools.islice(topo_ordered, limit))
2623
return self.source.revision_ids_to_search_result(result_set)
2625
def _present_source_revisions_for(self, revision_ids, if_present_ids=None):
2626
"""Returns set of all revisions in ancestry of revision_ids present in
2629
:param revision_ids: if None, all revisions in source are returned.
2630
:param if_present_ids: like revision_ids, but if any/all of these are
2631
absent no error is raised.
2633
if revision_ids is not None or if_present_ids is not None:
2634
# First, ensure all specified revisions exist. Callers expect
2635
# NoSuchRevision when they pass absent revision_ids here.
2636
if revision_ids is None:
2637
revision_ids = set()
2638
if if_present_ids is None:
2639
if_present_ids = set()
2640
revision_ids = set(revision_ids)
2641
if_present_ids = set(if_present_ids)
2642
all_wanted_ids = revision_ids.union(if_present_ids)
2643
graph = self.source.get_graph()
2644
present_revs = set(graph.get_parent_map(all_wanted_ids))
2645
missing = revision_ids.difference(present_revs)
2647
raise errors.NoSuchRevision(self.source, missing.pop())
2648
found_ids = all_wanted_ids.intersection(present_revs)
2649
source_ids = [rev_id for (rev_id, parents) in
2650
graph.iter_ancestry(found_ids)
2651
if rev_id != _mod_revision.NULL_REVISION
2652
and parents is not None]
2654
source_ids = self.source.all_revision_ids()
2655
return set(source_ids)
2658
def _get_repo_format_to_test(self):
2662
def is_compatible(cls, source, target):
2663
# The default implementation is compatible with everything
2664
return (source._format.supports_full_versioned_files and
2665
target._format.supports_full_versioned_files)
2668
class InterDifferingSerializer(InterVersionedFileRepository):
2452
2671
def _get_repo_format_to_test(self):