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
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]
450
446
revisions and file history. It's normally accessed only by the Branch,
451
447
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
449
The Repository builds on top of some byte storage facilies (the revisions,
450
signatures, inventories and texts attributes) and a Transport, which
451
respectively provide byte storage and a means to access the (possibly
454
The byte storage facilities are addressed via tuples, which we refer to
455
as 'keys' throughout the code base. Revision_keys, inventory_keys and
456
signature_keys are all 1-tuples: (revision_id,). text_keys are two-tuples:
457
(file_id, revision_id). We use this interface because it allows low
458
friction with the underlying code that implements disk indices, network
459
encoding and other parts of bzrlib.
461
:ivar revisions: A bzrlib.versionedfile.VersionedFiles instance containing
462
the serialised revisions for the repository. This can be used to obtain
463
revision graph information or to access raw serialised revisions.
464
The result of trying to insert data into the repository via this store
465
is undefined: it should be considered read-only except for implementors
467
:ivar signatures: A bzrlib.versionedfile.VersionedFiles instance containing
468
the serialised signatures for the repository. This can be used to
469
obtain access to raw serialised signatures. The result of trying to
470
insert data into the repository via this store is undefined: it should
471
be considered read-only except for implementors of repositories.
472
:ivar inventories: A bzrlib.versionedfile.VersionedFiles instance containing
473
the serialised inventories for the repository. This can be used to
474
obtain unserialised inventories. The result of trying to insert data
475
into the repository via this store is undefined: it should be
476
considered read-only except for implementors of repositories.
477
:ivar texts: A bzrlib.versionedfile.VersionedFiles instance containing the
478
texts of files and directories for the repository. This can be used to
479
obtain file texts or file graphs. Note that Repository.iter_file_bytes
480
is usually a better interface for accessing file texts.
481
The result of trying to insert data into the repository via this store
482
is undefined: it should be considered read-only except for implementors
457
484
:ivar _transport: Transport for file access to repository, typically
458
485
pointing to .bzr/repository.
513
540
if inv.root is None:
514
541
raise AssertionError()
515
542
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,
543
return self._inventory_add_lines(revision_id, parents,
518
544
inv_lines, check_content=False)
520
def _inventory_add_lines(self, inv_vf, revision_id, parents, lines,
546
def _inventory_add_lines(self, revision_id, parents, lines,
521
547
check_content=True):
522
548
"""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,
549
parents = [(parent,) for parent in parents]
550
return self.inventories.add_lines((revision_id,), parents, lines,
528
551
check_content=check_content)[0]
530
553
def add_revision(self, revision_id, rev, inv=None, config=None):
547
570
plaintext = Testament(rev, inv).as_short_text()
548
571
self.store_revision_signature(
549
572
gpg.GPGStrategy(config), plaintext, revision_id)
550
inventory_vf = self.get_inventory_weave()
551
if not revision_id in inventory_vf:
573
# check inventory present
574
if not self.inventories.get_parent_map([(revision_id,)]):
553
576
raise errors.WeaveRevisionNotPresent(revision_id,
556
579
# yes, this is not suitable for adding with ghosts.
557
580
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())
583
rev.inventory_sha1 = self.inventories.get_sha1s([(revision_id,)])[0]
584
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())
586
def _add_revision(self, revision):
587
text = self._serializer.write_revision_to_string(revision)
588
key = (revision.revision_id,)
589
parents = tuple((parent,) for parent in revision.parent_ids)
590
self.revisions.add_lines(key, parents, osutils.split_lines(text))
569
592
def all_revision_ids(self):
570
593
"""Returns a list of all the revision ids in the repository.
610
633
"""Construct the current default format repository in a_bzrdir."""
611
634
return RepositoryFormat.get_default_format().initialize(a_bzrdir)
613
def __init__(self, _format, a_bzrdir, control_files,
614
_revision_store, control_store, text_store):
636
def __init__(self, _format, a_bzrdir, control_files):
615
637
"""instantiate a Repository.
617
639
:param _format: The format of the repository on disk.
628
650
self.control_files = control_files
629
651
self._transport = control_files._transport
630
652
self.base = self._transport.base
631
self._revision_store = _revision_store
632
# backwards compatibility
633
self.weave_store = text_store
635
654
self._reconcile_does_inventory_gc = True
636
655
self._reconcile_fixes_text_parents = False
637
656
self._reconcile_backsup_inventory = True
638
657
# not right yet - should be more semantically clear ?
640
self.control_store = control_store
641
self.control_weaves = control_store
642
659
# TODO: make sure to construct the right store classes, etc, depending
643
660
# on whether escaping is required.
644
661
self._warn_if_deprecated()
766
783
last_revision.timezone)
768
785
# now gather global repository information
786
# XXX: This is available for many repos regardless of listability.
769
787
if self.bzrdir.root_transport.listable():
770
c, t = self._revision_store.total_size(self.get_transaction())
771
result['revisions'] = c
788
# XXX: do we want to __define len__() ?
789
# Maybe the versionedfiles object should provide a different
790
# method to get the number of keys.
791
result['revisions'] = len(self.revisions.keys())
775
795
def find_branches(self, using=False):
815
835
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
839
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
874
840
"""Return the revision ids that other has that this does not.
1064
1030
"""True if this repository has a copy of the revision."""
1065
1031
return revision_id in self.has_revisions((revision_id,))
1067
1034
def has_revisions(self, revision_ids):
1068
1035
"""Probe to find out the presence of multiple revisions.
1070
1037
:param revision_ids: An iterable of revision_ids.
1071
1038
: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())
1040
parent_map = self.revisions.get_parent_map(
1041
[(rev_id,) for rev_id in revision_ids])
1043
if _mod_revision.NULL_REVISION in revision_ids:
1044
result.add(_mod_revision.NULL_REVISION)
1045
result.update([key[0] for key in parent_map])
1078
1048
@needs_read_lock
1079
1049
def get_revision(self, revision_id):
1102
1072
for rev_id in revision_ids:
1103
1073
if not rev_id or not isinstance(rev_id, basestring):
1104
1074
raise errors.InvalidRevisionId(revision_id=rev_id, branch=self)
1105
revs = self._revision_store.get_revisions(revision_ids,
1106
self.get_transaction())
1075
keys = [(key,) for key in revision_ids]
1076
stream = self.revisions.get_record_stream(keys, 'unordered', True)
1078
for record in stream:
1079
if record.storage_kind == 'absent':
1080
raise errors.NoSuchRevision(self, record.key[0])
1081
text = record.get_bytes_as('fulltext')
1082
rev = self._serializer.read_revision_from_string(text)
1083
revs[record.key[0]] = rev
1084
return [revs[revid] for revid in revision_ids]
1109
1086
@needs_read_lock
1110
1087
def get_revision_xml(self, revision_id):
1114
1091
rev = self.get_revision(revision_id)
1115
1092
rev_tmp = StringIO()
1116
1093
# the current serializer..
1117
self._revision_store._serializer.write_revision(rev, rev_tmp)
1094
self._serializer.write_revision(rev, rev_tmp)
1118
1095
rev_tmp.seek(0)
1119
1096
return rev_tmp.getvalue()
1156
1133
@needs_write_lock
1157
1134
def add_signature_text(self, revision_id, signature):
1158
self._revision_store.add_revision_signature_text(revision_id,
1160
self.get_transaction())
1135
self.signatures.add_lines((revision_id,), (),
1136
osutils.split_lines(signature))
1162
1138
def find_text_key_references(self):
1163
1139
"""Find the text key references within the repository.
1170
1146
revision_id that they contain. The inventory texts from all present
1171
1147
revision ids are assessed to generate this report.
1173
revision_ids = self.all_revision_ids()
1174
w = self.get_inventory_weave()
1149
revision_keys = self.revisions.keys()
1150
w = self.inventories
1175
1151
pb = ui.ui_factory.nested_progress_bar()
1177
1153
return self._find_text_key_references_from_xml_inventory_lines(
1178
w.iter_lines_added_or_present_in_versions(revision_ids, pb=pb))
1154
w.iter_lines_added_or_present_in_keys(revision_keys, pb=pb))
1278
1254
setdefault = result.setdefault
1279
for file_id, revision_id in \
1280
1256
self._find_text_key_references_from_xml_inventory_lines(
1281
1257
line_iterator).iterkeys():
1282
1258
# once data is all ensured-consistent; then this is
1283
1259
# if revision_id == version_id
1284
if revision_id in revision_ids:
1285
setdefault(file_id, set()).add(revision_id)
1260
if key[-1:] in revision_ids:
1261
setdefault(key[0], set()).add(key[-1])
1288
1264
def fileids_altered_by_revision_ids(self, revision_ids, _inv_weave=None):
1295
1271
revision_ids. Each altered file-ids has the exact revision_ids that
1296
1272
altered it listed explicitly.
1298
selected_revision_ids = set(revision_ids)
1299
w = _inv_weave or self.get_inventory_weave()
1274
selected_keys = set((revid,) for revid in revision_ids)
1275
w = _inv_weave or self.inventories
1300
1276
pb = ui.ui_factory.nested_progress_bar()
1302
1278
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)
1279
w.iter_lines_added_or_present_in_keys(
1280
selected_keys, pb=pb),
1320
1296
bytes_iterator is an iterable of bytestrings for the file. The
1321
1297
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().
1298
this implementation, it is a list of bytes produced by
1299
VersionedFile.get_record_stream().
1325
1301
:param desired_files: a list of (file_id, revision_id, identifier)
1328
1304
transaction = self.get_transaction()
1329
1306
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)
1307
text_keys[(file_id, revision_id)] = callable_data
1308
for record in self.texts.get_record_stream(text_keys, 'unordered', True):
1309
if record.storage_kind == 'absent':
1310
raise errors.RevisionNotPresent(record.key, self)
1311
yield text_keys[record.key], record.get_bytes_as('fulltext')
1336
1313
def _generate_text_key_index(self, text_key_references=None,
1337
1314
ancestors=None):
1488
1465
yield ("revisions", None, revision_ids)
1490
1467
@needs_read_lock
1491
def get_inventory_weave(self):
1492
return self.control_weaves.get_weave('inventory',
1493
self.get_transaction())
1496
1468
def get_inventory(self, revision_id):
1497
1469
"""Get Inventory object by revision id."""
1498
1470
return self.iter_inventories([revision_id]).next()
1514
1486
def _iter_inventories(self, revision_ids):
1515
1487
"""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):
1488
for text, revision_id in self._iter_inventory_xmls(revision_ids):
1518
1489
yield self.deserialise_inventory(revision_id, text)
1491
def _iter_inventory_xmls(self, revision_ids):
1492
keys = [(revision_id,) for revision_id in revision_ids]
1493
stream = self.inventories.get_record_stream(keys, 'unordered', True)
1495
for record in stream:
1496
if record.storage_kind != 'absent':
1497
texts[record.key] = record.get_bytes_as('fulltext')
1499
raise errors.NoSuchRevision(self, record.key)
1501
yield texts[key], key[-1]
1520
1503
def deserialise_inventory(self, revision_id, xml):
1521
1504
"""Transform the xml into an inventory object.
1541
1524
@needs_read_lock
1542
1525
def get_inventory_xml(self, revision_id):
1543
1526
"""Get inventory XML as a file object."""
1527
texts = self._iter_inventory_xmls([revision_id])
1545
iw = self.get_inventory_weave()
1546
return iw.get_text(revision_id)
1529
text, revision_id = texts.next()
1530
except StopIteration:
1548
1531
raise errors.HistoryMissing(self, 'inventory', revision_id)
1550
1534
@needs_read_lock
1551
1535
def get_inventory_sha1(self, revision_id):
1653
1637
if not self.has_revision(revision_id):
1654
1638
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)
1639
graph = self.get_graph()
1641
search = graph._make_breadth_first_searcher([revision_id])
1644
found, ghosts = search.next_with_ghosts()
1645
except StopIteration:
1648
if _mod_revision.NULL_REVISION in keys:
1649
keys.remove(_mod_revision.NULL_REVISION)
1651
parent_map = graph.get_parent_map(keys)
1652
keys = tsort.topo_sort(parent_map)
1653
return [None] + list(keys)
1659
1655
def pack(self):
1660
1656
"""Compress the data within the repository.
1768
1764
@needs_read_lock
1769
1765
def has_signature_for_revision_id(self, revision_id):
1770
1766
"""Query for a revision signature for revision_id in the repository."""
1771
return self._revision_store.has_signature(revision_id,
1772
self.get_transaction())
1767
if not self.has_revision(revision_id):
1768
raise errors.NoSuchRevision(self, revision_id)
1769
sig_present = (1 == len(
1770
self.signatures.get_parent_map([(revision_id,)])))
1774
1773
@needs_read_lock
1775
1774
def get_signature_text(self, revision_id):
1776
1775
"""Return the text for a signature."""
1777
return self._revision_store.get_signature_text(revision_id,
1778
self.get_transaction())
1776
stream = self.signatures.get_record_stream([(revision_id,)],
1778
record = stream.next()
1779
if record.storage_kind == 'absent':
1780
raise errors.NoSuchRevision(self, revision_id)
1781
return record.get_bytes_as('fulltext')
1780
1783
@needs_read_lock
1781
1784
def check(self, revision_ids=None):
1908
1911
path, root = entries.next()
1909
1912
if root.revision != rev.revision_id:
1910
1913
raise errors.IncompatibleRevision(repr(repository))
1915
for path, ie in entries:
1916
text_keys[(ie.file_id, ie.revision)] = ie
1917
text_parent_map = repository.texts.get_parent_map(text_keys)
1918
missing_texts = set(text_keys) - set(text_parent_map)
1911
1919
# 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)
1920
for text_key in missing_texts:
1921
ie = text_keys[text_key]
1923
# FIXME: TODO: The following loop overlaps/duplicates that done by
1924
# commit to determine parents. There is a latent/real bug here where
1925
# the parents inserted are not those commit would do - in particular
1926
# they are not filtered by heads(). RBC, AB
1927
for revision, tree in parent_trees.iteritems():
1928
if ie.file_id not in tree:
1930
parent_id = tree.inventory[ie.file_id].revision
1931
if parent_id in text_parents:
1933
text_parents.append((ie.file_id, parent_id))
1934
lines = revision_tree.get_file(ie.file_id).readlines()
1935
repository.texts.add_lines(text_key, text_parents, lines)
1934
1937
# install the inventory
1935
1938
repository.add_inventory(rev.revision_id, inv, present_parents)
1947
1950
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,
1953
def __init__(self, _format, a_bzrdir, control_files):
1954
super(MetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
1957
1955
self._transport = control_files._transport
1959
1957
@needs_read_lock
1988
1986
class MetaDirVersionedFileRepository(MetaDirRepository):
1989
1987
"""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):
1989
def __init__(self, _format, a_bzrdir, control_files):
1992
1990
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
1994
class RepositoryFormatRegistry(registry.Registry):
2095
2090
from bzrlib import bzrdir
2096
2091
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
2093
def get_format_string(self):
2103
2094
"""Return the ASCII format string that identifies this format.
2111
2102
"""Return the short description for this format."""
2112
2103
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
2105
# TODO: this shouldn't be in the base class, it's specific to things that
2142
2106
# use weaves or knits -- mbp 20070207
2143
2107
def _get_versioned_file_store(self,
2520
2484
if self.source._transport.listable():
2521
2485
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())
2487
self.target.texts.insert_record_stream(
2488
self.source.texts.get_record_stream(
2489
self.source.texts.keys(), 'topological', False))
2528
2490
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,
2491
self.target.inventories.insert_record_stream(
2492
self.source.inventories.get_record_stream(
2493
self.source.inventories.keys(), 'topological', False))
2494
self.target.signatures.insert_record_stream(
2495
self.source.signatures.get_record_stream(
2496
self.source.signatures.keys(),
2498
self.target.revisions.insert_record_stream(
2499
self.source.revisions.get_record_stream(
2500
self.source.revisions.keys(),
2501
'topological', True))
2905
2871
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
2874
class InterOtherToRemote(InterRepository):
2947
2876
def __init__(self, source, target):
3073
3001
self.repository = repository
3074
3002
self.text_index = self.repository._generate_text_key_index()
3076
def calculate_file_version_parents(self, revision_id, file_id):
3004
def calculate_file_version_parents(self, text_key):
3077
3005
"""Calculate the correct parents for a file version according to
3078
3006
the inventories.
3080
parent_keys = self.text_index[(file_id, revision_id)]
3008
parent_keys = self.text_index[text_key]
3081
3009
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])
3011
return tuple(parent_keys)
3086
def check_file_version_parents(self, weave, file_id):
3013
def check_file_version_parents(self, texts, progress_bar=None):
3087
3014
"""Check the parents stored in a versioned file are correct.
3089
3016
It also detects file versions that are not referenced by their
3097
3024
file, but not used by the corresponding inventory.
3099
3026
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):
3027
self.file_ids = set([file_id for file_id, _ in
3028
self.text_index.iterkeys()])
3029
# text keys is now grouped by file_id
3030
n_weaves = len(self.file_ids)
3031
files_in_revisions = {}
3032
revisions_of_files = {}
3033
n_versions = len(self.text_index)
3034
progress_bar.update('loading text store', 0, n_versions)
3035
parent_map = self.repository.texts.get_parent_map(self.text_index)
3036
# On unlistable transports this could well be empty/error...
3037
text_keys = self.repository.texts.keys()
3038
unused_keys = frozenset(text_keys) - set(self.text_index)
3039
for num, key in enumerate(self.text_index.iterkeys()):
3040
if progress_bar is not None:
3041
progress_bar.update('checking text graph', num, n_versions)
3042
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
3044
knit_parents = parent_map[key]
3045
except errors.RevisionNotPresent:
3048
if correct_parents != knit_parents:
3049
wrong_parents[key] = (knit_parents, correct_parents)
3050
return wrong_parents, unused_keys
3120
3053
def _old_get_graph(repository, revision_id):