37
37
from cStringIO import StringIO
39
39
from bzrlib.inter import InterObject
40
from bzrlib.registry import Registry
40
41
from bzrlib.symbol_versioning import *
41
42
from bzrlib.textmerge import TextMerge
45
adapter_registry = Registry()
46
adapter_registry.register_lazy(('knit-delta-gz', 'fulltext'), 'bzrlib.knit',
47
'DeltaPlainToFullText')
48
adapter_registry.register_lazy(('knit-ft-gz', 'fulltext'), 'bzrlib.knit',
50
adapter_registry.register_lazy(('knit-annotated-delta-gz', 'knit-delta-gz'),
51
'bzrlib.knit', 'DeltaAnnotatedToUnannotated')
52
adapter_registry.register_lazy(('knit-annotated-delta-gz', 'fulltext'),
53
'bzrlib.knit', 'DeltaAnnotatedToFullText')
54
adapter_registry.register_lazy(('knit-annotated-ft-gz', 'knit-ft-gz'),
55
'bzrlib.knit', 'FTAnnotatedToUnannotated')
56
adapter_registry.register_lazy(('knit-annotated-ft-gz', 'fulltext'),
57
'bzrlib.knit', 'FTAnnotatedToFullText')
60
class ContentFactory(object):
61
"""Abstract interface for insertion and retrieval from a VersionedFile.
63
:ivar sha1: None, or the sha1 of the content fulltext.
64
:ivar storage_kind: The native storage kind of this factory. One of
65
'mpdiff', 'knit-annotated-ft', 'knit-annotated-delta', 'knit-ft',
66
'knit-delta', 'fulltext', 'knit-annotated-ft-gz',
67
'knit-annotated-delta-gz', 'knit-ft-gz', 'knit-delta-gz'.
68
:ivar key: The key of this content. Each key is a tuple with a single
70
:ivar parents: A tuple of parent keys for self.key. If the object has
71
no parent information, None (as opposed to () for an empty list of
76
"""Create a ContentFactory."""
78
self.storage_kind = None
83
class AbsentContentFactory(object):
84
"""A placeholder content factory for unavailable texts.
87
:ivar storage_kind: 'absent'.
88
:ivar key: The key of this content. Each key is a tuple with a single
93
def __init__(self, key):
94
"""Create a ContentFactory."""
96
self.storage_kind = 'absent'
101
def filter_absent(record_stream):
102
"""Adapt a record stream to remove absent records."""
103
for record in record_stream:
104
if record.storage_kind != 'absent':
44
108
class VersionedFile(object):
45
109
"""Versioned text file storage.
63
127
"""Copy this versioned file to name on transport."""
64
128
raise NotImplementedError(self.copy_to)
67
"""Return a unsorted list of versions."""
68
raise NotImplementedError(self.versions)
130
def get_record_stream(self, versions, ordering, include_delta_closure):
131
"""Get a stream of records for versions.
70
@deprecated_method(one_four)
71
def has_ghost(self, version_id):
72
"""Returns whether version is present as a ghost."""
73
raise NotImplementedError(self.has_ghost)
133
:param versions: The versions to include. Each version is a tuple
135
:param ordering: Either 'unordered' or 'topological'. A topologically
136
sorted stream has compression parents strictly before their
138
:param include_delta_closure: If True then the closure across any
139
compression parents will be included (in the data content of the
140
stream, not in the emitted records). This guarantees that
141
'fulltext' can be used successfully on every record.
142
:return: An iterator of ContentFactory objects, each of which is only
143
valid until the iterator is advanced.
145
raise NotImplementedError(self.get_record_stream)
75
147
def has_version(self, version_id):
76
148
"""Returns whether version is present."""
77
149
raise NotImplementedError(self.has_version)
151
def insert_record_stream(self, stream):
152
"""Insert a record stream into this versioned file.
154
:param stream: A stream of records to insert.
156
:seealso VersionedFile.get_record_stream:
158
raise NotImplementedError
79
160
def add_lines(self, version_id, parents, lines, parent_texts=None,
80
161
left_matching_blocks=None, nostore_sha=None, random_id=False,
81
162
check_content=True):
157
238
if '\n' in line[:-1]:
158
239
raise errors.BzrBadParameterContainsNewline("lines")
160
@deprecated_method(one_four)
161
def enable_cache(self):
162
"""Tell this versioned file that it should cache any data it reads.
164
This is advisory, implementations do not have to support caching.
168
@deprecated_method(one_four)
169
def clear_cache(self):
170
"""Remove any data cached in the versioned file object.
172
This only needs to be supported if caches are supported
176
@deprecated_method(one_four)
177
def clone_text(self, new_version_id, old_version_id, parents):
178
"""Add an identical text to old_version_id as new_version_id.
180
Must raise RevisionNotPresent if the old version or any of the
181
parents are not present in file history.
183
Must raise RevisionAlreadyPresent if the new version is
184
already present in file history."""
185
self._check_write_ok()
186
return self.add_lines(new_version_id, parents,
187
self.get_lines(old_version_id))
189
241
def get_format_signature(self):
190
242
"""Get a text description of the data encoding in this file.
271
323
if expected_sha1 != sha1:
272
324
raise errors.VersionedFileInvalidChecksum(version)
274
@deprecated_method(one_four)
275
def get_sha1(self, version_id):
276
"""Get the stored sha1 sum for the given revision.
278
:param version_id: The name of the version to lookup
280
return self.get_sha1s([version_id])[0]
282
326
def get_sha1s(self, version_ids):
283
327
"""Get the stored sha1 sums for the given revisions.
341
385
raise NotImplementedError(self.get_ancestry_with_ghosts)
343
@deprecated_method(one_four)
344
def get_graph(self, version_ids=None):
345
"""Return a graph from the versioned file.
347
Ghosts are not listed or referenced in the graph.
348
:param version_ids: Versions to select.
349
None means retrieve all versions.
351
if version_ids is None:
352
result = self.get_parent_map(self.versions())
355
pending = set(version_ids)
357
this_iteration = pending
359
parents = self.get_parent_map(this_iteration)
360
for version, parents in parents.iteritems():
361
result[version] = parents
362
for parent in parents:
367
for parents in result.itervalues():
368
references.update(parents)
369
existing_parents = self.get_parent_map(references)
370
for key, parents in result.iteritems():
371
present_parents = [parent for parent in parents if parent in
373
result[key] = tuple(present_parents)
376
@deprecated_method(one_four)
377
def get_graph_with_ghosts(self):
378
"""Return a graph for the entire versioned file.
380
Ghosts are referenced in parents list but are not
383
raise NotImplementedError(self.get_graph_with_ghosts)
385
387
def get_parent_map(self, version_ids):
386
388
"""Get a map of the parents of version_ids.
391
393
raise NotImplementedError(self.get_parent_map)
393
@deprecated_method(one_four)
394
def get_parents(self, version_id):
395
"""Return version names for parents of a version.
397
Must raise RevisionNotPresent if version is not present in
401
all = self.get_parent_map([version_id])[version_id]
403
raise errors.RevisionNotPresent(version_id, self)
405
parent_parents = self.get_parent_map(all)
406
for version_id in all:
407
if version_id in parent_parents:
408
result.append(version_id)
411
395
def get_parents_with_ghosts(self, version_id):
412
396
"""Return version names for parents of version_id.
423
407
raise errors.RevisionNotPresent(version_id, self)
425
@deprecated_method(one_four)
426
def annotate_iter(self, version_id):
427
"""Yield list of (version-id, line) pairs for the specified
430
Must raise RevisionNotPresent if the given version is
431
not present in file history.
433
return iter(self.annotate(version_id))
435
409
def annotate(self, version_id):
436
410
"""Return a list of (version-id, line) tuples for version_id.
481
456
raise NotImplementedError(self.iter_lines_added_or_present_in_versions)
483
@deprecated_method(one_four)
484
def iter_parents(self, version_ids):
485
"""Iterate through the parents for many version ids.
487
:param version_ids: An iterable yielding version_ids.
488
:return: An iterator that yields (version_id, parents). Requested
489
version_ids not present in the versioned file are simply skipped.
490
The order is undefined, allowing for different optimisations in
491
the underlying implementation.
493
return self.get_parent_map(version_ids).iteritems()
495
458
def plan_merge(self, ver_a, ver_b):
496
459
"""Return pseudo-annotation indicating how the two versions merge.
519
482
return PlanWeaveMerge(plan, a_marker, b_marker).merge_lines()[0]
485
class RecordingVersionedFileDecorator(object):
486
"""A minimal versioned file that records calls made on it.
488
Only enough methods have been added to support tests using it to date.
490
:ivar calls: A list of the calls made; can be reset at any time by
494
def __init__(self, backing_vf):
495
"""Create a RecordingVersionedFileDecorator decorating backing_vf.
497
:param backing_vf: The versioned file to answer all methods.
499
self._backing_vf = backing_vf
502
def get_lines(self, version_ids):
503
self.calls.append(("get_lines", version_ids))
504
return self._backing_vf.get_lines(version_ids)
522
507
class _PlanMergeVersionedFile(object):
523
508
"""A VersionedFile for uncommitted and committed texts.
712
697
ch_b = ch_a = True
713
698
lines_b.append(line)
715
assert state in ('irrelevant', 'ghost-a', 'ghost-b',
716
'killed-base', 'killed-both'), state
700
if state not in ('irrelevant', 'ghost-a', 'ghost-b',
701
'killed-base', 'killed-both'):
702
raise AssertionError(state)
717
703
for struct in outstanding_struct():