499
498
def add_inventory(self, revision_id, inv, parents):
500
499
"""Add the inventory inv to the repository as revision_id.
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.
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.
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]
529
528
def add_revision(self, revision_id, rev, inv=None, config=None):
530
529
"""Add rev to the revision store as revision_id.
774
773
:param using: If True, list only branches using this repository.
775
if using and not self.is_shared():
777
return [self.bzrdir.open_branch()]
778
except errors.NotBranchError:
777
780
class Evaluator(object):
779
782
def __init__(self):
808
811
def get_data_stream(self, revision_ids):
809
812
raise NotImplementedError(self.get_data_stream)
814
def get_data_stream_for_search(self, search_result):
815
"""Get a data stream that can be inserted to a repository.
817
:param search_result: A bzrlib.graph.SearchResult selecting the
819
:return: A data stream that can be inserted into a repository using
822
raise NotImplementedError(self.get_data_stream_for_search)
811
824
def insert_data_stream(self, stream):
812
825
"""XXX What does this really do?
828
841
knit = self._revision_store.get_signature_file(
829
842
self.get_transaction())
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):
858
return buffer.read(count)
840
859
knit.insert_data_stream(
841
(format, data_list, StringIO(knit_bytes).read))
860
(format, data_list, reader_func))
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.
866
These are returned in topological order.
868
revision_id: only return revision ids included by revision_id.
870
return InterRepository.get(other, self).search_missing_revision_ids(
871
revision_id, find_ghosts)
873
@deprecated_method(symbol_versioning.one_two)
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.
849
880
revision_id: only return revision ids included by revision_id.
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()
886
parents = other.get_graph().get_parent_map(keys)
889
return tsort.topo_sort(parents)
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,))
1057
def has_revisions(self, revision_ids):
1058
"""Probe to find out the presence of multiple revisions.
1060
:param revision_ids: An iterable of revision_ids.
1061
:return: A set of the revision_ids that were present.
1063
raise NotImplementedError(self.has_revisions)
1020
1065
return self._revision_store.has_revision_id(revision_id,
1021
1066
self.get_transaction())
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()
1497
def iter_inventories(self, revision_ids):
1498
"""Get many inventories by revision_ids.
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
1504
:return: An iterator of inventories.
1506
assert None not in revision_ids
1507
assert _mod_revision.NULL_REVISION not in revision_ids
1508
return self._iter_inventories(revision_ids)
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)
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.
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))
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.
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)
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] = ()
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:
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,)
1770
parent_ids = tuple(parent_id_list)
1705
1771
parent_map[revision_id] = parent_ids
1706
1772
return parent_map
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)
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)
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.
1810
1888
raise NotImplementedError(self.revision_graph_can_have_wrong_parents)
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)])
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.
1854
1933
Accepts an iterable of revision, tree, signature tuples. The signature
1857
1936
repository.start_write_group()
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)
1941
pb.update('Transferring revisions', n + 1, num_revisions)
1862
1943
repository.abort_write_group()
2267
2348
(copied, failures).
2269
2350
raise NotImplementedError(self.fetch)
2352
def _walk_to_common_revisions(self, revision_ids):
2353
"""Walk out from revision_ids in source to revisions target has.
2355
:param revision_ids: The start point for the search.
2356
:return: A set of revision ids.
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])
2366
next_revs, ghosts = searcher.next_with_ghosts()
2367
except StopIteration:
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))
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()
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.
2277
2392
:param revision_id: only return revision ids included by this
2394
:param find_ghosts: If True find missing revisions in deep history
2395
rather than just finding the surface difference.
2397
return list(self.search_missing_revision_ids(
2398
revision_id, find_ghosts).get_keys())
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.
2404
:param revision_id: only return revision ids included by this
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.
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:
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)
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,
2487
pb=pb, find_ghosts=find_ghosts)
2358
2488
return f.count_copied, f.failed_revisions
2432
2562
f = GenericRepoFetcher(to_repository=self.target,
2433
2563
from_repository=self.source,
2434
2564
last_revision=revision_id,
2565
pb=pb, find_ghosts=find_ghosts)
2436
2566
return f.count_copied, f.failed_revisions
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
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)
2608
self.source._eliminate_revisions_not_present(required_revisions))
2609
return self.source.revision_ids_to_search_result(result_set)
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,
2645
pb=pb, find_ghosts=find_ghosts)
2514
2646
return f.count_copied, f.failed_revisions
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
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)
2677
self.source._eliminate_revisions_not_present(required_revisions))
2678
return self.source.revision_ids_to_search_result(result_set)
2547
2681
class InterPackRepo(InterSameDataRepository):
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()
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().
2618
:param find_ghosts: Find ghosts throughough the ancestry of
2752
:param find_ghosts: Find ghosts throughout the ancestry of
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])
2626
self.target._pack_collection.revision_index.combined_index
2627
null_set = frozenset([_mod_revision.NULL_REVISION])
2630
next_revs = set(searcher.next())
2631
except StopIteration:
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)
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)
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,
2792
pb=pb, find_ghosts=find_ghosts)
2681
2793
return f.count_copied, f.failed_revisions
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,
2849
pb=pb, find_ghosts=find_ghosts)
2738
2850
return f.count_copied, f.failed_revisions
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,
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())
2889
my_pb = ui.ui_factory.nested_progress_bar()
2894
install_revisions(self.target, revisions_iterator(),
2895
len(revision_ids), pb)
2897
if my_pb is not None:
2775
2899
return len(revision_ids), 0
2785
2909
def is_compatible(source, target):
2786
2910
if not isinstance(source, remote.RemoteRepository):
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)
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,
2930
pb=pb, find_ghosts=find_ghosts)
2809
2931
return f.count_copied, f.failed_revisions
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)
2843
2966
def _get_repo_format_to_test(self):