22
22
from bzrlib.lazy_import import lazy_import
23
23
lazy_import(globals(), """
24
from copy import deepcopy
25
27
from bzrlib import (
33
32
from bzrlib.transport.memory import MemoryTransport
36
from cStringIO import StringIO
38
35
from bzrlib.inter import InterObject
39
36
from bzrlib.textmerge import TextMerge
40
37
from bzrlib.symbol_versioning import (deprecated_function,
61
58
self.finished = False
62
59
self._access_mode = access_mode
65
def check_not_reserved_id(version_id):
66
revision.check_not_reserved_id(version_id)
68
61
def copy_to(self, name, transport):
69
62
"""Copy this versioned file to name on transport."""
70
63
raise NotImplementedError(self.copy_to)
98
91
:param sha1: The sha1 of the full text.
99
92
: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
94
self._check_write_ok()
104
95
if self.has_version(version_id):
105
96
raise errors.RevisionAlreadyPresent(version_id, self)
213
200
Must raise RevisionAlreadyPresent if the new version is
214
201
already present in file history."""
215
new_version_id = osutils.safe_revision_id(new_version_id)
216
old_version_id = osutils.safe_revision_id(old_version_id)
217
202
self._check_write_ok()
218
203
return self._clone_text(new_version_id, old_version_id, parents)
231
216
raise NotImplementedError(self.create_empty)
233
def fix_parents(self, version_id, new_parents):
218
def fix_parents(self, version, new_parents):
234
219
"""Fix the parents list for version.
236
221
This is done by appending a new version to the index
238
223
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
226
self._check_write_ok()
244
return self._fix_parents(version_id, new_parents)
227
return self._fix_parents(version, new_parents)
246
def _fix_parents(self, version_id, new_parents):
229
def _fix_parents(self, version, new_parents):
247
230
"""Helper for fix_parents."""
248
231
raise NotImplementedError(self.fix_parents)
256
239
raise NotImplementedError(self.get_delta)
258
def get_deltas(self, version_ids):
241
def get_deltas(self, versions):
259
242
"""Get multiple deltas at once for constructing versions.
261
244
:return: dict(version_id:(delta_parent, sha1, noeol, delta))
263
246
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)
249
for version in versions:
250
result[version] = self.get_delta(version)
270
def make_mpdiffs(self, version_ids):
271
"""Create multiparent diffs for specified versions"""
272
knit_versions = set()
273
for version_id in version_ids:
274
knit_versions.add(version_id)
275
knit_versions.update(self.get_parents(version_id))
276
lines = dict(zip(knit_versions,
277
self._get_lf_split_line_list(knit_versions)))
279
for version_id in version_ids:
280
target = lines[version_id]
281
parents = [lines[p] for p in self.get_parents(version_id)]
283
left_parent_blocks = self._extract_blocks(version_id,
286
left_parent_blocks = None
287
diffs.append(multiparent.MultiParent.from_lines(target, parents,
291
def _extract_blocks(self, version_id, source, target):
294
def add_mpdiffs(self, records):
295
"""Add mpdiffs to this versionedfile
297
Records should be iterables of version, parents, expected_sha1,
298
mpdiff. mpdiff should be a MultiParent instance.
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)
310
vf_parents[version] = version_text
311
if expected_sha1 != self.get_sha1(version):
312
raise errors.VersionedFileInvalidChecksum(version)
314
253
def get_sha1(self, version_id):
315
254
"""Get the stored sha1 sum for the given revision.
319
258
raise NotImplementedError(self.get_sha1)
321
def get_sha1s(self, version_ids):
322
"""Get the stored sha1 sums for the given revisions.
324
:param version_ids: The names of the versions to lookup
325
:return: a list of sha1s in order according to the version_ids
327
raise NotImplementedError(self.get_sha1)
329
260
def get_suffixes(self):
330
261
"""Return the file suffixes associated with this versioned file."""
331
262
raise NotImplementedError(self.get_suffixes)
356
287
raise NotImplementedError(self.get_lines)
358
def _get_lf_split_line_list(self, version_ids):
359
return [StringIO(t).readlines() for t in self.get_texts(version_ids)]
361
def get_ancestry(self, version_ids, topo_sorted=True):
289
def get_ancestry(self, version_ids):
362
290
"""Return a list of all ancestors of given version(s). This
363
291
will not include the null revision.
365
This list will not be topologically sorted if topo_sorted=False is
368
293
Must raise RevisionNotPresent if any of the given versions are
369
294
not present in file history."""
370
295
if isinstance(version_ids, basestring):
390
315
:param version_ids: Versions to select.
391
316
None means retrieve all versions.
393
319
if version_ids is None:
394
return dict(self.iter_parents(self.versions()))
396
pending = set(osutils.safe_revision_id(v) for v in version_ids)
398
this_iteration = pending
400
for version, parents in self.iter_parents(this_iteration):
401
result[version] = parents
320
for version in self.versions():
321
result[version] = self.get_parents(version)
323
pending = set(version_ids)
325
version = pending.pop()
326
if version in result:
328
parents = self.get_parents(version)
402
329
for parent in parents:
403
330
if parent in result:
405
332
pending.add(parent)
333
result[version] = parents
408
336
def get_graph_with_ghosts(self):
499
427
raise NotImplementedError(self.iter_lines_added_or_present_in_versions)
501
def iter_parents(self, version_ids):
502
"""Iterate through the parents for many version ids.
504
:param version_ids: An iterable yielding version_ids.
505
:return: An iterator that yields (version_id, parents). Requested
506
version_ids not present in the versioned file are simply skipped.
507
The order is undefined, allowing for different optimisations in
508
the underlying implementation.
510
for version_id in version_ids:
512
yield version_id, tuple(self.get_parents(version_id))
513
except errors.RevisionNotPresent:
516
429
def transaction_finished(self):
517
430
"""The transaction that this file was opened in has finished.
753
665
new_version_ids.add(version)
754
666
return new_version_ids
669
class InterVersionedFileTestProviderAdapter(object):
670
"""A tool to generate a suite testing multiple inter versioned-file classes.
672
This is done by copying the test once for each InterVersionedFile provider
673
and injecting the transport_server, transport_readonly_server,
674
versionedfile_factory and versionedfile_factory_to classes into each copy.
675
Each copy is also given a new id() to make it easy to identify.
678
def __init__(self, transport_server, transport_readonly_server, formats):
679
self._transport_server = transport_server
680
self._transport_readonly_server = transport_readonly_server
681
self._formats = formats
683
def adapt(self, test):
684
result = unittest.TestSuite()
685
for (interversionedfile_class,
686
versionedfile_factory,
687
versionedfile_factory_to) in self._formats:
688
new_test = deepcopy(test)
689
new_test.transport_server = self._transport_server
690
new_test.transport_readonly_server = self._transport_readonly_server
691
new_test.interversionedfile_class = interversionedfile_class
692
new_test.versionedfile_factory = versionedfile_factory
693
new_test.versionedfile_factory_to = versionedfile_factory_to
694
def make_new_test_id():
695
new_id = "%s(%s)" % (new_test.id(), interversionedfile_class.__name__)
696
return lambda: new_id
697
new_test.id = make_new_test_id()
698
result.addTest(new_test)
702
def default_test_list():
703
"""Generate the default list of interversionedfile permutations to test."""
704
from bzrlib.weave import WeaveFile
705
from bzrlib.knit import KnitVersionedFile
707
# test the fallback InterVersionedFile from annotated knits to weave
708
result.append((InterVersionedFile,
711
for optimiser in InterVersionedFile._optimisers:
712
result.append((optimiser,
713
optimiser._matching_file_from_factory,
714
optimiser._matching_file_to_factory
716
# if there are specific combinations we want to use, we can add them