266
269
the data back accurately. (Checking the lines have been split
267
270
correctly is expensive and extremely unlikely to catch bugs so it
268
271
is not done at runtime unless check_content is True.)
269
:param parent_texts: An optional dictionary containing the opaque
272
:param parent_texts: An optional dictionary containing the opaque
270
273
representations of some or all of the parents of version_id to
271
274
allow delta optimisations. VERY IMPORTANT: the texts must be those
272
275
returned by add_lines or data corruption can be caused.
457
460
if isinstance(version_ids, basestring):
458
461
version_ids = [version_ids]
459
462
raise NotImplementedError(self.get_ancestry)
461
464
def get_ancestry_with_ghosts(self, version_ids):
462
465
"""Return a list of all ancestors of given version(s). This
463
466
will not include the null revision.
465
468
Must raise RevisionNotPresent if any of the given versions are
466
469
not present in file history.
468
471
Ghosts that are known about will be included in ancestry list,
469
472
but are not explicitly marked.
471
474
raise NotImplementedError(self.get_ancestry_with_ghosts)
473
476
def get_parent_map(self, version_ids):
474
477
"""Get a map of the parents of version_ids.
538
541
unchanged Alive in both a and b (possibly created in both)
539
542
new-a Created in a
540
543
new-b Created in b
541
ghost-a Killed in a, unborn in b
544
ghost-a Killed in a, unborn in b
542
545
ghost-b Killed in b, unborn in a
543
546
irrelevant Not in either revision
545
548
raise NotImplementedError(VersionedFile.plan_merge)
547
550
def weave_merge(self, plan, a_marker=TextMerge.A_MARKER,
548
551
b_marker=TextMerge.B_MARKER):
549
552
return PlanWeaveMerge(plan, a_marker, b_marker).merge_lines()[0]
801
804
the data back accurately. (Checking the lines have been split
802
805
correctly is expensive and extremely unlikely to catch bugs so it
803
806
is not done at runtime unless check_content is True.)
804
:param parent_texts: An optional dictionary containing the opaque
807
:param parent_texts: An optional dictionary containing the opaque
805
808
representations of some or all of the parents of version_id to
806
809
allow delta optimisations. VERY IMPORTANT: the texts must be those
807
810
returned by add_lines or data corruption can be caused.
926
929
has_key = index._has_key_from_parent_map
931
def get_missing_compression_parent_keys(self):
932
"""Return an iterable of keys of missing compression parents.
934
Check this after calling insert_record_stream to find out if there are
935
any missing compression parents. If there are, the records that
936
depend on them are not able to be inserted safely. The precise
937
behaviour depends on the concrete VersionedFiles class in use.
939
Classes that do not support this will raise NotImplementedError.
941
raise NotImplementedError(self.get_missing_compression_parent_keys)
928
943
def insert_record_stream(self, stream):
929
944
"""Insert a record stream into this container.
931
:param stream: A stream of records to insert.
946
:param stream: A stream of records to insert.
933
948
:seealso VersionedFile.get_record_stream:
1397
1412
class WeaveMerge(PlanWeaveMerge):
1398
1413
"""Weave merge that takes a VersionedFile and two versions as its input."""
1400
def __init__(self, versionedfile, ver_a, ver_b,
1415
def __init__(self, versionedfile, ver_a, ver_b,
1401
1416
a_marker=PlanWeaveMerge.A_MARKER, b_marker=PlanWeaveMerge.B_MARKER):
1402
1417
plan = versionedfile.plan_merge(ver_a, ver_b)
1403
1418
PlanWeaveMerge.__init__(self, plan, a_marker, b_marker)
1406
1421
class VirtualVersionedFiles(VersionedFiles):
1407
"""Dummy implementation for VersionedFiles that uses other functions for
1422
"""Dummy implementation for VersionedFiles that uses other functions for
1408
1423
obtaining fulltexts and parent maps.
1410
This is always on the bottom of the stack and uses string keys
1425
This is always on the bottom of the stack and uses string keys
1411
1426
(rather than tuples) internally.
1415
1430
"""Create a VirtualVersionedFiles.
1417
1432
:param get_parent_map: Same signature as Repository.get_parent_map.
1418
:param get_lines: Should return lines for specified key or None if
1433
:param get_lines: Should return lines for specified key or None if
1421
1436
super(VirtualVersionedFiles, self).__init__()
1422
1437
self._get_parent_map = get_parent_map
1423
1438
self._get_lines = get_lines
1425
1440
def check(self, progressbar=None):
1426
1441
"""See VersionedFiles.check.
1472
1487
pb.update("iterating texts", i, len(keys))
1473
1488
for l in self._get_lines(key):
1492
def network_bytes_to_kind_and_offset(network_bytes):
1493
"""Strip of a record kind from the front of network_bytes.
1495
:param network_bytes: The bytes of a record.
1496
:return: A tuple (storage_kind, offset_of_remaining_bytes)
1498
line_end = network_bytes.find('\n')
1499
storage_kind = network_bytes[:line_end]
1500
return storage_kind, line_end + 1
1503
class NetworkRecordStream(object):
1504
"""A record_stream which reconstitures a serialised stream."""
1506
def __init__(self, bytes_iterator):
1507
"""Create a NetworkRecordStream.
1509
:param bytes_iterator: An iterator of bytes. Each item in this
1510
iterator should have been obtained from a record_streams'
1511
record.get_bytes_as(record.storage_kind) call.
1513
self._bytes_iterator = bytes_iterator
1514
self._kind_factory = {'knit-ft-gz':knit.knit_network_to_record,
1515
'knit-delta-gz':knit.knit_network_to_record,
1516
'knit-annotated-ft-gz':knit.knit_network_to_record,
1517
'knit-annotated-delta-gz':knit.knit_network_to_record,
1518
'knit-delta-closure':knit.knit_delta_closure_to_records,
1519
'fulltext':fulltext_network_to_record,
1525
:return: An iterator as per VersionedFiles.get_record_stream().
1527
for bytes in self._bytes_iterator:
1528
storage_kind, line_end = network_bytes_to_kind_and_offset(bytes)
1529
for record in self._kind_factory[storage_kind](
1530
storage_kind, bytes, line_end):
1534
def fulltext_network_to_record(kind, bytes, line_end):
1535
"""Convert a network fulltext record to record."""
1536
meta_len, = struct.unpack('!L', bytes[line_end:line_end+4])
1537
record_meta = record_bytes[line_end+4:line_end+4+meta_len]
1538
key, parents = bencode.bdecode_as_tuple(record_meta)
1539
if parents == 'nil':
1541
fulltext = record_bytes[line_end+4+meta_len:]
1542
return FulltextContentFactory(key, parents, None, fulltext)
1545
def _length_prefix(bytes):
1546
return struct.pack('!L', len(bytes))
1549
def record_to_fulltext_bytes(self, record):
1550
if record.parents is None:
1553
parents = record.parents
1554
record_meta = bencode.bencode((record.key, parents))
1555
record_content = record.get_bytes_as('fulltext')
1556
return "fulltext\n%s%s%s" % (
1557
_length_prefix(record_meta), record_meta, record_content)