/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/store/versioned/__init__.py

Merge from bzr.ab.integration

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#! /usr/bin/python
2
 
 
3
1
# Copyright (C) 2005 Canonical Ltd
4
2
 
5
3
# This program is free software; you can redistribute it and/or modify
26
24
import urllib
27
25
 
28
26
from bzrlib.weavefile import read_weave, write_weave_v5
29
 
from bzrlib.weave import Weave
 
27
from bzrlib.weave import WeaveFile
30
28
from bzrlib.store import TransportStore, hash_prefix
31
29
from bzrlib.atomicfile import AtomicFile
32
30
from bzrlib.errors import NoSuchFile, FileExists
 
31
from bzrlib.symbol_versioning import *
33
32
from bzrlib.trace import mutter
34
33
 
35
34
 
36
 
class WeaveStore(TransportStore):
37
 
    """Collection of several weave files in a directory.
38
 
 
39
 
    This has some shortcuts for reading and writing them.
40
 
    """
41
 
    FILE_SUFFIX = '.weave'
 
35
class VersionedFileStore(TransportStore):
 
36
    """Collection of many versioned files in a transport."""
42
37
 
43
38
    def __init__(self, transport, prefixed=False, precious=False,
44
 
                 dir_mode=None, file_mode=None):
 
39
                 dir_mode=None, file_mode=None,
 
40
                 versionedfile_class=WeaveFile):
45
41
        super(WeaveStore, self).__init__(transport,
46
42
                dir_mode=dir_mode, file_mode=file_mode,
47
43
                prefixed=prefixed, compressed=False)
48
44
        self._precious = precious
 
45
        self._versionedfile_class = versionedfile_class
 
46
 
 
47
    def _clear_cache_id(self, file_id, transaction):
 
48
        """WARNING may lead to inconsistent object references for file_id.
 
49
 
 
50
        Remove file_id from the transaction map. 
 
51
 
 
52
        NOT in the transaction api because theres no reliable way to clear
 
53
        callers. So its here for very specialised use rather than having an
 
54
        'api' that isn't.
 
55
        """
 
56
        weave = transaction.map.find_weave(file_id)
 
57
        if weave is not None:
 
58
            mutter("old data in transaction in %s for %s", self, file_id)
 
59
            # FIXME abstraction violation - transaction now has stale data.
 
60
            transaction.map.remove_object(weave)
49
61
 
50
62
    def filename(self, file_id):
51
63
        """Return the path relative to the transport root."""
52
64
        if self._prefixed:
53
 
            return hash_prefix(file_id) + urllib.quote(file_id) + WeaveStore.FILE_SUFFIX
 
65
            return hash_prefix(file_id) + urllib.quote(file_id)
54
66
        else:
55
 
            return urllib.quote(file_id) + WeaveStore.FILE_SUFFIX
 
67
            return urllib.quote(file_id)
56
68
 
57
69
    def __iter__(self):
58
 
        l = len(WeaveStore.FILE_SUFFIX)
 
70
        suffixes = self._versionedfile_class.get_suffixes()
 
71
        ids = set()
59
72
        for relpath in self._iter_files_recursive():
60
 
            if relpath.endswith(WeaveStore.FILE_SUFFIX):
61
 
                yield os.path.basename(relpath[:-l])
 
73
            for suffix in suffixes:
 
74
                if relpath.endswith(suffix):
 
75
                    id = os.path.basename(relpath[:-len(suffix)])
 
76
                    if not id in ids:
 
77
                        yield id
 
78
                        ids.add(id)
62
79
 
63
80
    def has_id(self, fileid):
64
 
        return self._transport.has(self.filename(fileid))
65
 
 
66
 
    def _get(self, file_id):
67
 
        return self._transport.get(self.filename(file_id))
68
 
 
69
 
    def _put(self, file_id, f):
70
 
        # less round trips to mkdir on failure than mkdir always
71
 
        try:
72
 
            return self._transport.put(self.filename(file_id), f, mode=self._file_mode)
73
 
        except NoSuchFile:
74
 
            if not self._prefixed:
75
 
                raise
76
 
            self._transport.mkdir(hash_prefix(file_id), mode=self._dir_mode)
77
 
            return self._transport.put(self.filename(file_id), f, mode=self._file_mode)
 
81
        suffixes = self._versionedfile_class.get_suffixes()
 
82
        filename = self.filename(fileid)
 
83
        for suffix in suffixes:
 
84
            if not self._transport.has(filename + suffix):
 
85
                return False
 
86
        return True
 
87
 
 
88
    def get_empty(self, file_id, transaction):
 
89
        """Get an empty weave, which implies deleting the existing one first."""
 
90
        if self.has_id(file_id):
 
91
            self.delete(file_id, transaction)
 
92
        return self.get_weave_or_empty(file_id, transaction)
 
93
 
 
94
    def delete(self, file_id, transaction):
 
95
        """Remove file_id from the store."""
 
96
        suffixes = self._versionedfile_class.get_suffixes()
 
97
        filename = self.filename(file_id)
 
98
        for suffix in suffixes:
 
99
            self._transport.delete(filename + suffix)
 
100
        self._clear_cache_id(file_id, transaction)
78
101
 
79
102
    def get_weave(self, file_id, transaction):
80
103
        weave = transaction.map.find_weave(file_id)
81
 
        if weave:
82
 
            mutter("cache hit in %s for %s", self, file_id)
83
 
            return weave
84
 
        w = read_weave(self._get(file_id))
85
 
        transaction.map.add_weave(file_id, w)
86
 
        transaction.register_clean(w, precious=self._precious)
87
 
        # TODO: jam 20051219 This should check if there is a prelude
88
 
        #       which is already cached, and if so, should remove it
89
 
        #       But transaction doesn't seem to have a 'remove'
90
 
        #       One workaround would be to re-add the object with
91
 
        #       the PRELUDE marker.
92
 
        return w
93
 
 
94
 
    def get_weave_prelude(self, file_id, transaction):
95
 
        weave_id = file_id
96
 
        weave = transaction.map.find_weave(weave_id)
97
 
        if weave:
98
 
            mutter("cache hit in %s for %s", self, weave_id)
99
 
            return weave
100
 
        # We want transactions to also cache preludes if that
101
 
        # is all that we are loading. So we need a unique
102
 
        # identifier, so that someone who wants the whole text
103
 
        # won't get just the prelude
104
 
        weave_id = 'PRELUDE-' + file_id
105
 
        weave = transaction.map.find_weave(weave_id)
106
 
        if weave:
107
 
            mutter("cache hit in %s for %s", self, weave_id)
108
 
            return weave
109
 
        w = read_weave(self._get(file_id), prelude=True)
110
 
        transaction.map.add_weave(weave_id, w)
111
 
        transaction.register_clean(w, precious=self._precious)
112
 
        return w
113
 
 
 
104
        if weave is not None:
 
105
            #mutter("cache hit in %s for %s", self, file_id)
 
106
            return weave
 
107
        if transaction.writeable():
 
108
            w = self._versionedfile_class(self.filename(file_id), self._transport, self._file_mode)
 
109
            transaction.map.add_weave(file_id, w)
 
110
            transaction.register_dirty(w)
 
111
        else:
 
112
            w = self._versionedfile_class(self.filename(file_id),
 
113
                                          self._transport,
 
114
                                          self._file_mode,
 
115
                                          create=False,
 
116
                                          access_mode='r')
 
117
            transaction.map.add_weave(file_id, w)
 
118
            transaction.register_clean(w, precious=self._precious)
 
119
        return w
 
120
 
 
121
    @deprecated_method(zero_eight)
114
122
    def get_lines(self, file_id, rev_id, transaction):
115
123
        """Return text from a particular version of a weave.
116
124
 
117
 
        Returned as a list of lines."""
 
125
        Returned as a list of lines.
 
126
        """
 
127
        assert 0
118
128
        w = self.get_weave(file_id, transaction)
119
 
        return w.get(w.lookup(rev_id))
 
129
        return w.get_lines(rev_id)
120
130
    
121
 
    def get_weave_prelude_or_empty(self, file_id, transaction):
122
 
        """cheap version that reads the prelude but not the lines
123
 
        """
 
131
    def _new_weave(self, file_id, transaction):
 
132
        """Make a new weave for file_id and return it."""
 
133
        weave = self._make_new_versionedfile(file_id, transaction)
 
134
        transaction.map.add_weave(file_id, weave)
 
135
        # has to be dirty - its able to mutate on its own.
 
136
        transaction.register_dirty(weave)
 
137
        return weave
 
138
 
 
139
    def _make_new_versionedfile(self, file_id, transaction):
 
140
        if self.has_id(file_id):
 
141
            self.delete(file_id, transaction)
124
142
        try:
125
 
            return self.get_weave_prelude(file_id, transaction)
 
143
            weave = self._versionedfile_class(self.filename(file_id), self._transport, self._file_mode, create=True)
126
144
        except NoSuchFile:
127
 
            # We can cache here, because we know that there
128
 
            # is no complete object, since we got NoSuchFile
129
 
            weave = Weave(weave_name=file_id)
130
 
            transaction.map.add_weave(file_id, weave)
131
 
            transaction.register_clean(weave, precious=self._precious)
132
 
            return weave
 
145
            if not self._prefixed:
 
146
                # unexpected error - NoSuchFile is raised on a missing dir only and that
 
147
                # only occurs when we are prefixed.
 
148
                raise
 
149
            self._transport.mkdir(hash_prefix(file_id), mode=self._dir_mode)
 
150
            weave = self._versionedfile_class(self.filename(file_id), self._transport, self._file_mode, create=True)
 
151
        return weave
133
152
 
134
153
    def get_weave_or_empty(self, file_id, transaction):
135
154
        """Return a weave, or an empty one if it doesn't exist.""" 
136
155
        try:
137
156
            return self.get_weave(file_id, transaction)
138
157
        except NoSuchFile:
139
 
            weave = Weave(weave_name=file_id)
140
 
            transaction.map.add_weave(file_id, weave)
141
 
            transaction.register_clean(weave, precious=self._precious)
142
 
            return weave
 
158
            return self._new_weave(file_id, transaction)
143
159
 
 
160
    @deprecated_method(zero_eight)
144
161
    def put_weave(self, file_id, weave, transaction):
145
 
        """Write back a modified weave"""
146
 
        transaction.register_dirty(weave)
147
 
        # TODO FOR WRITE TRANSACTIONS: this should be done in a callback
148
 
        # from the transaction, when it decides to save.
149
 
        sio = StringIO()
150
 
        write_weave_v5(weave, sio)
151
 
        sio.seek(0)
152
 
        self._put(file_id, sio)
153
 
 
 
162
        """This is a deprecated API: It writes an entire collection of ids out.
 
163
        
 
164
        This became inappropriate when we made a versioned file api which
 
165
        tracks the state of the collection of versions for a single id.
 
166
        
 
167
        Its maintained for backwards compatability but will only work on
 
168
        weave stores - pre 0.8 repositories.
 
169
        """
 
170
        self._put_weave(self, file_id, weave, transaction)
 
171
 
 
172
    def _put_weave(self, file_id, weave, transaction):
 
173
        """Preserved here for upgrades-to-weaves to use."""
 
174
        myweave = self._make_new_versionedfile(file_id, transaction)
 
175
        myweave.join(weave)
 
176
 
 
177
    @deprecated_method(zero_eight)
154
178
    def add_text(self, file_id, rev_id, new_lines, parents, transaction):
155
 
        w = self.get_weave_or_empty(file_id, transaction)
156
 
        parent_idxs = map(w.lookup, parents)
157
 
        w.add(rev_id, parent_idxs, new_lines)
158
 
        self.put_weave(file_id, w, transaction)
 
179
        """This method was a shorthand for 
 
180
 
 
181
        vfile = self.get_weave_or_empty(file_id, transaction)
 
182
        vfile.add_lines(rev_id, parents, new_lines)
 
183
        """
 
184
        vfile = self.get_weave_or_empty(file_id, transaction)
 
185
        vfile.add_lines(rev_id, parents, new_lines)
159
186
        
 
187
    @deprecated_method(zero_eight)
160
188
    def add_identical_text(self, file_id, old_rev_id, new_rev_id, parents,
161
189
                           transaction):
162
 
        w = self.get_weave_or_empty(file_id, transaction)
163
 
        parent_idxs = map(w.lookup, parents)
164
 
        w.add_identical(old_rev_id, new_rev_id, parent_idxs)
165
 
        self.put_weave(file_id, w, transaction)
166
 
     
167
 
    def copy_multi(self, from_store, file_ids, pb=None):
 
190
        """This method was a shorthand for
 
191
 
 
192
        vfile = self.get_weave_or_empty(file_id, transaction)
 
193
        vfile.clone_text(new_rev_id, old_rev_id, parents)
 
194
        """
 
195
        vfile = self.get_weave_or_empty(file_id, transaction)
 
196
        vfile.clone_text(new_rev_id, old_rev_id, parents)
 
197
 
 
198
    def copy(self, source, result_id, transaction):
 
199
        """Copy the source versioned file to result_id in this store."""
 
200
        self._clear_cache_id(result_id, transaction)
 
201
        source.copy_to(self.filename(result_id), self._transport)
 
202
 
 
203
    def copy_all_ids(self, store_from, pb=None, from_transaction=None,
 
204
                     to_transaction=None):
 
205
        """Copy all the file ids from store_from into self."""
 
206
        if from_transaction is None:
 
207
            warn("Please pass from_transaction into "
 
208
                 "versioned_store.copy_all_ids.", stacklevel=2)
 
209
        if to_transaction is None:
 
210
            warn("Please pass to_transaction into "
 
211
                 "versioned_store.copy_all_ids.", stacklevel=2)
 
212
        if not store_from.listable():
 
213
            raise UnlistableStore(store_from)
 
214
        ids = []
 
215
        for count, file_id in enumerate(store_from):
 
216
            if pb:
 
217
                pb.update('listing files', count, count)
 
218
            ids.append(file_id)
 
219
        if pb:
 
220
            pb.clear()
 
221
        mutter('copy_all ids: %r', ids)
 
222
        self.copy_multi(store_from, ids, pb=pb,
 
223
                        from_transaction=from_transaction,
 
224
                        to_transaction=to_transaction)
 
225
 
 
226
    def copy_multi(self, from_store, file_ids, pb=None, from_transaction=None,
 
227
                   to_transaction=None):
 
228
        """Copy all the versions for multiple file_ids from from_store.
 
229
        
 
230
        :param from_transaction: required current transaction in from_store.
 
231
        """
 
232
        from bzrlib.transactions import PassThroughTransaction
168
233
        assert isinstance(from_store, WeaveStore)
 
234
        if from_transaction is None:
 
235
            warn("WeaveStore.copy_multi without a from_transaction parameter "
 
236
                 "is deprecated. Please provide a from_transaction.",
 
237
                 DeprecationWarning,
 
238
                 stacklevel=2)
 
239
            # we are reading one object - caching is irrelevant.
 
240
            from_transaction = PassThroughTransaction()
 
241
        if to_transaction is None:
 
242
            warn("WeaveStore.copy_multi without a to_transaction parameter "
 
243
                 "is deprecated. Please provide a to_transaction.",
 
244
                 DeprecationWarning,
 
245
                 stacklevel=2)
 
246
            # we are copying single objects, and there may be open tranasactions
 
247
            # so again with the passthrough
 
248
            to_transaction = PassThroughTransaction()
169
249
        for count, f in enumerate(file_ids):
170
250
            mutter("copy weave {%s} into %s", f, self)
171
251
            if pb:
172
252
                pb.update('copy', count, len(file_ids))
173
 
            self._put(f, from_store._get(f))
 
253
            # if we have it in cache, its faster.
 
254
            # joining is fast with knits, and bearable for weaves -
 
255
            # indeed the new case can be optimised if needed.
 
256
            target = self._make_new_versionedfile(f, to_transaction)
 
257
            target.join(from_store.get_weave(f, from_transaction))
174
258
        if pb:
175
259
            pb.clear()
 
260
 
 
261
    def total_size(self):
 
262
        count, bytes =  super(VersionedFileStore, self).total_size()
 
263
        return (count / len(self._versionedfile_class.get_suffixes())), bytes
 
264
 
 
265
WeaveStore = VersionedFileStore