~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/weavestore.py

  • Committer: Martin Pool
  • Date: 2005-09-12 09:50:44 UTC
  • Revision ID: mbp@sourcefrog.net-20050912095044-6acfdb5611729987
- no tests in bzrlib.fetch anymore

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/python
 
2
 
1
3
# Copyright (C) 2005 Canonical Ltd
2
4
 
3
5
# This program is free software; you can redistribute it and/or modify
14
16
# along with this program; if not, write to the Free Software
15
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
18
 
17
 
# XXX: Some consideration of the problems that might occur if there are
18
 
# files whose id differs only in case.  That should probably be forbidden.
19
 
 
20
 
 
21
 
import errno
 
19
# Author: Martin Pool <mbp@canonical.com>
 
20
 
 
21
 
22
22
import os
23
 
from cStringIO import StringIO
24
 
import urllib
25
 
 
26
 
from bzrlib.weavefile import read_weave, write_weave_v5
27
 
from bzrlib.weave import WeaveFile
28
 
from bzrlib.store import TransportStore, hash_prefix
29
 
from bzrlib.atomicfile import AtomicFile
30
 
from bzrlib.errors import NoSuchFile, FileExists
31
 
from bzrlib.symbol_versioning import *
32
 
from bzrlib.trace import mutter
33
 
 
34
 
 
35
 
class VersionedFileStore(TransportStore):
36
 
    """Collection of many versioned files in a transport."""
37
 
 
38
 
    def __init__(self, transport, prefixed=False, precious=False,
39
 
                 dir_mode=None, file_mode=None,
40
 
                 versionedfile_class=WeaveFile):
41
 
        super(WeaveStore, self).__init__(transport,
42
 
                dir_mode=dir_mode, file_mode=file_mode,
43
 
                prefixed=prefixed, compressed=False)
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)
61
 
 
62
 
    def filename(self, file_id):
63
 
        """Return the path relative to the transport root."""
64
 
        if self._prefixed:
65
 
            return hash_prefix(file_id) + urllib.quote(file_id)
66
 
        else:
67
 
            return urllib.quote(file_id)
68
 
 
69
 
    def __iter__(self):
70
 
        suffixes = self._versionedfile_class.get_suffixes()
71
 
        ids = set()
72
 
        for relpath in self._iter_files_recursive():
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)
79
 
 
80
 
    def has_id(self, fileid):
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)
101
 
 
102
 
    def get_weave(self, file_id, transaction):
103
 
        weave = transaction.map.find_weave(file_id)
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)
122
 
    def get_lines(self, file_id, rev_id, transaction):
123
 
        """Return text from a particular version of a weave.
124
 
 
125
 
        Returned as a list of lines.
126
 
        """
127
 
        assert 0
128
 
        w = self.get_weave(file_id, transaction)
129
 
        return w.get_lines(rev_id)
 
23
import bzrlib.weavefile
 
24
 
 
25
 
 
26
class WeaveStore(object):
 
27
    """Collection of several weave files."""
 
28
    def __init__(self, dir):
 
29
        self._dir = dir
 
30
 
 
31
 
 
32
    def get_weave(self, file_id):
 
33
        path = self._dir + os.sep + file_id + '.weave'
 
34
        return bzrlib.weavefile.read_weave(file(path, 'rb'))
130
35
    
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)
142
 
        try:
143
 
            weave = self._versionedfile_class(self.filename(file_id), self._transport, self._file_mode, create=True)
144
 
        except NoSuchFile:
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
152
 
 
153
 
    def get_weave_or_empty(self, file_id, transaction):
154
 
        """Return a weave, or an empty one if it doesn't exist.""" 
155
 
        try:
156
 
            return self.get_weave(file_id, transaction)
157
 
        except NoSuchFile:
158
 
            return self._new_weave(file_id, transaction)
159
 
 
160
 
    @deprecated_method(zero_eight)
161
 
    def put_weave(self, file_id, weave, transaction):
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)
178
 
    def add_text(self, file_id, rev_id, new_lines, parents, 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)
186
 
        
187
 
    @deprecated_method(zero_eight)
188
 
    def add_identical_text(self, file_id, old_rev_id, new_rev_id, parents,
189
 
                           transaction):
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
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()
249
 
        for count, f in enumerate(file_ids):
250
 
            mutter("copy weave {%s} into %s", f, self)
251
 
            if pb:
252
 
                pb.update('copy', count, len(file_ids))
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))
258
 
        if pb:
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