37
37
from bzrlib.errors import BzrError, BzrCheckError
39
from bzrlib.osutils import quotefn, splitpath, joinpath, appendpath
39
from bzrlib.osutils import quotefn, splitpath, joinpath, appendpath, sha_strings
40
40
from bzrlib.trace import mutter
41
41
from bzrlib.errors import NotVersionedError
117
114
__slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
118
115
'text_id', 'parent_id', 'children',
119
'text_version', 'name_version', ]
116
'revision', 'symlink_target']
118
def _add_text_to_weave(self, new_lines, parents, weave_store):
119
weave_store.add_text(self.file_id, self.revision, new_lines, parents)
122
121
def __init__(self, file_id, name, kind, parent_id, text_id=None):
123
122
"""Create an InventoryEntry
148
146
self.text_id = text_id
149
147
self.parent_id = parent_id
148
self.symlink_target = None
150
149
if kind == 'directory':
151
150
self.children = {}
152
151
elif kind == 'file':
153
elif kind == 'symlink':
155
156
raise BzrError("unhandled entry kind %r" % kind)
158
def read_symlink_target(self, path):
159
if self.kind == 'symlink':
161
self.symlink_target = os.readlink(path)
163
raise BzrError("os.readlink error, %s" % e)
159
165
def sorted_children(self):
160
166
l = self.children.items()
170
def check(self, checker, rev_id, inv, tree):
171
if self.parent_id != None:
172
if not inv.has_id(self.parent_id):
173
raise BzrCheckError('missing parent {%s} in inventory for revision {%s}'
174
% (self.parent_id, rev_id))
175
if self.kind == 'file':
176
revision = self.revision
177
t = (self.file_id, revision)
178
if t in checker.checked_texts:
179
prev_sha = checker.checked_texts[t]
180
if prev_sha != self.text_sha1:
181
raise BzrCheckError('mismatched sha1 on {%s} in {%s}' %
182
(self.file_id, rev_id))
184
checker.repeated_text_cnt += 1
186
mutter('check version {%s} of {%s}', rev_id, self.file_id)
187
file_lines = tree.get_file_lines(self.file_id)
188
checker.checked_text_cnt += 1
189
if self.text_size != sum(map(len, file_lines)):
190
raise BzrCheckError('text {%s} wrong size' % self.text_id)
191
if self.text_sha1 != sha_strings(file_lines):
192
raise BzrCheckError('text {%s} wrong sha1' % self.text_id)
193
checker.checked_texts[t] = self.text_sha1
194
elif self.kind == 'directory':
195
if self.text_sha1 != None or self.text_size != None or self.text_id != None:
196
raise BzrCheckError('directory {%s} has text in revision {%s}'
197
% (self.file_id, rev_id))
198
elif self.kind == 'root_directory':
200
elif self.kind == 'symlink':
201
if self.text_sha1 != None or self.text_size != None or self.text_id != None:
202
raise BzrCheckError('symlink {%s} has text in revision {%s}'
203
% (self.file_id, rev_id))
204
if self.symlink_target == None:
205
raise BzrCheckError('symlink {%s} has no target in revision {%s}'
206
% (self.file_id, rev_id))
208
raise BzrCheckError('unknown entry kind %r in revision {%s}' %
166
213
other = InventoryEntry(self.file_id, self.name, self.kind,
168
215
other.text_id = self.text_id
169
216
other.text_sha1 = self.text_sha1
170
217
other.text_size = self.text_size
171
other.text_version = self.text_version
172
other.name_version = self.name_version
218
other.symlink_target = self.symlink_target
219
other.revision = self.revision
173
220
# note that children are *not* copied; they're pulled across when
174
221
# others are added
224
def _get_snapshot_change(self, previous_entries):
225
if len(previous_entries) > 1:
227
elif len(previous_entries) == 0:
230
return 'modified/renamed/reparented'
178
232
def __repr__(self):
179
233
return ("%s(%r, %r, kind=%r, parent_id=%r)"
240
def snapshot(self, revision, path, previous_entries, work_tree,
242
"""Make a snapshot of this entry.
244
This means that all its fields are populated, that it has its
245
text stored in the text store or weave.
247
mutter('new parents of %s are %r', path, previous_entries)
248
self._read_tree_state(path, work_tree)
249
if len(previous_entries) == 1:
250
# cannot be unchanged unless there is only one parent file rev.
251
parent_ie = previous_entries.values()[0]
252
if self._unchanged(path, parent_ie, work_tree):
253
mutter("found unchanged entry")
254
self.revision = parent_ie.revision
256
mutter('new revision for {%s}', self.file_id)
257
self.revision = revision
258
change = self._get_snapshot_change(previous_entries)
259
if self.kind != 'file':
261
self._snapshot_text(previous_entries, work_tree, weave_store)
264
def _snapshot_text(self, file_parents, work_tree, weave_store):
265
mutter('storing file {%s} in revision {%s}',
266
self.file_id, self.revision)
267
# special case to avoid diffing on renames or
269
if (len(file_parents) == 1
270
and self.text_sha1 == file_parents.values()[0].text_sha1
271
and self.text_size == file_parents.values()[0].text_size):
272
previous_ie = file_parents.values()[0]
273
weave_store.add_identical_text(
274
self.file_id, previous_ie.revision,
275
self.revision, file_parents)
277
new_lines = work_tree.get_file(self.file_id).readlines()
278
self._add_text_to_weave(new_lines, file_parents, weave_store)
279
self.text_sha1 = sha_strings(new_lines)
280
self.text_size = sum(map(len, new_lines))
187
282
def __eq__(self, other):
188
283
if not isinstance(other, InventoryEntry):
189
284
return NotImplemented
191
286
return (self.file_id == other.file_id) \
192
287
and (self.name == other.name) \
288
and (other.symlink_target == self.symlink_target) \
193
289
and (self.text_sha1 == other.text_sha1) \
194
290
and (self.text_size == other.text_size) \
195
291
and (self.text_id == other.text_id) \
196
292
and (self.parent_id == other.parent_id) \
197
293
and (self.kind == other.kind) \
198
and (self.text_version == other.text_version) \
199
and (self.name_version == other.name_version)
294
and (self.revision == other.revision)
202
296
def __ne__(self, other):
203
297
return not (self == other)
205
299
def __hash__(self):
206
300
raise ValueError('not hashable')
302
def _unchanged(self, path, previous_ie, work_tree):
304
# different inv parent
305
if previous_ie.parent_id != self.parent_id:
308
elif previous_ie.name != self.name:
310
if self.kind == 'symlink':
311
if self.symlink_target != previous_ie.symlink_target:
313
if self.kind == 'file':
314
if self.text_sha1 != previous_ie.text_sha1:
317
# FIXME: 20050930 probe for the text size when getting sha1
318
# in _read_tree_state
319
self.text_size = previous_ie.text_size
322
def _read_tree_state(self, path, work_tree):
323
if self.kind == 'symlink':
324
self.read_symlink_target(work_tree.abspath(path))
325
if self.kind == 'file':
326
self.text_sha1 = work_tree.get_file_sha1(self.file_id)
210
329
class RootEntry(InventoryEntry):