45
46
from bzrlib.graph import DictParentsProvider, Graph, StackedParentsProvider
46
47
from bzrlib.transport.memory import MemoryTransport
49
from bzrlib.inter import InterObject
48
50
from bzrlib.registry import Registry
49
51
from bzrlib.symbol_versioning import *
50
52
from bzrlib.textmerge import TextMerge
158
160
self.storage_kind)
163
class InventoryDeltaContentFactory(ContentFactory):
165
def __init__(self, key, parents, sha1, delta, basis_id, format_flags,
168
self.storage_kind = 'inventory-delta'
170
self.parents = parents
172
self._basis_id = basis_id
173
self._format_flags = format_flags
176
def get_bytes_as(self, storage_kind):
177
if storage_kind == self.storage_kind:
178
return self._basis_id, self.key, self._delta, self._format_flags
179
elif storage_kind == 'inventory-delta-bytes':
180
serializer = inventory_delta.InventoryDeltaSerializer()
181
serializer.require_flags(*self._format_flags)
182
return ''.join(serializer.delta_to_lines(
183
self._basis_id, self.key, self._delta))
184
raise errors.UnavailableRepresentation(self.key, storage_kind,
161
188
class AbsentContentFactory(ContentFactory):
162
189
"""A placeholder content factory for unavailable texts.
176
203
self.parents = None
178
def get_bytes_as(self, storage_kind):
179
raise ValueError('A request was made for key: %s, but that'
180
' content is not available, and the calling'
181
' code does not handle if it is missing.'
185
206
class AdapterFactory(ContentFactory):
186
207
"""A content factory to adapt between key prefix's."""
913
934
raise NotImplementedError(self.annotate)
915
936
def check(self, progress_bar=None):
916
"""Check this object for integrity.
918
:param progress_bar: A progress bar to output as the check progresses.
919
:param keys: Specific keys within the VersionedFiles to check. When
920
this parameter is not None, check() becomes a generator as per
921
get_record_stream. The difference to get_record_stream is that
922
more or deeper checks will be performed.
923
:return: None, or if keys was supplied a generator as per
937
"""Check this object for integrity."""
926
938
raise NotImplementedError(self.check)
929
941
def check_not_reserved_id(version_id):
930
942
revision.check_not_reserved_id(version_id)
932
def clear_cache(self):
933
"""Clear whatever caches this VersionedFile holds.
935
This is generally called after an operation has been performed, when we
936
don't expect to be using this versioned file again soon.
939
944
def _check_lines_not_unicode(self, lines):
940
945
"""Check that lines being added to a versioned file are not unicode."""
941
946
for line in lines:
948
953
if '\n' in line[:-1]:
949
954
raise errors.BzrBadParameterContainsNewline("lines")
951
def get_known_graph_ancestry(self, keys):
952
"""Get a KnownGraph instance with the ancestry of keys."""
953
# most basic implementation is a loop around get_parent_map
957
this_parent_map = self.get_parent_map(pending)
958
parent_map.update(this_parent_map)
960
map(pending.update, this_parent_map.itervalues())
961
pending = pending.difference(parent_map)
962
kg = _mod_graph.KnownGraph(parent_map)
965
956
def get_parent_map(self, keys):
966
957
"""Get a map of the parents of keys.
1162
1153
def get_annotator(self):
1163
1154
return annotate.Annotator(self)
1165
def check(self, progress_bar=None, keys=None):
1156
def check(self, progress_bar=None):
1166
1157
"""See VersionedFiles.check()."""
1167
# XXX: This is over-enthusiastic but as we only thunk for Weaves today
1168
# this is tolerable. Ideally we'd pass keys down to check() and
1169
# have the older VersiondFile interface updated too.
1170
1158
for prefix, vf in self._iter_all_components():
1172
if keys is not None:
1173
return self.get_record_stream(keys, 'unordered', True)
1175
1161
def get_parent_map(self, keys):
1176
1162
"""Get a map of the parents of keys.
1489
1475
for struct in outstanding_struct():
1492
def base_from_plan(self):
1493
"""Construct a BASE file from the plan text."""
1495
for state, line in self.plan:
1496
if state in ('killed-a', 'killed-b', 'killed-both', 'unchanged'):
1497
# If unchanged, then this line is straight from base. If a or b
1498
# or both killed the line, then it *used* to be in base.
1499
base_lines.append(line)
1501
if state not in ('killed-base', 'irrelevant',
1502
'ghost-a', 'ghost-b',
1504
'conflicted-a', 'conflicted-b'):
1505
# killed-base, irrelevant means it doesn't apply
1506
# ghost-a/ghost-b are harder to say for sure, but they
1507
# aren't in the 'inc_c' which means they aren't in the
1508
# shared base of a & b. So we don't include them. And
1509
# obviously if the line is newly inserted, it isn't in base
1511
# If 'conflicted-a' or b, then it is new vs one base, but
1512
# old versus another base. However, if we make it present
1513
# in the base, it will be deleted from the target, and it
1514
# seems better to get a line doubled in the merge result,
1515
# rather than have it deleted entirely.
1516
# Example, each node is the 'text' at that point:
1524
# There was a criss-cross conflict merge. Both sides
1525
# include the other, but put themselves first.
1526
# Weave marks this as a 'clean' merge, picking OTHER over
1527
# THIS. (Though the details depend on order inserted into
1529
# LCA generates a plan:
1530
# [('unchanged', M),
1531
# ('conflicted-b', b),
1533
# ('conflicted-a', b),
1535
# If you mark 'conflicted-*' as part of BASE, then a 3-way
1536
# merge tool will cleanly generate "MaN" (as BASE vs THIS
1537
# removes one 'b', and BASE vs OTHER removes the other)
1538
# If you include neither, 3-way creates a clean "MbabN" as
1539
# THIS adds one 'b', and OTHER does too.
1540
# It seems that having the line 2 times is better than
1541
# having it omitted. (Easier to manually delete than notice
1542
# it needs to be added.)
1543
raise AssertionError('Unknown state: %s' % (state,))
1547
1479
class WeaveMerge(PlanWeaveMerge):
1548
1480
"""Weave merge that takes a VersionedFile and two versions as its input."""
1649
1581
self._kind_factory = {
1650
1582
'fulltext': fulltext_network_to_record,
1651
1583
'groupcompress-block': groupcompress.network_block_to_records,
1584
'inventory-delta': inventory_delta_network_to_record,
1652
1585
'knit-ft-gz': knit.knit_network_to_record,
1653
1586
'knit-delta-gz': knit.knit_network_to_record,
1654
1587
'knit-annotated-ft-gz': knit.knit_network_to_record,
1679
1612
return [FulltextContentFactory(key, parents, None, fulltext)]
1615
def inventory_delta_network_to_record(kind, bytes, line_end):
1616
"""Convert a network inventory-delta record to record."""
1617
meta_len, = struct.unpack('!L', bytes[line_end:line_end+4])
1618
record_meta = bytes[line_end+4:line_end+4+meta_len]
1619
key, parents = bencode.bdecode_as_tuple(record_meta)
1620
if parents == 'nil':
1622
inventory_delta_bytes = bytes[line_end+4+meta_len:]
1623
deserialiser = inventory_delta.InventoryDeltaSerializer()
1624
parse_result = deserialiser.parse_text_bytes(inventory_delta_bytes)
1625
basis_id, new_id, rich_root, tree_refs, delta = parse_result
1626
return [InventoryDeltaContentFactory(
1627
key, parents, None, delta, basis_id, (rich_root, tree_refs))]
1682
1630
def _length_prefix(bytes):
1683
1631
return struct.pack('!L', len(bytes))
1694
1642
_length_prefix(record_meta), record_meta, record_content)
1645
def record_to_inventory_delta_bytes(record):
1646
record_content = record.get_bytes_as('inventory-delta-bytes')
1647
if record.parents is None:
1650
parents = record.parents
1651
record_meta = bencode.bencode((record.key, parents))
1652
return "inventory-delta\n%s%s%s" % (
1653
_length_prefix(record_meta), record_meta, record_content)
1697
1656
def sort_groupcompress(parent_map):
1698
1657
"""Sort and group the keys in parent_map into groupcompress order.