1
# Copyright (C) 2005 Canonical Ltd
1
# Copyright (C) 2005, 2006 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
26
26
from bzrlib.weavefile import read_weave, write_weave_v5
27
from bzrlib.weave import WeaveFile
28
from bzrlib.store import TransportStore, hash_prefix
27
from bzrlib.weave import WeaveFile, Weave
28
from bzrlib.store import TransportStore
29
29
from bzrlib.atomicfile import AtomicFile
30
30
from bzrlib.errors import NoSuchFile, FileExists
31
31
from bzrlib.symbol_versioning import *
32
32
from bzrlib.trace import mutter
35
36
class VersionedFileStore(TransportStore):
36
37
"""Collection of many versioned files in a transport."""
39
# TODO: Rather than passing versionedfile_kwargs, perhaps pass in a
40
# transport factory callable?
38
41
def __init__(self, transport, prefixed=False, precious=False,
39
42
dir_mode=None, file_mode=None,
40
versionedfile_class=WeaveFile):
41
super(WeaveStore, self).__init__(transport,
43
versionedfile_class=WeaveFile,
44
versionedfile_kwargs={},
46
super(VersionedFileStore, self).__init__(transport,
42
47
dir_mode=dir_mode, file_mode=file_mode,
43
prefixed=prefixed, compressed=False)
48
prefixed=prefixed, compressed=False, escaped=escaped)
44
49
self._precious = precious
45
50
self._versionedfile_class = versionedfile_class
51
self._versionedfile_kwargs = versionedfile_kwargs
47
53
def _clear_cache_id(self, file_id, transaction):
48
54
"""WARNING may lead to inconsistent object references for file_id.
62
68
def filename(self, file_id):
63
69
"""Return the path relative to the transport root."""
65
return hash_prefix(file_id) + urllib.quote(file_id)
67
return urllib.quote(file_id)
70
return self._relpath(file_id)
69
72
def __iter__(self):
70
73
suffixes = self._versionedfile_class.get_suffixes()
72
75
for relpath in self._iter_files_recursive():
73
76
for suffix in suffixes:
74
77
if relpath.endswith(suffix):
75
id = os.path.basename(relpath[:-len(suffix)])
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:
84
break # only one suffix can match
80
86
def has_id(self, fileid):
81
87
suffixes = self._versionedfile_class.get_suffixes()
99
105
self._transport.delete(filename + suffix)
100
106
self._clear_cache_id(file_id, transaction)
108
def _get(self, file_id):
109
return self._transport.get(self.filename(file_id))
111
def _put(self, file_id, f):
112
fn = self.filename(file_id)
114
return self._transport.put(fn, f, mode=self._file_mode)
116
if not self._prefixed:
118
self._transport.mkdir(os.path.dirname(fn), mode=self._dir_mode)
119
return self._transport.put(fn, f, mode=self._file_mode)
102
121
def get_weave(self, file_id, transaction):
103
122
weave = transaction.map.find_weave(file_id)
104
123
if weave is not None:
105
124
#mutter("cache hit in %s for %s", self, file_id)
107
126
if transaction.writeable():
108
w = self._versionedfile_class(self.filename(file_id), self._transport, self._file_mode)
127
w = self._versionedfile_class(self.filename(file_id), self._transport, self._file_mode,
128
**self._versionedfile_kwargs)
109
129
transaction.map.add_weave(file_id, w)
110
130
transaction.register_dirty(w)
140
160
if self.has_id(file_id):
141
161
self.delete(file_id, transaction)
143
weave = self._versionedfile_class(self.filename(file_id), self._transport, self._file_mode, create=True)
163
weave = self._versionedfile_class(self.filename(file_id), self._transport, self._file_mode, create=True,
164
**self._versionedfile_kwargs)
144
165
except NoSuchFile:
145
166
if not self._prefixed:
146
167
# unexpected error - NoSuchFile is raised on a missing dir only and that
147
168
# only occurs when we are prefixed.
149
self._transport.mkdir(hash_prefix(file_id), mode=self._dir_mode)
150
weave = self._versionedfile_class(self.filename(file_id), self._transport, self._file_mode, create=True)
170
self._transport.mkdir(self.hash_prefix(file_id), mode=self._dir_mode)
171
weave = self._versionedfile_class(self.filename(file_id), self._transport,
172
self._file_mode, create=True,
173
**self._versionedfile_kwargs)
153
176
def get_weave_or_empty(self, file_id, transaction):
167
190
Its maintained for backwards compatability but will only work on
168
191
weave stores - pre 0.8 repositories.
170
self._put_weave(self, file_id, weave, transaction)
193
self._put_weave(file_id, weave, transaction)
172
195
def _put_weave(self, file_id, weave, transaction):
173
196
"""Preserved here for upgrades-to-weaves to use."""
246
269
# we are copying single objects, and there may be open tranasactions
247
270
# so again with the passthrough
248
271
to_transaction = PassThroughTransaction()
272
pb = bzrlib.ui.ui_factory.nested_progress_bar()
249
273
for count, f in enumerate(file_ids):
250
274
mutter("copy weave {%s} into %s", f, self)
252
pb.update('copy', count, len(file_ids))
275
pb.update('copy', count, len(file_ids))
253
276
# if we have it in cache, its faster.
254
277
# joining is fast with knits, and bearable for weaves -
255
278
# indeed the new case can be optimised if needed.
256
279
target = self._make_new_versionedfile(f, to_transaction)
257
280
target.join(from_store.get_weave(f, from_transaction))
261
283
def total_size(self):
262
284
count, bytes = super(VersionedFileStore, self).total_size()