1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
408
412
return self._get_delta(ie, basis_inv, path), True
410
414
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
415
# Note: as we read the content directly from the tree, we know its not
417
416
# been turned into unicode or badly split - but a broken tree
418
417
# implementation could give us bad output from readlines() so this is
419
418
# not a guarantee of safety. What would be better is always checking
420
419
# the content during test suite execution. RBC 20070912
421
return versionedfile.add_lines_with_ghosts(
422
self._new_revision_id, parents, new_lines,
420
parent_keys = tuple((file_id, parent) for parent in parents)
421
return self.repository.texts.add_lines(
422
(file_id, self._new_revision_id), parent_keys, new_lines,
423
423
nostore_sha=nostore_sha, random_id=self.random_revid,
424
424
check_content=False)[0:2]
450
450
revisions and file history. It's normally accessed only by the Branch,
451
451
which views a particular line of development through that history.
453
The Repository builds on top of Stores and a Transport, which respectively
454
describe the disk data format and the way of accessing the (possibly
453
The Repository builds on top of some byte storage facilies (the revisions,
454
signatures, inventories and texts attributes) and a Transport, which
455
respectively provide byte storage and a means to access the (possibly
458
The byte storage facilities are addressed via tuples, which we refer to
459
as 'keys' throughout the code base. Revision_keys, inventory_keys and
460
signature_keys are all 1-tuples: (revision_id,). text_keys are two-tuples:
461
(file_id, revision_id). We use this interface because it allows low
462
friction with the underlying code that implements disk indices, network
463
encoding and other parts of bzrlib.
465
:ivar revisions: A bzrlib.versionedfile.VersionedFiles instance containing
466
the serialised revisions for the repository. This can be used to obtain
467
revision graph information or to access raw serialised revisions.
468
The result of trying to insert data into the repository via this store
469
is undefined: it should be considered read-only except for implementors
471
:ivar signatures: A bzrlib.versionedfile.VersionedFiles instance containing
472
the serialised signatures for the repository. This can be used to
473
obtain access to raw serialised signatures. The result of trying to
474
insert data into the repository via this store is undefined: it should
475
be considered read-only except for implementors of repositories.
476
:ivar inventories: A bzrlib.versionedfile.VersionedFiles instance containing
477
the serialised inventories for the repository. This can be used to
478
obtain unserialised inventories. The result of trying to insert data
479
into the repository via this store is undefined: it should be
480
considered read-only except for implementors of repositories.
481
:ivar texts: A bzrlib.versionedfile.VersionedFiles instance containing the
482
texts of files and directories for the repository. This can be used to
483
obtain file texts or file graphs. Note that Repository.iter_file_bytes
484
is usually a better interface for accessing file texts.
485
The result of trying to insert data into the repository via this store
486
is undefined: it should be considered read-only except for implementors
457
488
:ivar _transport: Transport for file access to repository, typically
458
489
pointing to .bzr/repository.
513
544
if inv.root is None:
514
545
raise AssertionError()
515
546
inv_lines = self._serialise_inventory_to_lines(inv)
516
inv_vf = self.get_inventory_weave()
517
return self._inventory_add_lines(inv_vf, revision_id, parents,
547
return self._inventory_add_lines(revision_id, parents,
518
548
inv_lines, check_content=False)
520
def _inventory_add_lines(self, inv_vf, revision_id, parents, lines,
550
def _inventory_add_lines(self, revision_id, parents, lines,
521
551
check_content=True):
522
552
"""Store lines in inv_vf and return the sha1 of the inventory."""
524
for parent in parents:
526
final_parents.append(parent)
527
return inv_vf.add_lines(revision_id, final_parents, lines,
553
parents = [(parent,) for parent in parents]
554
return self.inventories.add_lines((revision_id,), parents, lines,
528
555
check_content=check_content)[0]
530
557
def add_revision(self, revision_id, rev, inv=None, config=None):
547
574
plaintext = Testament(rev, inv).as_short_text()
548
575
self.store_revision_signature(
549
576
gpg.GPGStrategy(config), plaintext, revision_id)
550
inventory_vf = self.get_inventory_weave()
551
if not revision_id in inventory_vf:
577
# check inventory present
578
if not self.inventories.get_parent_map([(revision_id,)]):
553
580
raise errors.WeaveRevisionNotPresent(revision_id,
556
583
# yes, this is not suitable for adding with ghosts.
557
584
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
560
rev.inventory_sha1 = inventory_vf.get_sha1s([revision_id])[0]
561
self._revision_store.add_revision(rev, self.get_transaction())
588
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
589
self._add_revision(rev)
563
def _add_revision_text(self, revision_id, text):
564
revision = self._revision_store._serializer.read_revision_from_string(
566
self._revision_store._add_revision(revision, StringIO(text),
567
self.get_transaction())
591
def _add_revision(self, revision):
592
text = self._serializer.write_revision_to_string(revision)
593
key = (revision.revision_id,)
594
parents = tuple((parent,) for parent in revision.parent_ids)
595
self.revisions.add_lines(key, parents, osutils.split_lines(text))
569
597
def all_revision_ids(self):
570
598
"""Returns a list of all the revision ids in the repository.
610
638
"""Construct the current default format repository in a_bzrdir."""
611
639
return RepositoryFormat.get_default_format().initialize(a_bzrdir)
613
def __init__(self, _format, a_bzrdir, control_files,
614
_revision_store, control_store, text_store):
641
def __init__(self, _format, a_bzrdir, control_files):
615
642
"""instantiate a Repository.
617
644
:param _format: The format of the repository on disk.
628
655
self.control_files = control_files
629
656
self._transport = control_files._transport
630
657
self.base = self._transport.base
631
self._revision_store = _revision_store
632
# backwards compatibility
633
self.weave_store = text_store
635
659
self._reconcile_does_inventory_gc = True
636
660
self._reconcile_fixes_text_parents = False
637
661
self._reconcile_backsup_inventory = True
638
662
# not right yet - should be more semantically clear ?
640
self.control_store = control_store
641
self.control_weaves = control_store
642
664
# TODO: make sure to construct the right store classes, etc, depending
643
665
# on whether escaping is required.
644
666
self._warn_if_deprecated()
766
788
last_revision.timezone)
768
790
# now gather global repository information
791
# XXX: This is available for many repos regardless of listability.
769
792
if self.bzrdir.root_transport.listable():
770
c, t = self._revision_store.total_size(self.get_transaction())
771
result['revisions'] = c
793
# XXX: do we want to __define len__() ?
794
# Maybe the versionedfiles object should provide a different
795
# method to get the number of keys.
796
result['revisions'] = len(self.revisions.keys())
775
800
def find_branches(self, using=False):
815
840
branches.extend(repository.find_branches())
818
def get_data_stream(self, revision_ids):
819
raise NotImplementedError(self.get_data_stream)
821
def get_data_stream_for_search(self, search_result):
822
"""Get a data stream that can be inserted to a repository.
824
:param search_result: A bzrlib.graph.SearchResult selecting the
826
:return: A data stream that can be inserted into a repository using
829
raise NotImplementedError(self.get_data_stream_for_search)
831
def insert_data_stream(self, stream):
832
"""XXX What does this really do?
834
Is it a substitute for fetch?
835
Should it manage its own write group ?
837
revisions_inserted = 0
838
for item_key, bytes in stream:
839
if item_key[0] == 'file':
840
(file_id,) = item_key[1:]
841
knit = self.weave_store.get_weave_or_empty(
842
file_id, self.get_transaction())
843
elif item_key == ('inventory',):
844
knit = self.get_inventory_weave()
845
elif item_key == ('revisions',):
846
knit = self._revision_store.get_revision_file(
847
self.get_transaction())
848
revisions_inserted += 1
849
elif item_key == ('signatures',):
850
knit = self._revision_store.get_signature_file(
851
self.get_transaction())
853
raise errors.RepositoryDataStreamError(
854
"Unrecognised data stream key '%s'" % (item_key,))
855
decoded_list = bencode.bdecode(bytes)
856
format = decoded_list.pop(0)
859
for version, options, parents, some_bytes in decoded_list:
860
data_list.append((version, options, len(some_bytes), parents))
861
knit_bytes += some_bytes
862
buffer = StringIO(knit_bytes)
863
def reader_func(count):
867
return buffer.read(count)
868
knit.insert_data_stream(
869
(format, data_list, reader_func))
870
return revisions_inserted
873
844
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
874
845
"""Return the revision ids that other has that this does not.
880
851
return InterRepository.get(other, self).search_missing_revision_ids(
881
852
revision_id, find_ghosts)
883
@deprecated_method(symbol_versioning.one_two)
854
@deprecated_method(one_two)
885
856
def missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
886
857
"""Return the revision ids that other has that this does not.
1064
1035
"""True if this repository has a copy of the revision."""
1065
1036
return revision_id in self.has_revisions((revision_id,))
1067
1039
def has_revisions(self, revision_ids):
1068
1040
"""Probe to find out the presence of multiple revisions.
1070
1042
:param revision_ids: An iterable of revision_ids.
1071
1043
:return: A set of the revision_ids that were present.
1073
raise NotImplementedError(self.has_revisions)
1075
return self._revision_store.has_revision_id(revision_id,
1076
self.get_transaction())
1045
parent_map = self.revisions.get_parent_map(
1046
[(rev_id,) for rev_id in revision_ids])
1048
if _mod_revision.NULL_REVISION in revision_ids:
1049
result.add(_mod_revision.NULL_REVISION)
1050
result.update([key[0] for key in parent_map])
1078
1053
@needs_read_lock
1079
1054
def get_revision(self, revision_id):
1102
1077
for rev_id in revision_ids:
1103
1078
if not rev_id or not isinstance(rev_id, basestring):
1104
1079
raise errors.InvalidRevisionId(revision_id=rev_id, branch=self)
1105
revs = self._revision_store.get_revisions(revision_ids,
1106
self.get_transaction())
1080
keys = [(key,) for key in revision_ids]
1081
stream = self.revisions.get_record_stream(keys, 'unordered', True)
1083
for record in stream:
1084
if record.storage_kind == 'absent':
1085
raise errors.NoSuchRevision(self, record.key[0])
1086
text = record.get_bytes_as('fulltext')
1087
rev = self._serializer.read_revision_from_string(text)
1088
revs[record.key[0]] = rev
1089
return [revs[revid] for revid in revision_ids]
1109
1091
@needs_read_lock
1110
1092
def get_revision_xml(self, revision_id):
1114
1096
rev = self.get_revision(revision_id)
1115
1097
rev_tmp = StringIO()
1116
1098
# the current serializer..
1117
self._revision_store._serializer.write_revision(rev, rev_tmp)
1099
self._serializer.write_revision(rev, rev_tmp)
1118
1100
rev_tmp.seek(0)
1119
1101
return rev_tmp.getvalue()
1156
1138
@needs_write_lock
1157
1139
def add_signature_text(self, revision_id, signature):
1158
self._revision_store.add_revision_signature_text(revision_id,
1160
self.get_transaction())
1140
self.signatures.add_lines((revision_id,), (),
1141
osutils.split_lines(signature))
1162
1143
def find_text_key_references(self):
1163
1144
"""Find the text key references within the repository.
1170
1151
revision_id that they contain. The inventory texts from all present
1171
1152
revision ids are assessed to generate this report.
1173
revision_ids = self.all_revision_ids()
1174
w = self.get_inventory_weave()
1154
revision_keys = self.revisions.keys()
1155
w = self.inventories
1175
1156
pb = ui.ui_factory.nested_progress_bar()
1177
1158
return self._find_text_key_references_from_xml_inventory_lines(
1178
w.iter_lines_added_or_present_in_versions(revision_ids, pb=pb))
1159
w.iter_lines_added_or_present_in_keys(revision_keys, pb=pb))
1278
1259
setdefault = result.setdefault
1279
for file_id, revision_id in \
1280
1261
self._find_text_key_references_from_xml_inventory_lines(
1281
1262
line_iterator).iterkeys():
1282
1263
# once data is all ensured-consistent; then this is
1283
1264
# if revision_id == version_id
1284
if revision_id in revision_ids:
1285
setdefault(file_id, set()).add(revision_id)
1265
if key[-1:] in revision_ids:
1266
setdefault(key[0], set()).add(key[-1])
1288
1269
def fileids_altered_by_revision_ids(self, revision_ids, _inv_weave=None):
1295
1276
revision_ids. Each altered file-ids has the exact revision_ids that
1296
1277
altered it listed explicitly.
1298
selected_revision_ids = set(revision_ids)
1299
w = _inv_weave or self.get_inventory_weave()
1279
selected_keys = set((revid,) for revid in revision_ids)
1280
w = _inv_weave or self.inventories
1300
1281
pb = ui.ui_factory.nested_progress_bar()
1302
1283
return self._find_file_ids_from_xml_inventory_lines(
1303
w.iter_lines_added_or_present_in_versions(
1304
selected_revision_ids, pb=pb),
1305
selected_revision_ids)
1284
w.iter_lines_added_or_present_in_keys(
1285
selected_keys, pb=pb),
1320
1301
bytes_iterator is an iterable of bytestrings for the file. The
1321
1302
kind of iterable and length of the bytestrings are unspecified, but for
1322
this implementation, it is a list of lines produced by
1323
VersionedFile.get_lines().
1303
this implementation, it is a list of bytes produced by
1304
VersionedFile.get_record_stream().
1325
1306
:param desired_files: a list of (file_id, revision_id, identifier)
1328
1309
transaction = self.get_transaction()
1329
1311
for file_id, revision_id, callable_data in desired_files:
1331
weave = self.weave_store.get_weave(file_id, transaction)
1332
except errors.NoSuchFile:
1333
raise errors.NoSuchIdInRepository(self, file_id)
1334
yield callable_data, weave.get_lines(revision_id)
1312
text_keys[(file_id, revision_id)] = callable_data
1313
for record in self.texts.get_record_stream(text_keys, 'unordered', True):
1314
if record.storage_kind == 'absent':
1315
raise errors.RevisionNotPresent(record.key, self)
1316
yield text_keys[record.key], record.get_bytes_as('fulltext')
1336
1318
def _generate_text_key_index(self, text_key_references=None,
1337
1319
ancestors=None):
1488
1470
yield ("revisions", None, revision_ids)
1490
1472
@needs_read_lock
1491
def get_inventory_weave(self):
1492
return self.control_weaves.get_weave('inventory',
1493
self.get_transaction())
1496
1473
def get_inventory(self, revision_id):
1497
1474
"""Get Inventory object by revision id."""
1498
1475
return self.iter_inventories([revision_id]).next()
1514
1491
def _iter_inventories(self, revision_ids):
1515
1492
"""single-document based inventory iteration."""
1516
texts = self.get_inventory_weave().get_texts(revision_ids)
1517
for text, revision_id in zip(texts, revision_ids):
1493
for text, revision_id in self._iter_inventory_xmls(revision_ids):
1518
1494
yield self.deserialise_inventory(revision_id, text)
1496
def _iter_inventory_xmls(self, revision_ids):
1497
keys = [(revision_id,) for revision_id in revision_ids]
1498
stream = self.inventories.get_record_stream(keys, 'unordered', True)
1500
for record in stream:
1501
if record.storage_kind != 'absent':
1502
texts[record.key] = record.get_bytes_as('fulltext')
1504
raise errors.NoSuchRevision(self, record.key)
1506
yield texts[key], key[-1]
1520
1508
def deserialise_inventory(self, revision_id, xml):
1521
1509
"""Transform the xml into an inventory object.
1541
1529
@needs_read_lock
1542
1530
def get_inventory_xml(self, revision_id):
1543
1531
"""Get inventory XML as a file object."""
1532
texts = self._iter_inventory_xmls([revision_id])
1545
iw = self.get_inventory_weave()
1546
return iw.get_text(revision_id)
1534
text, revision_id = texts.next()
1535
except StopIteration:
1548
1536
raise errors.HistoryMissing(self, 'inventory', revision_id)
1550
1539
@needs_read_lock
1551
1540
def get_inventory_sha1(self, revision_id):
1653
1642
if not self.has_revision(revision_id):
1654
1643
raise errors.NoSuchRevision(self, revision_id)
1655
w = self.get_inventory_weave()
1656
candidates = w.get_ancestry(revision_id, topo_sorted)
1657
return [None] + candidates # self._eliminate_revisions_not_present(candidates)
1644
graph = self.get_graph()
1646
search = graph._make_breadth_first_searcher([revision_id])
1649
found, ghosts = search.next_with_ghosts()
1650
except StopIteration:
1653
if _mod_revision.NULL_REVISION in keys:
1654
keys.remove(_mod_revision.NULL_REVISION)
1656
parent_map = graph.get_parent_map(keys)
1657
keys = tsort.topo_sort(parent_map)
1658
return [None] + list(keys)
1659
1660
def pack(self):
1660
1661
"""Compress the data within the repository.
1688
1690
def get_transaction(self):
1689
1691
return self.control_files.get_transaction()
1691
@deprecated_method(symbol_versioning.one_one)
1693
@deprecated_method(one_one)
1692
1694
def get_parents(self, revision_ids):
1693
1695
"""See StackedParentsProvider.get_parents"""
1694
1696
parent_map = self.get_parent_map(revision_ids)
1768
1770
@needs_read_lock
1769
1771
def has_signature_for_revision_id(self, revision_id):
1770
1772
"""Query for a revision signature for revision_id in the repository."""
1771
return self._revision_store.has_signature(revision_id,
1772
self.get_transaction())
1773
if not self.has_revision(revision_id):
1774
raise errors.NoSuchRevision(self, revision_id)
1775
sig_present = (1 == len(
1776
self.signatures.get_parent_map([(revision_id,)])))
1774
1779
@needs_read_lock
1775
1780
def get_signature_text(self, revision_id):
1776
1781
"""Return the text for a signature."""
1777
return self._revision_store.get_signature_text(revision_id,
1778
self.get_transaction())
1782
stream = self.signatures.get_record_stream([(revision_id,)],
1784
record = stream.next()
1785
if record.storage_kind == 'absent':
1786
raise errors.NoSuchRevision(self, revision_id)
1787
return record.get_bytes_as('fulltext')
1780
1789
@needs_read_lock
1781
1790
def check(self, revision_ids=None):
1908
1917
path, root = entries.next()
1909
1918
if root.revision != rev.revision_id:
1910
1919
raise errors.IncompatibleRevision(repr(repository))
1921
for path, ie in entries:
1922
text_keys[(ie.file_id, ie.revision)] = ie
1923
text_parent_map = repository.texts.get_parent_map(text_keys)
1924
missing_texts = set(text_keys) - set(text_parent_map)
1911
1925
# Add the texts that are not already present
1912
for path, ie in entries:
1913
w = repository.weave_store.get_weave_or_empty(ie.file_id,
1914
repository.get_transaction())
1915
if ie.revision not in w:
1917
# FIXME: TODO: The following loop *may* be overlapping/duplicate
1918
# with InventoryEntry.find_previous_heads(). if it is, then there
1919
# is a latent bug here where the parents may have ancestors of each
1921
for revision, tree in parent_trees.iteritems():
1922
if ie.file_id not in tree:
1924
parent_id = tree.inventory[ie.file_id].revision
1925
if parent_id in text_parents:
1927
text_parents.append(parent_id)
1929
vfile = repository.weave_store.get_weave_or_empty(ie.file_id,
1930
repository.get_transaction())
1931
lines = revision_tree.get_file(ie.file_id).readlines()
1932
vfile.add_lines(rev.revision_id, text_parents, lines)
1926
for text_key in missing_texts:
1927
ie = text_keys[text_key]
1929
# FIXME: TODO: The following loop overlaps/duplicates that done by
1930
# commit to determine parents. There is a latent/real bug here where
1931
# the parents inserted are not those commit would do - in particular
1932
# they are not filtered by heads(). RBC, AB
1933
for revision, tree in parent_trees.iteritems():
1934
if ie.file_id not in tree:
1936
parent_id = tree.inventory[ie.file_id].revision
1937
if parent_id in text_parents:
1939
text_parents.append((ie.file_id, parent_id))
1940
lines = revision_tree.get_file(ie.file_id).readlines()
1941
repository.texts.add_lines(text_key, text_parents, lines)
1934
1943
# install the inventory
1935
1944
repository.add_inventory(rev.revision_id, inv, present_parents)
1947
1956
typically pointing to .bzr/repository.
1950
def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
1951
super(MetaDirRepository, self).__init__(_format,
1959
def __init__(self, _format, a_bzrdir, control_files):
1960
super(MetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
1957
1961
self._transport = control_files._transport
1959
1963
@needs_read_lock
1988
1992
class MetaDirVersionedFileRepository(MetaDirRepository):
1989
1993
"""Repositories in a meta-dir, that work via versioned file objects."""
1991
def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
1995
def __init__(self, _format, a_bzrdir, control_files):
1992
1996
super(MetaDirVersionedFileRepository, self).__init__(_format, a_bzrdir,
1993
control_files, _revision_store, control_store, text_store)
1994
_revision_store.get_scope = self.get_transaction
1995
control_store.get_scope = self.get_transaction
1996
text_store.get_scope = self.get_transaction
1999
2000
class RepositoryFormatRegistry(registry.Registry):
2095
2096
from bzrlib import bzrdir
2096
2097
return bzrdir.format_registry.make_bzrdir('default').repository_format
2098
def _get_control_store(self, repo_transport, control_files):
2099
"""Return the control store for this repository."""
2100
raise NotImplementedError(self._get_control_store)
2102
2099
def get_format_string(self):
2103
2100
"""Return the ASCII format string that identifies this format.
2111
2108
"""Return the short description for this format."""
2112
2109
raise NotImplementedError(self.get_format_description)
2114
def _get_revision_store(self, repo_transport, control_files):
2115
"""Return the revision store object for this a_bzrdir."""
2116
raise NotImplementedError(self._get_revision_store)
2118
def _get_text_rev_store(self,
2125
"""Common logic for getting a revision store for a repository.
2127
see self._get_revision_store for the subclass-overridable method to
2128
get the store for a repository.
2130
from bzrlib.store.revision.text import TextRevisionStore
2131
dir_mode = control_files._dir_mode
2132
file_mode = control_files._file_mode
2133
text_store = TextStore(transport.clone(name),
2135
compressed=compressed,
2137
file_mode=file_mode)
2138
_revision_store = TextRevisionStore(text_store, serializer)
2139
return _revision_store
2141
2111
# TODO: this shouldn't be in the base class, it's specific to things that
2142
2112
# use weaves or knits -- mbp 20070207
2143
2113
def _get_versioned_file_store(self,
2366
2336
searcher.stop_searching_any(have_revs)
2367
2337
return searcher.get_result()
2369
@deprecated_method(symbol_versioning.one_two)
2339
@deprecated_method(one_two)
2370
2340
@needs_read_lock
2371
2341
def missing_revision_ids(self, revision_id=None, find_ghosts=True):
2372
2342
"""Return the revision ids that source has that target does not.
2520
2490
if self.source._transport.listable():
2521
2491
pb = ui.ui_factory.nested_progress_bar()
2523
self.target.weave_store.copy_all_ids(
2524
self.source.weave_store,
2526
from_transaction=self.source.get_transaction(),
2527
to_transaction=self.target.get_transaction())
2493
self.target.texts.insert_record_stream(
2494
self.source.texts.get_record_stream(
2495
self.source.texts.keys(), 'topological', False))
2528
2496
pb.update('copying inventory', 0, 1)
2529
self.target.control_weaves.copy_multi(
2530
self.source.control_weaves, ['inventory'],
2531
from_transaction=self.source.get_transaction(),
2532
to_transaction=self.target.get_transaction())
2533
self.target._revision_store.text_store.copy_all_ids(
2534
self.source._revision_store.text_store,
2497
self.target.inventories.insert_record_stream(
2498
self.source.inventories.get_record_stream(
2499
self.source.inventories.keys(), 'topological', False))
2500
self.target.signatures.insert_record_stream(
2501
self.source.signatures.get_record_stream(
2502
self.source.signatures.keys(),
2504
self.target.revisions.insert_record_stream(
2505
self.source.revisions.get_record_stream(
2506
self.source.revisions.keys(),
2507
'topological', True))
2905
2877
return len(revision_ids), 0
2908
class InterRemoteToOther(InterRepository):
2910
def __init__(self, source, target):
2911
InterRepository.__init__(self, source, target)
2912
self._real_inter = None
2915
def is_compatible(source, target):
2916
if not isinstance(source, remote.RemoteRepository):
2918
# Is source's model compatible with target's model?
2919
source._ensure_real()
2920
real_source = source._real_repository
2921
if isinstance(real_source, remote.RemoteRepository):
2922
raise NotImplementedError(
2923
"We don't support remote repos backed by remote repos yet.")
2924
return InterRepository._same_model(real_source, target)
2927
def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2928
"""See InterRepository.fetch()."""
2929
from bzrlib.fetch import RemoteToOtherFetcher
2930
mutter("Using fetch logic to copy between %s(remote) and %s(%s)",
2931
self.source, self.target, self.target._format)
2932
# TODO: jam 20070210 This should be an assert, not a translate
2933
revision_id = osutils.safe_revision_id(revision_id)
2934
f = RemoteToOtherFetcher(to_repository=self.target,
2935
from_repository=self.source,
2936
last_revision=revision_id,
2937
pb=pb, find_ghosts=find_ghosts)
2938
return f.count_copied, f.failed_revisions
2941
def _get_repo_format_to_test(self):
2945
2880
class InterOtherToRemote(InterRepository):
2947
2882
def __init__(self, source, target):
2967
2902
def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2968
2903
self._ensure_real_inter()
2969
self._real_inter.fetch(revision_id=revision_id, pb=pb,
2904
return self._real_inter.fetch(revision_id=revision_id, pb=pb,
2970
2905
find_ghosts=find_ghosts)
3073
3007
self.repository = repository
3074
3008
self.text_index = self.repository._generate_text_key_index()
3076
def calculate_file_version_parents(self, revision_id, file_id):
3010
def calculate_file_version_parents(self, text_key):
3077
3011
"""Calculate the correct parents for a file version according to
3078
3012
the inventories.
3080
parent_keys = self.text_index[(file_id, revision_id)]
3014
parent_keys = self.text_index[text_key]
3081
3015
if parent_keys == [_mod_revision.NULL_REVISION]:
3083
# strip the file_id, for the weave api
3084
return tuple([revision_id for file_id, revision_id in parent_keys])
3017
return tuple(parent_keys)
3086
def check_file_version_parents(self, weave, file_id):
3019
def check_file_version_parents(self, texts, progress_bar=None):
3087
3020
"""Check the parents stored in a versioned file are correct.
3089
3022
It also detects file versions that are not referenced by their
3097
3030
file, but not used by the corresponding inventory.
3099
3032
wrong_parents = {}
3100
unused_versions = set()
3101
versions = weave.versions()
3102
parent_map = weave.get_parent_map(versions)
3103
for num, revision_id in enumerate(versions):
3033
self.file_ids = set([file_id for file_id, _ in
3034
self.text_index.iterkeys()])
3035
# text keys is now grouped by file_id
3036
n_weaves = len(self.file_ids)
3037
files_in_revisions = {}
3038
revisions_of_files = {}
3039
n_versions = len(self.text_index)
3040
progress_bar.update('loading text store', 0, n_versions)
3041
parent_map = self.repository.texts.get_parent_map(self.text_index)
3042
# On unlistable transports this could well be empty/error...
3043
text_keys = self.repository.texts.keys()
3044
unused_keys = frozenset(text_keys) - set(self.text_index)
3045
for num, key in enumerate(self.text_index.iterkeys()):
3046
if progress_bar is not None:
3047
progress_bar.update('checking text graph', num, n_versions)
3048
correct_parents = self.calculate_file_version_parents(key)
3105
correct_parents = self.calculate_file_version_parents(
3106
revision_id, file_id)
3108
# The version is not part of the used keys.
3109
unused_versions.add(revision_id)
3112
knit_parents = tuple(parent_map[revision_id])
3113
except errors.RevisionNotPresent:
3115
if correct_parents != knit_parents:
3116
wrong_parents[revision_id] = (knit_parents, correct_parents)
3117
return wrong_parents, unused_versions
3050
knit_parents = parent_map[key]
3051
except errors.RevisionNotPresent:
3054
if correct_parents != knit_parents:
3055
wrong_parents[key] = (knit_parents, correct_parents)
3056
return wrong_parents, unused_keys
3120
3059
def _old_get_graph(repository, revision_id):