20
20
# files whose id differs only in case. That should probably be forbidden.
23
from cStringIO import StringIO
25
from cStringIO import StringIO
28
27
from bzrlib.weavefile import read_weave, write_weave_v5
29
28
from bzrlib.weave import Weave
30
from bzrlib.store import TransportStore, hash_prefix
29
from bzrlib.store import Store
31
30
from bzrlib.atomicfile import AtomicFile
32
from bzrlib.errors import NoSuchFile, FileExists
31
from bzrlib.errors import NoSuchFile
33
32
from bzrlib.trace import mutter
36
class WeaveStore(TransportStore):
35
class WeaveStore(Store):
37
36
"""Collection of several weave files in a directory.
39
38
This has some shortcuts for reading and writing them.
41
40
FILE_SUFFIX = '.weave'
43
def __init__(self, transport, prefixed=False, precious=False,
44
dir_mode=None, file_mode=None):
45
super(WeaveStore, self).__init__(transport,
46
dir_mode=dir_mode, file_mode=file_mode,
47
prefixed=prefixed, compressed=False)
48
self._precious = precious
42
def __init__(self, transport):
43
self._transport = transport
45
self.enable_cache = False
50
48
def filename(self, file_id):
51
49
"""Return the path relative to the transport root."""
53
return hash_prefix(file_id) + urllib.quote(file_id) + WeaveStore.FILE_SUFFIX
55
return urllib.quote(file_id) + WeaveStore.FILE_SUFFIX
50
return file_id + WeaveStore.FILE_SUFFIX
57
52
def __iter__(self):
58
53
l = len(WeaveStore.FILE_SUFFIX)
59
for relpath in self._iter_files_recursive():
60
if relpath.endswith(WeaveStore.FILE_SUFFIX):
61
yield os.path.basename(relpath[:-l])
54
for f in self._transport.list_dir('.'):
55
if f.endswith(WeaveStore.FILE_SUFFIX):
63
def has_id(self, fileid):
59
def __contains__(self, fileid):
64
61
return self._transport.has(self.filename(fileid))
66
63
def _get(self, file_id):
67
64
return self._transport.get(self.filename(file_id))
69
66
def _put(self, file_id, f):
72
self._transport.mkdir(hash_prefix(file_id), mode=self._dir_mode)
75
return self._transport.put(self.filename(file_id), f, mode=self._file_mode)
67
return self._transport.put(self.filename(file_id), f)
77
def get_weave(self, file_id, transaction):
78
weave = transaction.map.find_weave(file_id)
80
mutter("cache hit in %s for %s", self, file_id)
69
def get_weave(self, file_id):
71
if file_id in self._cache:
72
mutter("cache hit in %s for %s", self, file_id)
73
return self._cache[file_id]
82
74
w = read_weave(self._get(file_id))
83
transaction.map.add_weave(file_id, w)
84
transaction.register_clean(w, precious=self._precious)
85
# TODO: jam 20051219 This should check if there is a prelude
86
# which is already cached, and if so, should remove it
87
# But transaction doesn't seem to have a 'remove'
88
# One workaround would be to re-add the object with
92
def get_weave_prelude(self, file_id, transaction):
94
weave = transaction.map.find_weave(weave_id)
96
mutter("cache hit in %s for %s", self, weave_id)
98
# We want transactions to also cache preludes if that
99
# is all that we are loading. So we need a unique
100
# identifier, so that someone who wants the whole text
101
# won't get just the prelude
102
weave_id = 'PRELUDE-' + file_id
103
weave = transaction.map.find_weave(weave_id)
105
mutter("cache hit in %s for %s", self, weave_id)
107
w = read_weave(self._get(file_id), prelude=True)
108
transaction.map.add_weave(weave_id, w)
109
transaction.register_clean(w, precious=self._precious)
112
def get_lines(self, file_id, rev_id, transaction):
76
self._cache[file_id] = w
80
def get_lines(self, file_id, rev_id):
113
81
"""Return text from a particular version of a weave.
115
83
Returned as a list of lines."""
116
w = self.get_weave(file_id, transaction)
84
w = self.get_weave(file_id)
117
85
return w.get(w.lookup(rev_id))
119
def get_weave_prelude_or_empty(self, file_id, transaction):
120
"""cheap version that reads the prelude but not the lines
123
return self.get_weave_prelude(file_id, transaction)
125
# We can cache here, because we know that there
126
# is no complete object, since we got NoSuchFile
127
weave = Weave(weave_name=file_id)
128
transaction.map.add_weave(file_id, weave)
129
transaction.register_clean(weave, precious=self._precious)
132
def get_weave_or_empty(self, file_id, transaction):
88
def get_weave_or_empty(self, file_id):
133
89
"""Return a weave, or an empty one if it doesn't exist."""
135
return self.get_weave(file_id, transaction)
91
inf = self._get(file_id)
136
92
except NoSuchFile:
137
weave = Weave(weave_name=file_id)
138
transaction.map.add_weave(file_id, weave)
139
transaction.register_clean(weave, precious=self._precious)
93
return Weave(weave_name=file_id)
95
return read_weave(inf)
142
def put_weave(self, file_id, weave, transaction):
98
def put_weave(self, file_id, weave):
143
99
"""Write back a modified weave"""
144
transaction.register_dirty(weave)
145
# TODO FOR WRITE TRANSACTIONS: this should be done in a callback
146
# from the transaction, when it decides to save.
100
if self.enable_cache:
101
self._cache[file_id] = weave
148
104
write_weave_v5(weave, sio)
150
107
self._put(file_id, sio)
152
def add_text(self, file_id, rev_id, new_lines, parents, transaction):
153
w = self.get_weave_or_empty(file_id, transaction)
110
def add_text(self, file_id, rev_id, new_lines, parents):
111
w = self.get_weave_or_empty(file_id)
154
112
parent_idxs = map(w.lookup, parents)
155
113
w.add(rev_id, parent_idxs, new_lines)
156
self.put_weave(file_id, w, transaction)
114
self.put_weave(file_id, w)
158
def add_identical_text(self, file_id, old_rev_id, new_rev_id, parents,
160
w = self.get_weave_or_empty(file_id, transaction)
116
def add_identical_text(self, file_id, old_rev_id, new_rev_id, parents):
117
w = self.get_weave_or_empty(file_id)
161
118
parent_idxs = map(w.lookup, parents)
162
119
w.add_identical(old_rev_id, new_rev_id, parent_idxs)
163
self.put_weave(file_id, w, transaction)
120
self.put_weave(file_id, w)
165
122
def copy_multi(self, from_store, file_ids):
166
123
assert isinstance(from_store, WeaveStore)