2446
2465
return wrong_parents, unused_keys
2449
class InterDifferingSerializer(InterRepository):
2468
class InterVersionedFileRepository(InterRepository):
2470
_walk_to_common_revisions_batch_size = 50
2473
def fetch(self, revision_id=None, find_ghosts=False,
2475
"""Fetch the content required to construct revision_id.
2477
The content is copied from self.source to self.target.
2479
:param revision_id: if None all content is copied, if NULL_REVISION no
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)
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.
2499
:param revision_ids: The start point for the search.
2500
:return: A set of revision ids.
2502
target_graph = self.target.get_graph()
2503
revision_ids = frozenset(revision_ids)
2505
all_wanted_revs = revision_ids.union(if_present_ids)
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
2517
# Iterate the searcher until we have enough next_revs
2518
while len(next_revs) < self._walk_to_common_revisions_batch_size:
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
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)
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)
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:
2550
return searcher.get_result()
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,
2557
"""Return the revision ids that source has that target does not.
2559
:param revision_id: only return revision ids included by this
2561
:param revision_ids: return revision ids included by these
2562
revision_ids. NoSuchRevision will be raised if any of these
2563
revisions are not present.
2564
:param if_present_ids: like revision_ids, but will not cause
2565
NoSuchRevision if any of these are absent, instead they will simply
2566
not be in the result. This is useful for e.g. finding revisions
2567
to fetch for tags, which may reference absent revisions.
2568
:param find_ghosts: If True find missing revisions in deep history
2569
rather than just finding the surface difference.
2570
:return: A bzrlib.graph.SearchResult.
2572
if symbol_versioning.deprecated_passed(revision_id):
2573
symbol_versioning.warn(
2574
'search_missing_revision_ids(revision_id=...) was '
2575
'deprecated in 2.4. Use revision_ids=[...] instead.',
2576
DeprecationWarning, stacklevel=2)
2577
if revision_ids is not None:
2578
raise AssertionError(
2579
'revision_ids is mutually exclusive with revision_id')
2580
if revision_id is not None:
2581
revision_ids = [revision_id]
2583
# stop searching at found target revisions.
2584
if not find_ghosts and (revision_ids is not None or if_present_ids is
2586
result = self._walk_to_common_revisions(revision_ids,
2587
if_present_ids=if_present_ids)
2588
result_set = result.get_keys()
2590
# generic, possibly worst case, slow code path.
2591
target_ids = set(self.target.all_revision_ids())
2592
source_ids = self._present_source_revisions_for(
2593
revision_ids, if_present_ids)
2594
result_set = set(source_ids).difference(target_ids)
2595
if limit is not None:
2596
graph = self.source.get_graph()
2597
topo_ordered = list(graph.iter_topo_order(result_set))
2598
result_set = set(topo_ordered[:limit])
2599
return self.source.revision_ids_to_search_result(result_set)
2601
def _present_source_revisions_for(self, revision_ids, if_present_ids=None):
2602
"""Returns set of all revisions in ancestry of revision_ids present in
2605
:param revision_ids: if None, all revisions in source are returned.
2606
:param if_present_ids: like revision_ids, but if any/all of these are
2607
absent no error is raised.
2609
if revision_ids is not None or if_present_ids is not None:
2610
# First, ensure all specified revisions exist. Callers expect
2611
# NoSuchRevision when they pass absent revision_ids here.
2612
if revision_ids is None:
2613
revision_ids = set()
2614
if if_present_ids is None:
2615
if_present_ids = set()
2616
revision_ids = set(revision_ids)
2617
if_present_ids = set(if_present_ids)
2618
all_wanted_ids = revision_ids.union(if_present_ids)
2619
graph = self.source.get_graph()
2620
present_revs = set(graph.get_parent_map(all_wanted_ids))
2621
missing = revision_ids.difference(present_revs)
2623
raise errors.NoSuchRevision(self.source, missing.pop())
2624
found_ids = all_wanted_ids.intersection(present_revs)
2625
source_ids = [rev_id for (rev_id, parents) in
2626
graph.iter_ancestry(found_ids)
2627
if rev_id != _mod_revision.NULL_REVISION
2628
and parents is not None]
2630
source_ids = self.source.all_revision_ids()
2631
return set(source_ids)
2634
def _get_repo_format_to_test(self):
2638
def is_compatible(cls, source, target):
2639
# The default implementation is compatible with everything
2640
return (source._format.supports_full_versioned_files and
2641
target._format.supports_full_versioned_files)
2644
class InterDifferingSerializer(InterVersionedFileRepository):
2452
2647
def _get_repo_format_to_test(self):