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