~greatmay12/+junk/test1

« back to all changes in this revision

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

  • Committer: thitipong at ndrsolution
  • Date: 2011-11-14 06:31:02 UTC
  • Revision ID: thitipong@ndrsolution.com-20111114063102-9obte3yfi2azku7d
ndr redirect version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
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
 
22
import os
 
23
from cStringIO import StringIO
 
24
from warnings import warn
 
25
 
 
26
from bzrlib import (
 
27
    errors,
 
28
    osutils,
 
29
    )
 
30
from bzrlib.weavefile import read_weave, write_weave_v5
 
31
from bzrlib.weave import WeaveFile, Weave
 
32
from bzrlib.store import TransportStore
 
33
from bzrlib.atomicfile import AtomicFile
 
34
from bzrlib.symbol_versioning import (deprecated_method,
 
35
        )
 
36
from bzrlib.trace import mutter
 
37
import bzrlib.ui
 
38
 
 
39
 
 
40
class VersionedFileStore(TransportStore):
 
41
    """Collection of many versioned files in a transport."""
 
42
 
 
43
    # TODO: Rather than passing versionedfile_kwargs, perhaps pass in a
 
44
    # transport factory callable?
 
45
    def __init__(self, transport, prefixed=False, precious=False,
 
46
                 dir_mode=None, file_mode=None,
 
47
                 versionedfile_class=WeaveFile,
 
48
                 versionedfile_kwargs={},
 
49
                 escaped=False):
 
50
        super(VersionedFileStore, self).__init__(transport,
 
51
                dir_mode=dir_mode, file_mode=file_mode,
 
52
                prefixed=prefixed, compressed=False, escaped=escaped)
 
53
        self._precious = precious
 
54
        self._versionedfile_class = versionedfile_class
 
55
        self._versionedfile_kwargs = versionedfile_kwargs
 
56
        # Used for passing get_scope to versioned file constructors;
 
57
        self.get_scope = None
 
58
 
 
59
    def filename(self, file_id):
 
60
        """Return the path relative to the transport root."""
 
61
        return self._relpath(file_id)
 
62
 
 
63
    def __iter__(self):
 
64
        suffixes = self._versionedfile_class.get_suffixes()
 
65
        ids = set()
 
66
        for relpath in self._iter_files_recursive():
 
67
            for suffix in suffixes:
 
68
                if relpath.endswith(suffix):
 
69
                    # TODO: use standard remove_suffix function
 
70
                    escaped_id = os.path.basename(relpath[:-len(suffix)])
 
71
                    file_id = self._mapper.unmap(escaped_id)[0]
 
72
                    if file_id not in ids:
 
73
                        ids.add(file_id)
 
74
                        yield file_id
 
75
                    break # only one suffix can match
 
76
 
 
77
    def has_id(self, file_id):
 
78
        suffixes = self._versionedfile_class.get_suffixes()
 
79
        filename = self.filename(file_id)
 
80
        for suffix in suffixes:
 
81
            if not self._transport.has(filename + suffix):
 
82
                return False
 
83
        return True
 
84
 
 
85
    def get_empty(self, file_id, transaction):
 
86
        """Get an empty weave, which implies deleting the existing one first."""
 
87
        if self.has_id(file_id):
 
88
            self.delete(file_id, transaction)
 
89
        return self.get_weave_or_empty(file_id, transaction)
 
90
 
 
91
    def delete(self, file_id, transaction):
 
92
        """Remove file_id from the store."""
 
93
        suffixes = self._versionedfile_class.get_suffixes()
 
94
        filename = self.filename(file_id)
 
95
        for suffix in suffixes:
 
96
            self._transport.delete(filename + suffix)
 
97
 
 
98
    def _get(self, file_id):
 
99
        return self._transport.get(self.filename(file_id))
 
100
 
 
101
    def _put(self, file_id, f):
 
102
        fn = self.filename(file_id)
 
103
        try:
 
104
            return self._transport.put_file(fn, f, mode=self._file_mode)
 
105
        except errors.NoSuchFile:
 
106
            if not self._prefixed:
 
107
                raise
 
108
            self._transport.mkdir(os.path.dirname(fn), mode=self._dir_mode)
 
109
            return self._transport.put_file(fn, f, mode=self._file_mode)
 
110
 
 
111
    def get_weave(self, file_id, transaction, _filename=None):
 
112
        """Return the VersionedFile for file_id.
 
113
 
 
114
        :param _filename: filename that would be returned from self.filename for
 
115
        file_id. This is used to reduce duplicate filename calculations when
 
116
        using 'get_weave_or_empty'. FOR INTERNAL USE ONLY.
 
117
        """
 
118
        if _filename is None:
 
119
            _filename = self.filename(file_id)
 
120
        if transaction.writeable():
 
121
            w = self._versionedfile_class(_filename, self._transport, self._file_mode,
 
122
                get_scope=self.get_scope, **self._versionedfile_kwargs)
 
123
        else:
 
124
            w = self._versionedfile_class(_filename,
 
125
                                          self._transport,
 
126
                                          self._file_mode,
 
127
                                          create=False,
 
128
                                          access_mode='r',
 
129
                                          get_scope=self.get_scope,
 
130
                                          **self._versionedfile_kwargs)
 
131
        return w
 
132
 
 
133
    def _make_new_versionedfile(self, file_id, transaction,
 
134
        known_missing=False, _filename=None):
 
135
        """Make a new versioned file.
 
136
 
 
137
        :param _filename: filename that would be returned from self.filename for
 
138
        file_id. This is used to reduce duplicate filename calculations when
 
139
        using 'get_weave_or_empty'. FOR INTERNAL USE ONLY.
 
140
        """
 
141
        if not known_missing and self.has_id(file_id):
 
142
            self.delete(file_id, transaction)
 
143
        if _filename is None:
 
144
            _filename = self.filename(file_id)
 
145
        try:
 
146
            # we try without making the directory first because thats optimising
 
147
            # for the common case.
 
148
            weave = self._versionedfile_class(_filename, self._transport, self._file_mode, create=True,
 
149
                get_scope=self.get_scope, **self._versionedfile_kwargs)
 
150
        except errors.NoSuchFile:
 
151
            if not self._prefixed:
 
152
                # unexpected error - NoSuchFile is expected to be raised on a
 
153
                # missing dir only and that only occurs when we are prefixed.
 
154
                raise
 
155
            dirname = osutils.dirname(_filename)
 
156
            self._transport.mkdir(dirname, mode=self._dir_mode)
 
157
            weave = self._versionedfile_class(_filename, self._transport,
 
158
                                              self._file_mode, create=True,
 
159
                                              get_scope=self.get_scope,
 
160
                                              **self._versionedfile_kwargs)
 
161
        return weave
 
162
 
 
163
    def get_weave_or_empty(self, file_id, transaction):
 
164
        """Return a weave, or an empty one if it doesn't exist."""
 
165
        # This is typically used from 'commit' and 'fetch/push/pull' where
 
166
        # we scan across many versioned files once. As such the small overhead
 
167
        # of calculating the filename before doing a cache lookup is more than
 
168
        # compensated for by not calculating the filename when making new
 
169
        # versioned files.
 
170
        _filename = self.filename(file_id)
 
171
        try:
 
172
            return self.get_weave(file_id, transaction, _filename=_filename)
 
173
        except errors.NoSuchFile:
 
174
            weave = self._make_new_versionedfile(file_id, transaction,
 
175
                known_missing=True, _filename=_filename)
 
176
            return weave
 
177
 
 
178
    def _put_weave(self, file_id, weave, transaction):
 
179
        """Preserved here for upgrades-to-weaves to use."""
 
180
        myweave = self._make_new_versionedfile(file_id, transaction)
 
181
        myweave.insert_record_stream(weave.get_record_stream(
 
182
            [(version,) for version in weave.versions()],
 
183
            'topological', False))
 
184
 
 
185
    def copy_all_ids(self, store_from, pb=None, from_transaction=None,
 
186
                     to_transaction=None):
 
187
        """Copy all the file ids from store_from into self."""
 
188
        if from_transaction is None:
 
189
            warn("Please pass from_transaction into "
 
190
                 "versioned_store.copy_all_ids.", stacklevel=2)
 
191
        if to_transaction is None:
 
192
            warn("Please pass to_transaction into "
 
193
                 "versioned_store.copy_all_ids.", stacklevel=2)
 
194
        if not store_from.listable():
 
195
            raise errors.UnlistableStore(store_from)
 
196
        ids = []
 
197
        for count, file_id in enumerate(store_from):
 
198
            if pb:
 
199
                pb.update('listing files', count, count)
 
200
            ids.append(file_id)
 
201
        if pb:
 
202
            pb.clear()
 
203
        mutter('copy_all ids: %r', ids)
 
204
        self.copy_multi(store_from, ids, pb=pb,
 
205
                        from_transaction=from_transaction,
 
206
                        to_transaction=to_transaction)
 
207
 
 
208
    def copy_multi(self, from_store, file_ids, pb=None, from_transaction=None,
 
209
                   to_transaction=None):
 
210
        """Copy all the versions for multiple file_ids from from_store.
 
211
 
 
212
        :param from_transaction: required current transaction in from_store.
 
213
        """
 
214
        from bzrlib.transactions import PassThroughTransaction
 
215
        if from_transaction is None:
 
216
            warn("WeaveStore.copy_multi without a from_transaction parameter "
 
217
                 "is deprecated. Please provide a from_transaction.",
 
218
                 DeprecationWarning,
 
219
                 stacklevel=2)
 
220
            # we are reading one object - caching is irrelevant.
 
221
            from_transaction = PassThroughTransaction()
 
222
        if to_transaction is None:
 
223
            warn("WeaveStore.copy_multi without a to_transaction parameter "
 
224
                 "is deprecated. Please provide a to_transaction.",
 
225
                 DeprecationWarning,
 
226
                 stacklevel=2)
 
227
            # we are copying single objects, and there may be open tranasactions
 
228
            # so again with the passthrough
 
229
            to_transaction = PassThroughTransaction()
 
230
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
231
        try:
 
232
            for count, f in enumerate(file_ids):
 
233
                mutter("copy weave {%s} into %s", f, self)
 
234
                pb.update('copy', count, len(file_ids))
 
235
                # if we have it in cache, its faster.
 
236
                # joining is fast with knits, and bearable for weaves -
 
237
                # indeed the new case can be optimised if needed.
 
238
                target = self._make_new_versionedfile(f, to_transaction)
 
239
                source = from_store.get_weave(f, from_transaction)
 
240
                target.insert_record_stream(source.get_record_stream(
 
241
                    [(version,) for version in source.versions()],
 
242
                    'topological', False))
 
243
        finally:
 
244
            pb.finished()
 
245
 
 
246
    def total_size(self):
 
247
        count, bytes =  super(VersionedFileStore, self).total_size()
 
248
        return (count / len(self._versionedfile_class.get_suffixes())), bytes
 
249
 
 
250
WeaveStore = VersionedFileStore