37
39
revision as _mod_revision,
42
45
from bzrlib.bundle import serializer
43
46
from bzrlib.revisiontree import RevisionTree
44
47
from bzrlib.store.versioned import VersionedFileStore
48
from bzrlib.store.text import TextStore
45
49
from bzrlib.testament import Testament
50
from bzrlib.util import bencode
48
from bzrlib import registry
49
53
from bzrlib.decorators import needs_read_lock, needs_write_lock
50
54
from bzrlib.inter import InterObject
51
55
from bzrlib.inventory import Inventory, InventoryDirectory, ROOT_ID
52
56
from bzrlib.symbol_versioning import (
58
from bzrlib.trace import mutter, mutter_callsite, warning
59
from bzrlib.trace import mutter, mutter_callsite, note, warning
61
62
# Old formats display a warning, but only once
157
158
"""Tell the builder that the inventory is finished."""
158
159
if self.new_inventory.root is None:
159
160
raise AssertionError('Root entry should be supplied to'
160
' record_entry_contents, as of bzr 0.10.')
161
' record_entry_contents, as of bzr 0.10.',
162
DeprecationWarning, stacklevel=2)
161
163
self.new_inventory.add(InventoryDirectory(ROOT_ID, '', None))
162
164
self.new_inventory.revision_id = self._new_revision_id
163
165
self.inv_sha1 = self.repository.add_inventory(
236
238
content - stat, length, exec, sha/link target. This is only
237
239
accessed when the entry has a revision of None - that is when it is
238
240
a candidate to commit.
239
:return: A tuple (change_delta, version_recorded, fs_hash).
240
change_delta is an inventory_delta change for this entry against
241
the basis tree of the commit, or None if no change occured against
241
:return: A tuple (change_delta, version_recorded). change_delta is
242
an inventory_delta change for this entry against the basis tree of
243
the commit, or None if no change occured against the basis tree.
243
244
version_recorded is True if a new version of the entry has been
244
245
recorded. For instance, committing a merge where a file was only
245
246
changed on the other side will return (delta, False).
246
fs_hash is either None, or the hash details for the path (currently
247
a tuple of the contents sha1 and the statvalue returned by
248
tree.get_file_with_stat()).
250
248
if self.new_inventory.root is None:
251
249
if ie.parent_id is not None:
288
286
delta = (None, path, ie.file_id, ie)
289
return delta, False, None
291
289
# we don't need to commit this, because the caller already
292
290
# determined that an existing revision of this file is
293
# appropriate. If its not being considered for committing then
294
# it and all its parents to the root must be unaltered so
295
# no-change against the basis.
296
if ie.revision == self._new_revision_id:
297
raise AssertionError("Impossible situation, a skipped "
298
"inventory entry (%r) claims to be modified in this "
299
"commit (%r).", (ie, self._new_revision_id))
300
return None, False, None
292
return None, (ie.revision == self._new_revision_id)
301
293
# XXX: Friction: parent_candidates should return a list not a dict
302
294
# so that we don't have to walk the inventories again.
303
295
parent_candiate_entries = ie.parent_candidates(parent_invs)
352
341
ie.text_size = parent_entry.text_size
353
342
ie.text_sha1 = parent_entry.text_sha1
354
343
ie.executable = parent_entry.executable
355
return self._get_delta(ie, basis_inv, path), False, None
344
return self._get_delta(ie, basis_inv, path), False
357
346
# Either there is only a hash change(no hash cache entry,
358
347
# or same size content change), or there is no change on
365
354
# absence of a content change in the file.
366
355
nostore_sha = None
367
356
ie.executable = content_summary[2]
368
file_obj, stat_value = tree.get_file_with_stat(ie.file_id, path)
370
lines = file_obj.readlines()
357
lines = tree.get_file(ie.file_id, path).readlines()
374
359
ie.text_sha1, ie.text_size = self._add_text_to_weave(
375
360
ie.file_id, lines, heads, nostore_sha)
376
# Let the caller know we generated a stat fingerprint.
377
fingerprint = (ie.text_sha1, stat_value)
378
361
except errors.ExistingContent:
379
362
# Turns out that the file content was unchanged, and we were
380
363
# only going to store a new node if it was changed. Carry over
383
366
ie.text_size = parent_entry.text_size
384
367
ie.text_sha1 = parent_entry.text_sha1
385
368
ie.executable = parent_entry.executable
386
return self._get_delta(ie, basis_inv, path), False, None
369
return self._get_delta(ie, basis_inv, path), False
387
370
elif kind == 'directory':
389
372
# all data is meta here, nothing specific to directory, so
391
374
ie.revision = parent_entry.revision
392
return self._get_delta(ie, basis_inv, path), False, None
375
return self._get_delta(ie, basis_inv, path), False
394
377
self._add_text_to_weave(ie.file_id, lines, heads, None)
395
378
elif kind == 'symlink':
403
386
# unchanged, carry over.
404
387
ie.revision = parent_entry.revision
405
388
ie.symlink_target = parent_entry.symlink_target
406
return self._get_delta(ie, basis_inv, path), False, None
389
return self._get_delta(ie, basis_inv, path), False
407
390
ie.symlink_target = current_link_target
409
392
self._add_text_to_weave(ie.file_id, lines, heads, None)
415
398
# unchanged, carry over.
416
399
ie.reference_revision = parent_entry.reference_revision
417
400
ie.revision = parent_entry.revision
418
return self._get_delta(ie, basis_inv, path), False, None
401
return self._get_delta(ie, basis_inv, path), False
419
402
ie.reference_revision = content_summary[3]
421
404
self._add_text_to_weave(ie.file_id, lines, heads, None)
423
406
raise NotImplementedError('unknown kind')
424
407
ie.revision = self._new_revision_id
425
return self._get_delta(ie, basis_inv, path), True, fingerprint
408
return self._get_delta(ie, basis_inv, path), True
427
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
428
416
# Note: as we read the content directly from the tree, we know its not
429
417
# been turned into unicode or badly split - but a broken tree
430
418
# implementation could give us bad output from readlines() so this is
431
419
# not a guarantee of safety. What would be better is always checking
432
420
# the content during test suite execution. RBC 20070912
433
parent_keys = tuple((file_id, parent) for parent in parents)
434
return self.repository.texts.add_lines(
435
(file_id, self._new_revision_id), parent_keys, new_lines,
421
return versionedfile.add_lines_with_ghosts(
422
self._new_revision_id, parents, new_lines,
436
423
nostore_sha=nostore_sha, random_id=self.random_revid,
437
424
check_content=False)[0:2]
463
450
revisions and file history. It's normally accessed only by the Branch,
464
451
which views a particular line of development through that history.
466
The Repository builds on top of some byte storage facilies (the revisions,
467
signatures, inventories and texts attributes) and a Transport, which
468
respectively provide byte storage and a means to access the (possibly
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
471
The byte storage facilities are addressed via tuples, which we refer to
472
as 'keys' throughout the code base. Revision_keys, inventory_keys and
473
signature_keys are all 1-tuples: (revision_id,). text_keys are two-tuples:
474
(file_id, revision_id). We use this interface because it allows low
475
friction with the underlying code that implements disk indices, network
476
encoding and other parts of bzrlib.
478
:ivar revisions: A bzrlib.versionedfile.VersionedFiles instance containing
479
the serialised revisions for the repository. This can be used to obtain
480
revision graph information or to access raw serialised revisions.
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
484
:ivar signatures: A bzrlib.versionedfile.VersionedFiles instance containing
485
the serialised signatures for the repository. This can be used to
486
obtain access to raw serialised signatures. The result of trying to
487
insert data into the repository via this store is undefined: it should
488
be considered read-only except for implementors of repositories.
489
:ivar inventories: A bzrlib.versionedfile.VersionedFiles instance containing
490
the serialised inventories for the repository. This can be used to
491
obtain unserialised inventories. The result of trying to insert data
492
into the repository via this store is undefined: it should be
493
considered read-only except for implementors of repositories.
494
:ivar texts: A bzrlib.versionedfile.VersionedFiles instance containing the
495
texts of files and directories for the repository. This can be used to
496
obtain file texts or file graphs. Note that Repository.iter_file_bytes
497
is usually a better interface for accessing file texts.
498
The result of trying to insert data into the repository via this store
499
is undefined: it should be considered read-only except for implementors
501
457
:ivar _transport: Transport for file access to repository, typically
502
458
pointing to .bzr/repository.
540
def add_fallback_repository(self, repository):
541
"""Add a repository to use for looking up data not held locally.
543
:param repository: A repository.
545
if not self._format.supports_external_lookups:
546
raise errors.UnstackableRepositoryFormat(self._format, self.base)
547
self._check_fallback_repository(repository)
548
self._fallback_repositories.append(repository)
549
self.texts.add_fallback_versioned_files(repository.texts)
550
self.inventories.add_fallback_versioned_files(repository.inventories)
551
self.revisions.add_fallback_versioned_files(repository.revisions)
552
self.signatures.add_fallback_versioned_files(repository.signatures)
554
def _check_fallback_repository(self, repository):
555
"""Check that this repository can fallback to repository safely.
557
Raise an error if not.
559
:param repository: A repository to fallback to.
561
return InterRepository._assert_same_model(self, repository)
563
496
def add_inventory(self, revision_id, inv, parents):
564
497
"""Add the inventory inv to the repository as revision_id.
580
513
if inv.root is None:
581
514
raise AssertionError()
582
515
inv_lines = self._serialise_inventory_to_lines(inv)
583
return self._inventory_add_lines(revision_id, parents,
516
inv_vf = self.get_inventory_weave()
517
return self._inventory_add_lines(inv_vf, revision_id, parents,
584
518
inv_lines, check_content=False)
586
def _inventory_add_lines(self, revision_id, parents, lines,
520
def _inventory_add_lines(self, inv_vf, revision_id, parents, lines,
587
521
check_content=True):
588
522
"""Store lines in inv_vf and return the sha1 of the inventory."""
589
parents = [(parent,) for parent in parents]
590
return self.inventories.add_lines((revision_id,), parents, lines,
524
for parent in parents:
526
final_parents.append(parent)
527
return inv_vf.add_lines(revision_id, final_parents, lines,
591
528
check_content=check_content)[0]
593
530
def add_revision(self, revision_id, rev, inv=None, config=None):
610
547
plaintext = Testament(rev, inv).as_short_text()
611
548
self.store_revision_signature(
612
549
gpg.GPGStrategy(config), plaintext, revision_id)
613
# check inventory present
614
if not self.inventories.get_parent_map([(revision_id,)]):
550
inventory_vf = self.get_inventory_weave()
551
if not revision_id in inventory_vf:
616
553
raise errors.WeaveRevisionNotPresent(revision_id,
619
556
# yes, this is not suitable for adding with ghosts.
620
557
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
624
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
625
self._add_revision(rev)
560
rev.inventory_sha1 = inventory_vf.get_sha1s([revision_id])[0]
561
self._revision_store.add_revision(rev, self.get_transaction())
627
def _add_revision(self, revision):
628
text = self._serializer.write_revision_to_string(revision)
629
key = (revision.revision_id,)
630
parents = tuple((parent,) for parent in revision.parent_ids)
631
self.revisions.add_lines(key, parents, osutils.split_lines(text))
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())
633
569
def all_revision_ids(self):
634
570
"""Returns a list of all the revision ids in the repository.
636
This is conceptually deprecated because code should generally work on
637
the graph reachable from a particular revision, and ignore any other
638
revisions that might be present. There is no direct replacement
572
This is deprecated because code should generally work on the graph
573
reachable from a particular revision, and ignore any other revisions
574
that might be present. There is no direct replacement method.
641
576
if 'evil' in debug.debug_flags:
642
577
mutter_callsite(2, "all_revision_ids is linear with history.")
675
610
"""Construct the current default format repository in a_bzrdir."""
676
611
return RepositoryFormat.get_default_format().initialize(a_bzrdir)
678
def __init__(self, _format, a_bzrdir, control_files):
613
def __init__(self, _format, a_bzrdir, control_files,
614
_revision_store, control_store, text_store):
679
615
"""instantiate a Repository.
681
617
:param _format: The format of the repository on disk.
692
628
self.control_files = control_files
693
629
self._transport = control_files._transport
694
630
self.base = self._transport.base
631
self._revision_store = _revision_store
632
# backwards compatibility
633
self.weave_store = text_store
696
635
self._reconcile_does_inventory_gc = True
697
636
self._reconcile_fixes_text_parents = False
698
637
self._reconcile_backsup_inventory = True
699
638
# not right yet - should be more semantically clear ?
640
self.control_store = control_store
641
self.control_weaves = control_store
701
642
# TODO: make sure to construct the right store classes, etc, depending
702
643
# on whether escaping is required.
703
644
self._warn_if_deprecated()
704
645
self._write_group = None
705
# Additional places to query for data.
706
self._fallback_repositories = []
707
# What order should fetch operations request streams in?
708
# The default is unordered as that is the cheapest for an origin to
710
self._fetch_order = 'unordered'
711
# Does this repository use deltas that can be fetched as-deltas ?
712
# (E.g. knits, where the knit deltas can be transplanted intact.
713
# We default to False, which will ensure that enough data to get
714
# a full text out of any fetch stream will be grabbed.
715
self._fetch_uses_deltas = False
716
# Should fetch trigger a reconcile after the fetch? Only needed for
717
# some repository formats that can suffer internal inconsistencies.
718
self._fetch_reconcile = False
720
647
def __repr__(self):
721
648
return '%s(%r)' % (self.__class__.__name__,
844
766
last_revision.timezone)
846
768
# now gather global repository information
847
# XXX: This is available for many repos regardless of listability.
848
769
if self.bzrdir.root_transport.listable():
849
# XXX: do we want to __define len__() ?
850
# Maybe the versionedfiles object should provide a different
851
# method to get the number of keys.
852
result['revisions'] = len(self.revisions.keys())
770
c, t = self._revision_store.total_size(self.get_transaction())
771
result['revisions'] = c
856
775
def find_branches(self, using=False):
896
815
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
for item_key, bytes in stream:
838
if item_key[0] == 'file':
839
(file_id,) = item_key[1:]
840
knit = self.weave_store.get_weave_or_empty(
841
file_id, self.get_transaction())
842
elif item_key == ('inventory',):
843
knit = self.get_inventory_weave()
844
elif item_key == ('revisions',):
845
knit = self._revision_store.get_revision_file(
846
self.get_transaction())
847
elif item_key == ('signatures',):
848
knit = self._revision_store.get_signature_file(
849
self.get_transaction())
851
raise errors.RepositoryDataStreamError(
852
"Unrecognised data stream key '%s'" % (item_key,))
853
decoded_list = bencode.bdecode(bytes)
854
format = decoded_list.pop(0)
857
for version, options, parents, some_bytes in decoded_list:
858
data_list.append((version, options, len(some_bytes), parents))
859
knit_bytes += some_bytes
860
buffer = StringIO(knit_bytes)
861
def reader_func(count):
865
return buffer.read(count)
866
knit.insert_data_stream(
867
(format, data_list, reader_func))
900
870
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
901
871
"""Return the revision ids that other has that this does not.
981
951
not _mod_revision.is_null(revision_id)):
982
952
self.get_revision(revision_id)
984
# if there is no specific appropriate InterRepository, this will get
985
# the InterRepository base class, which raises an
986
# IncompatibleRepositories when asked to fetch.
987
954
inter = InterRepository.get(source, self)
988
return inter.fetch(revision_id=revision_id, pb=pb,
989
find_ghosts=find_ghosts)
956
return inter.fetch(revision_id=revision_id, pb=pb, find_ghosts=find_ghosts)
957
except NotImplementedError:
958
raise errors.IncompatibleRepositories(source, self)
991
960
def create_bundle(self, target, base, fileobj, format=None):
992
961
return serializer.write_bundle(self, target, base, fileobj, format)
1094
1061
"""True if this repository has a copy of the revision."""
1095
1062
return revision_id in self.has_revisions((revision_id,))
1098
1064
def has_revisions(self, revision_ids):
1099
1065
"""Probe to find out the presence of multiple revisions.
1101
1067
:param revision_ids: An iterable of revision_ids.
1102
1068
:return: A set of the revision_ids that were present.
1104
parent_map = self.revisions.get_parent_map(
1105
[(rev_id,) for rev_id in revision_ids])
1107
if _mod_revision.NULL_REVISION in revision_ids:
1108
result.add(_mod_revision.NULL_REVISION)
1109
result.update([key[0] for key in parent_map])
1070
raise NotImplementedError(self.has_revisions)
1072
return self._revision_store.has_revision_id(revision_id,
1073
self.get_transaction())
1112
1075
@needs_read_lock
1113
1076
def get_revision(self, revision_id):
1136
1099
for rev_id in revision_ids:
1137
1100
if not rev_id or not isinstance(rev_id, basestring):
1138
1101
raise errors.InvalidRevisionId(revision_id=rev_id, branch=self)
1139
keys = [(key,) for key in revision_ids]
1140
stream = self.revisions.get_record_stream(keys, 'unordered', True)
1142
for record in stream:
1143
if record.storage_kind == 'absent':
1144
raise errors.NoSuchRevision(self, record.key[0])
1145
text = record.get_bytes_as('fulltext')
1146
rev = self._serializer.read_revision_from_string(text)
1147
revs[record.key[0]] = rev
1148
return [revs[revid] for revid in revision_ids]
1102
revs = self._revision_store.get_revisions(revision_ids,
1103
self.get_transaction())
1150
1106
@needs_read_lock
1151
1107
def get_revision_xml(self, revision_id):
1210
1167
revision_id that they contain. The inventory texts from all present
1211
1168
revision ids are assessed to generate this report.
1213
revision_keys = self.revisions.keys()
1214
w = self.inventories
1170
revision_ids = self.all_revision_ids()
1171
w = self.get_inventory_weave()
1215
1172
pb = ui.ui_factory.nested_progress_bar()
1217
1174
return self._find_text_key_references_from_xml_inventory_lines(
1218
w.iter_lines_added_or_present_in_keys(revision_keys, pb=pb))
1175
w.iter_lines_added_or_present_in_versions(revision_ids, pb=pb))
1318
1275
setdefault = result.setdefault
1276
for file_id, revision_id in \
1320
1277
self._find_text_key_references_from_xml_inventory_lines(
1321
1278
line_iterator).iterkeys():
1322
1279
# once data is all ensured-consistent; then this is
1323
1280
# if revision_id == version_id
1324
if key[-1:] in revision_ids:
1325
setdefault(key[0], set()).add(key[-1])
1281
if revision_id in revision_ids:
1282
setdefault(file_id, set()).add(revision_id)
1328
1285
def fileids_altered_by_revision_ids(self, revision_ids, _inv_weave=None):
1335
1292
revision_ids. Each altered file-ids has the exact revision_ids that
1336
1293
altered it listed explicitly.
1338
selected_keys = set((revid,) for revid in revision_ids)
1339
w = _inv_weave or self.inventories
1295
selected_revision_ids = set(revision_ids)
1296
w = _inv_weave or self.get_inventory_weave()
1340
1297
pb = ui.ui_factory.nested_progress_bar()
1342
1299
return self._find_file_ids_from_xml_inventory_lines(
1343
w.iter_lines_added_or_present_in_keys(
1344
selected_keys, pb=pb),
1300
w.iter_lines_added_or_present_in_versions(
1301
selected_revision_ids, pb=pb),
1302
selected_revision_ids)
1360
1317
bytes_iterator is an iterable of bytestrings for the file. The
1361
1318
kind of iterable and length of the bytestrings are unspecified, but for
1362
this implementation, it is a list of bytes produced by
1363
VersionedFile.get_record_stream().
1319
this implementation, it is a list of lines produced by
1320
VersionedFile.get_lines().
1365
1322
:param desired_files: a list of (file_id, revision_id, identifier)
1368
1325
transaction = self.get_transaction()
1370
1326
for file_id, revision_id, callable_data in desired_files:
1371
text_keys[(file_id, revision_id)] = callable_data
1372
for record in self.texts.get_record_stream(text_keys, 'unordered', True):
1373
if record.storage_kind == 'absent':
1374
raise errors.RevisionNotPresent(record.key, self)
1375
yield text_keys[record.key], record.get_bytes_as('fulltext')
1328
weave = self.weave_store.get_weave(file_id, transaction)
1329
except errors.NoSuchFile:
1330
raise errors.NoSuchIdInRepository(self, file_id)
1331
yield callable_data, weave.get_lines(revision_id)
1377
1333
def _generate_text_key_index(self, text_key_references=None,
1378
1334
ancestors=None):
1550
1511
def _iter_inventories(self, revision_ids):
1551
1512
"""single-document based inventory iteration."""
1552
for text, revision_id in self._iter_inventory_xmls(revision_ids):
1513
texts = self.get_inventory_weave().get_texts(revision_ids)
1514
for text, revision_id in zip(texts, revision_ids):
1553
1515
yield self.deserialise_inventory(revision_id, text)
1555
def _iter_inventory_xmls(self, revision_ids):
1556
keys = [(revision_id,) for revision_id in revision_ids]
1557
stream = self.inventories.get_record_stream(keys, 'unordered', True)
1559
for record in stream:
1560
if record.storage_kind != 'absent':
1561
texts[record.key] = record.get_bytes_as('fulltext')
1563
raise errors.NoSuchRevision(self, record.key)
1565
yield texts[key], key[-1]
1567
1517
def deserialise_inventory(self, revision_id, xml):
1568
1518
"""Transform the xml into an inventory object.
1588
1538
@needs_read_lock
1589
1539
def get_inventory_xml(self, revision_id):
1590
1540
"""Get inventory XML as a file object."""
1591
texts = self._iter_inventory_xmls([revision_id])
1593
text, revision_id = texts.next()
1594
except StopIteration:
1542
iw = self.get_inventory_weave()
1543
return iw.get_text(revision_id)
1595
1545
raise errors.HistoryMissing(self, 'inventory', revision_id)
1598
1547
@needs_read_lock
1599
1548
def get_inventory_sha1(self, revision_id):
1666
1616
def revision_tree(self, revision_id):
1667
1617
"""Return Tree for a revision on this branch.
1669
`revision_id` may be NULL_REVISION for the empty tree revision.
1619
`revision_id` may be None for the empty tree revision.
1671
revision_id = _mod_revision.ensure_null(revision_id)
1672
1621
# TODO: refactor this to use an existing revision object
1673
1622
# so we don't need to read it in twice.
1674
if revision_id == _mod_revision.NULL_REVISION:
1623
if revision_id is None or revision_id == _mod_revision.NULL_REVISION:
1675
1624
return RevisionTree(self, Inventory(root_id=None),
1676
1625
_mod_revision.NULL_REVISION)
1701
1650
if not self.has_revision(revision_id):
1702
1651
raise errors.NoSuchRevision(self, revision_id)
1703
graph = self.get_graph()
1705
search = graph._make_breadth_first_searcher([revision_id])
1708
found, ghosts = search.next_with_ghosts()
1709
except StopIteration:
1712
if _mod_revision.NULL_REVISION in keys:
1713
keys.remove(_mod_revision.NULL_REVISION)
1715
parent_map = graph.get_parent_map(keys)
1716
keys = tsort.topo_sort(parent_map)
1717
return [None] + list(keys)
1652
w = self.get_inventory_weave()
1653
candidates = w.get_ancestry(revision_id, topo_sorted)
1654
return [None] + candidates # self._eliminate_revisions_not_present(candidates)
1719
1656
def pack(self):
1720
1657
"""Compress the data within the repository.
1749
1685
def get_transaction(self):
1750
1686
return self.control_files.get_transaction()
1752
@deprecated_method(one_one)
1688
@deprecated_method(symbol_versioning.one_one)
1753
1689
def get_parents(self, revision_ids):
1754
1690
"""See StackedParentsProvider.get_parents"""
1755
1691
parent_map = self.get_parent_map(revision_ids)
1756
1692
return [parent_map.get(r, None) for r in revision_ids]
1758
def get_parent_map(self, revision_ids):
1694
def get_parent_map(self, keys):
1759
1695
"""See graph._StackedParentsProvider.get_parent_map"""
1760
# revisions index works in keys; this just works in revisions
1761
# therefore wrap and unwrap
1764
for revision_id in revision_ids:
1697
for revision_id in keys:
1698
if revision_id is None:
1699
raise ValueError('get_parent_map(None) is not valid')
1765
1700
if revision_id == _mod_revision.NULL_REVISION:
1766
result[revision_id] = ()
1767
elif revision_id is None:
1768
raise ValueError('get_parent_map(None) is not valid')
1770
query_keys.append((revision_id ,))
1771
for ((revision_id,), parent_keys) in \
1772
self.revisions.get_parent_map(query_keys).iteritems():
1774
result[revision_id] = tuple(parent_revid
1775
for (parent_revid,) in parent_keys)
1777
result[revision_id] = (_mod_revision.NULL_REVISION,)
1701
parent_map[revision_id] = ()
1704
parent_id_list = self.get_revision(revision_id).parent_ids
1705
except errors.NoSuchRevision:
1708
if len(parent_id_list) == 0:
1709
parent_ids = (_mod_revision.NULL_REVISION,)
1711
parent_ids = tuple(parent_id_list)
1712
parent_map[revision_id] = parent_ids
1780
1715
def _make_parents_provider(self):
1830
1765
@needs_read_lock
1831
1766
def has_signature_for_revision_id(self, revision_id):
1832
1767
"""Query for a revision signature for revision_id in the repository."""
1833
if not self.has_revision(revision_id):
1834
raise errors.NoSuchRevision(self, revision_id)
1835
sig_present = (1 == len(
1836
self.signatures.get_parent_map([(revision_id,)])))
1768
return self._revision_store.has_signature(revision_id,
1769
self.get_transaction())
1839
1771
@needs_read_lock
1840
1772
def get_signature_text(self, revision_id):
1841
1773
"""Return the text for a signature."""
1842
stream = self.signatures.get_record_stream([(revision_id,)],
1844
record = stream.next()
1845
if record.storage_kind == 'absent':
1846
raise errors.NoSuchRevision(self, revision_id)
1847
return record.get_bytes_as('fulltext')
1774
return self._revision_store.get_signature_text(revision_id,
1775
self.get_transaction())
1849
1777
@needs_read_lock
1850
1778
def check(self, revision_ids=None):
1978
1905
path, root = entries.next()
1979
1906
if root.revision != rev.revision_id:
1980
1907
raise errors.IncompatibleRevision(repr(repository))
1908
# Add the texts that are not already present
1982
1909
for path, ie in entries:
1983
text_keys[(ie.file_id, ie.revision)] = ie
1984
text_parent_map = repository.texts.get_parent_map(text_keys)
1985
missing_texts = set(text_keys) - set(text_parent_map)
1986
# Add the texts that are not already present
1987
for text_key in missing_texts:
1988
ie = text_keys[text_key]
1990
# FIXME: TODO: The following loop overlaps/duplicates that done by
1991
# commit to determine parents. There is a latent/real bug here where
1992
# the parents inserted are not those commit would do - in particular
1993
# they are not filtered by heads(). RBC, AB
1994
for revision, tree in parent_trees.iteritems():
1995
if ie.file_id not in tree:
1997
parent_id = tree.inventory[ie.file_id].revision
1998
if parent_id in text_parents:
2000
text_parents.append((ie.file_id, parent_id))
2001
lines = revision_tree.get_file(ie.file_id).readlines()
2002
repository.texts.add_lines(text_key, text_parents, lines)
1910
w = repository.weave_store.get_weave_or_empty(ie.file_id,
1911
repository.get_transaction())
1912
if ie.revision not in w:
1914
# FIXME: TODO: The following loop *may* be overlapping/duplicate
1915
# with InventoryEntry.find_previous_heads(). if it is, then there
1916
# is a latent bug here where the parents may have ancestors of each
1918
for revision, tree in parent_trees.iteritems():
1919
if ie.file_id not in tree:
1921
parent_id = tree.inventory[ie.file_id].revision
1922
if parent_id in text_parents:
1924
text_parents.append(parent_id)
1926
vfile = repository.weave_store.get_weave_or_empty(ie.file_id,
1927
repository.get_transaction())
1928
lines = revision_tree.get_file(ie.file_id).readlines()
1929
vfile.add_lines(rev.revision_id, text_parents, lines)
2004
1931
# install the inventory
2005
1932
repository.add_inventory(rev.revision_id, inv, present_parents)
2017
1944
typically pointing to .bzr/repository.
2020
def __init__(self, _format, a_bzrdir, control_files):
2021
super(MetaDirRepository, self).__init__(_format, a_bzrdir, control_files)
1947
def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
1948
super(MetaDirRepository, self).__init__(_format,
2022
1954
self._transport = control_files._transport
2024
1957
def is_shared(self):
2025
1958
"""Return True if this repository is flagged as a shared repository."""
2026
1959
return self._transport.has('shared-storage')
2052
1985
class MetaDirVersionedFileRepository(MetaDirRepository):
2053
1986
"""Repositories in a meta-dir, that work via versioned file objects."""
2055
def __init__(self, _format, a_bzrdir, control_files):
1988
def __init__(self, _format, a_bzrdir, control_files, _revision_store, control_store, text_store):
2056
1989
super(MetaDirVersionedFileRepository, self).__init__(_format, a_bzrdir,
1990
control_files, _revision_store, control_store, text_store)
1991
_revision_store.get_scope = self.get_transaction
1992
control_store.get_scope = self.get_transaction
1993
text_store.get_scope = self.get_transaction
2060
1996
class RepositoryFormatRegistry(registry.Registry):
2168
2108
"""Return the short description for this format."""
2169
2109
raise NotImplementedError(self.get_format_description)
2111
def _get_revision_store(self, repo_transport, control_files):
2112
"""Return the revision store object for this a_bzrdir."""
2113
raise NotImplementedError(self._get_revision_store)
2115
def _get_text_rev_store(self,
2122
"""Common logic for getting a revision store for a repository.
2124
see self._get_revision_store for the subclass-overridable method to
2125
get the store for a repository.
2127
from bzrlib.store.revision.text import TextRevisionStore
2128
dir_mode = control_files._dir_mode
2129
file_mode = control_files._file_mode
2130
text_store = TextStore(transport.clone(name),
2132
compressed=compressed,
2134
file_mode=file_mode)
2135
_revision_store = TextRevisionStore(text_store, serializer)
2136
return _revision_store
2171
2138
# TODO: this shouldn't be in the base class, it's specific to things that
2172
2139
# use weaves or knits -- mbp 20070207
2173
2140
def _get_versioned_file_store(self,
2310
2277
'bzrlib.repofmt.pack_repo',
2311
2278
'RepositoryFormatKnitPack4',
2313
format_registry.register_lazy(
2314
'Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n',
2315
'bzrlib.repofmt.pack_repo',
2316
'RepositoryFormatKnitPack5',
2318
format_registry.register_lazy(
2319
'Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n',
2320
'bzrlib.repofmt.pack_repo',
2321
'RepositoryFormatKnitPack5RichRoot',
2323
format_registry.register_lazy(
2324
'Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6)\n',
2325
'bzrlib.repofmt.pack_repo',
2326
'RepositoryFormatKnitPack5RichRootBroken',
2329
2280
# Development formats.
2330
# 1.7->1.8 go below here
2331
format_registry.register_lazy(
2332
"Bazaar development format 2 (needs bzr.dev from before 1.8)\n",
2333
'bzrlib.repofmt.pack_repo',
2334
'RepositoryFormatPackDevelopment2',
2336
format_registry.register_lazy(
2337
("Bazaar development format 2 with subtree support "
2338
"(needs bzr.dev from before 1.8)\n"),
2339
'bzrlib.repofmt.pack_repo',
2340
'RepositoryFormatPackDevelopment2Subtree',
2282
# development 0 - stub to introduce development versioning scheme.
2283
format_registry.register_lazy(
2284
"Bazaar development format 0 (needs bzr.dev from before 1.3)\n",
2285
'bzrlib.repofmt.pack_repo',
2286
'RepositoryFormatPackDevelopment0',
2288
format_registry.register_lazy(
2289
("Bazaar development format 0 with subtree support "
2290
"(needs bzr.dev from before 1.3)\n"),
2291
'bzrlib.repofmt.pack_repo',
2292
'RepositoryFormatPackDevelopment0Subtree',
2294
# 1.3->1.4 go below here
2344
2297
class InterRepository(InterObject):
2353
2306
InterRepository.get(other).method_name(parameters).
2356
_walk_to_common_revisions_batch_size = 1
2357
2309
_optimisers = []
2358
2310
"""The available optimised InterRepository types."""
2360
def __init__(self, source, target):
2361
InterObject.__init__(self, source, target)
2362
# These two attributes may be overridden by e.g. InterOtherToRemote to
2363
# provide a faster implementation.
2364
self.target_get_graph = self.target.get_graph
2365
self.target_get_parent_map = self.target.get_parent_map
2367
2312
def copy_content(self, revision_id=None):
2368
2313
raise NotImplementedError(self.copy_content)
2377
2322
:param pb: optional progress bar to use for progress reports. If not
2378
2323
provided a default one will be created.
2380
:returns: (copied_revision_count, failures).
2325
Returns the copied revision count and the failed revisions in a tuple:
2382
# Normally we should find a specific InterRepository subclass to do
2383
# the fetch; if nothing else then at least InterSameDataRepository.
2384
# If none of them is suitable it looks like fetching is not possible;
2385
# we try to give a good message why. _assert_same_model will probably
2386
# give a helpful message; otherwise a generic one.
2387
self._assert_same_model(self.source, self.target)
2388
raise errors.IncompatibleRepositories(self.source, self.target,
2389
"no suitableInterRepository found")
2328
raise NotImplementedError(self.fetch)
2391
2330
def _walk_to_common_revisions(self, revision_ids):
2392
2331
"""Walk out from revision_ids in source to revisions target has.
2394
2333
:param revision_ids: The start point for the search.
2395
2334
:return: A set of revision ids.
2397
target_graph = self.target_get_graph()
2336
target_graph = self.target.get_graph()
2398
2337
revision_ids = frozenset(revision_ids)
2399
# Fast path for the case where all the revisions are already in the
2401
# (Although this does incur an extra round trip for the
2402
# fairly common case where the target doesn't already have the revision
2404
2338
if set(target_graph.get_parent_map(revision_ids)) == revision_ids:
2405
2339
return graph.SearchResult(revision_ids, set(), 0, set())
2406
2340
missing_revs = set()
2408
2342
# ensure we don't pay silly lookup costs.
2409
2343
searcher = source_graph._make_breadth_first_searcher(revision_ids)
2410
2344
null_set = frozenset([_mod_revision.NULL_REVISION])
2411
searcher_exhausted = False
2415
# Iterate the searcher until we have enough next_revs
2416
while len(next_revs) < self._walk_to_common_revisions_batch_size:
2418
next_revs_part, ghosts_part = searcher.next_with_ghosts()
2419
next_revs.update(next_revs_part)
2420
ghosts.update(ghosts_part)
2421
except StopIteration:
2422
searcher_exhausted = True
2424
# If there are ghosts in the source graph, and the caller asked for
2425
# them, make sure that they are present in the target.
2426
# We don't care about other ghosts as we can't fetch them and
2347
next_revs, ghosts = searcher.next_with_ghosts()
2348
except StopIteration:
2350
if revision_ids.intersection(ghosts):
2351
absent_ids = set(revision_ids.intersection(ghosts))
2352
# If all absent_ids are present in target, no error is needed.
2353
absent_ids.difference_update(
2354
set(target_graph.get_parent_map(absent_ids)))
2356
raise errors.NoSuchRevision(self.source, absent_ids.pop())
2357
# we don't care about other ghosts as we can't fetch them and
2427
2358
# haven't been asked to.
2428
ghosts_to_check = set(revision_ids.intersection(ghosts))
2429
revs_to_get = set(next_revs).union(ghosts_to_check)
2431
have_revs = set(target_graph.get_parent_map(revs_to_get))
2432
# we always have NULL_REVISION present.
2433
have_revs = have_revs.union(null_set)
2434
# Check if the target is missing any ghosts we need.
2435
ghosts_to_check.difference_update(have_revs)
2437
# One of the caller's revision_ids is a ghost in both the
2438
# source and the target.
2439
raise errors.NoSuchRevision(
2440
self.source, ghosts_to_check.pop())
2441
missing_revs.update(next_revs - have_revs)
2442
searcher.stop_searching_any(have_revs)
2443
if searcher_exhausted:
2359
next_revs = set(next_revs)
2360
# we always have NULL_REVISION present.
2361
have_revs = set(target_graph.get_parent_map(next_revs)).union(null_set)
2362
missing_revs.update(next_revs - have_revs)
2363
searcher.stop_searching_any(have_revs)
2445
2364
return searcher.get_result()
2447
@deprecated_method(one_two)
2366
@deprecated_method(symbol_versioning.one_two)
2448
2367
@needs_read_lock
2449
2368
def missing_revision_ids(self, revision_id=None, find_ghosts=True):
2450
2369
"""Return the revision ids that source has that target does not.
2488
2407
def _same_model(source, target):
2489
"""True if source and target have the same data representation.
2491
Note: this is always called on the base class; overriding it in a
2492
subclass will have no effect.
2495
InterRepository._assert_same_model(source, target)
2497
except errors.IncompatibleRepositories, e:
2408
"""True if source and target have the same data representation."""
2409
if source.supports_rich_root() != target.supports_rich_root():
2501
def _assert_same_model(source, target):
2502
"""Raise an exception if two repositories do not use the same model.
2504
if source.supports_rich_root() != target.supports_rich_root():
2505
raise errors.IncompatibleRepositories(source, target,
2506
"different rich-root support")
2507
2411
if source._serializer != target._serializer:
2508
raise errors.IncompatibleRepositories(source, target,
2509
"different serializers")
2512
2416
class InterSameDataRepository(InterRepository):
2555
2459
@needs_write_lock
2556
2460
def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2557
2461
"""See InterRepository.fetch()."""
2558
from bzrlib.fetch import RepoFetcher
2462
from bzrlib.fetch import GenericRepoFetcher
2559
2463
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2560
2464
self.source, self.source._format, self.target,
2561
2465
self.target._format)
2562
f = RepoFetcher(to_repository=self.target,
2466
f = GenericRepoFetcher(to_repository=self.target,
2563
2467
from_repository=self.source,
2564
2468
last_revision=revision_id,
2565
2469
pb=pb, find_ghosts=find_ghosts)
2613
2517
if self.source._transport.listable():
2614
2518
pb = ui.ui_factory.nested_progress_bar()
2616
self.target.texts.insert_record_stream(
2617
self.source.texts.get_record_stream(
2618
self.source.texts.keys(), 'topological', False))
2520
self.target.weave_store.copy_all_ids(
2521
self.source.weave_store,
2523
from_transaction=self.source.get_transaction(),
2524
to_transaction=self.target.get_transaction())
2619
2525
pb.update('copying inventory', 0, 1)
2620
self.target.inventories.insert_record_stream(
2621
self.source.inventories.get_record_stream(
2622
self.source.inventories.keys(), 'topological', False))
2623
self.target.signatures.insert_record_stream(
2624
self.source.signatures.get_record_stream(
2625
self.source.signatures.keys(),
2627
self.target.revisions.insert_record_stream(
2628
self.source.revisions.get_record_stream(
2629
self.source.revisions.keys(),
2630
'topological', True))
2526
self.target.control_weaves.copy_multi(
2527
self.source.control_weaves, ['inventory'],
2528
from_transaction=self.source.get_transaction(),
2529
to_transaction=self.target.get_transaction())
2530
self.target._revision_store.text_store.copy_all_ids(
2531
self.source._revision_store.text_store,
2636
2538
@needs_write_lock
2637
2539
def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2638
2540
"""See InterRepository.fetch()."""
2639
from bzrlib.fetch import RepoFetcher
2541
from bzrlib.fetch import GenericRepoFetcher
2640
2542
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2641
2543
self.source, self.source._format, self.target, self.target._format)
2642
f = RepoFetcher(to_repository=self.target,
2544
f = GenericRepoFetcher(to_repository=self.target,
2643
2545
from_repository=self.source,
2644
2546
last_revision=revision_id,
2645
2547
pb=pb, find_ghosts=find_ghosts)
2717
2619
@needs_write_lock
2718
2620
def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2719
2621
"""See InterRepository.fetch()."""
2720
from bzrlib.fetch import RepoFetcher
2622
from bzrlib.fetch import KnitRepoFetcher
2721
2623
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2722
2624
self.source, self.source._format, self.target, self.target._format)
2723
f = RepoFetcher(to_repository=self.target,
2625
f = KnitRepoFetcher(to_repository=self.target,
2724
2626
from_repository=self.source,
2725
2627
last_revision=revision_id,
2726
2628
pb=pb, find_ghosts=find_ghosts)
2787
2689
@needs_write_lock
2788
2690
def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2789
2691
"""See InterRepository.fetch()."""
2790
if (len(self.source._fallback_repositories) > 0 or
2791
len(self.target._fallback_repositories) > 0):
2792
# The pack layer is not aware of fallback repositories, so when
2793
# fetching from a stacked repository or into a stacked repository
2794
# we use the generic fetch logic which uses the VersionedFiles
2795
# attributes on repository.
2796
from bzrlib.fetch import RepoFetcher
2797
fetcher = RepoFetcher(self.target, self.source, revision_id,
2799
return fetcher.count_copied, fetcher.failed_revisions
2800
2692
from bzrlib.repofmt.pack_repo import Packer
2801
2693
mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2802
2694
self.source, self.source._format, self.target, self.target._format)
2807
2699
# to fetch from all packs to one without
2808
2700
# inventory parsing etc, IFF nothing to be copied is in the target.
2810
source_revision_ids = frozenset(self.source.all_revision_ids())
2811
revision_ids = source_revision_ids - \
2812
frozenset(self.target_get_parent_map(source_revision_ids))
2702
revision_ids = self.source.all_revision_ids()
2813
2703
revision_keys = [(revid,) for revid in revision_ids]
2814
2704
index = self.target._pack_collection.revision_index.combined_index
2815
2705
present_revision_ids = set(item[1][0] for item in
2856
2746
if not find_ghosts and revision_id is not None:
2857
2747
return self._walk_to_common_revisions([revision_id])
2858
2748
elif revision_id is not None:
2859
# Find ghosts: search for revisions pointing from one repository to
2860
# the other, and vice versa, anywhere in the history of revision_id.
2861
graph = self.target_get_graph(other_repository=self.source)
2862
searcher = graph._make_breadth_first_searcher([revision_id])
2866
next_revs, ghosts = searcher.next_with_ghosts()
2867
except StopIteration:
2869
if revision_id in ghosts:
2870
raise errors.NoSuchRevision(self.source, revision_id)
2871
found_ids.update(next_revs)
2872
found_ids.update(ghosts)
2873
found_ids = frozenset(found_ids)
2874
# Double query here: should be able to avoid this by changing the
2875
# graph api further.
2876
result_set = found_ids - frozenset(
2877
self.target_get_parent_map(found_ids))
2749
source_ids = self.source.get_ancestry(revision_id)
2750
if source_ids[0] is not None:
2751
raise AssertionError()
2879
2754
source_ids = self.source.all_revision_ids()
2880
# source_ids is the worst possible case we may need to pull.
2881
# now we want to filter source_ids against what we actually
2882
# have in target, but don't try to check for existence where we know
2883
# we do not have a revision as that would be pointless.
2884
target_ids = set(self.target.all_revision_ids())
2885
result_set = set(source_ids).difference(target_ids)
2755
# source_ids is the worst possible case we may need to pull.
2756
# now we want to filter source_ids against what we actually
2757
# have in target, but don't try to check for existence where we know
2758
# we do not have a revision as that would be pointless.
2759
target_ids = set(self.target.all_revision_ids())
2760
result_set = set(source_ids).difference(target_ids)
2886
2761
return self.source.revision_ids_to_search_result(result_set)
2940
2815
def is_compatible(source, target):
2941
2816
"""Be compatible with Knit1 source and Knit3 target"""
2817
from bzrlib.repofmt.knitrepo import RepositoryFormatKnit3
2943
from bzrlib.repofmt.knitrepo import (
2819
from bzrlib.repofmt.knitrepo import (RepositoryFormatKnit1,
2820
RepositoryFormatKnit3)
2821
from bzrlib.repofmt.pack_repo import (
2822
RepositoryFormatKnitPack1,
2823
RepositoryFormatKnitPack3,
2824
RepositoryFormatPackDevelopment0,
2825
RepositoryFormatPackDevelopment0Subtree,
2944
2828
RepositoryFormatKnit1,
2829
RepositoryFormatKnitPack1,
2830
RepositoryFormatPackDevelopment0,
2945
2833
RepositoryFormatKnit3,
2947
from bzrlib.repofmt.pack_repo import (
2948
RepositoryFormatKnitPack1,
2949
2834
RepositoryFormatKnitPack3,
2950
RepositoryFormatKnitPack4,
2951
RepositoryFormatKnitPack5,
2952
RepositoryFormatKnitPack5RichRoot,
2953
RepositoryFormatPackDevelopment2,
2954
RepositoryFormatPackDevelopment2Subtree,
2957
RepositoryFormatKnit1, # no rr, no subtree
2958
RepositoryFormatKnitPack1, # no rr, no subtree
2959
RepositoryFormatPackDevelopment2, # no rr, no subtree
2960
RepositoryFormatKnitPack5, # no rr, no subtree
2963
RepositoryFormatKnit3, # rr, subtree
2964
RepositoryFormatKnitPack3, # rr, subtree
2965
RepositoryFormatKnitPack4, # rr, no subtree
2966
RepositoryFormatKnitPack5RichRoot,# rr, no subtree
2967
RepositoryFormatPackDevelopment2Subtree, # rr, subtree
2969
for format in norichroot:
2970
if format.rich_root_data:
2971
raise AssertionError('Format %s is a rich-root format'
2972
' but is included in the non-rich-root list'
2974
for format in richroot:
2975
if not format.rich_root_data:
2976
raise AssertionError('Format %s is not a rich-root format'
2977
' but is included in the rich-root list'
2979
# TODO: One alternative is to just check format.rich_root_data,
2980
# instead of keeping membership lists. However, the formats
2981
# *also* have to use the same 'Knit' style of storage
2982
# (line-deltas, fulltexts, etc.)
2983
return (isinstance(source._format, norichroot) and
2984
isinstance(target._format, richroot))
2835
RepositoryFormatPackDevelopment0Subtree,
2837
return (isinstance(source._format, nosubtrees) and
2838
isinstance(target._format, subtrees))
2985
2839
except AttributeError:
3048
2902
return len(revision_ids), 0
3051
class InterOtherToRemote(InterRepository):
3053
_walk_to_common_revisions_batch_size = 50
3055
def __init__(self, source, target):
3056
InterRepository.__init__(self, source, target)
3057
self._real_inter = None
3060
def is_compatible(source, target):
3061
if isinstance(target, remote.RemoteRepository):
3065
def _ensure_real_inter(self):
3066
if self._real_inter is None:
3067
self.target._ensure_real()
3068
real_target = self.target._real_repository
3069
self._real_inter = InterRepository.get(self.source, real_target)
3070
# Make _real_inter use the RemoteRepository for get_parent_map
3071
self._real_inter.target_get_graph = self.target.get_graph
3072
self._real_inter.target_get_parent_map = self.target.get_parent_map
3074
def copy_content(self, revision_id=None):
3075
self._ensure_real_inter()
3076
self._real_inter.copy_content(revision_id=revision_id)
3078
def fetch(self, revision_id=None, pb=None, find_ghosts=False):
3079
self._ensure_real_inter()
3080
return self._real_inter.fetch(revision_id=revision_id, pb=pb,
3081
find_ghosts=find_ghosts)
3084
def _get_repo_format_to_test(self):
3088
2905
class InterRemoteToOther(InterRepository):
3090
2907
def __init__(self, source, target):
3103
2920
"We don't support remote repos backed by remote repos yet.")
3104
2921
return InterRepository._same_model(real_source, target)
2924
def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2925
"""See InterRepository.fetch()."""
2926
from bzrlib.fetch import RemoteToOtherFetcher
2927
mutter("Using fetch logic to copy between %s(remote) and %s(%s)",
2928
self.source, self.target, self.target._format)
2929
# TODO: jam 20070210 This should be an assert, not a translate
2930
revision_id = osutils.safe_revision_id(revision_id)
2931
f = RemoteToOtherFetcher(to_repository=self.target,
2932
from_repository=self.source,
2933
last_revision=revision_id,
2934
pb=pb, find_ghosts=find_ghosts)
2935
return f.count_copied, f.failed_revisions
2938
def _get_repo_format_to_test(self):
2942
class InterOtherToRemote(InterRepository):
2944
def __init__(self, source, target):
2945
InterRepository.__init__(self, source, target)
2946
self._real_inter = None
2949
def is_compatible(source, target):
2950
if isinstance(target, remote.RemoteRepository):
3106
2954
def _ensure_real_inter(self):
3107
2955
if self._real_inter is None:
3108
self.source._ensure_real()
3109
real_source = self.source._real_repository
3110
self._real_inter = InterRepository.get(real_source, self.target)
2956
self.target._ensure_real()
2957
real_target = self.target._real_repository
2958
self._real_inter = InterRepository.get(self.source, real_target)
3112
def fetch(self, revision_id=None, pb=None, find_ghosts=False):
3113
self._ensure_real_inter()
3114
return self._real_inter.fetch(revision_id=revision_id, pb=pb,
3115
find_ghosts=find_ghosts)
3117
2960
def copy_content(self, revision_id=None):
3118
2961
self._ensure_real_inter()
3119
2962
self._real_inter.copy_content(revision_id=revision_id)
2964
def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2965
self._ensure_real_inter()
2966
self._real_inter.fetch(revision_id=revision_id, pb=pb,
2967
find_ghosts=find_ghosts)
3122
2970
def _get_repo_format_to_test(self):
3127
2974
InterRepository.register_optimiser(InterDifferingSerializer)
3128
2975
InterRepository.register_optimiser(InterSameDataRepository)
3129
2976
InterRepository.register_optimiser(InterWeaveRepo)
3223
3070
self.repository = repository
3224
3071
self.text_index = self.repository._generate_text_key_index()
3226
def calculate_file_version_parents(self, text_key):
3073
def calculate_file_version_parents(self, revision_id, file_id):
3227
3074
"""Calculate the correct parents for a file version according to
3228
3075
the inventories.
3230
parent_keys = self.text_index[text_key]
3077
parent_keys = self.text_index[(file_id, revision_id)]
3231
3078
if parent_keys == [_mod_revision.NULL_REVISION]:
3233
return tuple(parent_keys)
3080
# strip the file_id, for the weave api
3081
return tuple([revision_id for file_id, revision_id in parent_keys])
3235
def check_file_version_parents(self, texts, progress_bar=None):
3083
def check_file_version_parents(self, weave, file_id):
3236
3084
"""Check the parents stored in a versioned file are correct.
3238
3086
It also detects file versions that are not referenced by their
3246
3094
file, but not used by the corresponding inventory.
3248
3096
wrong_parents = {}
3249
self.file_ids = set([file_id for file_id, _ in
3250
self.text_index.iterkeys()])
3251
# text keys is now grouped by file_id
3252
n_weaves = len(self.file_ids)
3253
files_in_revisions = {}
3254
revisions_of_files = {}
3255
n_versions = len(self.text_index)
3256
progress_bar.update('loading text store', 0, n_versions)
3257
parent_map = self.repository.texts.get_parent_map(self.text_index)
3258
# On unlistable transports this could well be empty/error...
3259
text_keys = self.repository.texts.keys()
3260
unused_keys = frozenset(text_keys) - set(self.text_index)
3261
for num, key in enumerate(self.text_index.iterkeys()):
3262
if progress_bar is not None:
3263
progress_bar.update('checking text graph', num, n_versions)
3264
correct_parents = self.calculate_file_version_parents(key)
3097
unused_versions = set()
3098
versions = weave.versions()
3099
parent_map = weave.get_parent_map(versions)
3100
for num, revision_id in enumerate(versions):
3266
knit_parents = parent_map[key]
3267
except errors.RevisionNotPresent:
3270
if correct_parents != knit_parents:
3271
wrong_parents[key] = (knit_parents, correct_parents)
3272
return wrong_parents, unused_keys
3102
correct_parents = self.calculate_file_version_parents(
3103
revision_id, file_id)
3105
# The version is not part of the used keys.
3106
unused_versions.add(revision_id)
3109
knit_parents = tuple(parent_map[revision_id])
3110
except errors.RevisionNotPresent:
3112
if correct_parents != knit_parents:
3113
wrong_parents[revision_id] = (knit_parents, correct_parents)
3114
return wrong_parents, unused_versions
3275
3117
def _old_get_graph(repository, revision_id):