~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/versionedfile.py

  • Committer: Wouter van Heyst
  • Date: 2006-06-07 16:05:27 UTC
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: larstiq@larstiq.dyndns.org-20060607160527-2b3649154d0e2e84
more code cleanup

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005 by Canonical Ltd
2
2
#
3
3
# Authors:
4
4
#   Johan Rydberg <jrydberg@gnu.org>
7
7
# it under the terms of the GNU General Public License as published by
8
8
# the Free Software Foundation; either version 2 of the License, or
9
9
# (at your option) any later version.
10
 
#
 
10
 
11
11
# This program is distributed in the hope that it will be useful,
12
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
14
# GNU General Public License for more details.
15
 
#
 
15
 
16
16
# You should have received a copy of the GNU General Public License
17
17
# along with this program; if not, write to the Free Software
18
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
19
 
20
20
"""Versioned text file storage api."""
21
21
 
22
 
from bzrlib.lazy_import import lazy_import
23
 
lazy_import(globals(), """
24
 
 
25
 
from bzrlib import (
26
 
    errors,
27
 
    osutils,
28
 
    tsort,
29
 
    revision,
30
 
    ui,
31
 
    )
32
 
from bzrlib.transport.memory import MemoryTransport
33
 
""")
34
 
 
 
22
 
 
23
from copy import deepcopy
 
24
from unittest import TestSuite
 
25
 
 
26
 
 
27
import bzrlib.errors as errors
35
28
from bzrlib.inter import InterObject
 
29
from bzrlib.symbol_versioning import *
36
30
from bzrlib.textmerge import TextMerge
37
 
from bzrlib.symbol_versioning import (deprecated_function,
38
 
        deprecated_method,
39
 
        zero_eight,
40
 
        )
 
31
from bzrlib.transport.memory import MemoryTransport
 
32
from bzrlib.tsort import topo_sort
 
33
from bzrlib import ui
41
34
 
42
35
 
43
36
class VersionedFile(object):
58
51
        self.finished = False
59
52
        self._access_mode = access_mode
60
53
 
61
 
    @staticmethod
62
 
    def check_not_reserved_id(version_id):
63
 
        revision.check_not_reserved_id(version_id)
64
 
 
65
54
    def copy_to(self, name, transport):
66
55
        """Copy this versioned file to name on transport."""
67
56
        raise NotImplementedError(self.copy_to)
68
 
 
 
57
    
69
58
    @deprecated_method(zero_eight)
70
59
    def names(self):
71
60
        """Return a list of all the versions in this versioned file.
95
84
        :param sha1: The sha1 of the full text.
96
85
        :param delta: The delta instructions. See get_delta for details.
97
86
        """
98
 
        version_id = osutils.safe_revision_id(version_id)
99
 
        parents = [osutils.safe_revision_id(v) for v in parents]
100
87
        self._check_write_ok()
101
88
        if self.has_version(version_id):
102
89
            raise errors.RevisionAlreadyPresent(version_id, self)
139
126
                 provided back to future add_lines calls in the parent_texts
140
127
                 dictionary.
141
128
        """
142
 
        version_id = osutils.safe_revision_id(version_id)
143
 
        parents = [osutils.safe_revision_id(v) for v in parents]
144
129
        self._check_write_ok()
145
130
        return self._add_lines(version_id, parents, lines, parent_texts)
146
131
 
154
139
        
155
140
        This takes the same parameters as add_lines.
156
141
        """
157
 
        version_id = osutils.safe_revision_id(version_id)
158
 
        parents = [osutils.safe_revision_id(v) for v in parents]
159
142
        self._check_write_ok()
160
143
        return self._add_lines_with_ghosts(version_id, parents, lines,
161
144
                                           parent_texts)
187
170
        if self._access_mode != 'w':
188
171
            raise errors.ReadOnlyObjectDirtiedError(self)
189
172
 
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
 
    
197
173
    def clear_cache(self):
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
 
174
        """Remove any data cached in the versioned file object."""
203
175
 
204
176
    def clone_text(self, new_version_id, old_version_id, parents):
205
177
        """Add an identical text to old_version_id as new_version_id.
209
181
 
210
182
        Must raise RevisionAlreadyPresent if the new version is
211
183
        already present in file history."""
212
 
        new_version_id = osutils.safe_revision_id(new_version_id)
213
 
        old_version_id = osutils.safe_revision_id(old_version_id)
214
184
        self._check_write_ok()
215
185
        return self._clone_text(new_version_id, old_version_id, parents)
216
186
 
227
197
        """
228
198
        raise NotImplementedError(self.create_empty)
229
199
 
230
 
    def fix_parents(self, version_id, new_parents):
 
200
    def fix_parents(self, version, new_parents):
231
201
        """Fix the parents list for version.
232
202
        
233
203
        This is done by appending a new version to the index
235
205
        the parents list must be a superset of the current
236
206
        list.
237
207
        """
238
 
        version_id = osutils.safe_revision_id(version_id)
239
 
        new_parents = [osutils.safe_revision_id(p) for p in new_parents]
240
208
        self._check_write_ok()
241
 
        return self._fix_parents(version_id, new_parents)
 
209
        return self._fix_parents(version, new_parents)
242
210
 
243
 
    def _fix_parents(self, version_id, new_parents):
 
211
    def _fix_parents(self, version, new_parents):
244
212
        """Helper for fix_parents."""
245
213
        raise NotImplementedError(self.fix_parents)
246
214
 
252
220
        """
253
221
        raise NotImplementedError(self.get_delta)
254
222
 
255
 
    def get_deltas(self, version_ids):
 
223
    def get_deltas(self, versions):
256
224
        """Get multiple deltas at once for constructing versions.
257
225
        
258
226
        :return: dict(version_id:(delta_parent, sha1, noeol, delta))
260
228
        version_id is the version_id created by that delta.
261
229
        """
262
230
        result = {}
263
 
        for version_id in version_ids:
264
 
            result[version_id] = self.get_delta(version_id)
 
231
        for version in versions:
 
232
            result[version] = self.get_delta(version)
265
233
        return result
266
234
 
267
235
    def get_sha1(self, version_id):
284
252
        return ''.join(self.get_lines(version_id))
285
253
    get_string = get_text
286
254
 
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
 
 
295
255
    def get_lines(self, version_id):
296
256
        """Return version contents as a sequence of lines.
297
257
 
300
260
        """
301
261
        raise NotImplementedError(self.get_lines)
302
262
 
303
 
    def get_ancestry(self, version_ids, topo_sorted=True):
 
263
    def get_ancestry(self, version_ids):
304
264
        """Return a list of all ancestors of given version(s). This
305
265
        will not include the null revision.
306
266
 
307
 
        This list will not be topologically sorted if topo_sorted=False is
308
 
        passed.
309
 
 
310
267
        Must raise RevisionNotPresent if any of the given versions are
311
268
        not present in file history."""
312
269
        if isinstance(version_ids, basestring):
330
287
        
331
288
        Ghosts are not listed or referenced in the graph.
332
289
        :param version_ids: Versions to select.
333
 
                            None means retrieve all versions.
 
290
                            None means retreive all versions.
334
291
        """
335
292
        result = {}
336
293
        if version_ids is None:
337
294
            for version in self.versions():
338
295
                result[version] = self.get_parents(version)
339
296
        else:
340
 
            pending = set(osutils.safe_revision_id(v) for v in version_ids)
 
297
            pending = set(version_ids)
341
298
            while pending:
342
299
                version = pending.pop()
343
300
                if version in result:
424
381
            version_ids,
425
382
            ignore_missing)
426
383
 
427
 
    def iter_lines_added_or_present_in_versions(self, version_ids=None, 
428
 
                                                pb=None):
 
384
    def iter_lines_added_or_present_in_versions(self, version_ids=None):
429
385
        """Iterate over the lines in the versioned file from version_ids.
430
386
 
431
387
        This may return lines from other versions, and does not return the
432
388
        specific version marker at this point. The api may be changed
433
389
        during development to include the version that the versioned file
434
390
        thinks is relevant, but given that such hints are just guesses,
435
 
        its better not to have it if we don't need it.
436
 
 
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).
 
391
        its better not to have it if we dont need it.
440
392
 
441
393
        NOTES: Lines are normalised: they will all have \n terminators.
442
394
               Lines are returned in arbitrary order.
494
446
        """
495
447
        raise NotImplementedError(VersionedFile.plan_merge)
496
448
        
497
 
    def weave_merge(self, plan, a_marker=TextMerge.A_MARKER,
 
449
    def weave_merge(self, plan, a_marker=TextMerge.A_MARKER, 
498
450
                    b_marker=TextMerge.B_MARKER):
499
451
        return PlanWeaveMerge(plan, a_marker, b_marker).merge_lines()[0]
500
452
 
531
483
       
532
484
        # We previously considered either 'unchanged' or 'killed-both' lines
533
485
        # to be possible places to resynchronize.  However, assuming agreement
534
 
        # on killed-both lines may be too aggressive. -- mbp 20060324
 
486
        # on killed-both lines may be too agressive. -- mbp 20060324
535
487
        for state, line in self.plan:
536
488
            if state == 'unchanged':
537
489
                # resync and flush queued conflicts changes if any
584
536
    InterVersionedFile.get(other).method_name(parameters).
585
537
    """
586
538
 
587
 
    _optimisers = []
 
539
    _optimisers = set()
588
540
    """The available optimised InterVersionedFile types."""
589
541
 
590
542
    def join(self, pb=None, msg=None, version_ids=None, ignore_missing=False):
611
563
            target = temp_source
612
564
        version_ids = self._get_source_version_ids(version_ids, ignore_missing)
613
565
        graph = self.source.get_graph(version_ids)
614
 
        order = tsort.topo_sort(graph.items())
 
566
        order = topo_sort(graph.items())
615
567
        pb = ui.ui_factory.nested_progress_bar()
616
568
        parent_texts = {}
617
569
        try:
669
621
            # None cannot be in source.versions
670
622
            return set(self.source.versions())
671
623
        else:
672
 
            version_ids = [osutils.safe_revision_id(v) for v in version_ids]
673
624
            if ignore_missing:
674
625
                return set(self.source.versions()).intersection(set(version_ids))
675
626
            else:
682
633
                    else:
683
634
                        new_version_ids.add(version)
684
635
                return new_version_ids
 
636
 
 
637
 
 
638
class InterVersionedFileTestProviderAdapter(object):
 
639
    """A tool to generate a suite testing multiple inter versioned-file classes.
 
640
 
 
641
    This is done by copying the test once for each interversionedfile provider
 
642
    and injecting the transport_server, transport_readonly_server,
 
643
    versionedfile_factory and versionedfile_factory_to classes into each copy.
 
644
    Each copy is also given a new id() to make it easy to identify.
 
645
    """
 
646
 
 
647
    def __init__(self, transport_server, transport_readonly_server, formats):
 
648
        self._transport_server = transport_server
 
649
        self._transport_readonly_server = transport_readonly_server
 
650
        self._formats = formats
 
651
    
 
652
    def adapt(self, test):
 
653
        result = TestSuite()
 
654
        for (interversionedfile_class,
 
655
             versionedfile_factory,
 
656
             versionedfile_factory_to) in self._formats:
 
657
            new_test = deepcopy(test)
 
658
            new_test.transport_server = self._transport_server
 
659
            new_test.transport_readonly_server = self._transport_readonly_server
 
660
            new_test.interversionedfile_class = interversionedfile_class
 
661
            new_test.versionedfile_factory = versionedfile_factory
 
662
            new_test.versionedfile_factory_to = versionedfile_factory_to
 
663
            def make_new_test_id():
 
664
                new_id = "%s(%s)" % (new_test.id(), interversionedfile_class.__name__)
 
665
                return lambda: new_id
 
666
            new_test.id = make_new_test_id()
 
667
            result.addTest(new_test)
 
668
        return result
 
669
 
 
670
    @staticmethod
 
671
    def default_test_list():
 
672
        """Generate the default list of interversionedfile permutations to test."""
 
673
        from bzrlib.weave import WeaveFile
 
674
        from bzrlib.knit import KnitVersionedFile
 
675
        result = []
 
676
        # test the fallback InterVersionedFile from annotated knits to weave
 
677
        result.append((InterVersionedFile, 
 
678
                       KnitVersionedFile,
 
679
                       WeaveFile))
 
680
        for optimiser in InterVersionedFile._optimisers:
 
681
            result.append((optimiser,
 
682
                           optimiser._matching_file_from_factory,
 
683
                           optimiser._matching_file_to_factory
 
684
                           ))
 
685
        # if there are specific combinations we want to use, we can add them 
 
686
        # here.
 
687
        return result