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):
37
class WeaveStore(Store):
37
38
"""Collection of several weave files in a directory.
39
40
This has some shortcuts for reading and writing them.
41
42
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
44
def __init__(self, transport):
45
self._transport = transport
47
self.enable_cache = False
50
50
def filename(self, file_id):
51
51
"""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
52
return file_id + WeaveStore.FILE_SUFFIX
57
54
def __iter__(self):
58
55
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])
56
for f in self._transport.list_dir('.'):
57
if f.endswith(WeaveStore.FILE_SUFFIX):
63
def has_id(self, fileid):
61
def __contains__(self, fileid):
64
63
return self._transport.has(self.filename(fileid))
66
65
def _get(self, file_id):
67
66
return self._transport.get(self.filename(file_id))
69
68
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)
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
return self._transport.put(self.filename(file_id), f)
72
def get_weave(self, file_id):
74
if file_id in self._cache:
75
return self._cache[file_id]
82
76
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):
78
self._cache[file_id] = w
82
def get_lines(self, file_id, rev_id):
113
83
"""Return text from a particular version of a weave.
115
85
Returned as a list of lines."""
116
w = self.get_weave(file_id, transaction)
86
w = self.get_weave(file_id)
117
87
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):
90
def get_weave_or_empty(self, file_id):
133
91
"""Return a weave, or an empty one if it doesn't exist."""
135
return self.get_weave(file_id, transaction)
93
inf = self._get(file_id)
136
94
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)
95
return Weave(weave_name=file_id)
97
return read_weave(inf)
142
def put_weave(self, file_id, weave, transaction):
100
def put_weave(self, file_id, weave):
143
101
"""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.
102
if self.enable_cache:
103
self._cache[file_id] = weave
148
106
write_weave_v5(weave, sio)
150
109
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)
112
def add_text(self, file_id, rev_id, new_lines, parents):
113
w = self.get_weave_or_empty(file_id)
154
114
parent_idxs = map(w.lookup, parents)
155
115
w.add(rev_id, parent_idxs, new_lines)
156
self.put_weave(file_id, w, transaction)
116
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)
161
parent_idxs = map(w.lookup, parents)
162
w.add_identical(old_rev_id, new_rev_id, parent_idxs)
163
self.put_weave(file_id, w, transaction)
165
119
def copy_multi(self, from_store, file_ids):
166
120
assert isinstance(from_store, WeaveStore)