19
19
# But those depend on its position within a particular inventory, and
20
20
# it would be nice not to need to hold the backpointer here.
22
# TODO: Perhaps split InventoryEntry into subclasses for files,
23
# directories, etc etc.
26
22
# This should really be an id randomly assigned when the tree is
27
23
# created, but it's not for now.
28
24
ROOT_ID = "TREE_ROOT"
79
75
>>> i = Inventory()
82
>>> i.add(InventoryEntry('123', 'src', 'directory', ROOT_ID))
83
InventoryEntry('123', 'src', kind='directory', parent_id='TREE_ROOT')
78
>>> i.add(InventoryDirectory('123', 'src', ROOT_ID))
79
InventoryDirectory('123', 'src', parent_id='TREE_ROOT')
84
80
>>> i.add(InventoryEntry('2323', 'hello.c', 'file', parent_id='123'))
85
81
InventoryEntry('2323', 'hello.c', kind='file', parent_id='123')
86
82
>>> for j in i.iter_entries():
89
('src', InventoryEntry('123', 'src', kind='directory', parent_id='TREE_ROOT'))
85
('src', InventoryDirectory('123', 'src', parent_id='TREE_ROOT'))
90
86
('src/hello.c', InventoryEntry('2323', 'hello.c', kind='file', parent_id='123'))
91
87
>>> i.add(InventoryEntry('2323', 'bye.c', 'file', '123'))
92
88
Traceback (most recent call last):
94
90
BzrError: inventory already contains entry with id {2323}
95
91
>>> i.add(InventoryEntry('2324', 'bye.c', 'file', '123'))
96
92
InventoryEntry('2324', 'bye.c', kind='file', parent_id='123')
97
>>> i.add(InventoryEntry('2325', 'wibble', 'directory', '123'))
98
InventoryEntry('2325', 'wibble', kind='directory', parent_id='123')
93
>>> i.add(InventoryDirectory('2325', 'wibble', '123'))
94
InventoryDirectory('2325', 'wibble', parent_id='123')
99
95
>>> i.path2id('src/wibble')
192
188
# TODO: would be cool to actually set it to the timestamp of the
193
189
# revision it was last changed
195
if self.kind == 'directory':
196
item.type = tarfile.DIRTYPE
201
elif self.kind == 'file':
202
item.type = tarfile.REGTYPE
203
fileobj = tree.get_file(self.file_id)
204
item.size = self.text_size
205
if tree.is_executable(self.file_id):
209
elif self.kind == 'symlink':
210
iterm.type = tarfile.SYMTYPE
214
item.linkname = self.symlink_target
216
raise BzrError("don't know how to export {%s} of kind %r" %
217
(self.file_id, self.kind))
191
fileobj = self._put_in_tar(item, tree)
218
192
return item, fileobj
220
194
def has_text(self):
256
230
self.text_id = text_id
257
231
self.parent_id = parent_id
258
232
self.symlink_target = None
259
if kind == 'directory':
263
elif kind == 'symlink':
233
if kind not in ('directory', 'file', 'symlink'):
266
234
raise BzrError("unhandled entry kind %r" % kind)
268
236
def kind_character(self):
269
237
"""Return a short kind indicator useful for appending to names."""
270
if self.kind == 'directory':
272
238
if self.kind == 'file':
274
240
if self.kind == 'symlink':
278
244
known_kinds = ('file', 'directory', 'symlink', 'root_directory')
246
def __new__(cls, file_id, name, kind, parent_id, text_id=None):
247
"""Factory method to return the appropriate concrete class."""
248
return object.__new__(InventoryEntry, file_id, name, kind, parent_id,
251
def _put_in_tar(self, item, tree):
252
"""populate item for stashing in a tar, and return the content stream.
254
If no content is available, return None.
256
if self.kind == 'file':
257
item.type = tarfile.REGTYPE
258
fileobj = tree.get_file(self.file_id)
259
item.size = self.text_size
260
if tree.is_executable(self.file_id):
264
elif self.kind == 'symlink':
265
iterm.type = tarfile.SYMTYPE
269
item.linkname = self.symlink_target
271
raise BzrError("don't know how to export {%s} of kind %r" %
272
(self.file_id, self.kind))
280
275
def put_on_disk(self, dest, dp, tree):
281
"""Create a representation of self on disk in the prefix dest."""
276
"""Create a representation of self on disk in the prefix dest.
278
This is a template method - implement _put_on_disk in subclasses.
282
280
fullpath = appendpath(dest, dp)
283
if self.kind == 'directory':
285
elif self.kind == 'file':
281
self._put_on_disk(fullpath, tree)
282
mutter(" export {%s} kind %s to %s" % (self.file_id, self.kind, fullpath))
284
def _put_on_disk(self, fullpath, tree):
285
"""Put this entry onto disk at fullpath, from tree tree."""
286
if self.kind == 'file':
286
287
pumpfile(tree.get_file(self.file_id), file(fullpath, 'wb'))
287
288
if tree.is_executable(self.file_id):
288
289
os.chmod(fullpath, 0755)
293
294
raise BzrError("Failed to create symlink %r -> %r, error: %s" % (fullpath, self.symlink_target, e))
295
296
raise BzrError("don't know how to export {%s} of kind %r" % (self.file_id, self.kind))
296
mutter(" export {%s} kind %s to %s" % (self.file_id, self.kind, fullpath))
298
298
def sorted_children(self):
299
299
l = self.children.items()
305
305
return kind in ('file', 'directory', 'symlink')
307
307
def check(self, checker, rev_id, inv, tree):
308
"""Check this inventory entry is intact.
310
This is a template method, override _check for kind specific
308
313
if self.parent_id != None:
309
314
if not inv.has_id(self.parent_id):
310
315
raise BzrCheckError('missing parent {%s} in inventory for revision {%s}'
311
316
% (self.parent_id, rev_id))
317
self._check(checker, rev_id, tree)
319
def _check(self, checker, rev_id, tree):
320
"""Check this inventory entry for kind specific errors."""
312
321
if self.kind == 'file':
313
322
revision = self.revision
314
323
t = (self.file_id, revision)
328
337
if self.text_sha1 != sha_strings(file_lines):
329
338
raise BzrCheckError('text {%s} wrong sha1' % self.text_id)
330
339
checker.checked_texts[t] = self.text_sha1
331
elif self.kind == 'directory':
332
if self.text_sha1 != None or self.text_size != None or self.text_id != None:
333
raise BzrCheckError('directory {%s} has text in revision {%s}'
334
% (self.file_id, rev_id))
335
elif self.kind == 'root_directory':
337
340
elif self.kind == 'symlink':
338
341
if self.text_sha1 != None or self.text_size != None or self.text_id != None:
339
342
raise BzrCheckError('symlink {%s} has text in revision {%s}'
470
473
class RootEntry(InventoryEntry):
475
def _check(self, checker, rev_id, tree):
476
"""See InventoryEntry._check"""
471
478
def __init__(self, file_id):
472
479
self.file_id = file_id
473
480
self.children = {}
475
482
self.parent_id = None
485
def __new__(cls, file_id):
486
"""Only present until the InventoryEntry.__new__ can go away."""
487
return object.__new__(RootEntry, file_id)
478
489
def __eq__(self, other):
479
490
if not isinstance(other, RootEntry):
480
491
return NotImplemented
483
494
and (self.children == other.children)
497
class InventoryDirectory(InventoryEntry):
498
"""A directory in an inventory."""
500
def _check(self, checker, rev_id, tree):
501
"""See InventoryEntry._check"""
502
if self.text_sha1 != None or self.text_size != None or self.text_id != None:
503
raise BzrCheckError('directory {%s} has text in revision {%s}'
504
% (self.file_id, rev_id))
507
other = InventoryDirectory(self.file_id, self.name, self.parent_id)
508
other.revision = self.revision
509
# note that children are *not* copied; they're pulled across when
513
def __init__(self, file_id, name, parent_id):
514
super(InventoryDirectory, self).__init__(file_id, name, 'directory',
518
def kind_character(self):
519
"""See InventoryEntry.kind_character."""
522
def __new__(cls, file_id, name, parent_id):
523
"""Only present until the InventoryEntry.__new__ can go away."""
524
result = object.__new__(InventoryDirectory, file_id, name, 'directory',
526
# type.__call__ is strange, it doesn't __init__ when the returned type
528
#result.__init__(file_id, name, 'directory', parent_id)
531
def _put_in_tar(self, item, tree):
532
"""See InventoryEntry._put_in_tar."""
533
item.type = tarfile.DIRTYPE
540
def _put_on_disk(self, fullpath, tree):
541
"""See InventoryEntry._put_on_disk."""
545
return ("%s(%r, %r, parent_id=%r)"
546
% (self.__class__.__name__,
487
552
class Inventory(object):
488
553
"""Inventory of versioned files in a tree.
701
766
if parent_id == None:
702
767
raise NotVersionedError(parent_path)
704
ie = InventoryEntry(file_id, parts[-1],
705
kind=kind, parent_id=parent_id)
769
if kind == 'directory':
770
ie = InventoryDirectory(file_id, parts[-1], parent_id)
772
ie = InventoryEntry(file_id, parts[-1],
773
kind=kind, parent_id=parent_id)
706
774
return self.add(ie)