~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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1 by mbp at sourcefrog
import from baz patch-364
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
6379.6.7 by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear.
27
from __future__ import absolute_import
28
1442.1.51 by Robert Collins
teach iter about suffixes
29
import os
1185.1.41 by Robert Collins
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid
30
1955.3.13 by John Arbash Meinel
Run the full test suite, and fix up any deprecation warnings.
31
from bzrlib import (
32
    errors,
3350.6.1 by Robert Collins
* New ``versionedfile.KeyMapper`` interface to abstract out the access to
33
    versionedfile,
1955.3.13 by John Arbash Meinel
Run the full test suite, and fix up any deprecation warnings.
34
    )
3224.5.26 by Andrew Bennetts
More minor import tidying suggested by pyflakes.
35
from bzrlib.errors import BzrError, UnlistableStore
1104 by Martin Pool
- Add a simple UIFactory
36
from bzrlib.trace import mutter
1 by mbp at sourcefrog
import from baz patch-364
37
38
######################################################################
39
# stores
40
41
class StoreError(Exception):
42
    pass
43
44
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
45
class Store(object):
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
46
    """This class represents the abstract storage layout for saving information.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
47
1 by mbp at sourcefrog
import from baz patch-364
48
    Files can be added, but not modified once they are in.  Typically
49
    the hash is used as the name, or something else known to be unique,
50
    such as a UUID.
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
51
    """
52
53
    def __len__(self):
54
        raise NotImplementedError('Children should define their length')
55
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
56
    def get(self, fileid, suffix=None):
1442.1.50 by Robert Collins
test get with suffixes
57
        """Returns a file reading from a particular entry.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
58
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
59
        If suffix is present, retrieve the named suffix for fileid.
1442.1.50 by Robert Collins
test get with suffixes
60
        """
61
        raise NotImplementedError
1442.1.35 by Robert Collins
convert all users of __getitem__ into TransportStores to use .get instead
62
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
63
    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
64
        """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.
65
        raise NotImplementedError
66
67
    def __iter__(self):
68
        raise NotImplementedError
69
907.1.43 by John Arbash Meinel
Restoring compatibility for Storage.add(file, fileid), it is a little arbitrary, and compatibility is better
70
    def add(self, f, fileid):
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
71
        """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.
72
        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.
73
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
74
    def has_id(self, fileid, suffix=None):
75
        """Return True or false for the presence of fileid in the store.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
76
77
        suffix, if present, is a per file suffix, i.e. for digital signature
1442.1.47 by Robert Collins
test for has with suffixed files
78
        data."""
1442.1.45 by Robert Collins
replace __contains__ calls in stores with has_id
79
        raise NotImplementedError
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
80
1400.1.1 by Robert Collins
implement a basic test for the ui branch command from http servers
81
    def listable(self):
82
        """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'.
83
        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
84
1563.2.14 by Robert Collins
Prepare weave store to delegate copy details to the versioned file.
85
    def copy_all_ids(self, store_from, pb=None):
86
        """Copy all the file ids from store_from into self."""
87
        if not store_from.listable():
88
            raise UnlistableStore(store_from)
89
        ids = []
90
        for count, file_id in enumerate(store_from):
91
            if pb:
92
                pb.update('listing files', count, count)
93
            ids.append(file_id)
94
        if pb:
95
            pb.clear()
96
        mutter('copy_all ids: %r', ids)
97
        self.copy_multi(store_from, ids, pb=pb)
98
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
99
    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.
100
        """Copy texts for ids from other into self.
101
102
        If an id is present in self, it is skipped.  A count of copied
103
        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.
104
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
105
        :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.
106
        :param ids: A list of entry ids to be copied
4463.1.1 by Martin Pool
Update docstrings for recent progress changes
107
        :param pb: A ProgressTask object, if none is given, the default will be created.
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
108
        :param permit_failure: Allow missing entries to be ignored
109
        :return: (n_copied, [failed]) The number of entries copied successfully,
110
            followed by a list of entries which could not be copied (because they
111
            were missing)
907.1.1 by John Arbash Meinel
Reworking the Branch and Store code to support an abstracted filesystem layer.
112
        """
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.
113
        if pb:
114
            pb.update('preparing to copy')
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
115
        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.
116
        count = 0
117
        for fileid in ids:
118
            count += 1
119
            if self.has_id(fileid):
120
                continue
121
            try:
1442.1.54 by Robert Collins
Teach store.copy_all about fileid suffixes
122
                self._copy_one(fileid, None, other, pb)
123
                for suffix in self._suffixes:
124
                    try:
125
                        self._copy_one(fileid, suffix, other, pb)
126
                    except KeyError:
127
                        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.
128
                if pb:
129
                    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.
130
            except KeyError:
131
                if permit_failure:
132
                    failed.add(fileid)
133
                else:
134
                    raise
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.
135
        if pb:
136
            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.
137
        return count, failed
138
1442.1.54 by Robert Collins
Teach store.copy_all about fileid suffixes
139
    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.
140
        """Most generic copy-one object routine.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
141
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.
142
        Subclasses can override this to provide an optimised
143
        copy between their own instances. Such overriden routines
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
144
        should call this if they have no optimised facility for a
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.
145
        specific 'other'.
146
        """
1185.16.159 by John Arbash Meinel
Updated the stores, all tests pass, and a store doesn't have to be 100% compressed
147
        mutter('Store._copy_one: %r', fileid)
1442.1.54 by Robert Collins
Teach store.copy_all about fileid suffixes
148
        f = other.get(fileid, suffix)
149
        self.add(f, fileid, suffix)
1185.10.1 by Aaron Bentley
Added --basis option to bzr branch
150
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
151
152
class TransportStore(Store):
153
    """A TransportStore is a Store superclass for Stores that use Transports."""
154
1442.1.33 by Robert Collins
teach TransportStore.add to accept an optional file suffix, which does not alter the fileid.
155
    def add(self, f, fileid, suffix=None):
1442.1.28 by Robert Collins
pull up core TransportStore.add from TextStore.add and CompressedTextStore.add
156
        """Add contents of a file into the store.
157
1955.3.13 by John Arbash Meinel
Run the full test suite, and fix up any deprecation warnings.
158
        f -- A file-like object
1442.1.28 by Robert Collins
pull up core TransportStore.add from TextStore.add and CompressedTextStore.add
159
        """
1185.16.159 by John Arbash Meinel
Updated the stores, all tests pass, and a store doesn't have to be 100% compressed
160
        mutter("add store entry %r", 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
161
        names = self._id_to_names(fileid, suffix)
162
        if self._transport.has_any(names):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
163
            raise BzrError("store %r already contains id %r"
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
164
                           % (self._transport.base, fileid))
1442.1.28 by Robert Collins
pull up core TransportStore.add from TextStore.add and CompressedTextStore.add
165
1185.16.159 by John Arbash Meinel
Updated the stores, all tests pass, and a store doesn't have to be 100% compressed
166
        # Most of the time, just adding the file will work
167
        # if we find a time where it fails, (because the dir
168
        # 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
169
        self._add(names[0], f)
170
171
    def _add(self, relpath, f):
172
        """Actually add the file to the given location.
173
        This should be overridden by children.
174
        """
175
        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
176
1442.1.24 by Robert Collins
Pull up _check_id and _relpath from Text and CompressedText stores into TransportStore
177
    def _check_fileid(self, fileid):
3350.6.1 by Robert Collins
* New ``versionedfile.KeyMapper`` interface to abstract out the access to
178
        if type(fileid) != str:
179
            raise TypeError('Fileids should be bytestrings: %s %r' % (
180
                type(fileid), fileid))
1442.1.24 by Robert Collins
Pull up _check_id and _relpath from Text and CompressedText stores into TransportStore
181
        if '\\' in fileid or '/' in fileid:
182
            raise ValueError("invalid store id %r" % 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
    def _id_to_names(self, fileid, suffix):
185
        """Return the names in the expected order"""
1442.1.47 by Robert Collins
test for has with suffixed files
186
        if suffix is not None:
187
            fn = self._relpath(fileid, [suffix])
188
        else:
189
            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
190
1185.65.13 by Robert Collins
Merge from integration
191
        # 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
192
        fn_gz = fn + '.gz'
193
        if self._compressed:
194
            return fn_gz, fn
195
        else:
196
            return fn, fn_gz
197
198
    def has_id(self, fileid, suffix=None):
199
        """See Store.has_id."""
1651.1.5 by Martin Pool
Review cleanup of TransportStore.has_id
200
        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
201
202
    def _get_name(self, fileid, suffix=None):
203
        """A special check, which returns the name of an existing file.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
204
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
205
        This is similar in spirit to 'has_id', but it is designed
206
        to return information about which file the store has.
207
        """
208
        for name in self._id_to_names(fileid, suffix=suffix):
209
            if self._transport.has(name):
210
                return name
211
        return None
1442.1.38 by Robert Collins
unify __contains__ for TransportStore classes
212
1442.1.36 by Robert Collins
convert get() in TextStore and CompressedTextStore into a template method
213
    def _get(self, filename):
214
        """Return an vanilla file stream for clients to read from.
215
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
216
        This is the body of a template method on 'get', and should be
1442.1.36 by Robert Collins
convert get() in TextStore and CompressedTextStore into a template method
217
        implemented by subclasses.
218
        """
219
        raise NotImplementedError
220
1442.1.50 by Robert Collins
test get with suffixes
221
    def get(self, fileid, suffix=None):
222
        """See Store.get()."""
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
223
        names = self._id_to_names(fileid, suffix)
224
        for name in names:
225
            try:
226
                return self._get(name)
227
            except errors.NoSuchFile:
228
                pass
229
        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.
230
1185.58.4 by John Arbash Meinel
Added permission checking to Branch, and propogated that change into the stores.
231
    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.
232
                 dir_mode=None, file_mode=None,
233
                 escaped=False):
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
234
        super(TransportStore, self).__init__()
1442.1.44 by Robert Collins
Many transport related tweaks:
235
        self._transport = a_transport
1442.1.25 by Robert Collins
Test TransportStore._relpath for simple cases: pull up _prefixed attribute as a result.
236
        self._prefixed = prefixed
1185.65.13 by Robert Collins
Merge from integration
237
        # 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
238
        self._compressed = compressed
1442.1.43 by Robert Collins
add registration of suffixes, in preparation for ensuring iteration is regular
239
        self._suffixes = set()
1185.80.1 by John Arbash Meinel
Text store and weave store both allow escaping fileid paths.
240
        self._escaped = escaped
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
241
1185.58.6 by John Arbash Meinel
Stores don't have to have a dir_mode or file_mode set
242
        # It is okay for these to be None, it just means they
243
        # will just use the filesystem defaults
244
        self._dir_mode = dir_mode
245
        self._file_mode = file_mode
3350.6.1 by Robert Collins
* New ``versionedfile.KeyMapper`` interface to abstract out the access to
246
        # Create a key mapper to use
247
        if escaped and prefixed:
248
            self._mapper = versionedfile.HashEscapedPrefixMapper()
249
        elif not escaped and prefixed:
250
            self._mapper = versionedfile.HashPrefixMapper()
251
        elif self._escaped:
3350.6.8 by Martin Pool
Change stray pdb calls to exceptions
252
            raise ValueError(
253
                "%r: escaped unprefixed stores are not permitted."
254
                % (self,))
1608.2.1 by Martin Pool
[merge] Storage filename escaping
255
        else:
3350.6.1 by Robert Collins
* New ``versionedfile.KeyMapper`` interface to abstract out the access to
256
            self._mapper = versionedfile.PrefixMapper()
1608.2.1 by Martin Pool
[merge] Storage filename escaping
257
1479 by Robert Collins
More quoting at the transport layer bugfixes.
258
    def _iter_files_recursive(self):
259
        """Iterate through the files in the transport."""
260
        for quoted_relpath in self._transport.iter_files_recursive():
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
261
            yield quoted_relpath
1479 by Robert Collins
More quoting at the transport layer bugfixes.
262
1442.1.51 by Robert Collins
teach iter about suffixes
263
    def __iter__(self):
1479 by Robert Collins
More quoting at the transport layer bugfixes.
264
        for relpath in self._iter_files_recursive():
1442.1.51 by Robert Collins
teach iter about suffixes
265
            # worst case is one of each suffix.
266
            name = os.path.basename(relpath)
267
            if name.endswith('.gz'):
268
                name = name[:-3]
269
            skip = False
270
            for count in range(len(self._suffixes)):
271
                for suffix in self._suffixes:
272
                    if name.endswith('.' + suffix):
273
                        skip = True
274
            if not skip:
3350.6.1 by Robert Collins
* New ``versionedfile.KeyMapper`` interface to abstract out the access to
275
                yield self._mapper.unmap(name)[0]
1442.1.51 by Robert Collins
teach iter about suffixes
276
1442.1.40 by Robert Collins
unify __len__() implementations for TransportStore classes
277
    def __len__(self):
1442.1.50 by Robert Collins
test get with suffixes
278
        return len(list(self.__iter__()))
1442.1.40 by Robert Collins
unify __len__() implementations for TransportStore classes
279
1185.16.157 by John Arbash Meinel
Added ability for TextStore to handle both compressed and uncompressed, it just looks for one type first
280
    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
281
        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
282
        if suffixes:
283
            for suffix in suffixes:
284
                if not suffix in self._suffixes:
285
                    raise ValueError("Unregistered suffix %r" % suffix)
286
                self._check_fileid(suffix)
287
        else:
288
            suffixes = []
3350.6.1 by Robert Collins
* New ``versionedfile.KeyMapper`` interface to abstract out the access to
289
        path = self._mapper.map((fileid,))
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
290
        full_path = '.'.join([path] + suffixes)
291
        return full_path
1442.1.24 by Robert Collins
Pull up _check_id and _relpath from Text and CompressedText stores into TransportStore
292
1092.2.24 by Robert Collins
merge from martins newformat branch - brings in transport abstraction
293
    def __repr__(self):
294
        if self._transport is None:
295
            return "%s(None)" % (self.__class__.__name__)
296
        else:
297
            return "%s(%r)" % (self.__class__.__name__, self._transport.base)
298
299
    __str__ = __repr__
1185.10.1 by Aaron Bentley
Added --basis option to bzr branch
300
1400.1.1 by Robert Collins
implement a basic test for the ui branch command from http servers
301
    def listable(self):
302
        """Return True if this store is able to be listed."""
303
        return self._transport.listable()
304
1442.1.43 by Robert Collins
add registration of suffixes, in preparation for ensuring iteration is regular
305
    def register_suffix(self, suffix):
306
        """Register a suffix as being expected in this store."""
307
        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
308
        if suffix == 'gz':
309
            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
310
        self._suffixes.add(suffix)
311
1442.1.37 by Robert Collins
pull up total_size into TransportStore
312
    def total_size(self):
313
        """Return (count, bytes)
314
315
        This is the (compressed) size stored on disk, not the size of
316
        the content."""
317
        total = 0
318
        count = 0
1442.1.44 by Robert Collins
Many transport related tweaks:
319
        for relpath in self._transport.iter_files_recursive():
1442.1.37 by Robert Collins
pull up total_size into TransportStore
320
            count += 1
1442.1.44 by Robert Collins
Many transport related tweaks:
321
            total += self._transport.stat(relpath).st_size
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
322
1442.1.37 by Robert Collins
pull up total_size into TransportStore
323
        return count, total