/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
1
# Copyright (C) 2005 by Canonical Ltd
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.
10
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.
15
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
# Remaing to do is to figure out if get_graph should return a simple
21
# map, or a graph object of some kind.
22
23
24
"""Versioned text file storage api."""
25
26
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.
27
from copy import deepcopy
28
from unittest import TestSuite
29
30
31
from bzrlib.inter import InterObject
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.
32
from bzrlib.symbol_versioning import *
1563.2.13 by Robert Collins
InterVersionedFile implemented.
33
from bzrlib.transport.memory import MemoryTransport
34
from bzrlib.tsort import topo_sort
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.
35
36
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
37
class VersionedFile(object):
38
    """Versioned text file storage.
39
    
40
    A versioned file manages versions of line-based text files,
41
    keeping track of the originating version for each line.
42
43
    To clients the "lines" of the file are represented as a list of
44
    strings. These strings will typically have terminal newline
45
    characters, but this is not required.  In particular files commonly
46
    do not have a newline at the end of the file.
47
48
    Texts are identified by a version-id string.
49
    """
50
1563.2.15 by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages.
51
    def copy_to(self, name, transport):
52
        """Copy this versioned file to name on transport."""
53
        raise NotImplementedError(self.copy_to)
54
    
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.
55
    @deprecated_method(zero_eight)
56
    def names(self):
57
        """Return a list of all the versions in this versioned file.
58
59
        Please use versionedfile.versions() now.
60
        """
61
        return self.versions()
62
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
63
    def versions(self):
64
        """Return a unsorted list of versions."""
65
        raise NotImplementedError(self.versions)
66
67
    def has_version(self, version_id):
68
        """Returns whether version is present."""
69
        raise NotImplementedError(self.has_version)
70
71
    def add_lines(self, version_id, parents, lines):
72
        """Add a single text on top of the versioned file.
73
74
        Must raise RevisionAlreadyPresent if the new version is
75
        already present in file history.
76
77
        Must raise RevisionNotPresent if any of the given parents are
78
        not present in file history."""
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
79
        raise NotImplementedError(self.add_lines)
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
80
1563.2.19 by Robert Collins
stub out a check for knits.
81
    def check(self, progress_bar=None):
82
        """Check the versioned file for integrity."""
83
        raise NotImplementedError(self.check)
84
1563.2.7 by Robert Collins
add versioned file clear_cache entry.
85
    def clear_cache(self):
86
        """Remove any data cached in the versioned file object."""
87
1563.2.5 by Robert Collins
Remove unused transaction references from knit.py and the versionedfile interface.
88
    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.
89
        """Add an identical text to old_version_id as new_version_id.
90
91
        Must raise RevisionNotPresent if the old version or any of the
92
        parents are not present in file history.
93
94
        Must raise RevisionAlreadyPresent if the new version is
95
        already present in file history."""
96
        raise NotImplementedError(self.clone_text)
97
1563.2.13 by Robert Collins
InterVersionedFile implemented.
98
    def create_empty(self, name, transport, mode=None):
99
        """Create a new versioned file of this exact type.
100
101
        :param name: the file name
102
        :param transport: the transport
103
        :param mode: optional file mode.
104
        """
105
        raise NotImplementedError(self.create_empty)
106
1563.2.15 by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages.
107
    def get_suffixes(self):
108
        """Return the file suffixes associated with this versioned file."""
109
        raise NotImplementedError(self.get_suffixes)
110
    
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
111
    def get_text(self, version_id):
112
        """Return version contents as a text string.
113
114
        Raises RevisionNotPresent if version is not present in
115
        file history.
116
        """
117
        return ''.join(self.get_lines(version_id))
118
    get_string = get_text
119
120
    def get_lines(self, version_id):
121
        """Return version contents as a sequence of lines.
122
123
        Raises RevisionNotPresent if version is not present in
124
        file history.
125
        """
126
        raise NotImplementedError(self.get_lines)
127
128
    def get_ancestry(self, version_ids):
129
        """Return a list of all ancestors of given version(s). This
130
        will not include the null revision.
131
132
        Must raise RevisionNotPresent if any of the given versions are
133
        not present in file history."""
134
        if isinstance(version_ids, basestring):
135
            version_ids = [version_ids]
136
        raise NotImplementedError(self.get_ancestry)
137
        
1563.2.13 by Robert Collins
InterVersionedFile implemented.
138
    def get_graph(self):
139
        """Return a graph for the entire versioned file."""
140
        result = {}
141
        for version in self.versions():
142
            result[version] = self.get_parents(version)
143
        return result
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
144
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.
145
    @deprecated_method(zero_eight)
146
    def parent_names(self, version):
147
        """Return version names for parents of a version.
148
        
149
        See get_parents for the current api.
150
        """
151
        return self.get_parents(version)
152
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
153
    def get_parents(self, version_id):
154
        """Return version names for parents of a version.
155
156
        Must raise RevisionNotPresent if version is not present in
157
        file history.
158
        """
159
        raise NotImplementedError(self.get_parents)
160
161
    def annotate_iter(self, version_id):
162
        """Yield list of (version-id, line) pairs for the specified
163
        version.
164
165
        Must raise RevisionNotPresent if any of the given versions are
166
        not present in file history.
167
        """
168
        raise NotImplementedError(self.annotate_iter)
169
170
    def annotate(self, version_id):
171
        return list(self.annotate_iter(version_id))
172
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
173
    def join(self, other, pb=None, msg=None, version_ids=None):
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
174
        """Integrate versions from other into this versioned file.
175
176
        If version_ids is None all versions from other should be
177
        incorporated into this versioned file.
178
179
        Must raise RevisionNotPresent if any of the specified versions
180
        are not present in the other files history."""
1563.2.13 by Robert Collins
InterVersionedFile implemented.
181
        return InterVersionedFile.get(other, self).join(pb, msg, version_ids)
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
182
183
    def walk(self, version_ids=None):
184
        """Walk the versioned file as a weave-like structure, for
185
        versions relative to version_ids.  Yields sequence of (lineno,
186
        insert, deletes, text) for each relevant line.
187
188
        Must raise RevisionNotPresent if any of the specified versions
189
        are not present in the file history.
190
191
        :param version_ids: the version_ids to walk with respect to. If not
192
                            supplied the entire weave-like structure is walked.
193
        """
194
        raise NotImplementedError(self.walk)
195
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.
196
    @deprecated_method(zero_eight)
197
    def iter_names(self):
198
        """Walk the names list."""
199
        return iter(self.versions())
200
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
201
    def plan_merge(self, ver_a, ver_b):
202
        """Return pseudo-annotation indicating how the two versions merge.
203
204
        This is computed between versions a and b and their common
205
        base.
206
207
        Weave lines present in none of them are skipped entirely.
208
        """
209
        inc_a = set(self.inclusions([ver_a]))
210
        inc_b = set(self.inclusions([ver_b]))
211
        inc_c = inc_a & inc_b
212
213
        for lineno, insert, deleteset, line in self.walk():
214
            if deleteset & inc_c:
215
                # killed in parent; can't be in either a or b
216
                # not relevant to our work
217
                yield 'killed-base', line
218
            elif insert in inc_c:
219
                # was inserted in base
220
                killed_a = bool(deleteset & inc_a)
221
                killed_b = bool(deleteset & inc_b)
222
                if killed_a and killed_b:
223
                    yield 'killed-both', line
224
                elif killed_a:
225
                    yield 'killed-a', line
226
                elif killed_b:
227
                    yield 'killed-b', line
228
                else:
229
                    yield 'unchanged', line
230
            elif insert in inc_a:
231
                if deleteset & inc_a:
232
                    yield 'ghost-a', line
233
                else:
234
                    # new in A; not in B
235
                    yield 'new-a', line
236
            elif insert in inc_b:
237
                if deleteset & inc_b:
238
                    yield 'ghost-b', line
239
                else:
240
                    yield 'new-b', line
241
            else:
242
                # not in either revision
243
                yield 'irrelevant', line
244
245
        yield 'unchanged', ''           # terminator
246
1563.2.2 by Robert Collins
merge from bzr.dev
247
    def weave_merge(self, plan, a_marker='<<<<<<< \n', b_marker='>>>>>>> \n'):
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
248
        lines_a = []
249
        lines_b = []
250
        ch_a = ch_b = False
251
        # TODO: Return a structured form of the conflicts (e.g. 2-tuples for
252
        # conflicted regions), rather than just inserting the markers.
253
        # 
254
        # TODO: Show some version information (e.g. author, date) on 
255
        # conflicted regions.
256
        for state, line in plan:
257
            if state == 'unchanged' or state == 'killed-both':
258
                # resync and flush queued conflicts changes if any
259
                if not lines_a and not lines_b:
260
                    pass
261
                elif ch_a and not ch_b:
262
                    # one-sided change:                    
263
                    for l in lines_a: yield l
264
                elif ch_b and not ch_a:
265
                    for l in lines_b: yield l
266
                elif lines_a == lines_b:
267
                    for l in lines_a: yield l
268
                else:
1563.2.2 by Robert Collins
merge from bzr.dev
269
                    yield a_marker
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
270
                    for l in lines_a: yield l
271
                    yield '=======\n'
272
                    for l in lines_b: yield l
1563.2.2 by Robert Collins
merge from bzr.dev
273
                    yield b_marker
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
274
275
                del lines_a[:]
276
                del lines_b[:]
277
                ch_a = ch_b = False
278
                
279
            if state == 'unchanged':
280
                if line:
281
                    yield line
282
            elif state == 'killed-a':
283
                ch_a = True
284
                lines_b.append(line)
285
            elif state == 'killed-b':
286
                ch_b = True
287
                lines_a.append(line)
288
            elif state == 'new-a':
289
                ch_a = True
290
                lines_a.append(line)
291
            elif state == 'new-b':
292
                ch_b = True
293
                lines_b.append(line)
294
            else:
295
                assert state in ('irrelevant', 'ghost-a', 'ghost-b', 'killed-base',
296
                                 'killed-both'), \
297
                       state
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.
298
299
300
class InterVersionedFile(InterObject):
301
    """This class represents operations taking place between two versionedfiles..
302
303
    Its instances have methods like join, and contain
304
    references to the source and target versionedfiles these operations can be 
305
    carried out on.
306
307
    Often we will provide convenience methods on 'versionedfile' which carry out
308
    operations with another versionedfile - they will always forward to
309
    InterVersionedFile.get(other).method_name(parameters).
310
    """
311
312
    _optimisers = set()
313
    """The available optimised InterVersionedFile types."""
314
1563.2.13 by Robert Collins
InterVersionedFile implemented.
315
    def join(self, pb=None, msg=None, version_ids=None):
316
        """Integrate versions from self.source into self.target.
317
318
        If version_ids is None all versions from source should be
319
        incorporated into this versioned file.
320
321
        Must raise RevisionNotPresent if any of the specified versions
322
        are not present in the other files history.
323
        """
324
        # the default join: 
325
        # - make a temporary versioned file of type target
326
        # - insert the source content into it one at a time
327
        # - join them
328
        # Make a new target-format versioned file. 
329
        temp_source = self.target.create_empty("temp", MemoryTransport())
330
        graph = self.source.get_graph()
331
        order = topo_sort(graph.items())
332
        for version in order:
333
            temp_source.add_lines(version,
334
                                  self.source.get_parents(version),
335
                                  self.source.get_lines(version))
336
        
337
        # this should hit the native code path for target
338
        return self.target.join(temp_source, pb, msg, version_ids)
339
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.
340
341
class InterVersionedFileTestProviderAdapter(object):
342
    """A tool to generate a suite testing multiple inter versioned-file classes.
343
344
    This is done by copying the test once for each interversionedfile provider
345
    and injecting the transport_server, transport_readonly_server,
346
    versionedfile_factory and versionedfile_factory_to classes into each copy.
347
    Each copy is also given a new id() to make it easy to identify.
348
    """
349
350
    def __init__(self, transport_server, transport_readonly_server, formats):
351
        self._transport_server = transport_server
352
        self._transport_readonly_server = transport_readonly_server
353
        self._formats = formats
354
    
355
    def adapt(self, test):
356
        result = TestSuite()
357
        for (interversionedfile_class,
358
             versionedfile_factory,
359
             versionedfile_factory_to) in self._formats:
360
            new_test = deepcopy(test)
361
            new_test.transport_server = self._transport_server
362
            new_test.transport_readonly_server = self._transport_readonly_server
363
            new_test.interversionedfile_class = interversionedfile_class
364
            new_test.versionedfile_factory = versionedfile_factory
365
            new_test.versionedfile_factory_to = versionedfile_factory_to
366
            def make_new_test_id():
367
                new_id = "%s(%s)" % (new_test.id(), interversionedfile_class.__name__)
368
                return lambda: new_id
369
            new_test.id = make_new_test_id()
370
            result.addTest(new_test)
371
        return result
372
373
    @staticmethod
374
    def default_test_list():
375
        """Generate the default list of interversionedfile permutations to test."""
376
        from bzrlib.weave import WeaveFile
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
377
        from bzrlib.knit import KnitVersionedFile
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.
378
        result = []
379
        # test the fallback InterVersionedFile from weave to annotated knits
380
        result.append((InterVersionedFile, 
381
                       WeaveFile,
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
382
                       KnitVersionedFile))
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.
383
        for optimiser in InterVersionedFile._optimisers:
384
            result.append((optimiser,
385
                           optimiser._matching_file_factory,
386
                           optimiser._matching_file_factory
387
                           ))
388
        # if there are specific combinations we want to use, we can add them 
389
        # here.
390
        return result