70
64
"""Return a unsorted list of versions."""
71
65
raise NotImplementedError(self.versions)
73
def has_ghost(self, version_id):
74
"""Returns whether version is present as a ghost."""
75
raise NotImplementedError(self.has_ghost)
77
67
def has_version(self, version_id):
78
68
"""Returns whether version is present."""
79
69
raise NotImplementedError(self.has_version)
81
def add_delta(self, version_id, parents, delta_parent, sha1, noeol, delta):
82
"""Add a text to the versioned file via a pregenerated delta.
84
:param version_id: The version id being added.
85
:param parents: The parents of the version_id.
86
:param delta_parent: The parent this delta was created against.
87
:param sha1: The sha1 of the full text.
88
:param delta: The delta instructions. See get_delta for details.
90
self._check_write_ok()
91
if self.has_version(version_id):
92
raise errors.RevisionAlreadyPresent(version_id, self)
93
return self._add_delta(version_id, parents, delta_parent, sha1, noeol, delta)
95
def _add_delta(self, version_id, parents, delta_parent, sha1, noeol, delta):
96
"""Class specific routine to add a delta.
98
This generic version simply applies the delta to the delta_parent and
101
# strip annotation from delta
103
for start, stop, delta_len, delta_lines in delta:
104
new_delta.append((start, stop, delta_len, [text for origin, text in delta_lines]))
105
if delta_parent is not None:
106
parent_full = self.get_lines(delta_parent)
109
new_full = self._apply_delta(parent_full, new_delta)
110
# its impossible to have noeol on an empty file
111
if noeol and new_full[-1][-1] == '\n':
112
new_full[-1] = new_full[-1][:-1]
113
self.add_lines(version_id, parents, new_full)
115
def add_lines(self, version_id, parents, lines, parent_texts=None):
71
def add_lines(self, version_id, parents, lines):
116
72
"""Add a single text on top of the versioned file.
118
74
Must raise RevisionAlreadyPresent if the new version is
119
75
already present in file history.
121
77
Must raise RevisionNotPresent if any of the given parents are
122
not present in file history.
123
:param parent_texts: An optional dictionary containing the opaque
124
representations of some or all of the parents of
125
version_id to allow delta optimisations.
126
VERY IMPORTANT: the texts must be those returned
127
by add_lines or data corruption can be caused.
128
:return: An opaque representation of the inserted version which can be
129
provided back to future add_lines calls in the parent_texts
132
self._check_write_ok()
133
return self._add_lines(version_id, parents, lines, parent_texts)
135
def _add_lines(self, version_id, parents, lines, parent_texts):
136
"""Helper to do the class specific add_lines."""
78
not present in file history."""
137
79
raise NotImplementedError(self.add_lines)
139
def add_lines_with_ghosts(self, version_id, parents, lines,
141
"""Add lines to the versioned file, allowing ghosts to be present.
143
This takes the same parameters as add_lines.
145
self._check_write_ok()
146
return self._add_lines_with_ghosts(version_id, parents, lines,
149
def _add_lines_with_ghosts(self, version_id, parents, lines, parent_texts):
150
"""Helper to do class specific add_lines_with_ghosts."""
151
raise NotImplementedError(self.add_lines_with_ghosts)
153
81
def check(self, progress_bar=None):
154
82
"""Check the versioned file for integrity."""
155
83
raise NotImplementedError(self.check)
157
def _check_write_ok(self):
158
"""Is the versioned file marked as 'finished' ? Raise if it is."""
160
raise errors.OutSideTransaction()
161
if self._access_mode != 'w':
162
raise errors.ReadOnlyObjectDirtiedError(self)
164
85
def clear_cache(self):
165
86
"""Remove any data cached in the versioned file object."""
189
105
raise NotImplementedError(self.create_empty)
191
def fix_parents(self, version, new_parents):
192
"""Fix the parents list for version.
194
This is done by appending a new version to the index
195
with identical data except for the parents list.
196
the parents list must be a superset of the current
199
self._check_write_ok()
200
return self._fix_parents(version, new_parents)
202
def _fix_parents(self, version, new_parents):
203
"""Helper for fix_parents."""
204
raise NotImplementedError(self.fix_parents)
206
def get_delta(self, version):
207
"""Get a delta for constructing version from some other version.
209
:return: (delta_parent, sha1, noeol, delta)
210
Where delta_parent is a version id or None to indicate no parent.
212
raise NotImplementedError(self.get_delta)
214
def get_deltas(self, versions):
215
"""Get multiple deltas at once for constructing versions.
217
:return: dict(version_id:(delta_parent, sha1, noeol, delta))
218
Where delta_parent is a version id or None to indicate no parent, and
219
version_id is the version_id created by that delta.
222
for version in versions:
223
result[version] = self.get_delta(version)
226
107
def get_suffixes(self):
227
108
"""Return the file suffixes associated with this versioned file."""
228
109
raise NotImplementedError(self.get_suffixes)
254
135
version_ids = [version_ids]
255
136
raise NotImplementedError(self.get_ancestry)
257
def get_ancestry_with_ghosts(self, version_ids):
258
"""Return a list of all ancestors of given version(s). This
259
will not include the null revision.
261
Must raise RevisionNotPresent if any of the given versions are
262
not present in file history.
264
Ghosts that are known about will be included in ancestry list,
265
but are not explicitly marked.
267
raise NotImplementedError(self.get_ancestry_with_ghosts)
269
138
def get_graph(self):
270
"""Return a graph for the entire versioned file.
272
Ghosts are not listed or referenced in the graph.
139
"""Return a graph for the entire versioned file."""
275
141
for version in self.versions():
276
142
result[version] = self.get_parents(version)
279
def get_graph_with_ghosts(self):
280
"""Return a graph for the entire versioned file.
282
Ghosts are referenced in parents list but are not
285
raise NotImplementedError(self.get_graph_with_ghosts)
287
145
@deprecated_method(zero_eight)
288
146
def parent_names(self, version):
289
147
"""Return version names for parents of a version.
323
170
def annotate(self, version_id):
324
171
return list(self.annotate_iter(version_id))
326
def _apply_delta(self, lines, delta):
327
"""Apply delta to lines."""
330
for start, end, count, delta_lines in delta:
331
lines[offset+start:offset+end] = delta_lines
332
offset = offset + (start - end) + count
335
def join(self, other, pb=None, msg=None, version_ids=None,
336
ignore_missing=False):
173
def join(self, other, pb=None, msg=None, version_ids=None):
337
174
"""Integrate versions from other into this versioned file.
339
176
If version_ids is None all versions from other should be
340
177
incorporated into this versioned file.
342
179
Must raise RevisionNotPresent if any of the specified versions
343
are not present in the other files history unless ignore_missing
344
is supplied when they are silently skipped.
346
self._check_write_ok()
347
return InterVersionedFile.get(other, self).join(
353
def iter_lines_added_or_present_in_versions(self, version_ids=None):
354
"""Iterate over the lines in the versioned file from version_ids.
356
This may return lines from other versions, and does not return the
357
specific version marker at this point. The api may be changed
358
during development to include the version that the versioned file
359
thinks is relevant, but given that such hints are just guesses,
360
its better not to have it if we dont need it.
362
NOTES: Lines are normalised: they will all have \n terminators.
363
Lines are returned in arbitrary order.
365
raise NotImplementedError(self.iter_lines_added_or_present_in_versions)
367
def transaction_finished(self):
368
"""The transaction that this file was opened in has finished.
370
This records self.finished = True and should cause all mutating
375
@deprecated_method(zero_eight)
180
are not present in the other files history."""
181
return InterVersionedFile.get(other, self).join(pb, msg, version_ids)
376
183
def walk(self, version_ids=None):
377
184
"""Walk the versioned file as a weave-like structure, for
378
185
versions relative to version_ids. Yields sequence of (lineno,
511
312
_optimisers = set()
512
313
"""The available optimised InterVersionedFile types."""
514
def join(self, pb=None, msg=None, version_ids=None, ignore_missing=False):
315
def join(self, pb=None, msg=None, version_ids=None):
515
316
"""Integrate versions from self.source into self.target.
517
318
If version_ids is None all versions from source should be
518
319
incorporated into this versioned file.
520
321
Must raise RevisionNotPresent if any of the specified versions
521
are not present in the other files history unless ignore_missing is
522
supplied when they are silently skipped.
322
are not present in the other files history.
524
324
# the default join:
525
# - if the target is empty, just add all the versions from
526
# source to target, otherwise:
527
325
# - make a temporary versioned file of type target
528
326
# - insert the source content into it one at a time
530
if not self.target.versions():
533
# Make a new target-format versioned file.
534
temp_source = self.target.create_empty("temp", MemoryTransport())
328
# Make a new target-format versioned file.
329
temp_source = self.target.create_empty("temp", MemoryTransport())
536
330
graph = self.source.get_graph()
537
331
order = topo_sort(graph.items())
538
pb = ui.ui_factory.nested_progress_bar()
541
# TODO for incremental cross-format work:
542
# make a versioned file with the following content:
543
# all revisions we have been asked to join
544
# all their ancestors that are *not* in target already.
545
# the immediate parents of the above two sets, with
546
# empty parent lists - these versions are in target already
547
# and the incorrect version data will be ignored.
548
# TODO: for all ancestors that are present in target already,
549
# check them for consistent data, this requires moving sha1 from
551
# TODO: remove parent texts when they are not relevant any more for
552
# memory pressure reduction. RBC 20060313
553
# pb.update('Converting versioned data', 0, len(order))
554
# deltas = self.source.get_deltas(order)
555
for index, version in enumerate(order):
556
pb.update('Converting versioned data', index, len(order))
557
parent_text = target.add_lines(version,
558
self.source.get_parents(version),
559
self.source.get_lines(version),
560
parent_texts=parent_texts)
561
parent_texts[version] = parent_text
562
#delta_parent, sha1, noeol, delta = deltas[version]
563
#target.add_delta(version,
564
# self.source.get_parents(version),
569
#target.get_lines(version)
571
# this should hit the native code path for target
572
if target is not self.target:
573
return self.target.join(temp_source,
332
for version in order:
333
temp_source.add_lines(version,
334
self.source.get_parents(version),
335
self.source.get_lines(version))
337
# this should hit the native code path for target
338
return self.target.join(temp_source, pb, msg, version_ids)
582
341
class InterVersionedFileTestProviderAdapter(object):