~bzr-pqm/bzr/bzr.dev

2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2005, 2006 Canonical Ltd
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
2
#
3
# Authors:
4
#   Johan Rydberg <jrydberg@gnu.org>
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
10
#
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
15
#
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20
"""Versioned text file storage api."""
21
1996.3.7 by John Arbash Meinel
lazy import versionedfile, late-load bzrlib.merge
22
from bzrlib.lazy_import import lazy_import
23
lazy_import(globals(), """
24
25
from bzrlib import (
26
    errors,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
27
    osutils,
1996.3.7 by John Arbash Meinel
lazy import versionedfile, late-load bzrlib.merge
28
    tsort,
2229.2.1 by Aaron Bentley
Reject reserved ids in versiondfile, tree, branch and repository
29
    revision,
1996.3.7 by John Arbash Meinel
lazy import versionedfile, late-load bzrlib.merge
30
    ui,
31
    )
32
from bzrlib.transport.memory import MemoryTransport
33
""")
34
1563.2.12 by Robert Collins
Checkpointing: created InterObject to factor out common inter object worker code, added InterVersionedFile and tests to allow making join work between any versionedfile.
35
from bzrlib.inter import InterObject
1551.6.7 by Aaron Bentley
Implemented two-way merge, refactored weave merge
36
from bzrlib.textmerge import TextMerge
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
37
from bzrlib.symbol_versioning import (deprecated_function,
38
        deprecated_method,
39
        zero_eight,
40
        )
1563.2.11 by Robert Collins
Consolidate reweave and join as we have no separate usage, make reweave tests apply to all versionedfile implementations and deprecate the old reweave apis.
41
42
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
43
class VersionedFile(object):
44
    """Versioned text file storage.
45
    
46
    A versioned file manages versions of line-based text files,
47
    keeping track of the originating version for each line.
48
49
    To clients the "lines" of the file are represented as a list of
50
    strings. These strings will typically have terminal newline
51
    characters, but this is not required.  In particular files commonly
52
    do not have a newline at the end of the file.
53
54
    Texts are identified by a version-id string.
55
    """
56
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
57
    def __init__(self, access_mode):
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
58
        self.finished = False
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
59
        self._access_mode = access_mode
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
60
2229.2.1 by Aaron Bentley
Reject reserved ids in versiondfile, tree, branch and repository
61
    @staticmethod
2229.2.3 by Aaron Bentley
change reserved_id to is_reserved_id, add check_not_reserved for DRY
62
    def check_not_reserved_id(version_id):
63
        revision.check_not_reserved_id(version_id)
2229.2.1 by Aaron Bentley
Reject reserved ids in versiondfile, tree, branch and repository
64
1563.2.15 by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages.
65
    def copy_to(self, name, transport):
66
        """Copy this versioned file to name on transport."""
67
        raise NotImplementedError(self.copy_to)
1863.1.1 by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit
68
1563.2.11 by Robert Collins
Consolidate reweave and join as we have no separate usage, make reweave tests apply to all versionedfile implementations and deprecate the old reweave apis.
69
    @deprecated_method(zero_eight)
70
    def names(self):
71
        """Return a list of all the versions in this versioned file.
72
73
        Please use versionedfile.versions() now.
74
        """
75
        return self.versions()
76
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
77
    def versions(self):
78
        """Return a unsorted list of versions."""
79
        raise NotImplementedError(self.versions)
80
1594.2.8 by Robert Collins
add ghost aware apis to knits.
81
    def has_ghost(self, version_id):
82
        """Returns whether version is present as a ghost."""
83
        raise NotImplementedError(self.has_ghost)
84
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
85
    def has_version(self, version_id):
86
        """Returns whether version is present."""
87
        raise NotImplementedError(self.has_version)
88
1596.2.37 by Robert Collins
Switch to delta based content copying in the generic versioned file copier.
89
    def add_delta(self, version_id, parents, delta_parent, sha1, noeol, delta):
90
        """Add a text to the versioned file via a pregenerated delta.
91
92
        :param version_id: The version id being added.
93
        :param parents: The parents of the version_id.
94
        :param delta_parent: The parent this delta was created against.
95
        :param sha1: The sha1 of the full text.
96
        :param delta: The delta instructions. See get_delta for details.
97
        """
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
98
        version_id = osutils.safe_revision_id(version_id)
99
        parents = [osutils.safe_revision_id(v) for v in parents]
1596.2.37 by Robert Collins
Switch to delta based content copying in the generic versioned file copier.
100
        self._check_write_ok()
101
        if self.has_version(version_id):
102
            raise errors.RevisionAlreadyPresent(version_id, self)
103
        return self._add_delta(version_id, parents, delta_parent, sha1, noeol, delta)
104
105
    def _add_delta(self, version_id, parents, delta_parent, sha1, noeol, delta):
106
        """Class specific routine to add a delta.
107
108
        This generic version simply applies the delta to the delta_parent and
109
        then inserts it.
110
        """
111
        # strip annotation from delta
112
        new_delta = []
113
        for start, stop, delta_len, delta_lines in delta:
114
            new_delta.append((start, stop, delta_len, [text for origin, text in delta_lines]))
115
        if delta_parent is not None:
116
            parent_full = self.get_lines(delta_parent)
117
        else:
118
            parent_full = []
119
        new_full = self._apply_delta(parent_full, new_delta)
120
        # its impossible to have noeol on an empty file
121
        if noeol and new_full[-1][-1] == '\n':
122
            new_full[-1] = new_full[-1][:-1]
123
        self.add_lines(version_id, parents, new_full)
124
1596.2.32 by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility.
125
    def add_lines(self, version_id, parents, lines, parent_texts=None):
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
126
        """Add a single text on top of the versioned file.
127
128
        Must raise RevisionAlreadyPresent if the new version is
129
        already present in file history.
130
131
        Must raise RevisionNotPresent if any of the given parents are
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
132
        not present in file history.
1596.2.32 by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility.
133
        :param parent_texts: An optional dictionary containing the opaque 
1616.1.1 by Martin Pool
[merge] robertc
134
             representations of some or all of the parents of 
135
             version_id to allow delta optimisations. 
136
             VERY IMPORTANT: the texts must be those returned
137
             by add_lines or data corruption can be caused.
1596.2.32 by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility.
138
        :return: An opaque representation of the inserted version which can be
139
                 provided back to future add_lines calls in the parent_texts
140
                 dictionary.
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
141
        """
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
142
        version_id = osutils.safe_revision_id(version_id)
143
        parents = [osutils.safe_revision_id(v) for v in parents]
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
144
        self._check_write_ok()
1596.2.32 by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility.
145
        return self._add_lines(version_id, parents, lines, parent_texts)
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
146
1596.2.32 by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility.
147
    def _add_lines(self, version_id, parents, lines, parent_texts):
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
148
        """Helper to do the class specific add_lines."""
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
149
        raise NotImplementedError(self.add_lines)
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
150
1596.2.32 by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility.
151
    def add_lines_with_ghosts(self, version_id, parents, lines,
152
                              parent_texts=None):
153
        """Add lines to the versioned file, allowing ghosts to be present.
154
        
155
        This takes the same parameters as add_lines.
156
        """
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
157
        version_id = osutils.safe_revision_id(version_id)
158
        parents = [osutils.safe_revision_id(v) for v in parents]
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
159
        self._check_write_ok()
1596.2.32 by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility.
160
        return self._add_lines_with_ghosts(version_id, parents, lines,
161
                                           parent_texts)
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
162
1596.2.32 by Robert Collins
Reduce re-extraction of texts during weave to knit joins by providing a memoisation facility.
163
    def _add_lines_with_ghosts(self, version_id, parents, lines, parent_texts):
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
164
        """Helper to do class specific add_lines_with_ghosts."""
1594.2.8 by Robert Collins
add ghost aware apis to knits.
165
        raise NotImplementedError(self.add_lines_with_ghosts)
166
1563.2.19 by Robert Collins
stub out a check for knits.
167
    def check(self, progress_bar=None):
168
        """Check the versioned file for integrity."""
169
        raise NotImplementedError(self.check)
170
1666.1.6 by Robert Collins
Make knit the default format.
171
    def _check_lines_not_unicode(self, lines):
172
        """Check that lines being added to a versioned file are not unicode."""
173
        for line in lines:
174
            if line.__class__ is not str:
175
                raise errors.BzrBadParameterUnicode("lines")
176
177
    def _check_lines_are_lines(self, lines):
178
        """Check that the lines really are full lines without inline EOL."""
179
        for line in lines:
180
            if '\n' in line[:-1]:
181
                raise errors.BzrBadParameterContainsNewline("lines")
182
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
183
    def _check_write_ok(self):
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
184
        """Is the versioned file marked as 'finished' ? Raise if it is."""
185
        if self.finished:
186
            raise errors.OutSideTransaction()
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
187
        if self._access_mode != 'w':
188
            raise errors.ReadOnlyObjectDirtiedError(self)
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
189
1863.1.1 by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit
190
    def enable_cache(self):
191
        """Tell this versioned file that it should cache any data it reads.
192
        
193
        This is advisory, implementations do not have to support caching.
194
        """
195
        pass
196
    
1563.2.7 by Robert Collins
add versioned file clear_cache entry.
197
    def clear_cache(self):
1863.1.1 by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit
198
        """Remove any data cached in the versioned file object.
199
200
        This only needs to be supported if caches are supported
201
        """
202
        pass
1563.2.7 by Robert Collins
add versioned file clear_cache entry.
203
1563.2.5 by Robert Collins
Remove unused transaction references from knit.py and the versionedfile interface.
204
    def clone_text(self, new_version_id, old_version_id, parents):
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
205
        """Add an identical text to old_version_id as new_version_id.
206
207
        Must raise RevisionNotPresent if the old version or any of the
208
        parents are not present in file history.
209
210
        Must raise RevisionAlreadyPresent if the new version is
211
        already present in file history."""
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
212
        new_version_id = osutils.safe_revision_id(new_version_id)
213
        old_version_id = osutils.safe_revision_id(old_version_id)
1594.2.24 by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching.
214
        self._check_write_ok()
215
        return self._clone_text(new_version_id, old_version_id, parents)
216
217
    def _clone_text(self, new_version_id, old_version_id, parents):
218
        """Helper function to do the _clone_text work."""
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
219
        raise NotImplementedError(self.clone_text)
220
1563.2.13 by Robert Collins
InterVersionedFile implemented.
221
    def create_empty(self, name, transport, mode=None):
222
        """Create a new versioned file of this exact type.
223
224
        :param name: the file name
225
        :param transport: the transport
226
        :param mode: optional file mode.
227
        """
228
        raise NotImplementedError(self.create_empty)
229
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
230
    def fix_parents(self, version_id, new_parents):
1594.2.7 by Robert Collins
Add versionedfile.fix_parents api for correcting data post hoc.
231
        """Fix the parents list for version.
232
        
233
        This is done by appending a new version to the index
234
        with identical data except for the parents list.
235
        the parents list must be a superset of the current
236
        list.
237
        """
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
238
        version_id = osutils.safe_revision_id(version_id)
239
        new_parents = [osutils.safe_revision_id(p) for p in new_parents]
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
240
        self._check_write_ok()
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
241
        return self._fix_parents(version_id, new_parents)
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
242
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
243
    def _fix_parents(self, version_id, new_parents):
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
244
        """Helper for fix_parents."""
1594.2.7 by Robert Collins
Add versionedfile.fix_parents api for correcting data post hoc.
245
        raise NotImplementedError(self.fix_parents)
246
1596.2.36 by Robert Collins
add a get_delta api to versioned_file.
247
    def get_delta(self, version):
248
        """Get a delta for constructing version from some other version.
249
        
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
250
        :return: (delta_parent, sha1, noeol, delta)
1596.2.36 by Robert Collins
add a get_delta api to versioned_file.
251
        Where delta_parent is a version id or None to indicate no parent.
252
        """
253
        raise NotImplementedError(self.get_delta)
254
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
255
    def get_deltas(self, version_ids):
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
256
        """Get multiple deltas at once for constructing versions.
257
        
258
        :return: dict(version_id:(delta_parent, sha1, noeol, delta))
259
        Where delta_parent is a version id or None to indicate no parent, and
260
        version_id is the version_id created by that delta.
261
        """
262
        result = {}
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
263
        for version_id in version_ids:
264
            result[version_id] = self.get_delta(version_id)
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
265
        return result
266
1666.1.6 by Robert Collins
Make knit the default format.
267
    def get_sha1(self, version_id):
268
        """Get the stored sha1 sum for the given revision.
269
        
270
        :param name: The name of the version to lookup
271
        """
272
        raise NotImplementedError(self.get_sha1)
273
1563.2.15 by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages.
274
    def get_suffixes(self):
275
        """Return the file suffixes associated with this versioned file."""
276
        raise NotImplementedError(self.get_suffixes)
277
    
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
278
    def get_text(self, version_id):
279
        """Return version contents as a text string.
280
281
        Raises RevisionNotPresent if version is not present in
282
        file history.
283
        """
284
        return ''.join(self.get_lines(version_id))
285
    get_string = get_text
286
1756.2.1 by Aaron Bentley
Implement get_texts
287
    def get_texts(self, version_ids):
288
        """Return the texts of listed versions as a list of strings.
289
290
        Raises RevisionNotPresent if version is not present in
291
        file history.
292
        """
293
        return [''.join(self.get_lines(v)) for v in version_ids]
294
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
295
    def get_lines(self, version_id):
296
        """Return version contents as a sequence of lines.
297
298
        Raises RevisionNotPresent if version is not present in
299
        file history.
300
        """
301
        raise NotImplementedError(self.get_lines)
302
2530.1.1 by Aaron Bentley
Make topological sorting optional for get_ancestry
303
    def get_ancestry(self, version_ids, topo_sorted=True):
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
304
        """Return a list of all ancestors of given version(s). This
305
        will not include the null revision.
306
2490.2.32 by Aaron Bentley
Merge of not-sorting-ancestry branch
307
        This list will not be topologically sorted if topo_sorted=False is
308
        passed.
2530.1.1 by Aaron Bentley
Make topological sorting optional for get_ancestry
309
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
310
        Must raise RevisionNotPresent if any of the given versions are
311
        not present in file history."""
312
        if isinstance(version_ids, basestring):
313
            version_ids = [version_ids]
314
        raise NotImplementedError(self.get_ancestry)
315
        
1594.2.8 by Robert Collins
add ghost aware apis to knits.
316
    def get_ancestry_with_ghosts(self, version_ids):
317
        """Return a list of all ancestors of given version(s). This
318
        will not include the null revision.
319
320
        Must raise RevisionNotPresent if any of the given versions are
321
        not present in file history.
322
        
323
        Ghosts that are known about will be included in ancestry list,
324
        but are not explicitly marked.
325
        """
326
        raise NotImplementedError(self.get_ancestry_with_ghosts)
327
        
1684.3.1 by Robert Collins
Fix versioned file joins with empty targets.
328
    def get_graph(self, version_ids=None):
329
        """Return a graph from the versioned file. 
1594.2.8 by Robert Collins
add ghost aware apis to knits.
330
        
331
        Ghosts are not listed or referenced in the graph.
1684.3.1 by Robert Collins
Fix versioned file joins with empty targets.
332
        :param version_ids: Versions to select.
1759.2.1 by Jelmer Vernooij
Fix some types (found using aspell).
333
                            None means retrieve all versions.
1594.2.8 by Robert Collins
add ghost aware apis to knits.
334
        """
1563.2.13 by Robert Collins
InterVersionedFile implemented.
335
        result = {}
1684.3.1 by Robert Collins
Fix versioned file joins with empty targets.
336
        if version_ids is None:
337
            for version in self.versions():
338
                result[version] = self.get_parents(version)
339
        else:
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
340
            pending = set(osutils.safe_revision_id(v) for v in version_ids)
1684.3.1 by Robert Collins
Fix versioned file joins with empty targets.
341
            while pending:
342
                version = pending.pop()
343
                if version in result:
344
                    continue
345
                parents = self.get_parents(version)
346
                for parent in parents:
347
                    if parent in result:
348
                        continue
349
                    pending.add(parent)
350
                result[version] = parents
1563.2.13 by Robert Collins
InterVersionedFile implemented.
351
        return result
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
352
1594.2.8 by Robert Collins
add ghost aware apis to knits.
353
    def get_graph_with_ghosts(self):
354
        """Return a graph for the entire versioned file.
355
        
356
        Ghosts are referenced in parents list but are not
357
        explicitly listed.
358
        """
359
        raise NotImplementedError(self.get_graph_with_ghosts)
360
1563.2.11 by Robert Collins
Consolidate reweave and join as we have no separate usage, make reweave tests apply to all versionedfile implementations and deprecate the old reweave apis.
361
    @deprecated_method(zero_eight)
362
    def parent_names(self, version):
363
        """Return version names for parents of a version.
364
        
365
        See get_parents for the current api.
366
        """
367
        return self.get_parents(version)
368
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
369
    def get_parents(self, version_id):
370
        """Return version names for parents of a version.
371
372
        Must raise RevisionNotPresent if version is not present in
373
        file history.
374
        """
375
        raise NotImplementedError(self.get_parents)
376
1594.2.8 by Robert Collins
add ghost aware apis to knits.
377
    def get_parents_with_ghosts(self, version_id):
378
        """Return version names for parents of version_id.
379
380
        Will raise RevisionNotPresent if version_id is not present
381
        in the history.
382
383
        Ghosts that are known about will be included in the parent list,
384
        but are not explicitly marked.
385
        """
386
        raise NotImplementedError(self.get_parents_with_ghosts)
387
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
388
    def annotate_iter(self, version_id):
389
        """Yield list of (version-id, line) pairs for the specified
390
        version.
391
392
        Must raise RevisionNotPresent if any of the given versions are
393
        not present in file history.
394
        """
395
        raise NotImplementedError(self.annotate_iter)
396
397
    def annotate(self, version_id):
398
        return list(self.annotate_iter(version_id))
399
1596.2.37 by Robert Collins
Switch to delta based content copying in the generic versioned file copier.
400
    def _apply_delta(self, lines, delta):
401
        """Apply delta to lines."""
402
        lines = list(lines)
403
        offset = 0
404
        for start, end, count, delta_lines in delta:
405
            lines[offset+start:offset+end] = delta_lines
406
            offset = offset + (start - end) + count
407
        return lines
408
1563.2.31 by Robert Collins
Convert Knit repositories to use knits.
409
    def join(self, other, pb=None, msg=None, version_ids=None,
410
             ignore_missing=False):
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
411
        """Integrate versions from other into this versioned file.
412
413
        If version_ids is None all versions from other should be
414
        incorporated into this versioned file.
415
416
        Must raise RevisionNotPresent if any of the specified versions
1563.2.31 by Robert Collins
Convert Knit repositories to use knits.
417
        are not present in the other files history unless ignore_missing
418
        is supplied when they are silently skipped.
419
        """
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
420
        self._check_write_ok()
1563.2.31 by Robert Collins
Convert Knit repositories to use knits.
421
        return InterVersionedFile.get(other, self).join(
422
            pb,
423
            msg,
424
            version_ids,
425
            ignore_missing)
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
426
2039.1.1 by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000)
427
    def iter_lines_added_or_present_in_versions(self, version_ids=None, 
428
                                                pb=None):
1594.2.6 by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved.
429
        """Iterate over the lines in the versioned file from version_ids.
430
431
        This may return lines from other versions, and does not return the
432
        specific version marker at this point. The api may be changed
433
        during development to include the version that the versioned file
434
        thinks is relevant, but given that such hints are just guesses,
1759.2.2 by Jelmer Vernooij
Revert some of my spelling fixes and fix some typos after review by Aaron.
435
        its better not to have it if we don't need it.
1594.2.6 by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved.
436
2039.1.1 by Aaron Bentley
Clean up progress properly when interrupted during fetch (#54000)
437
        If a progress bar is supplied, it may be used to indicate progress.
438
        The caller is responsible for cleaning up progress bars (because this
439
        is an iterator).
440
1594.2.6 by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved.
441
        NOTES: Lines are normalised: they will all have \n terminators.
442
               Lines are returned in arbitrary order.
443
        """
444
        raise NotImplementedError(self.iter_lines_added_or_present_in_versions)
445
1594.2.21 by Robert Collins
Teach versioned files to prevent mutation after finishing.
446
    def transaction_finished(self):
447
        """The transaction that this file was opened in has finished.
448
449
        This records self.finished = True and should cause all mutating
450
        operations to error.
451
        """
452
        self.finished = True
453
1594.2.6 by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved.
454
    @deprecated_method(zero_eight)
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
455
    def walk(self, version_ids=None):
456
        """Walk the versioned file as a weave-like structure, for
457
        versions relative to version_ids.  Yields sequence of (lineno,
458
        insert, deletes, text) for each relevant line.
459
460
        Must raise RevisionNotPresent if any of the specified versions
461
        are not present in the file history.
462
463
        :param version_ids: the version_ids to walk with respect to. If not
464
                            supplied the entire weave-like structure is walked.
1594.2.6 by Robert Collins
Introduce a api specifically for looking at lines in some versions of the inventory, for fileid_involved.
465
466
        walk is deprecated in favour of iter_lines_added_or_present_in_versions
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
467
        """
468
        raise NotImplementedError(self.walk)
469
1563.2.11 by Robert Collins
Consolidate reweave and join as we have no separate usage, make reweave tests apply to all versionedfile implementations and deprecate the old reweave apis.
470
    @deprecated_method(zero_eight)
471
    def iter_names(self):
472
        """Walk the names list."""
473
        return iter(self.versions())
474
1551.6.15 by Aaron Bentley
Moved plan_merge into Weave
475
    def plan_merge(self, ver_a, ver_b):
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
476
        """Return pseudo-annotation indicating how the two versions merge.
477
478
        This is computed between versions a and b and their common
479
        base.
480
481
        Weave lines present in none of them are skipped entirely.
1664.2.2 by Aaron Bentley
Added legend for plan-merge output
482
483
        Legend:
484
        killed-base Dead in base revision
485
        killed-both Killed in each revision
486
        killed-a    Killed in a
487
        killed-b    Killed in b
488
        unchanged   Alive in both a and b (possibly created in both)
489
        new-a       Created in a
490
        new-b       Created in b
1664.2.5 by Aaron Bentley
Update plan-merge legend
491
        ghost-a     Killed in a, unborn in b    
492
        ghost-b     Killed in b, unborn in a
1664.2.2 by Aaron Bentley
Added legend for plan-merge output
493
        irrelevant  Not in either revision
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
494
        """
1551.6.15 by Aaron Bentley
Moved plan_merge into Weave
495
        raise NotImplementedError(VersionedFile.plan_merge)
496
        
1996.3.7 by John Arbash Meinel
lazy import versionedfile, late-load bzrlib.merge
497
    def weave_merge(self, plan, a_marker=TextMerge.A_MARKER,
1551.6.14 by Aaron Bentley
Tweaks from merge review
498
                    b_marker=TextMerge.B_MARKER):
1551.6.12 by Aaron Bentley
Indicate conflicts from merge_lines, insead of guessing
499
        return PlanWeaveMerge(plan, a_marker, b_marker).merge_lines()[0]
1551.6.10 by Aaron Bentley
Renamed WeaveMerge to PlanMerge, added plan method, created planless WeaveMerge
500
1664.2.7 by Aaron Bentley
Merge bzr.dev
501
1551.6.10 by Aaron Bentley
Renamed WeaveMerge to PlanMerge, added plan method, created planless WeaveMerge
502
class PlanWeaveMerge(TextMerge):
1551.6.13 by Aaron Bentley
Cleanup
503
    """Weave merge that takes a plan as its input.
504
    
1551.6.14 by Aaron Bentley
Tweaks from merge review
505
    This exists so that VersionedFile.plan_merge is implementable.
506
    Most callers will want to use WeaveMerge instead.
1551.6.13 by Aaron Bentley
Cleanup
507
    """
508
1551.6.14 by Aaron Bentley
Tweaks from merge review
509
    def __init__(self, plan, a_marker=TextMerge.A_MARKER,
510
                 b_marker=TextMerge.B_MARKER):
1551.6.10 by Aaron Bentley
Renamed WeaveMerge to PlanMerge, added plan method, created planless WeaveMerge
511
        TextMerge.__init__(self, a_marker, b_marker)
512
        self.plan = plan
513
1551.6.7 by Aaron Bentley
Implemented two-way merge, refactored weave merge
514
    def _merge_struct(self):
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
515
        lines_a = []
516
        lines_b = []
517
        ch_a = ch_b = False
1664.2.8 by Aaron Bentley
Fix WeaveMerge when plan doesn't end with unchanged lines
518
519
        def outstanding_struct():
520
            if not lines_a and not lines_b:
521
                return
522
            elif ch_a and not ch_b:
523
                # one-sided change:
524
                yield(lines_a,)
525
            elif ch_b and not ch_a:
526
                yield (lines_b,)
527
            elif lines_a == lines_b:
528
                yield(lines_a,)
529
            else:
530
                yield (lines_a, lines_b)
1551.6.13 by Aaron Bentley
Cleanup
531
       
1616.1.18 by Martin Pool
(weave-merge) don't treat killed-both lines as points of agreement;
532
        # We previously considered either 'unchanged' or 'killed-both' lines
533
        # to be possible places to resynchronize.  However, assuming agreement
1759.2.1 by Jelmer Vernooij
Fix some types (found using aspell).
534
        # on killed-both lines may be too aggressive. -- mbp 20060324
1551.6.7 by Aaron Bentley
Implemented two-way merge, refactored weave merge
535
        for state, line in self.plan:
1616.1.18 by Martin Pool
(weave-merge) don't treat killed-both lines as points of agreement;
536
            if state == 'unchanged':
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
537
                # resync and flush queued conflicts changes if any
1664.2.8 by Aaron Bentley
Fix WeaveMerge when plan doesn't end with unchanged lines
538
                for struct in outstanding_struct():
539
                    yield struct
1551.6.11 by Aaron Bentley
Switched TextMerge_lines to work on a list
540
                lines_a = []
541
                lines_b = []
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
542
                ch_a = ch_b = False
543
                
544
            if state == 'unchanged':
545
                if line:
1551.6.5 by Aaron Bentley
Got weave merge producing structural output
546
                    yield ([line],)
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
547
            elif state == 'killed-a':
548
                ch_a = True
549
                lines_b.append(line)
550
            elif state == 'killed-b':
551
                ch_b = True
552
                lines_a.append(line)
553
            elif state == 'new-a':
554
                ch_a = True
555
                lines_a.append(line)
556
            elif state == 'new-b':
557
                ch_b = True
558
                lines_b.append(line)
559
            else:
1551.6.6 by Aaron Bentley
Cleanup
560
                assert state in ('irrelevant', 'ghost-a', 'ghost-b', 
561
                                 'killed-base', 'killed-both'), state
1664.2.8 by Aaron Bentley
Fix WeaveMerge when plan doesn't end with unchanged lines
562
        for struct in outstanding_struct():
563
            yield struct
1563.2.12 by Robert Collins
Checkpointing: created InterObject to factor out common inter object worker code, added InterVersionedFile and tests to allow making join work between any versionedfile.
564
1664.2.14 by Aaron Bentley
spacing fix
565
1551.6.10 by Aaron Bentley
Renamed WeaveMerge to PlanMerge, added plan method, created planless WeaveMerge
566
class WeaveMerge(PlanWeaveMerge):
1551.6.13 by Aaron Bentley
Cleanup
567
    """Weave merge that takes a VersionedFile and two versions as its input"""
568
1551.6.10 by Aaron Bentley
Renamed WeaveMerge to PlanMerge, added plan method, created planless WeaveMerge
569
    def __init__(self, versionedfile, ver_a, ver_b, 
1551.6.14 by Aaron Bentley
Tweaks from merge review
570
        a_marker=PlanWeaveMerge.A_MARKER, b_marker=PlanWeaveMerge.B_MARKER):
1551.6.15 by Aaron Bentley
Moved plan_merge into Weave
571
        plan = versionedfile.plan_merge(ver_a, ver_b)
1551.6.10 by Aaron Bentley
Renamed WeaveMerge to PlanMerge, added plan method, created planless WeaveMerge
572
        PlanWeaveMerge.__init__(self, plan, a_marker, b_marker)
573
574
1563.2.12 by Robert Collins
Checkpointing: created InterObject to factor out common inter object worker code, added InterVersionedFile and tests to allow making join work between any versionedfile.
575
class InterVersionedFile(InterObject):
576
    """This class represents operations taking place between two versionedfiles..
577
578
    Its instances have methods like join, and contain
579
    references to the source and target versionedfiles these operations can be 
580
    carried out on.
581
582
    Often we will provide convenience methods on 'versionedfile' which carry out
583
    operations with another versionedfile - they will always forward to
584
    InterVersionedFile.get(other).method_name(parameters).
585
    """
586
1910.2.15 by Aaron Bentley
Back out inter.get changes, make optimizers an ordered list
587
    _optimisers = []
1563.2.12 by Robert Collins
Checkpointing: created InterObject to factor out common inter object worker code, added InterVersionedFile and tests to allow making join work between any versionedfile.
588
    """The available optimised InterVersionedFile types."""
589
1563.2.31 by Robert Collins
Convert Knit repositories to use knits.
590
    def join(self, pb=None, msg=None, version_ids=None, ignore_missing=False):
1563.2.13 by Robert Collins
InterVersionedFile implemented.
591
        """Integrate versions from self.source into self.target.
592
593
        If version_ids is None all versions from source should be
594
        incorporated into this versioned file.
595
596
        Must raise RevisionNotPresent if any of the specified versions
1563.2.31 by Robert Collins
Convert Knit repositories to use knits.
597
        are not present in the other files history unless ignore_missing is 
598
        supplied when they are silently skipped.
1563.2.13 by Robert Collins
InterVersionedFile implemented.
599
        """
600
        # the default join: 
1594.2.11 by Robert Collins
Setup fast-code paths for copying into empty weaves and weave->empty knit.
601
        # - if the target is empty, just add all the versions from 
602
        #   source to target, otherwise:
1563.2.13 by Robert Collins
InterVersionedFile implemented.
603
        # - make a temporary versioned file of type target
604
        # - insert the source content into it one at a time
605
        # - join them
1594.2.11 by Robert Collins
Setup fast-code paths for copying into empty weaves and weave->empty knit.
606
        if not self.target.versions():
607
            target = self.target
608
        else:
609
            # Make a new target-format versioned file. 
610
            temp_source = self.target.create_empty("temp", MemoryTransport())
611
            target = temp_source
1684.3.2 by Robert Collins
Factor out version_ids-to-join selection in InterVersionedfile.
612
        version_ids = self._get_source_version_ids(version_ids, ignore_missing)
1684.3.1 by Robert Collins
Fix versioned file joins with empty targets.
613
        graph = self.source.get_graph(version_ids)
1996.3.7 by John Arbash Meinel
lazy import versionedfile, late-load bzrlib.merge
614
        order = tsort.topo_sort(graph.items())
1563.2.37 by Robert Collins
Merge in nested progress bars
615
        pb = ui.ui_factory.nested_progress_bar()
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
616
        parent_texts = {}
1563.2.37 by Robert Collins
Merge in nested progress bars
617
        try:
1596.2.28 by Robert Collins
more knit profile based tuning.
618
            # TODO for incremental cross-format work:
1596.2.27 by Robert Collins
Note potential improvements in knit adds.
619
            # make a versioned file with the following content:
620
            # all revisions we have been asked to join
621
            # all their ancestors that are *not* in target already.
622
            # the immediate parents of the above two sets, with 
623
            # empty parent lists - these versions are in target already
624
            # and the incorrect version data will be ignored.
625
            # TODO: for all ancestors that are present in target already,
626
            # check them for consistent data, this requires moving sha1 from
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
627
            # 
628
            # TODO: remove parent texts when they are not relevant any more for 
629
            # memory pressure reduction. RBC 20060313
630
            # pb.update('Converting versioned data', 0, len(order))
631
            # deltas = self.source.get_deltas(order)
1563.2.37 by Robert Collins
Merge in nested progress bars
632
            for index, version in enumerate(order):
633
                pb.update('Converting versioned data', index, len(order))
1596.2.38 by Robert Collins
rollback from using deltas to using fulltexts - deltas need more work to be ready.
634
                parent_text = target.add_lines(version,
635
                                               self.source.get_parents(version),
636
                                               self.source.get_lines(version),
637
                                               parent_texts=parent_texts)
638
                parent_texts[version] = parent_text
639
                #delta_parent, sha1, noeol, delta = deltas[version]
640
                #target.add_delta(version,
641
                #                 self.source.get_parents(version),
642
                #                 delta_parent,
643
                #                 sha1,
644
                #                 noeol,
645
                #                 delta)
646
                #target.get_lines(version)
1563.2.37 by Robert Collins
Merge in nested progress bars
647
            
648
            # this should hit the native code path for target
1594.2.11 by Robert Collins
Setup fast-code paths for copying into empty weaves and weave->empty knit.
649
            if target is not self.target:
650
                return self.target.join(temp_source,
651
                                        pb,
652
                                        msg,
653
                                        version_ids,
654
                                        ignore_missing)
1563.2.37 by Robert Collins
Merge in nested progress bars
655
        finally:
656
            pb.finished()
1563.2.13 by Robert Collins
InterVersionedFile implemented.
657
1684.3.2 by Robert Collins
Factor out version_ids-to-join selection in InterVersionedfile.
658
    def _get_source_version_ids(self, version_ids, ignore_missing):
659
        """Determine the version ids to be used from self.source.
660
661
        :param version_ids: The caller-supplied version ids to check. (None 
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
662
                            for all). If None is in version_ids, it is stripped.
1684.3.2 by Robert Collins
Factor out version_ids-to-join selection in InterVersionedfile.
663
        :param ignore_missing: if True, remove missing ids from the version 
664
                               list. If False, raise RevisionNotPresent on
665
                               a missing version id.
666
        :return: A set of version ids.
667
        """
668
        if version_ids is None:
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
669
            # None cannot be in source.versions
1684.3.2 by Robert Collins
Factor out version_ids-to-join selection in InterVersionedfile.
670
            return set(self.source.versions())
671
        else:
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
672
            version_ids = [osutils.safe_revision_id(v) for v in version_ids]
1684.3.2 by Robert Collins
Factor out version_ids-to-join selection in InterVersionedfile.
673
            if ignore_missing:
674
                return set(self.source.versions()).intersection(set(version_ids))
675
            else:
676
                new_version_ids = set()
677
                for version in version_ids:
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
678
                    if version is None:
679
                        continue
1684.3.2 by Robert Collins
Factor out version_ids-to-join selection in InterVersionedfile.
680
                    if not self.source.has_version(version):
681
                        raise errors.RevisionNotPresent(version, str(self.source))
682
                    else:
683
                        new_version_ids.add(version)
684
                return new_version_ids