77
89
"""Returns whether version is present."""
78
90
raise NotImplementedError(self.has_version)
80
def add_lines(self, version_id, parents, lines, parent_texts=None,
81
left_matching_blocks=None, nostore_sha=None, random_id=False,
92
def add_delta(self, version_id, parents, delta_parent, sha1, noeol, delta):
93
"""Add a text to the versioned file via a pregenerated delta.
95
:param version_id: The version id being added.
96
:param parents: The parents of the version_id.
97
:param delta_parent: The parent this delta was created against.
98
:param sha1: The sha1 of the full text.
99
:param delta: The delta instructions. See get_delta for details.
101
version_id = osutils.safe_revision_id(version_id)
102
parents = [osutils.safe_revision_id(v) for v in parents]
103
self._check_write_ok()
104
if self.has_version(version_id):
105
raise errors.RevisionAlreadyPresent(version_id, self)
106
return self._add_delta(version_id, parents, delta_parent, sha1, noeol, delta)
108
def _add_delta(self, version_id, parents, delta_parent, sha1, noeol, delta):
109
"""Class specific routine to add a delta.
111
This generic version simply applies the delta to the delta_parent and
114
# strip annotation from delta
116
for start, stop, delta_len, delta_lines in delta:
117
new_delta.append((start, stop, delta_len, [text for origin, text in delta_lines]))
118
if delta_parent is not None:
119
parent_full = self.get_lines(delta_parent)
122
new_full = self._apply_delta(parent_full, new_delta)
123
# its impossible to have noeol on an empty file
124
if noeol and new_full[-1][-1] == '\n':
125
new_full[-1] = new_full[-1][:-1]
126
self.add_lines(version_id, parents, new_full)
128
def add_lines(self, version_id, parents, lines, parent_texts=None):
83
129
"""Add a single text on top of the versioned file.
85
131
Must raise RevisionAlreadyPresent if the new version is
88
134
Must raise RevisionNotPresent if any of the given parents are
89
135
not present in file history.
91
:param lines: A list of lines. Each line must be a bytestring. And all
92
of them except the last must be terminated with \n and contain no
93
other \n's. The last line may either contain no \n's or a single
94
terminated \n. If the lines list does meet this constraint the add
95
routine may error or may succeed - but you will be unable to read
96
the data back accurately. (Checking the lines have been split
97
correctly is expensive and extremely unlikely to catch bugs so it
98
is not done at runtime unless check_content is True.)
99
136
:param parent_texts: An optional dictionary containing the opaque
100
representations of some or all of the parents of version_id to
101
allow delta optimisations. VERY IMPORTANT: the texts must be those
102
returned by add_lines or data corruption can be caused.
103
:param left_matching_blocks: a hint about which areas are common
104
between the text and its left-hand-parent. The format is
105
the SequenceMatcher.get_matching_blocks format.
106
:param nostore_sha: Raise ExistingContent and do not add the lines to
107
the versioned file if the digest of the lines matches this.
108
:param random_id: If True a random id has been selected rather than
109
an id determined by some deterministic process such as a converter
110
from a foreign VCS. When True the backend may choose not to check
111
for uniqueness of the resulting key within the versioned file, so
112
this should only be done when the result is expected to be unique
114
:param check_content: If True, the lines supplied are verified to be
115
bytestrings that are correctly formed lines.
116
:return: The text sha1, the number of bytes in the text, and an opaque
117
representation of the inserted version which can be provided
118
back to future add_lines calls in the parent_texts dictionary.
137
representations of some or all of the parents of
138
version_id to allow delta optimisations.
139
VERY IMPORTANT: the texts must be those returned
140
by add_lines or data corruption can be caused.
141
:return: An opaque representation of the inserted version which can be
142
provided back to future add_lines calls in the parent_texts
145
version_id = osutils.safe_revision_id(version_id)
146
parents = [osutils.safe_revision_id(v) for v in parents]
120
147
self._check_write_ok()
121
return self._add_lines(version_id, parents, lines, parent_texts,
122
left_matching_blocks, nostore_sha, random_id, check_content)
148
return self._add_lines(version_id, parents, lines, parent_texts)
124
def _add_lines(self, version_id, parents, lines, parent_texts,
125
left_matching_blocks, nostore_sha, random_id, check_content):
150
def _add_lines(self, version_id, parents, lines, parent_texts):
126
151
"""Helper to do the class specific add_lines."""
127
152
raise NotImplementedError(self.add_lines)
129
154
def add_lines_with_ghosts(self, version_id, parents, lines,
130
parent_texts=None, nostore_sha=None, random_id=False,
132
156
"""Add lines to the versioned file, allowing ghosts to be present.
134
This takes the same parameters as add_lines and returns the same.
158
This takes the same parameters as add_lines.
160
version_id = osutils.safe_revision_id(version_id)
161
parents = [osutils.safe_revision_id(v) for v in parents]
136
162
self._check_write_ok()
137
163
return self._add_lines_with_ghosts(version_id, parents, lines,
138
parent_texts, nostore_sha, random_id, check_content)
140
def _add_lines_with_ghosts(self, version_id, parents, lines, parent_texts,
141
nostore_sha, random_id, check_content):
166
def _add_lines_with_ghosts(self, version_id, parents, lines, parent_texts):
142
167
"""Helper to do class specific add_lines_with_ghosts."""
143
168
raise NotImplementedError(self.add_lines_with_ghosts)
204
231
raise NotImplementedError(self.create_empty)
206
def get_format_signature(self):
207
"""Get a text description of the data encoding in this file.
211
raise NotImplementedError(self.get_format_signature)
233
def fix_parents(self, version_id, new_parents):
234
"""Fix the parents list for version.
236
This is done by appending a new version to the index
237
with identical data except for the parents list.
238
the parents list must be a superset of the current
241
version_id = osutils.safe_revision_id(version_id)
242
new_parents = [osutils.safe_revision_id(p) for p in new_parents]
243
self._check_write_ok()
244
return self._fix_parents(version_id, new_parents)
246
def _fix_parents(self, version_id, new_parents):
247
"""Helper for fix_parents."""
248
raise NotImplementedError(self.fix_parents)
250
def get_delta(self, version):
251
"""Get a delta for constructing version from some other version.
253
:return: (delta_parent, sha1, noeol, delta)
254
Where delta_parent is a version id or None to indicate no parent.
256
raise NotImplementedError(self.get_delta)
258
def get_deltas(self, version_ids):
259
"""Get multiple deltas at once for constructing versions.
261
:return: dict(version_id:(delta_parent, sha1, noeol, delta))
262
Where delta_parent is a version id or None to indicate no parent, and
263
version_id is the version_id created by that delta.
266
for version_id in version_ids:
267
result[version_id] = self.get_delta(version_id)
213
270
def make_mpdiffs(self, version_ids):
214
"""Create multiparent diffs for specified versions."""
271
"""Create multiparent diffs for specified versions"""
215
272
knit_versions = set()
216
273
for version_id in version_ids:
217
274
knit_versions.add(version_id)
237
294
def add_mpdiffs(self, records):
238
"""Add mpdiffs to this VersionedFile.
295
"""Add mpdiffs to this versionedfile
240
297
Records should be iterables of version, parents, expected_sha1,
241
mpdiff. mpdiff should be a MultiParent instance.
298
mpdiff. mpdiff should be a MultiParent instance.
243
# Does this need to call self._check_write_ok()? (IanC 20070919)
245
mpvf = multiparent.MultiMemoryVersionedFile()
247
for version, parent_ids, expected_sha1, mpdiff in records:
248
versions.append(version)
249
mpvf.add_diff(mpdiff, version, parent_ids)
250
needed_parents = set()
251
for version, parent_ids, expected_sha1, mpdiff in records:
252
needed_parents.update(p for p in parent_ids
253
if not mpvf.has_version(p))
254
for parent_id, lines in zip(needed_parents,
255
self._get_lf_split_line_list(needed_parents)):
256
mpvf.add_version(lines, parent_id, [])
257
for (version, parent_ids, expected_sha1, mpdiff), lines in\
258
zip(records, mpvf.get_line_list(versions)):
259
if len(parent_ids) == 1:
260
left_matching_blocks = list(mpdiff.get_matching_blocks(0,
261
mpvf.get_diff(parent_ids[0]).num_lines()))
263
left_matching_blocks = None
264
_, _, version_text = self.add_lines(version, parent_ids, lines,
265
vf_parents, left_matching_blocks=left_matching_blocks)
301
for version, parents, expected_sha1, mpdiff in records:
302
mpvf = multiparent.MultiMemoryVersionedFile()
303
needed_parents = [p for p in parents if not mpvf.has_version(p)]
304
parent_lines = self._get_lf_split_line_list(needed_parents)
305
for parent_id, lines in zip(needed_parents, parent_lines):
306
mpvf.add_version(lines, parent_id, [])
307
mpvf.add_diff(mpdiff, version, parents)
308
lines = mpvf.get_line_list([version])[0]
309
version_text = self.add_lines(version, parents, lines, vf_parents)
266
310
vf_parents[version] = version_text
267
for (version, parent_ids, expected_sha1, mpdiff), sha1 in\
268
zip(records, self.get_sha1s(versions)):
269
if expected_sha1 != sha1:
311
if expected_sha1 != self.get_sha1(version):
270
312
raise errors.VersionedFileInvalidChecksum(version)
272
314
def get_sha1(self, version_id):
273
315
"""Get the stored sha1 sum for the given revision.
275
:param version_id: The name of the version to lookup
317
:param name: The name of the version to lookup
277
319
raise NotImplementedError(self.get_sha1)
423
def iter_lines_added_or_present_in_versions(self, version_ids=None,
482
def iter_lines_added_or_present_in_versions(self, version_ids=None,
425
484
"""Iterate over the lines in the versioned file from version_ids.
427
This may return lines from other versions. Each item the returned
428
iterator yields is a tuple of a line and a text version that that line
429
is present in (not introduced in).
431
Ordering of results is in whatever order is most suitable for the
432
underlying storage format.
486
This may return lines from other versions, and does not return the
487
specific version marker at this point. The api may be changed
488
during development to include the version that the versioned file
489
thinks is relevant, but given that such hints are just guesses,
490
its better not to have it if we don't need it.
434
492
If a progress bar is supplied, it may be used to indicate progress.
435
493
The caller is responsible for cleaning up progress bars (because this
466
522
self.finished = True
524
@deprecated_method(zero_eight)
525
def walk(self, version_ids=None):
526
"""Walk the versioned file as a weave-like structure, for
527
versions relative to version_ids. Yields sequence of (lineno,
528
insert, deletes, text) for each relevant line.
530
Must raise RevisionNotPresent if any of the specified versions
531
are not present in the file history.
533
:param version_ids: the version_ids to walk with respect to. If not
534
supplied the entire weave-like structure is walked.
536
walk is deprecated in favour of iter_lines_added_or_present_in_versions
538
raise NotImplementedError(self.walk)
540
@deprecated_method(zero_eight)
541
def iter_names(self):
542
"""Walk the names list."""
543
return iter(self.versions())
468
545
def plan_merge(self, ver_a, ver_b):
469
546
"""Return pseudo-annotation indicating how the two versions merge.
492
569
return PlanWeaveMerge(plan, a_marker, b_marker).merge_lines()[0]
495
class _PlanMergeVersionedFile(object):
496
"""A VersionedFile for uncommitted and committed texts.
498
It is intended to allow merges to be planned with working tree texts.
499
It implements only the small part of the VersionedFile interface used by
500
PlanMerge. It falls back to multiple versionedfiles for data not stored in
501
_PlanMergeVersionedFile itself.
504
def __init__(self, file_id, fallback_versionedfiles=None):
507
:param file_id: Used when raising exceptions.
508
:param fallback_versionedfiles: If supplied, the set of fallbacks to
509
use. Otherwise, _PlanMergeVersionedFile.fallback_versionedfiles
510
can be appended to later.
512
self._file_id = file_id
513
if fallback_versionedfiles is None:
514
self.fallback_versionedfiles = []
516
self.fallback_versionedfiles = fallback_versionedfiles
520
def plan_merge(self, ver_a, ver_b, base=None):
521
"""See VersionedFile.plan_merge"""
522
from bzrlib.merge import _PlanMerge
524
return _PlanMerge(ver_a, ver_b, self).plan_merge()
525
old_plan = list(_PlanMerge(ver_a, base, self).plan_merge())
526
new_plan = list(_PlanMerge(ver_a, ver_b, self).plan_merge())
527
return _PlanMerge._subtract_plans(old_plan, new_plan)
529
def plan_lca_merge(self, ver_a, ver_b, base=None):
530
from bzrlib.merge import _PlanLCAMerge
531
graph = self._get_graph()
532
new_plan = _PlanLCAMerge(ver_a, ver_b, self, graph).plan_merge()
535
old_plan = _PlanLCAMerge(ver_a, base, self, graph).plan_merge()
536
return _PlanLCAMerge._subtract_plans(list(old_plan), list(new_plan))
538
def add_lines(self, version_id, parents, lines):
539
"""See VersionedFile.add_lines
541
Lines are added locally, not fallback versionedfiles. Also, ghosts are
542
permitted. Only reserved ids are permitted.
544
if not revision.is_reserved_id(version_id):
545
raise ValueError('Only reserved ids may be used')
547
raise ValueError('Parents may not be None')
549
raise ValueError('Lines may not be None')
550
self._parents[version_id] = parents
551
self._lines[version_id] = lines
553
def get_lines(self, version_id):
554
"""See VersionedFile.get_ancestry"""
555
lines = self._lines.get(version_id)
556
if lines is not None:
558
for versionedfile in self.fallback_versionedfiles:
560
return versionedfile.get_lines(version_id)
561
except errors.RevisionNotPresent:
564
raise errors.RevisionNotPresent(version_id, self._file_id)
566
def get_ancestry(self, version_id, topo_sorted=False):
567
"""See VersionedFile.get_ancestry.
569
Note that this implementation assumes that if a VersionedFile can
570
answer get_ancestry at all, it can give an authoritative answer. In
571
fact, ghosts can invalidate this assumption. But it's good enough
572
99% of the time, and far cheaper/simpler.
574
Also note that the results of this version are never topologically
575
sorted, and are a set.
578
raise ValueError('This implementation does not provide sorting')
579
parents = self._parents.get(version_id)
581
for vf in self.fallback_versionedfiles:
583
return vf.get_ancestry(version_id, topo_sorted=False)
584
except errors.RevisionNotPresent:
587
raise errors.RevisionNotPresent(version_id, self._file_id)
588
ancestry = set([version_id])
589
for parent in parents:
590
ancestry.update(self.get_ancestry(parent, topo_sorted=False))
593
def get_parents(self, version_id):
594
"""See VersionedFile.get_parents"""
595
parents = self._parents.get(version_id)
596
if parents is not None:
598
for versionedfile in self.fallback_versionedfiles:
600
return versionedfile.get_parents(version_id)
601
except errors.RevisionNotPresent:
604
raise errors.RevisionNotPresent(version_id, self._file_id)
606
def _get_graph(self):
607
from bzrlib.graph import (
610
_StackedParentsProvider,
612
from bzrlib.repofmt.knitrepo import _KnitParentsProvider
613
parent_providers = [DictParentsProvider(self._parents)]
614
for vf in self.fallback_versionedfiles:
615
parent_providers.append(_KnitParentsProvider(vf))
616
return Graph(_StackedParentsProvider(parent_providers))
619
572
class PlanWeaveMerge(TextMerge):
620
573
"""Weave merge that takes a plan as its input.
751
698
# TODO: remove parent texts when they are not relevant any more for
752
699
# memory pressure reduction. RBC 20060313
753
700
# pb.update('Converting versioned data', 0, len(order))
701
# deltas = self.source.get_deltas(order)
755
702
for index, version in enumerate(order):
756
pb.update('Converting versioned data', index, total)
757
_, _, parent_text = target.add_lines(version,
703
pb.update('Converting versioned data', index, len(order))
704
parent_text = target.add_lines(version,
758
705
self.source.get_parents(version),
759
706
self.source.get_lines(version),
760
707
parent_texts=parent_texts)
761
708
parent_texts[version] = parent_text
709
#delta_parent, sha1, noeol, delta = deltas[version]
710
#target.add_delta(version,
711
# self.source.get_parents(version),
716
#target.get_lines(version)
763
718
# this should hit the native code path for target
764
719
if target is not self.target: