~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: Robert Collins
  • Date: 2005-10-03 14:28:37 UTC
  • mto: (1393.1.30)
  • mto: This revision was merged to the branch mainline in revision 1400.
  • Revision ID: robertc@robertcollins.net-20051003142837-78bf906d4edcbd62
factor out inventory directory logic into 'InventoryDirectory' class

Show diffs side-by-side

added added

removed removed

Lines of Context:
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.
21
21
 
22
 
# TODO: Perhaps split InventoryEntry into subclasses for files,
23
 
# directories, etc etc.
24
 
 
25
 
 
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()
80
76
    >>> i.path2id('')
81
77
    'TREE_ROOT'
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():
87
83
    ...   print j
88
84
    ... 
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')
100
96
    '2325'
101
97
    >>> '2325' in i
192
188
        # TODO: would be cool to actually set it to the timestamp of the
193
189
        # revision it was last changed
194
190
        item.mtime = now
195
 
        if self.kind == 'directory':
196
 
            item.type = tarfile.DIRTYPE
197
 
            fileobj = None
198
 
            item.name += '/'
199
 
            item.size = 0
200
 
            item.mode = 0755
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):
206
 
                item.mode = 0755
207
 
            else:
208
 
                item.mode = 0644
209
 
        elif self.kind == 'symlink':
210
 
            iterm.type = tarfile.SYMTYPE
211
 
            fileobj = None
212
 
            item.size = 0
213
 
            item.mode = 0755
214
 
            item.linkname = self.symlink_target
215
 
        else:
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
219
193
 
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':
260
 
            self.children = {}
261
 
        elif kind == 'file':
262
 
            pass
263
 
        elif kind == 'symlink':
264
 
            pass
265
 
        else:
 
233
        if kind not in ('directory', 'file', 'symlink'):
266
234
            raise BzrError("unhandled entry kind %r" % kind)
267
235
 
268
236
    def kind_character(self):
269
237
        """Return a short kind indicator useful for appending to names."""
270
 
        if self.kind == 'directory':
271
 
            return '/'
272
238
        if self.kind == 'file':
273
239
            return ''
274
240
        if self.kind == 'symlink':
277
243
 
278
244
    known_kinds = ('file', 'directory', 'symlink', 'root_directory')
279
245
 
 
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,
 
249
                              text_id)
 
250
 
 
251
    def _put_in_tar(self, item, tree):
 
252
        """populate item for stashing in a tar, and return the content stream.
 
253
 
 
254
        If no content is available, return None.
 
255
        """
 
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):
 
261
                item.mode = 0755
 
262
            else:
 
263
                item.mode = 0644
 
264
        elif self.kind == 'symlink':
 
265
            iterm.type = tarfile.SYMTYPE
 
266
            fileobj = None
 
267
            item.size = 0
 
268
            item.mode = 0755
 
269
            item.linkname = self.symlink_target
 
270
        else:
 
271
            raise BzrError("don't know how to export {%s} of kind %r" %
 
272
                    (self.file_id, self.kind))
 
273
        return fileobj
 
274
 
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.
 
277
        
 
278
        This is a template method - implement _put_on_disk in subclasses.
 
279
        """
282
280
        fullpath = appendpath(dest, dp)
283
 
        if self.kind == 'directory':
284
 
            os.mkdir(fullpath)
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))
 
283
 
 
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))
294
295
        else:
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))
297
297
 
298
298
    def sorted_children(self):
299
299
        l = self.children.items()
305
305
        return kind in ('file', 'directory', 'symlink')
306
306
 
307
307
    def check(self, checker, rev_id, inv, tree):
 
308
        """Check this inventory entry is intact.
 
309
 
 
310
        This is a template method, override _check for kind specific
 
311
        tests.
 
312
        """
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)
 
318
 
 
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':
336
 
            pass
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}'
468
471
 
469
472
 
470
473
class RootEntry(InventoryEntry):
 
474
 
 
475
    def _check(self, checker, rev_id, tree):
 
476
        """See InventoryEntry._check"""
 
477
 
471
478
    def __init__(self, file_id):
472
479
        self.file_id = file_id
473
480
        self.children = {}
475
482
        self.parent_id = None
476
483
        self.name = ''
477
484
 
 
485
    def __new__(cls, file_id):
 
486
        """Only present until the InventoryEntry.__new__ can go away."""
 
487
        return object.__new__(RootEntry, file_id)
 
488
 
478
489
    def __eq__(self, other):
479
490
        if not isinstance(other, RootEntry):
480
491
            return NotImplemented
483
494
               and (self.children == other.children)
484
495
 
485
496
 
 
497
class InventoryDirectory(InventoryEntry):
 
498
    """A directory in an inventory."""
 
499
 
 
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))
 
505
 
 
506
    def copy(self):
 
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
 
510
        # others are added
 
511
        return other
 
512
 
 
513
    def __init__(self, file_id, name, parent_id):
 
514
        super(InventoryDirectory, self).__init__(file_id, name, 'directory',
 
515
                                                 parent_id)
 
516
        self.children = {}
 
517
 
 
518
    def kind_character(self):
 
519
        """See InventoryEntry.kind_character."""
 
520
        return '/'
 
521
 
 
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',
 
525
                                parent_id)
 
526
        # type.__call__ is strange, it doesn't __init__ when the returned type
 
527
        # differs
 
528
        #result.__init__(file_id, name, 'directory', parent_id)
 
529
        return result
 
530
 
 
531
    def _put_in_tar(self, item, tree):
 
532
        """See InventoryEntry._put_in_tar."""
 
533
        item.type = tarfile.DIRTYPE
 
534
        fileobj = None
 
535
        item.name += '/'
 
536
        item.size = 0
 
537
        item.mode = 0755
 
538
        return fileobj
 
539
 
 
540
    def _put_on_disk(self, fullpath, tree):
 
541
        """See InventoryEntry._put_on_disk."""
 
542
        os.mkdir(fullpath)
 
543
 
 
544
    def __repr__(self):
 
545
        return ("%s(%r, %r, parent_id=%r)"
 
546
                % (self.__class__.__name__,
 
547
                   self.file_id,
 
548
                   self.name,
 
549
                   self.parent_id))
 
550
 
486
551
 
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)
703
768
 
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)
 
771
        else:
 
772
            ie = InventoryEntry(file_id, parts[-1],
 
773
                                kind=kind, parent_id=parent_id)
706
774
        return self.add(ie)
707
775
 
708
776