408
408
return self._get_delta(ie, basis_inv, path), True
410
410
def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
411
versionedfile = self.repository.weave_store.get_weave_or_empty(
412
file_id, self.repository.get_transaction())
413
# Don't change this to add_lines - add_lines_with_ghosts is cheaper
414
# than add_lines, and allows committing when a parent is ghosted for
416
411
# Note: as we read the content directly from the tree, we know its not
417
412
# been turned into unicode or badly split - but a broken tree
418
413
# implementation could give us bad output from readlines() so this is
419
414
# not a guarantee of safety. What would be better is always checking
420
415
# the content during test suite execution. RBC 20070912
421
return versionedfile.add_lines_with_ghosts(
422
self._new_revision_id, parents, new_lines,
416
parent_keys = tuple((file_id, parent) for parent in parents)
417
return self.repository.texts.add_lines(
418
(file_id, self._new_revision_id), parent_keys, new_lines,
423
419
nostore_sha=nostore_sha, random_id=self.random_revid,
424
420
check_content=False)[0:2]
453
449
The Repository builds on top of Stores and a Transport, which respectively
454
450
describe the disk data format and the way of accessing the (possibly
453
:ivar revisions: A bzrlib.versionedfile.VersionedFiles instance containing
454
the serialised revisions for the repository. This can be used to obtain
455
revision graph information or to access raw serialised revisions.
456
The result of trying to insert data into the repository via this store
457
is undefined: it should be considered read-only except for implementors
458
461
# What class to use for a CommitBuilder. Often its simpler to change this
510
513
if inv.root is None:
511
514
raise AssertionError()
512
515
inv_lines = self._serialise_inventory_to_lines(inv)
513
inv_vf = self.get_inventory_weave()
514
return self._inventory_add_lines(inv_vf, revision_id, parents,
516
return self._inventory_add_lines(revision_id, parents,
515
517
inv_lines, check_content=False)
517
def _inventory_add_lines(self, inv_vf, revision_id, parents, lines,
519
def _inventory_add_lines(self, revision_id, parents, lines,
518
520
check_content=True):
519
521
"""Store lines in inv_vf and return the sha1 of the inventory."""
521
for parent in parents:
523
final_parents.append(parent)
524
return inv_vf.add_lines(revision_id, final_parents, lines,
522
parents = [(parent,) for parent in parents]
523
return self.inventories.add_lines((revision_id,), parents, lines,
525
524
check_content=check_content)[0]
527
526
def add_revision(self, revision_id, rev, inv=None, config=None):
544
543
plaintext = Testament(rev, inv).as_short_text()
545
544
self.store_revision_signature(
546
545
gpg.GPGStrategy(config), plaintext, revision_id)
547
inventory_vf = self.get_inventory_weave()
548
if not revision_id in inventory_vf:
546
# check inventory present
547
if not self.inventories.get_parent_map([(revision_id,)]):
550
549
raise errors.WeaveRevisionNotPresent(revision_id,
553
552
# yes, this is not suitable for adding with ghosts.
554
553
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
557
rev.inventory_sha1 = inventory_vf.get_sha1s([revision_id])[0]
558
self._revision_store.add_revision(rev, self.get_transaction())
556
rev.inventory_sha1 = self.inventories.get_sha1s([(revision_id,)])[0]
557
self._add_revision(rev)
560
def _add_revision_text(self, revision_id, text):
561
revision = self._revision_store._serializer.read_revision_from_string(
563
self._revision_store._add_revision(revision, StringIO(text),
564
self.get_transaction())
559
def _add_revision(self, revision):
560
text = self._serializer.write_revision_to_string(revision)
561
key = (revision.revision_id,)
562
parents = tuple((parent,) for parent in revision.parent_ids)
563
self.revisions.add_lines(key, parents, osutils.split_lines(text))
566
565
def all_revision_ids(self):
567
566
"""Returns a list of all the revision ids in the repository.
607
606
"""Construct the current default format repository in a_bzrdir."""
608
607
return RepositoryFormat.get_default_format().initialize(a_bzrdir)
610
def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
609
def __init__(self, _format, a_bzrdir, control_files):
611
610
"""instantiate a Repository.
613
612
:param _format: The format of the repository on disk.
614
613
:param a_bzrdir: The BzrDir of the repository.
614
:param revisions: The revisions store for the repository.
616
616
In the future we will have a single api for all stores for
617
617
getting file texts, inventories and revisions, then
622
622
# the following are part of the public API for Repository:
623
623
self.bzrdir = a_bzrdir
624
624
self.control_files = control_files
625
self._revision_store = _revision_store
626
# backwards compatibility
627
self.weave_store = text_store
625
self._transport = control_files._transport
629
627
self._reconcile_does_inventory_gc = True
630
628
self._reconcile_fixes_text_parents = False
631
629
self._reconcile_backsup_inventory = True
632
630
# not right yet - should be more semantically clear ?
634
self.control_store = control_store
635
self.control_weaves = control_store
636
632
# TODO: make sure to construct the right store classes, etc, depending
637
633
# on whether escaping is required.
638
634
self._warn_if_deprecated()
762
758
last_revision.timezone)
764
760
# now gather global repository information
761
# XXX: This is available for many repos regardless of listability.
765
762
if self.bzrdir.root_transport.listable():
766
c, t = self._revision_store.total_size(self.get_transaction())
767
result['revisions'] = c
763
# XXX: do we want to __define len__() ?
764
result['revisions'] = len(self.revisions.keys())
771
768
def find_branches(self, using=False):
811
808
branches.extend(repository.find_branches())
814
def get_data_stream(self, revision_ids):
815
raise NotImplementedError(self.get_data_stream)
817
def get_data_stream_for_search(self, search_result):
818
"""Get a data stream that can be inserted to a repository.
820
:param search_result: A bzrlib.graph.SearchResult selecting the
822
:return: A data stream that can be inserted into a repository using
825
raise NotImplementedError(self.get_data_stream_for_search)
827
def insert_data_stream(self, stream):
828
"""XXX What does this really do?
830
Is it a substitute for fetch?
831
Should it manage its own write group ?
833
for item_key, bytes in stream:
834
if item_key[0] == 'file':
835
(file_id,) = item_key[1:]
836
knit = self.weave_store.get_weave_or_empty(
837
file_id, self.get_transaction())
838
elif item_key == ('inventory',):
839
knit = self.get_inventory_weave()
840
elif item_key == ('revisions',):
841
knit = self._revision_store.get_revision_file(
842
self.get_transaction())
843
elif item_key == ('signatures',):
844
knit = self._revision_store.get_signature_file(
845
self.get_transaction())
847
raise errors.RepositoryDataStreamError(
848
"Unrecognised data stream key '%s'" % (item_key,))
849
decoded_list = bencode.bdecode(bytes)
850
format = decoded_list.pop(0)
853
for version, options, parents, some_bytes in decoded_list:
854
data_list.append((version, options, len(some_bytes), parents))
855
knit_bytes += some_bytes
856
buffer = StringIO(knit_bytes)
857
def reader_func(count):
861
return buffer.read(count)
862
knit.insert_data_stream(
863
(format, data_list, reader_func))
866
812
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
867
813
"""Return the revision ids that other has that this does not.
1057
1003
"""True if this repository has a copy of the revision."""
1058
1004
return revision_id in self.has_revisions((revision_id,))
1060
1007
def has_revisions(self, revision_ids):
1061
1008
"""Probe to find out the presence of multiple revisions.
1063
1010
:param revision_ids: An iterable of revision_ids.
1064
1011
:return: A set of the revision_ids that were present.
1066
raise NotImplementedError(self.has_revisions)
1068
return self._revision_store.has_revision_id(revision_id,
1069
self.get_transaction())
1013
parent_map = self.revisions.get_parent_map(
1014
[(rev_id,) for rev_id in revision_ids])
1016
if _mod_revision.NULL_REVISION in revision_ids:
1017
result.add(_mod_revision.NULL_REVISION)
1018
result.update([key[0] for key in parent_map])
1071
1021
@needs_read_lock
1072
1022
def get_revision(self, revision_id):
1095
1045
for rev_id in revision_ids:
1096
1046
if not rev_id or not isinstance(rev_id, basestring):
1097
1047
raise errors.InvalidRevisionId(revision_id=rev_id, branch=self)
1098
revs = self._revision_store.get_revisions(revision_ids,
1099
self.get_transaction())
1048
keys = [(key,) for key in revision_ids]
1049
stream = self.revisions.get_record_stream(keys, 'unordered', True)
1051
for record in stream:
1052
if record.storage_kind == 'absent':
1053
raise errors.NoSuchRevision(self, record.key[0])
1054
text = record.get_bytes_as('fulltext')
1055
rev = self._serializer.read_revision_from_string(text)
1056
revs[record.key[0]] = rev
1057
return [revs[revid] for revid in revision_ids]
1102
1059
@needs_read_lock
1103
1060
def get_revision_xml(self, revision_id):
1107
1064
rev = self.get_revision(revision_id)
1108
1065
rev_tmp = StringIO()
1109
1066
# the current serializer..
1110
self._revision_store._serializer.write_revision(rev, rev_tmp)
1067
self._serializer.write_revision(rev, rev_tmp)
1111
1068
rev_tmp.seek(0)
1112
1069
return rev_tmp.getvalue()
1149
1106
@needs_write_lock
1150
1107
def add_signature_text(self, revision_id, signature):
1151
self._revision_store.add_revision_signature_text(revision_id,
1153
self.get_transaction())
1108
self.signatures.add_lines((revision_id,), (),
1109
osutils.split_lines(signature))
1155
1111
def find_text_key_references(self):
1156
1112
"""Find the text key references within the repository.
1163
1119
revision_id that they contain. The inventory texts from all present
1164
1120
revision ids are assessed to generate this report.
1166
revision_ids = self.all_revision_ids()
1167
w = self.get_inventory_weave()
1122
revision_keys = self.revisions.keys()
1123
w = self.inventories
1168
1124
pb = ui.ui_factory.nested_progress_bar()
1170
1126
return self._find_text_key_references_from_xml_inventory_lines(
1171
w.iter_lines_added_or_present_in_versions(revision_ids, pb=pb))
1127
w.iter_lines_added_or_present_in_keys(revision_keys, pb=pb))
1271
1227
setdefault = result.setdefault
1272
for file_id, revision_id in \
1273
1229
self._find_text_key_references_from_xml_inventory_lines(
1274
1230
line_iterator).iterkeys():
1275
1231
# once data is all ensured-consistent; then this is
1276
1232
# if revision_id == version_id
1277
if revision_id in revision_ids:
1278
setdefault(file_id, set()).add(revision_id)
1233
if key[-1:] in revision_ids:
1234
setdefault(key[0], set()).add(key[-1])
1281
1237
def fileids_altered_by_revision_ids(self, revision_ids, _inv_weave=None):
1288
1244
revision_ids. Each altered file-ids has the exact revision_ids that
1289
1245
altered it listed explicitly.
1291
selected_revision_ids = set(revision_ids)
1292
w = _inv_weave or self.get_inventory_weave()
1247
selected_keys = set((revid,) for revid in revision_ids)
1248
w = _inv_weave or self.inventories
1293
1249
pb = ui.ui_factory.nested_progress_bar()
1295
1251
return self._find_file_ids_from_xml_inventory_lines(
1296
w.iter_lines_added_or_present_in_versions(
1297
selected_revision_ids, pb=pb),
1298
selected_revision_ids)
1252
w.iter_lines_added_or_present_in_keys(
1253
selected_keys, pb=pb),
1313
1269
bytes_iterator is an iterable of bytestrings for the file. The
1314
1270
kind of iterable and length of the bytestrings are unspecified, but for
1315
this implementation, it is a list of lines produced by
1316
VersionedFile.get_lines().
1271
this implementation, it is a list of bytes produced by
1272
VersionedFile.get_record_stream().
1318
1274
:param desired_files: a list of (file_id, revision_id, identifier)
1321
1277
transaction = self.get_transaction()
1322
1279
for file_id, revision_id, callable_data in desired_files:
1324
weave = self.weave_store.get_weave(file_id, transaction)
1325
except errors.NoSuchFile:
1326
raise errors.NoSuchIdInRepository(self, file_id)
1327
yield callable_data, weave.get_lines(revision_id)
1280
text_keys[(file_id, revision_id)] = callable_data
1281
for record in self.texts.get_record_stream(text_keys, 'unordered', True):
1282
if record.storage_kind == 'absent':
1283
raise errors.RevisionNotPresent(record.key, self)
1284
yield text_keys[record.key], record.get_bytes_as('fulltext')
1329
1286
def _generate_text_key_index(self, text_key_references=None,
1330
1287
ancestors=None):
1481
1438
yield ("revisions", None, revision_ids)
1483
1440
@needs_read_lock
1484
def get_inventory_weave(self):
1485
return self.control_weaves.get_weave('inventory',
1486
self.get_transaction())
1489
1441
def get_inventory(self, revision_id):
1490
1442
"""Get Inventory object by revision id."""
1491
1443
return self.iter_inventories([revision_id]).next()
1507
1459
def _iter_inventories(self, revision_ids):
1508
1460
"""single-document based inventory iteration."""
1509
texts = self.get_inventory_weave().get_texts(revision_ids)
1510
for text, revision_id in zip(texts, revision_ids):
1461
for text, revision_id in self._iter_inventory_xmls(revision_ids):
1511
1462
yield self.deserialise_inventory(revision_id, text)
1464
def _iter_inventory_xmls(self, revision_ids):
1465
keys = [(revision_id,) for revision_id in revision_ids]
1466
stream = self.inventories.get_record_stream(keys, 'unordered', True)
1468
for record in stream:
1469
if record.storage_kind != 'absent':
1470
texts[record.key] = record.get_bytes_as('fulltext')
1472
raise errors.NoSuchRevision(self, record.key)
1474
yield texts[key], key[-1]
1513
1476
def deserialise_inventory(self, revision_id, xml):
1514
1477
"""Transform the xml into an inventory object.
1534
1497
@needs_read_lock
1535
1498
def get_inventory_xml(self, revision_id):
1536
1499
"""Get inventory XML as a file object."""
1500
texts = self._iter_inventory_xmls([revision_id])
1538
iw = self.get_inventory_weave()
1539
return iw.get_text(revision_id)
1502
text, revision_id = texts.next()
1503
except StopIteration:
1541
1504
raise errors.HistoryMissing(self, 'inventory', revision_id)
1543
1507
@needs_read_lock
1544
1508
def get_inventory_sha1(self, revision_id):
1646
1610
if not self.has_revision(revision_id):
1647
1611
raise errors.NoSuchRevision(self, revision_id)
1648
w = self.get_inventory_weave()
1649
candidates = w.get_ancestry(revision_id, topo_sorted)
1650
return [None] + candidates # self._eliminate_revisions_not_present(candidates)
1612
graph = self.get_graph()
1614
search = graph._make_breadth_first_searcher([revision_id])
1617
found, ghosts = search.next_with_ghosts()
1618
except StopIteration:
1621
if _mod_revision.NULL_REVISION in keys:
1622
keys.remove(_mod_revision.NULL_REVISION)
1624
parent_map = graph.get_parent_map(keys)
1625
keys = tsort.topo_sort(parent_map)
1626
return [None] + list(keys)
1652
1628
def pack(self):
1653
1629
"""Compress the data within the repository.
1684
1660
@deprecated_method(symbol_versioning.one_five)
1685
1661
def revision_parents(self, revision_id):
1686
return self.get_inventory_weave().parent_names(revision_id)
1662
return self.get_revision(revision_id).parent_ids
1688
1664
@deprecated_method(symbol_versioning.one_one)
1689
1665
def get_parents(self, revision_ids):
1763
1739
@needs_read_lock
1764
1740
def has_signature_for_revision_id(self, revision_id):
1765
1741
"""Query for a revision signature for revision_id in the repository."""
1766
return self._revision_store.has_signature(revision_id,
1767
self.get_transaction())
1742
if not self.has_revision(revision_id):
1743
raise errors.NoSuchRevision(self, revision_id)
1744
sig_present = (1 == len(
1745
self.signatures.get_parent_map([(revision_id,)])))
1769
1748
@needs_read_lock
1770
1749
def get_signature_text(self, revision_id):
1771
1750
"""Return the text for a signature."""
1772
return self._revision_store.get_signature_text(revision_id,
1773
self.get_transaction())
1751
stream = self.signatures.get_record_stream([(revision_id,)],
1753
record = stream.next()
1754
if record.storage_kind == 'absent':
1755
raise errors.NoSuchRevision(self, revision_id)
1756
return record.get_bytes_as('fulltext')
1775
1758
@needs_read_lock
1776
1759
def check(self, revision_ids=None):
1903
1886
path, root = entries.next()
1904
1887
if root.revision != rev.revision_id:
1905
1888
raise errors.IncompatibleRevision(repr(repository))
1890
for path, ie in entries:
1891
text_keys[(ie.file_id, ie.revision)] = ie
1892
text_parent_map = repository.texts.get_parent_map(text_keys)
1893
missing_texts = set(text_keys) - set(text_parent_map)
1906
1894
# Add the texts that are not already present
1907
for path, ie in entries:
1908
w = repository.weave_store.get_weave_or_empty(ie.file_id,
1909
repository.get_transaction())
1910
if ie.revision not in w:
1912
# FIXME: TODO: The following loop *may* be overlapping/duplicate
1913
# with InventoryEntry.find_previous_heads(). if it is, then there
1914
# is a latent bug here where the parents may have ancestors of each
1916
for revision, tree in parent_trees.iteritems():
1917
if ie.file_id not in tree:
1919
parent_id = tree.inventory[ie.file_id].revision
1920
if parent_id in text_parents:
1922
text_parents.append(parent_id)
1924
vfile = repository.weave_store.get_weave_or_empty(ie.file_id,
1925
repository.get_transaction())
1926
lines = revision_tree.get_file(ie.file_id).readlines()
1927
vfile.add_lines(rev.revision_id, text_parents, lines)
1895
for text_key in missing_texts:
1896
ie = text_keys[text_key]
1898
# FIXME: TODO: The following loop overlaps/duplicates that done by
1899
# commit to determine parents. There is a latent/real bug here where
1900
# the parents inserted are not those commit would do - in particular
1901
# they are not filtered by heads(). RBC, AB
1902
for revision, tree in parent_trees.iteritems():
1903
if ie.file_id not in tree:
1905
parent_id = tree.inventory[ie.file_id].revision
1906
if parent_id in text_parents:
1908
text_parents.append((ie.file_id, parent_id))
1909
lines = revision_tree.get_file(ie.file_id).readlines()
1910
repository.texts.add_lines(text_key, text_parents, lines)
1929
1912
# install the inventory
1930
1913
repository.add_inventory(rev.revision_id, inv, present_parents)
1938
1921
class MetaDirRepository(Repository):
1939
1922
"""Repositories in the new meta-dir layout."""
1941
def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
1942
super(MetaDirRepository, self).__init__(_format,
1924
def __init__(self, _format, a_bzrdir, control_files):
1925
super(MetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
1948
1926
dir_mode = self.control_files._dir_mode
1949
1927
file_mode = self.control_files._file_mode
1979
1957
class MetaDirVersionedFileRepository(MetaDirRepository):
1980
1958
"""Repositories in a meta-dir, that work via versioned file objects."""
1982
def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
1960
def __init__(self, _format, a_bzrdir, control_files):
1983
1961
super(MetaDirVersionedFileRepository, self).__init__(_format, a_bzrdir,
1984
control_files, _revision_store, control_store, text_store)
1985
_revision_store.get_scope = self.get_transaction
1986
control_store.get_scope = self.get_transaction
1987
text_store.get_scope = self.get_transaction
1990
1965
class RepositoryFormatRegistry(registry.Registry):
2086
2061
from bzrlib import bzrdir
2087
2062
return bzrdir.format_registry.make_bzrdir('default').repository_format
2089
def _get_control_store(self, repo_transport, control_files):
2090
"""Return the control store for this repository."""
2091
raise NotImplementedError(self._get_control_store)
2093
2064
def get_format_string(self):
2094
2065
"""Return the ASCII format string that identifies this format.
2102
2073
"""Return the short description for this format."""
2103
2074
raise NotImplementedError(self.get_format_description)
2105
def _get_revision_store(self, repo_transport, control_files):
2106
"""Return the revision store object for this a_bzrdir."""
2107
raise NotImplementedError(self._get_revision_store)
2109
def _get_text_rev_store(self,
2116
"""Common logic for getting a revision store for a repository.
2118
see self._get_revision_store for the subclass-overridable method to
2119
get the store for a repository.
2121
from bzrlib.store.revision.text import TextRevisionStore
2122
dir_mode = control_files._dir_mode
2123
file_mode = control_files._file_mode
2124
text_store = TextStore(transport.clone(name),
2126
compressed=compressed,
2128
file_mode=file_mode)
2129
_revision_store = TextRevisionStore(text_store, serializer)
2130
return _revision_store
2132
2076
# TODO: this shouldn't be in the base class, it's specific to things that
2133
2077
# use weaves or knits -- mbp 20070207
2134
2078
def _get_versioned_file_store(self,
2509
2453
if self.source.control_files._transport.listable():
2510
2454
pb = ui.ui_factory.nested_progress_bar()
2512
self.target.weave_store.copy_all_ids(
2513
self.source.weave_store,
2515
from_transaction=self.source.get_transaction(),
2516
to_transaction=self.target.get_transaction())
2456
self.target.texts.insert_record_stream(
2457
self.source.texts.get_record_stream(
2458
self.source.texts.keys(), 'topological', False))
2517
2459
pb.update('copying inventory', 0, 1)
2518
self.target.control_weaves.copy_multi(
2519
self.source.control_weaves, ['inventory'],
2520
from_transaction=self.source.get_transaction(),
2521
to_transaction=self.target.get_transaction())
2522
self.target._revision_store.text_store.copy_all_ids(
2523
self.source._revision_store.text_store,
2460
self.target.inventories.insert_record_stream(
2461
self.source.inventories.get_record_stream(
2462
self.source.inventories.keys(), 'topological', False))
2463
self.target.signatures.insert_record_stream(
2464
self.source.signatures.get_record_stream(
2465
self.source.signatures.keys(),
2467
self.target.revisions.insert_record_stream(
2468
self.source.revisions.get_record_stream(
2469
self.source.revisions.keys(),
2470
'topological', True))
2894
2840
return len(revision_ids), 0
2897
class InterRemoteToOther(InterRepository):
2899
def __init__(self, source, target):
2900
InterRepository.__init__(self, source, target)
2901
self._real_inter = None
2904
def is_compatible(source, target):
2905
if not isinstance(source, remote.RemoteRepository):
2907
# Is source's model compatible with target's model?
2908
source._ensure_real()
2909
real_source = source._real_repository
2910
if isinstance(real_source, remote.RemoteRepository):
2911
raise NotImplementedError(
2912
"We don't support remote repos backed by remote repos yet.")
2913
return InterRepository._same_model(real_source, target)
2916
def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2917
"""See InterRepository.fetch()."""
2918
from bzrlib.fetch import RemoteToOtherFetcher
2919
mutter("Using fetch logic to copy between %s(remote) and %s(%s)",
2920
self.source, self.target, self.target._format)
2921
# TODO: jam 20070210 This should be an assert, not a translate
2922
revision_id = osutils.safe_revision_id(revision_id)
2923
f = RemoteToOtherFetcher(to_repository=self.target,
2924
from_repository=self.source,
2925
last_revision=revision_id,
2926
pb=pb, find_ghosts=find_ghosts)
2927
return f.count_copied, f.failed_revisions
2930
def _get_repo_format_to_test(self):
2934
2843
class InterOtherToRemote(InterRepository):
2936
2845
def __init__(self, source, target):
3062
2970
self.repository = repository
3063
2971
self.text_index = self.repository._generate_text_key_index()
3065
def calculate_file_version_parents(self, revision_id, file_id):
2973
def calculate_file_version_parents(self, text_key):
3066
2974
"""Calculate the correct parents for a file version according to
3067
2975
the inventories.
3069
parent_keys = self.text_index[(file_id, revision_id)]
2977
parent_keys = self.text_index[text_key]
3070
2978
if parent_keys == [_mod_revision.NULL_REVISION]:
3072
# strip the file_id, for the weave api
3073
return tuple([revision_id for file_id, revision_id in parent_keys])
2980
return tuple(parent_keys)
3075
def check_file_version_parents(self, weave, file_id):
2982
def check_file_version_parents(self, texts, progress_bar=None):
3076
2983
"""Check the parents stored in a versioned file are correct.
3078
2985
It also detects file versions that are not referenced by their
3086
2993
file, but not used by the corresponding inventory.
3088
2995
wrong_parents = {}
3089
unused_versions = set()
3090
versions = weave.versions()
3091
parent_map = weave.get_parent_map(versions)
3092
for num, revision_id in enumerate(versions):
2996
self.file_ids = set([file_id for file_id, _ in
2997
self.text_index.iterkeys()])
2998
# text keys is now grouped by file_id
2999
n_weaves = len(self.file_ids)
3000
files_in_revisions = {}
3001
revisions_of_files = {}
3002
n_versions = len(self.text_index)
3003
progress_bar.update('loading text store', 0, n_versions)
3004
parent_map = self.repository.texts.get_parent_map(self.text_index)
3005
# On unlistable transports this could well be empty/error...
3006
text_keys = self.repository.texts.keys()
3007
unused_keys = frozenset(text_keys) - set(self.text_index)
3008
for num, key in enumerate(self.text_index.iterkeys()):
3009
if progress_bar is not None:
3010
progress_bar.update('checking text graph', num, n_versions)
3011
correct_parents = self.calculate_file_version_parents(key)
3094
correct_parents = self.calculate_file_version_parents(
3095
revision_id, file_id)
3097
# The version is not part of the used keys.
3098
unused_versions.add(revision_id)
3101
knit_parents = tuple(parent_map[revision_id])
3102
except errors.RevisionNotPresent:
3104
if correct_parents != knit_parents:
3105
wrong_parents[revision_id] = (knit_parents, correct_parents)
3106
return wrong_parents, unused_versions
3013
knit_parents = parent_map[key]
3014
except errors.RevisionNotPresent:
3017
if correct_parents != knit_parents:
3018
wrong_parents[key] = (knit_parents, correct_parents)
3019
return wrong_parents, unused_keys
3109
3022
def _old_get_graph(repository, revision_id):