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