~bzr-pqm/bzr/bzr.dev

1608.2.1 by Martin Pool
[merge] Storage filename escaping
1
# Copyright (C) 2005, 2006 Canonical Ltd
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
1393.1.19 by Martin Pool
- add WeaveStore.__iter__, __contains__ and copy_multi()
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
1471 by Robert Collins
Bugfix to previous url escaping patch - include weave stores
21
import errno
22
import os
1393.1.19 by Martin Pool
- add WeaveStore.__iter__, __contains__ and copy_multi()
23
from cStringIO import StringIO
1471 by Robert Collins
Bugfix to previous url escaping patch - include weave stores
24
import urllib
1223 by Martin Pool
- store inventories in weave
25
26
from bzrlib.weavefile import read_weave, write_weave_v5
1608.2.1 by Martin Pool
[merge] Storage filename escaping
27
from bzrlib.weave import WeaveFile, Weave
1185.80.6 by John Arbash Meinel
Adding tests to make sure weave stores can retrieve the files they add.
28
from bzrlib.store import TransportStore
1223 by Martin Pool
- store inventories in weave
29
from bzrlib.atomicfile import AtomicFile
1429 by Robert Collins
merge in niemeyers prefixed-store patch
30
from bzrlib.errors import NoSuchFile, FileExists
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
31
from bzrlib.symbol_versioning import *
1393.1.19 by Martin Pool
- add WeaveStore.__iter__, __contains__ and copy_multi()
32
from bzrlib.trace import mutter
1185.80.10 by John Arbash Meinel
Adding progress indicators and improved get order for 'bzr branch'
33
import bzrlib.ui
1393.2.2 by John Arbash Meinel
Updated stores to use Transport
34
35
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
36
class VersionedFileStore(TransportStore):
37
    """Collection of many versioned files in a transport."""
1393.1.19 by Martin Pool
- add WeaveStore.__iter__, __contains__ and copy_multi()
38
1651.1.1 by Martin Pool
[merge][wip] Storage escaping
39
    # TODO: Rather than passing versionedfile_kwargs, perhaps pass in a
40
    # transport factory callable?
1185.58.4 by John Arbash Meinel
Added permission checking to Branch, and propogated that change into the stores.
41
    def __init__(self, transport, prefixed=False, precious=False,
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
42
                 dir_mode=None, file_mode=None,
1608.2.1 by Martin Pool
[merge] Storage filename escaping
43
                 versionedfile_class=WeaveFile,
1651.1.1 by Martin Pool
[merge][wip] Storage escaping
44
                 versionedfile_kwargs={},
1608.2.1 by Martin Pool
[merge] Storage filename escaping
45
                 escaped=False):
46
        super(VersionedFileStore, self).__init__(transport,
1185.58.4 by John Arbash Meinel
Added permission checking to Branch, and propogated that change into the stores.
47
                dir_mode=dir_mode, file_mode=file_mode,
1185.80.1 by John Arbash Meinel
Text store and weave store both allow escaping fileid paths.
48
                prefixed=prefixed, compressed=False, escaped=escaped)
1417.1.10 by Robert Collins
add a cache bound to Transactions, and a precious facility, so that we keep inventory.weave in memory, but can discard weaves for other such files.
49
        self._precious = precious
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
50
        self._versionedfile_class = versionedfile_class
1628.1.4 by Robert Collins
Change knit format to use non-delta, non-annotated revisions and signatures.
51
        self._versionedfile_kwargs = versionedfile_kwargs
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
52
1563.2.25 by Robert Collins
Merge in upstream.
53
    def _clear_cache_id(self, file_id, transaction):
54
        """WARNING may lead to inconsistent object references for file_id.
55
56
        Remove file_id from the transaction map. 
57
58
        NOT in the transaction api because theres no reliable way to clear
59
        callers. So its here for very specialised use rather than having an
60
        'api' that isn't.
61
        """
62
        weave = transaction.map.find_weave(file_id)
1563.2.34 by Robert Collins
Remove the commit and rollback transaction methods as misleading, and implement a WriteTransaction
63
        if weave is not None:
1563.2.25 by Robert Collins
Merge in upstream.
64
            mutter("old data in transaction in %s for %s", self, file_id)
1563.2.34 by Robert Collins
Remove the commit and rollback transaction methods as misleading, and implement a WriteTransaction
65
            # FIXME abstraction violation - transaction now has stale data.
66
            transaction.map.remove_object(weave)
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
67
1223 by Martin Pool
- store inventories in weave
68
    def filename(self, file_id):
1393.2.2 by John Arbash Meinel
Updated stores to use Transport
69
        """Return the path relative to the transport root."""
1608.2.1 by Martin Pool
[merge] Storage filename escaping
70
        return self._relpath(file_id)
1429 by Robert Collins
merge in niemeyers prefixed-store patch
71
1393.1.19 by Martin Pool
- add WeaveStore.__iter__, __contains__ and copy_multi()
72
    def __iter__(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
73
        suffixes = self._versionedfile_class.get_suffixes()
1563.2.15 by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages.
74
        ids = set()
1479 by Robert Collins
More quoting at the transport layer bugfixes.
75
        for relpath in self._iter_files_recursive():
1563.2.15 by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages.
76
            for suffix in suffixes:
77
                if relpath.endswith(suffix):
1608.2.1 by Martin Pool
[merge] Storage filename escaping
78
                    # TODO: use standard remove_suffix function
79
                    escaped_id = os.path.basename(relpath[:-len(suffix)])
80
                    file_id = self._unescape(escaped_id)
81
                    if file_id not in ids:
82
                        ids.add(file_id)
83
                        yield file_id
84
                    break # only one suffix can match
1393.1.19 by Martin Pool
- add WeaveStore.__iter__, __contains__ and copy_multi()
85
1442.1.45 by Robert Collins
replace __contains__ calls in stores with has_id
86
    def has_id(self, fileid):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
87
        suffixes = self._versionedfile_class.get_suffixes()
1563.2.15 by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages.
88
        filename = self.filename(fileid)
89
        for suffix in suffixes:
90
            if not self._transport.has(filename + suffix):
91
                return False
92
        return True
1223 by Martin Pool
- store inventories in weave
93
1563.2.25 by Robert Collins
Merge in upstream.
94
    def get_empty(self, file_id, transaction):
95
        """Get an empty weave, which implies deleting the existing one first."""
96
        if self.has_id(file_id):
97
            self.delete(file_id, transaction)
98
        return self.get_weave_or_empty(file_id, transaction)
99
100
    def delete(self, file_id, transaction):
101
        """Remove file_id from the store."""
102
        suffixes = self._versionedfile_class.get_suffixes()
103
        filename = self.filename(file_id)
104
        for suffix in suffixes:
105
            self._transport.delete(filename + suffix)
106
        self._clear_cache_id(file_id, transaction)
1393.2.2 by John Arbash Meinel
Updated stores to use Transport
107
108
    def _get(self, file_id):
109
        return self._transport.get(self.filename(file_id))
110
111
    def _put(self, file_id, f):
1185.80.6 by John Arbash Meinel
Adding tests to make sure weave stores can retrieve the files they add.
112
        fn = self.filename(file_id)
113
        try:
114
            return self._transport.put(fn, f, mode=self._file_mode)
115
        except NoSuchFile:
116
            if not self._prefixed:
117
                raise
118
            self._transport.mkdir(os.path.dirname(fn), mode=self._dir_mode)
119
            return self._transport.put(fn, f, mode=self._file_mode)
1223 by Martin Pool
- store inventories in weave
120
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
121
    def get_weave(self, file_id, transaction):
122
        weave = transaction.map.find_weave(file_id)
1563.2.34 by Robert Collins
Remove the commit and rollback transaction methods as misleading, and implement a WriteTransaction
123
        if weave is not None:
1594.3.2 by Robert Collins
make trivial ranges work for HTTP really.
124
            #mutter("cache hit in %s for %s", self, file_id)
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
125
            return weave
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
126
        if transaction.writeable():
1628.1.4 by Robert Collins
Change knit format to use non-delta, non-annotated revisions and signatures.
127
            w = self._versionedfile_class(self.filename(file_id), self._transport, self._file_mode,
128
                                          **self._versionedfile_kwargs)
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
129
            transaction.map.add_weave(file_id, w)
130
            transaction.register_dirty(w)
131
        else:
132
            w = self._versionedfile_class(self.filename(file_id),
133
                                          self._transport,
134
                                          self._file_mode,
135
                                          create=False,
1628.1.4 by Robert Collins
Change knit format to use non-delta, non-annotated revisions and signatures.
136
                                          access_mode='r',
137
                                          **self._versionedfile_kwargs)
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
138
            transaction.map.add_weave(file_id, w)
139
            transaction.register_clean(w, precious=self._precious)
1363 by Martin Pool
- add quick-and-dirty cache for weaves to speed check command
140
        return w
1262 by Martin Pool
- fetch should also copy ancestry records
141
1563.2.18 by Robert Collins
get knit repositories really using knits for text storage.
142
    @deprecated_method(zero_eight)
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
143
    def get_lines(self, file_id, rev_id, transaction):
1262 by Martin Pool
- fetch should also copy ancestry records
144
        """Return text from a particular version of a weave.
145
1563.2.35 by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too.
146
        Returned as a list of lines.
147
        """
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
148
        w = self.get_weave(file_id, transaction)
1563.2.18 by Robert Collins
get knit repositories really using knits for text storage.
149
        return w.get_lines(rev_id)
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
150
    
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
151
    def _new_weave(self, file_id, transaction):
152
        """Make a new weave for file_id and return it."""
1563.2.34 by Robert Collins
Remove the commit and rollback transaction methods as misleading, and implement a WriteTransaction
153
        weave = self._make_new_versionedfile(file_id, transaction)
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
154
        transaction.map.add_weave(file_id, weave)
1594.2.23 by Robert Collins
Test versioned file storage handling of clean/dirty status for accessed versioned files.
155
        # has to be dirty - its able to mutate on its own.
156
        transaction.register_dirty(weave)
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
157
        return weave
1185.33.88 by Martin Pool
[patch] read only the table-of-contents of weaves to speed up commit
158
1563.2.34 by Robert Collins
Remove the commit and rollback transaction methods as misleading, and implement a WriteTransaction
159
    def _make_new_versionedfile(self, file_id, transaction):
160
        if self.has_id(file_id):
161
            self.delete(file_id, transaction)
1563.2.14 by Robert Collins
Prepare weave store to delegate copy details to the versioned file.
162
        try:
1628.1.4 by Robert Collins
Change knit format to use non-delta, non-annotated revisions and signatures.
163
            weave = self._versionedfile_class(self.filename(file_id), self._transport, self._file_mode, create=True,
164
                                              **self._versionedfile_kwargs)
1563.2.14 by Robert Collins
Prepare weave store to delegate copy details to the versioned file.
165
        except NoSuchFile:
166
            if not self._prefixed:
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
167
                # unexpected error - NoSuchFile is raised on a missing dir only and that
168
                # only occurs when we are prefixed.
1563.2.14 by Robert Collins
Prepare weave store to delegate copy details to the versioned file.
169
                raise
1608.2.1 by Martin Pool
[merge] Storage filename escaping
170
            self._transport.mkdir(self.hash_prefix(file_id), mode=self._dir_mode)
1651.1.1 by Martin Pool
[merge][wip] Storage escaping
171
            weave = self._versionedfile_class(self.filename(file_id), self._transport, 
172
                                              self._file_mode, create=True,
1628.1.4 by Robert Collins
Change knit format to use non-delta, non-annotated revisions and signatures.
173
                                              **self._versionedfile_kwargs)
1563.2.14 by Robert Collins
Prepare weave store to delegate copy details to the versioned file.
174
        return weave
175
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
176
    def get_weave_or_empty(self, file_id, transaction):
1224 by Martin Pool
- new method WeaveStore.get_weave_or_empty
177
        """Return a weave, or an empty one if it doesn't exist.""" 
178
        try:
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
179
            return self.get_weave(file_id, transaction)
1393.2.2 by John Arbash Meinel
Updated stores to use Transport
180
        except NoSuchFile:
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
181
            return self._new_weave(file_id, transaction)
1224 by Martin Pool
- new method WeaveStore.get_weave_or_empty
182
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
183
    @deprecated_method(zero_eight)
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
184
    def put_weave(self, file_id, weave, transaction):
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
185
        """This is a deprecated API: It writes an entire collection of ids out.
186
        
187
        This became inappropriate when we made a versioned file api which
188
        tracks the state of the collection of versions for a single id.
189
        
190
        Its maintained for backwards compatability but will only work on
191
        weave stores - pre 0.8 repositories.
192
        """
1664.1.1 by Robert Collins
Bugfix: deprecated put_weave method was broken.
193
        self._put_weave(file_id, weave, transaction)
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
194
195
    def _put_weave(self, file_id, weave, transaction):
196
        """Preserved here for upgrades-to-weaves to use."""
1563.2.34 by Robert Collins
Remove the commit and rollback transaction methods as misleading, and implement a WriteTransaction
197
        myweave = self._make_new_versionedfile(file_id, transaction)
1563.2.15 by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages.
198
        myweave.join(weave)
1223 by Martin Pool
- store inventories in weave
199
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
200
    @deprecated_method(zero_eight)
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
201
    def add_text(self, file_id, rev_id, new_lines, parents, transaction):
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
202
        """This method was a shorthand for 
203
204
        vfile = self.get_weave_or_empty(file_id, transaction)
205
        vfile.add_lines(rev_id, parents, new_lines)
206
        """
207
        vfile = self.get_weave_or_empty(file_id, transaction)
208
        vfile.add_lines(rev_id, parents, new_lines)
1223 by Martin Pool
- store inventories in weave
209
        
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
210
    @deprecated_method(zero_eight)
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
211
    def add_identical_text(self, file_id, old_rev_id, new_rev_id, parents,
212
                           transaction):
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
213
        """This method was a shorthand for
214
215
        vfile = self.get_weave_or_empty(file_id, transaction)
216
        vfile.clone_text(new_rev_id, old_rev_id, parents)
217
        """
218
        vfile = self.get_weave_or_empty(file_id, transaction)
219
        vfile.clone_text(new_rev_id, old_rev_id, parents)
1563.2.25 by Robert Collins
Merge in upstream.
220
 
221
    def copy(self, source, result_id, transaction):
222
        """Copy the source versioned file to result_id in this store."""
223
        self._clear_cache_id(result_id, transaction)
224
        source.copy_to(self.filename(result_id), self._transport)
225
 
1563.2.34 by Robert Collins
Remove the commit and rollback transaction methods as misleading, and implement a WriteTransaction
226
    def copy_all_ids(self, store_from, pb=None, from_transaction=None,
227
                     to_transaction=None):
1563.2.14 by Robert Collins
Prepare weave store to delegate copy details to the versioned file.
228
        """Copy all the file ids from store_from into self."""
229
        if from_transaction is None:
1563.2.34 by Robert Collins
Remove the commit and rollback transaction methods as misleading, and implement a WriteTransaction
230
            warn("Please pass from_transaction into "
231
                 "versioned_store.copy_all_ids.", stacklevel=2)
232
        if to_transaction is None:
233
            warn("Please pass to_transaction into "
1563.2.14 by Robert Collins
Prepare weave store to delegate copy details to the versioned file.
234
                 "versioned_store.copy_all_ids.", stacklevel=2)
235
        if not store_from.listable():
236
            raise UnlistableStore(store_from)
237
        ids = []
238
        for count, file_id in enumerate(store_from):
239
            if pb:
240
                pb.update('listing files', count, count)
241
            ids.append(file_id)
242
        if pb:
243
            pb.clear()
244
        mutter('copy_all ids: %r', ids)
245
        self.copy_multi(store_from, ids, pb=pb,
1563.2.34 by Robert Collins
Remove the commit and rollback transaction methods as misleading, and implement a WriteTransaction
246
                        from_transaction=from_transaction,
247
                        to_transaction=to_transaction)
1563.2.14 by Robert Collins
Prepare weave store to delegate copy details to the versioned file.
248
1563.2.34 by Robert Collins
Remove the commit and rollback transaction methods as misleading, and implement a WriteTransaction
249
    def copy_multi(self, from_store, file_ids, pb=None, from_transaction=None,
250
                   to_transaction=None):
1563.2.14 by Robert Collins
Prepare weave store to delegate copy details to the versioned file.
251
        """Copy all the versions for multiple file_ids from from_store.
252
        
253
        :param from_transaction: required current transaction in from_store.
254
        """
1563.2.34 by Robert Collins
Remove the commit and rollback transaction methods as misleading, and implement a WriteTransaction
255
        from bzrlib.transactions import PassThroughTransaction
1393.1.19 by Martin Pool
- add WeaveStore.__iter__, __contains__ and copy_multi()
256
        assert isinstance(from_store, WeaveStore)
1563.2.14 by Robert Collins
Prepare weave store to delegate copy details to the versioned file.
257
        if from_transaction is None:
258
            warn("WeaveStore.copy_multi without a from_transaction parameter "
259
                 "is deprecated. Please provide a from_transaction.",
260
                 DeprecationWarning,
261
                 stacklevel=2)
1563.2.34 by Robert Collins
Remove the commit and rollback transaction methods as misleading, and implement a WriteTransaction
262
            # we are reading one object - caching is irrelevant.
263
            from_transaction = PassThroughTransaction()
264
        if to_transaction is None:
265
            warn("WeaveStore.copy_multi without a to_transaction parameter "
266
                 "is deprecated. Please provide a to_transaction.",
267
                 DeprecationWarning,
268
                 stacklevel=2)
269
            # we are copying single objects, and there may be open tranasactions
270
            # so again with the passthrough
271
            to_transaction = PassThroughTransaction()
1608.2.1 by Martin Pool
[merge] Storage filename escaping
272
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
1185.80.10 by John Arbash Meinel
Adding progress indicators and improved get order for 'bzr branch'
273
        for count, f in enumerate(file_ids):
1393.1.19 by Martin Pool
- add WeaveStore.__iter__, __contains__ and copy_multi()
274
            mutter("copy weave {%s} into %s", f, self)
1185.80.10 by John Arbash Meinel
Adding progress indicators and improved get order for 'bzr branch'
275
            pb.update('copy', count, len(file_ids))
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
276
            # if we have it in cache, its faster.
1563.2.15 by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages.
277
            # joining is fast with knits, and bearable for weaves -
1563.2.34 by Robert Collins
Remove the commit and rollback transaction methods as misleading, and implement a WriteTransaction
278
            # indeed the new case can be optimised if needed.
279
            target = self._make_new_versionedfile(f, to_transaction)
1563.2.15 by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages.
280
            target.join(from_store.get_weave(f, from_transaction))
1608.2.1 by Martin Pool
[merge] Storage filename escaping
281
        pb.finished()
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
282
1563.2.31 by Robert Collins
Convert Knit repositories to use knits.
283
    def total_size(self):
284
        count, bytes =  super(VersionedFileStore, self).total_size()
285
        return (count / len(self._versionedfile_class.get_suffixes())), bytes
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
286
287
WeaveStore = VersionedFileStore