~bzr-pqm/bzr/bzr.dev

2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
1
# Copyright (C) 2005, 2006 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1 by mbp at sourcefrog
import from baz patch-364
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1 by mbp at sourcefrog
import from baz patch-364
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1 by mbp at sourcefrog
import from baz patch-364
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
1374 by Martin Pool
todo
17
# TODO: Could remember a bias towards whether a particular store is typically
18
# compressed or not.
19
711 by Martin Pool
- store docs
20
"""
1861.2.6 by Alexander Belchenko
branding: change Bazaar-NG to Bazaar
21
Stores are the main data-storage mechanism for Bazaar.
1 by mbp at sourcefrog
import from baz patch-364
22
23
A store is a simple write-once container indexed by a universally
711 by Martin Pool
- store docs
24
unique ID.
25
"""
1 by mbp at sourcefrog
import from baz patch-364
26
1442.1.51 by Robert Collins
teach iter about suffixes
27
import os
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
28
from cStringIO import StringIO
1479 by Robert Collins
More quoting at the transport layer bugfixes.
29
import urllib
1429 by Robert Collins
merge in niemeyers prefixed-store patch
30
from zlib import adler32
1185.1.41 by Robert Collins
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid
31
1442.1.44 by Robert Collins
Many transport related tweaks:
32
import bzrlib
1955.3.13 by John Arbash Meinel
Run the full test suite, and fix up any deprecation warnings.
33
from bzrlib import (
34
    errors,
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
35
    osutils,
1955.3.13 by John Arbash Meinel
Run the full test suite, and fix up any deprecation warnings.
36
    symbol_versioning,
37
    urlutils,
38
    )
1393.2.3 by John Arbash Meinel
Fixing typos, updating stores, getting tests to pass.
39
from bzrlib.errors import BzrError, UnlistableStore, TransportNotPossible
1955.3.13 by John Arbash Meinel
Run the full test suite, and fix up any deprecation warnings.
40
from bzrlib.symbol_versioning import (
41
    deprecated_function,
42
    zero_eight,
43
    zero_eleven,
44
    )
1104 by Martin Pool
- Add a simple UIFactory
45
from bzrlib.trace import mutter
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
46
from bzrlib.transport import Transport
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
47
from bzrlib.transport.local import LocalTransport
1 by mbp at sourcefrog
import from baz patch-364
48
49
######################################################################
50
# stores
51
52
class StoreError(Exception):
53
    pass
54
55
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
56
class Store(object):
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
57
    """This class represents the abstract storage layout for saving information.
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
58
    
1 by mbp at sourcefrog
import from baz patch-364
59
    Files can be added, but not modified once they are in.  Typically
60
    the hash is used as the name, or something else known to be unique,
61
    such as a UUID.
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
62
    """
63
64
    def __len__(self):
65
        raise NotImplementedError('Children should define their length')
66
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
67
    def get(self, fileid, suffix=None):
1442.1.50 by Robert Collins
test get with suffixes
68
        """Returns a file reading from a particular entry.
69
        
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
70
        If suffix is present, retrieve the named suffix for fileid.
1442.1.50 by Robert Collins
test get with suffixes
71
        """
72
        raise NotImplementedError
1442.1.35 by Robert Collins
convert all users of __getitem__ into TransportStores to use .get instead
73
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
74
    def __getitem__(self, fileid):
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
75
        """DEPRECATED. Please use .get(fileid) instead."""
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
76
        raise NotImplementedError
77
1442.1.45 by Robert Collins
replace __contains__ calls in stores with has_id
78
    #def __contains__(self, fileid):
79
    #    """Deprecated, please use has_id"""
80
    #    raise NotImplementedError
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
81
82
    def __iter__(self):
83
        raise NotImplementedError
84
907.1.43 by John Arbash Meinel
Restoring compatibility for Storage.add(file, fileid), it is a little arbitrary, and compatibility is better
85
    def add(self, f, fileid):
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
86
        """Add a file object f to the store accessible from the given fileid"""
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
87
        raise NotImplementedError('Children of Store must define their method of adding entries.')
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
88
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
89
    def has_id(self, fileid, suffix=None):
90
        """Return True or false for the presence of fileid in the store.
1442.1.47 by Robert Collins
test for has with suffixed files
91
        
92
        suffix, if present, is a per file suffix, i.e. for digital signature 
93
        data."""
1442.1.45 by Robert Collins
replace __contains__ calls in stores with has_id
94
        raise NotImplementedError
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
95
1400.1.1 by Robert Collins
implement a basic test for the ui branch command from http servers
96
    def listable(self):
97
        """Return True if this store is able to be listed."""
1963.2.6 by Robey Pointer
pychecker is on crack; go back to using 'is None'.
98
        return (getattr(self, "__iter__", None) is not None)
1400.1.1 by Robert Collins
implement a basic test for the ui branch command from http servers
99
1563.2.14 by Robert Collins
Prepare weave store to delegate copy details to the versioned file.
100
    def copy_all_ids(self, store_from, pb=None):
101
        """Copy all the file ids from store_from into self."""
102
        if not store_from.listable():
103
            raise UnlistableStore(store_from)
104
        ids = []
105
        for count, file_id in enumerate(store_from):
106
            if pb:
107
                pb.update('listing files', count, count)
108
            ids.append(file_id)
109
        if pb:
110
            pb.clear()
111
        mutter('copy_all ids: %r', ids)
112
        self.copy_multi(store_from, ids, pb=pb)
113
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
114
    def copy_multi(self, other, ids, pb=None, permit_failure=False):
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
115
        """Copy texts for ids from other into self.
116
117
        If an id is present in self, it is skipped.  A count of copied
118
        ids is returned, which may be less than len(ids).
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
119
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
120
        :param other: Another Store object
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
121
        :param ids: A list of entry ids to be copied
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
122
        :param pb: A ProgressBar object, if none is given, the default will be created.
123
        :param permit_failure: Allow missing entries to be ignored
124
        :return: (n_copied, [failed]) The number of entries copied successfully,
125
            followed by a list of entries which could not be copied (because they
126
            were missing)
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
127
        """
1185.79.2 by John Arbash Meinel
Adding progress bars to copy_all and copy_multi, fixing ordering of repository.clone() to pull inventories after weaves.
128
        if pb:
129
            pb.update('preparing to copy')
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
130
        failed = set()
1442.1.53 by Robert Collins
Unroll the multiple-copy logic enough to remove the duplicate iteration and yet retain the optimised gzip->gzip copy.
131
        count = 0
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
132
        ids = [osutils.safe_file_id(i) for i in ids] # get the list for showing a length.
1442.1.53 by Robert Collins
Unroll the multiple-copy logic enough to remove the duplicate iteration and yet retain the optimised gzip->gzip copy.
133
        for fileid in ids:
134
            count += 1
135
            if self.has_id(fileid):
136
                continue
137
            try:
1442.1.54 by Robert Collins
Teach store.copy_all about fileid suffixes
138
                self._copy_one(fileid, None, other, pb)
139
                for suffix in self._suffixes:
140
                    try:
141
                        self._copy_one(fileid, suffix, other, pb)
142
                    except KeyError:
143
                        pass
1185.79.2 by John Arbash Meinel
Adding progress bars to copy_all and copy_multi, fixing ordering of repository.clone() to pull inventories after weaves.
144
                if pb:
145
                    pb.update('copy', count, len(ids))
1442.1.53 by Robert Collins
Unroll the multiple-copy logic enough to remove the duplicate iteration and yet retain the optimised gzip->gzip copy.
146
            except KeyError:
147
                if permit_failure:
148
                    failed.add(fileid)
149
                else:
150
                    raise
151
        assert count == len(ids)
1185.79.2 by John Arbash Meinel
Adding progress bars to copy_all and copy_multi, fixing ordering of repository.clone() to pull inventories after weaves.
152
        if pb:
153
            pb.clear()
1442.1.53 by Robert Collins
Unroll the multiple-copy logic enough to remove the duplicate iteration and yet retain the optimised gzip->gzip copy.
154
        return count, failed
155
1442.1.54 by Robert Collins
Teach store.copy_all about fileid suffixes
156
    def _copy_one(self, fileid, suffix, other, pb):
1442.1.53 by Robert Collins
Unroll the multiple-copy logic enough to remove the duplicate iteration and yet retain the optimised gzip->gzip copy.
157
        """Most generic copy-one object routine.
158
        
159
        Subclasses can override this to provide an optimised
160
        copy between their own instances. Such overriden routines
161
        should call this if they have no optimised facility for a 
162
        specific 'other'.
163
        """
1185.16.159 by John Arbash Meinel
Updated the stores, all tests pass, and a store doesn't have to be 100% compressed
164
        mutter('Store._copy_one: %r', fileid)
1442.1.54 by Robert Collins
Teach store.copy_all about fileid suffixes
165
        f = other.get(fileid, suffix)
166
        self.add(f, fileid, suffix)
1185.10.1 by Aaron Bentley
Added --basis option to bzr branch
167
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
168
169
class TransportStore(Store):
170
    """A TransportStore is a Store superclass for Stores that use Transports."""
171
1442.1.33 by Robert Collins
teach TransportStore.add to accept an optional file suffix, which does not alter the fileid.
172
    def add(self, f, fileid, suffix=None):
1442.1.28 by Robert Collins
pull up core TransportStore.add from TextStore.add and CompressedTextStore.add
173
        """Add contents of a file into the store.
174
1955.3.13 by John Arbash Meinel
Run the full test suite, and fix up any deprecation warnings.
175
        f -- A file-like object
1442.1.28 by Robert Collins
pull up core TransportStore.add from TextStore.add and CompressedTextStore.add
176
        """
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
177
        fileid = osutils.safe_file_id(fileid)
1185.16.159 by John Arbash Meinel
Updated the stores, all tests pass, and a store doesn't have to be 100% compressed
178
        mutter("add store entry %r", fileid)
1955.3.13 by John Arbash Meinel
Run the full test suite, and fix up any deprecation warnings.
179
        if isinstance(f, str):
180
            symbol_versioning.warn(zero_eleven % 'Passing a string to Store.add',
181
                DeprecationWarning, stacklevel=2)
182
            f = StringIO(f)
1442.1.33 by Robert Collins
teach TransportStore.add to accept an optional file suffix, which does not alter the fileid.
183
        
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
184
        names = self._id_to_names(fileid, suffix)
185
        if self._transport.has_any(names):
186
            raise BzrError("store %r already contains id %r" 
187
                           % (self._transport.base, fileid))
1442.1.28 by Robert Collins
pull up core TransportStore.add from TextStore.add and CompressedTextStore.add
188
1185.16.159 by John Arbash Meinel
Updated the stores, all tests pass, and a store doesn't have to be 100% compressed
189
        # Most of the time, just adding the file will work
190
        # if we find a time where it fails, (because the dir
191
        # doesn't exist), then create the dir, and try again
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
192
        self._add(names[0], f)
193
194
    def _add(self, relpath, f):
195
        """Actually add the file to the given location.
196
        This should be overridden by children.
197
        """
198
        raise NotImplementedError('children need to implement this function.')
1442.1.28 by Robert Collins
pull up core TransportStore.add from TextStore.add and CompressedTextStore.add
199
1442.1.24 by Robert Collins
Pull up _check_id and _relpath from Text and CompressedText stores into TransportStore
200
    def _check_fileid(self, fileid):
201
        if not isinstance(fileid, basestring):
202
            raise TypeError('Fileids should be a string type: %s %r' % (type(fileid), fileid))
203
        if '\\' in fileid or '/' in fileid:
204
            raise ValueError("invalid store id %r" % fileid)
205
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
206
    def _id_to_names(self, fileid, suffix):
207
        """Return the names in the expected order"""
1442.1.47 by Robert Collins
test for has with suffixed files
208
        if suffix is not None:
209
            fn = self._relpath(fileid, [suffix])
210
        else:
211
            fn = self._relpath(fileid)
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
212
1185.65.13 by Robert Collins
Merge from integration
213
        # FIXME RBC 20051128 this belongs in TextStore.
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
214
        fn_gz = fn + '.gz'
215
        if self._compressed:
216
            return fn_gz, fn
217
        else:
218
            return fn, fn_gz
219
220
    def has_id(self, fileid, suffix=None):
221
        """See Store.has_id."""
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
222
        fileid = osutils.safe_file_id(fileid)
1651.1.5 by Martin Pool
Review cleanup of TransportStore.has_id
223
        return self._transport.has_any(self._id_to_names(fileid, suffix))
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
224
225
    def _get_name(self, fileid, suffix=None):
226
        """A special check, which returns the name of an existing file.
227
        
228
        This is similar in spirit to 'has_id', but it is designed
229
        to return information about which file the store has.
230
        """
231
        for name in self._id_to_names(fileid, suffix=suffix):
232
            if self._transport.has(name):
233
                return name
234
        return None
1442.1.38 by Robert Collins
unify __contains__ for TransportStore classes
235
1442.1.36 by Robert Collins
convert get() in TextStore and CompressedTextStore into a template method
236
    def _get(self, filename):
237
        """Return an vanilla file stream for clients to read from.
238
239
        This is the body of a template method on 'get', and should be 
240
        implemented by subclasses.
241
        """
242
        raise NotImplementedError
243
1442.1.50 by Robert Collins
test get with suffixes
244
    def get(self, fileid, suffix=None):
245
        """See Store.get()."""
2294.1.10 by John Arbash Meinel
Switch all apis over to utf8 file ids. All tests pass
246
        fileid = osutils.safe_file_id(fileid)
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
247
        names = self._id_to_names(fileid, suffix)
248
        for name in names:
249
            try:
250
                return self._get(name)
251
            except errors.NoSuchFile:
252
                pass
253
        raise KeyError(fileid)
1433 by Robert Collins
merge in and make incremental Gustavo Niemeyers nested log patch, and remove all bare exceptions in store and transport packages.
254
1185.58.4 by John Arbash Meinel
Added permission checking to Branch, and propogated that change into the stores.
255
    def __init__(self, a_transport, prefixed=False, compressed=False,
1185.80.1 by John Arbash Meinel
Text store and weave store both allow escaping fileid paths.
256
                 dir_mode=None, file_mode=None,
257
                 escaped=False):
1651.1.7 by Martin Pool
Move small ImmutableMemoryStore class into test module,
258
        assert isinstance(a_transport, Transport)
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
259
        super(TransportStore, self).__init__()
1442.1.44 by Robert Collins
Many transport related tweaks:
260
        self._transport = a_transport
1442.1.25 by Robert Collins
Test TransportStore._relpath for simple cases: pull up _prefixed attribute as a result.
261
        self._prefixed = prefixed
1185.65.13 by Robert Collins
Merge from integration
262
        # FIXME RBC 20051128 this belongs in TextStore.
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
263
        self._compressed = compressed
1442.1.43 by Robert Collins
add registration of suffixes, in preparation for ensuring iteration is regular
264
        self._suffixes = set()
1185.80.1 by John Arbash Meinel
Text store and weave store both allow escaping fileid paths.
265
        self._escaped = escaped
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
266
1185.58.6 by John Arbash Meinel
Stores don't have to have a dir_mode or file_mode set
267
        # It is okay for these to be None, it just means they
268
        # will just use the filesystem defaults
269
        self._dir_mode = dir_mode
270
        self._file_mode = file_mode
1185.58.4 by John Arbash Meinel
Added permission checking to Branch, and propogated that change into the stores.
271
1608.2.1 by Martin Pool
[merge] Storage filename escaping
272
    def _unescape(self, file_id):
273
        """If filename escaping is enabled for this store, unescape and return the filename."""
274
        if self._escaped:
275
            return urllib.unquote(file_id)
276
        else:
277
            return file_id
278
1479 by Robert Collins
More quoting at the transport layer bugfixes.
279
    def _iter_files_recursive(self):
280
        """Iterate through the files in the transport."""
281
        for quoted_relpath in self._transport.iter_files_recursive():
1608.2.1 by Martin Pool
[merge] Storage filename escaping
282
            # transport iterator always returns quoted paths, regardless of
283
            # escaping
1479 by Robert Collins
More quoting at the transport layer bugfixes.
284
            yield urllib.unquote(quoted_relpath)
285
1442.1.51 by Robert Collins
teach iter about suffixes
286
    def __iter__(self):
1479 by Robert Collins
More quoting at the transport layer bugfixes.
287
        for relpath in self._iter_files_recursive():
1442.1.51 by Robert Collins
teach iter about suffixes
288
            # worst case is one of each suffix.
289
            name = os.path.basename(relpath)
290
            if name.endswith('.gz'):
291
                name = name[:-3]
292
            skip = False
293
            for count in range(len(self._suffixes)):
294
                for suffix in self._suffixes:
295
                    if name.endswith('.' + suffix):
296
                        skip = True
297
            if not skip:
1608.2.1 by Martin Pool
[merge] Storage filename escaping
298
                yield self._unescape(name)
1442.1.51 by Robert Collins
teach iter about suffixes
299
1442.1.40 by Robert Collins
unify __len__() implementations for TransportStore classes
300
    def __len__(self):
1442.1.50 by Robert Collins
test get with suffixes
301
        return len(list(self.__iter__()))
1442.1.40 by Robert Collins
unify __len__() implementations for TransportStore classes
302
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
303
    def _relpath(self, fileid, suffixes=None):
1442.1.24 by Robert Collins
Pull up _check_id and _relpath from Text and CompressedText stores into TransportStore
304
        self._check_fileid(fileid)
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
305
        if suffixes:
306
            for suffix in suffixes:
307
                if not suffix in self._suffixes:
308
                    raise ValueError("Unregistered suffix %r" % suffix)
309
                self._check_fileid(suffix)
310
        else:
311
            suffixes = []
1725.2.2 by Robert Collins
reduce file path escaping calls during commit.
312
        fileid = self._escape_file_id(fileid)
1442.1.24 by Robert Collins
Pull up _check_id and _relpath from Text and CompressedText stores into TransportStore
313
        if self._prefixed:
1185.80.5 by John Arbash Meinel
Changing the escaping just a little bit. Now we can handle unicode characters.
314
            # hash_prefix adds the '/' separator
1725.2.2 by Robert Collins
reduce file path escaping calls during commit.
315
            prefix = self.hash_prefix(fileid, escaped=True)
1442.1.24 by Robert Collins
Pull up _check_id and _relpath from Text and CompressedText stores into TransportStore
316
        else:
1608.2.1 by Martin Pool
[merge] Storage filename escaping
317
            prefix = ''
318
        path = prefix + fileid
1185.80.5 by John Arbash Meinel
Changing the escaping just a little bit. Now we can handle unicode characters.
319
        full_path = u'.'.join([path] + suffixes)
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
320
        return urlutils.escape(full_path)
1442.1.24 by Robert Collins
Pull up _check_id and _relpath from Text and CompressedText stores into TransportStore
321
1608.2.1 by Martin Pool
[merge] Storage filename escaping
322
    def _escape_file_id(self, file_id):
323
        """Turn a file id into a filesystem safe string.
324
325
        This is similar to a plain urllib.quote, except
326
        it uses specific safe characters, so that it doesn't
327
        have to translate a lot of valid file ids.
328
        """
329
        if not self._escaped:
330
            return file_id
1608.2.12 by Martin Pool
Store-escaping must quote uppercase characters too, so that they're safely
331
        if isinstance(file_id, unicode):
332
            file_id = file_id.encode('utf-8')
1608.2.1 by Martin Pool
[merge] Storage filename escaping
333
        # @ does not get escaped. This is because it is a valid
334
        # filesystem character we use all the time, and it looks
335
        # a lot better than seeing %40 all the time.
1608.2.12 by Martin Pool
Store-escaping must quote uppercase characters too, so that they're safely
336
        safe = "abcdefghijklmnopqrstuvwxyz0123456789-_@,."
337
        r = [((c in safe) and c or ('%%%02x' % ord(c)))
338
             for c in file_id]
339
        return ''.join(r)
1608.2.1 by Martin Pool
[merge] Storage filename escaping
340
1725.2.2 by Robert Collins
reduce file path escaping calls during commit.
341
    def hash_prefix(self, fileid, escaped=False):
1608.2.1 by Martin Pool
[merge] Storage filename escaping
342
        # fileid should be unescaped
1725.2.2 by Robert Collins
reduce file path escaping calls during commit.
343
        if not escaped and self._escaped:
1608.2.1 by Martin Pool
[merge] Storage filename escaping
344
            fileid = self._escape_file_id(fileid)
345
        return "%02x/" % (adler32(fileid) & 0xff)
1442.1.24 by Robert Collins
Pull up _check_id and _relpath from Text and CompressedText stores into TransportStore
346
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
347
    def __repr__(self):
348
        if self._transport is None:
349
            return "%s(None)" % (self.__class__.__name__)
350
        else:
351
            return "%s(%r)" % (self.__class__.__name__, self._transport.base)
352
353
    __str__ = __repr__
1185.10.1 by Aaron Bentley
Added --basis option to bzr branch
354
1400.1.1 by Robert Collins
implement a basic test for the ui branch command from http servers
355
    def listable(self):
356
        """Return True if this store is able to be listed."""
357
        return self._transport.listable()
358
1442.1.43 by Robert Collins
add registration of suffixes, in preparation for ensuring iteration is regular
359
    def register_suffix(self, suffix):
360
        """Register a suffix as being expected in this store."""
361
        self._check_fileid(suffix)
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
362
        if suffix == 'gz':
363
            raise ValueError('You cannot register the "gz" suffix.')
1442.1.43 by Robert Collins
add registration of suffixes, in preparation for ensuring iteration is regular
364
        self._suffixes.add(suffix)
365
1442.1.37 by Robert Collins
pull up total_size into TransportStore
366
    def total_size(self):
367
        """Return (count, bytes)
368
369
        This is the (compressed) size stored on disk, not the size of
370
        the content."""
371
        total = 0
372
        count = 0
1442.1.44 by Robert Collins
Many transport related tweaks:
373
        for relpath in self._transport.iter_files_recursive():
1442.1.37 by Robert Collins
pull up total_size into TransportStore
374
            count += 1
1442.1.44 by Robert Collins
Many transport related tweaks:
375
            total += self._transport.stat(relpath).st_size
1442.1.37 by Robert Collins
pull up total_size into TransportStore
376
                
377
        return count, total
378
1092.2.1 by Robert Collins
minor refactors to store, create an ImmutableMemoryStore for testing or other such operations
379
1563.2.14 by Robert Collins
Prepare weave store to delegate copy details to the versioned file.
380
@deprecated_function(zero_eight)
1185.79.2 by John Arbash Meinel
Adding progress bars to copy_all and copy_multi, fixing ordering of repository.clone() to pull inventories after weaves.
381
def copy_all(store_from, store_to, pb=None):
1185.10.1 by Aaron Bentley
Added --basis option to bzr branch
382
    """Copy all ids from one store to another."""
1563.2.14 by Robert Collins
Prepare weave store to delegate copy details to the versioned file.
383
    store_to.copy_all_ids(store_from, pb)