~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: Martin Pool
  • Date: 2005-05-30 01:39:13 UTC
  • Revision ID: mbp@sourcefrog.net-20050530013913-4ac43c29e1302170
- make sure any bzr output is flushed before 
  running external diff

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
 
# TODO: Maybe store inventory_id in the file?  Not really needed.
19
 
 
20
 
 
21
18
# This should really be an id randomly assigned when the tree is
22
19
# created, but it's not for now.
23
20
ROOT_ID = "TREE_ROOT"
24
21
 
25
22
 
26
23
import sys, os.path, types, re
27
 
from sets import Set
28
24
 
29
25
try:
30
26
    from cElementTree import Element, ElementTree, SubElement
32
28
    from elementtree.ElementTree import Element, ElementTree, SubElement
33
29
 
34
30
from xml import XMLMixin
35
 
from errors import bailout, BzrError
 
31
from errors import bailout, BzrError, BzrCheckError
36
32
 
37
33
import bzrlib
38
34
from bzrlib.osutils import uuid, quotefn, splitpath, joinpath, appendpath
101
97
 
102
98
    # TODO: split InventoryEntry into subclasses for files,
103
99
    # directories, etc etc.
 
100
 
 
101
    text_sha1 = None
 
102
    text_size = None
104
103
    
105
104
    def __init__(self, file_id, name, kind, parent_id, text_id=None):
106
105
        """Create an InventoryEntry
115
114
        '123'
116
115
        >>> e = InventoryEntry('123', 'src/hello.c', 'file', ROOT_ID)
117
116
        Traceback (most recent call last):
118
 
        BzrError: ("InventoryEntry name is not a simple filename: 'src/hello.c'", [])
 
117
        BzrCheckError: InventoryEntry name 'src/hello.c' is invalid
119
118
        """
120
 
        
121
 
        if len(splitpath(name)) != 1:
122
 
            bailout('InventoryEntry name is not a simple filename: %r'
123
 
                    % name)
 
119
        if '/' in name or '\\' in name:
 
120
            raise BzrCheckError('InventoryEntry name %r is invalid' % name)
124
121
        
125
122
        self.file_id = file_id
126
123
        self.name = name
127
124
        self.kind = kind
128
125
        self.text_id = text_id
129
126
        self.parent_id = parent_id
130
 
        self.text_sha1 = None
131
 
        self.text_size = None
132
127
        if kind == 'directory':
133
128
            self.children = {}
134
129
        elif kind == 'file':
149
144
                               self.parent_id, text_id=self.text_id)
150
145
        other.text_sha1 = self.text_sha1
151
146
        other.text_size = self.text_size
 
147
        # note that children are *not* copied; they're pulled across when
 
148
        # others are added
152
149
        return other
153
150
 
154
151
 
213
210
 
214
211
    from_element = classmethod(from_element)
215
212
 
216
 
    def __cmp__(self, other):
217
 
        if self is other:
218
 
            return 0
 
213
    def __eq__(self, other):
219
214
        if not isinstance(other, InventoryEntry):
220
215
            return NotImplemented
221
216
 
222
 
        return cmp(self.file_id, other.file_id) \
223
 
               or cmp(self.name, other.name) \
224
 
               or cmp(self.text_sha1, other.text_sha1) \
225
 
               or cmp(self.text_size, other.text_size) \
226
 
               or cmp(self.text_id, other.text_id) \
227
 
               or cmp(self.parent_id, other.parent_id) \
228
 
               or cmp(self.kind, other.kind)
 
217
        return (self.file_id == other.file_id) \
 
218
               and (self.name == other.name) \
 
219
               and (self.text_sha1 == other.text_sha1) \
 
220
               and (self.text_size == other.text_size) \
 
221
               and (self.text_id == other.text_id) \
 
222
               and (self.parent_id == other.parent_id) \
 
223
               and (self.kind == other.kind)
 
224
 
 
225
 
 
226
    def __ne__(self, other):
 
227
        return not (self == other)
 
228
 
 
229
    def __hash__(self):
 
230
        raise ValueError('not hashable')
229
231
 
230
232
 
231
233
 
237
239
        self.parent_id = None
238
240
        self.name = ''
239
241
 
240
 
    def __cmp__(self, other):
241
 
        if self is other:
242
 
            return 0
 
242
    def __eq__(self, other):
243
243
        if not isinstance(other, RootEntry):
244
244
            return NotImplemented
245
 
        return cmp(self.file_id, other.file_id) \
246
 
               or cmp(self.children, other.children)
 
245
        
 
246
        return (self.file_id == other.file_id) \
 
247
               and (self.children == other.children)
247
248
 
248
249
 
249
250
 
290
291
    </inventory>
291
292
 
292
293
    """
293
 
 
294
 
    ## TODO: Make sure only canonical filenames are stored.
295
 
 
296
 
    ## TODO: Do something sensible about the possible collisions on
297
 
    ## case-losing filesystems.  Perhaps we should just always forbid
298
 
    ## such collisions.
299
 
 
300
 
    ## TODO: No special cases for root, rather just give it a file id
301
 
    ## like everything else.
302
 
 
303
 
    ## TODO: Probably change XML serialization to use nesting rather
304
 
    ## than parent_id pointers.
305
 
 
306
 
    ## TODO: Perhaps hold the ElementTree in memory and work directly
307
 
    ## on that rather than converting into Python objects every time?
308
 
 
309
294
    def __init__(self):
310
295
        """Create or read an inventory.
311
296
 
344
329
            if ie.kind == 'directory':
345
330
                for cn, cie in self.iter_entries(from_dir=ie.file_id):
346
331
                    yield os.path.join(name, cn), cie
347
 
                    
 
332
 
 
333
 
 
334
    def entries(self):
 
335
        """Return list of (path, ie) for all entries except the root.
 
336
 
 
337
        This may be faster than iter_entries.
 
338
        """
 
339
        accum = []
 
340
        def descend(dir_ie, dir_path):
 
341
            kids = dir_ie.children.items()
 
342
            kids.sort()
 
343
            for name, ie in kids:
 
344
                child_path = os.path.join(dir_path, name)
 
345
                accum.append((child_path, ie))
 
346
                if ie.kind == 'directory':
 
347
                    descend(ie, child_path)
 
348
 
 
349
        descend(self.root, '')
 
350
        return accum
348
351
 
349
352
 
350
353
    def directories(self):
351
 
        """Return (path, entry) pairs for all directories.
 
354
        """Return (path, entry) pairs for all directories, including the root.
352
355
        """
353
 
        def descend(parent_ie):
354
 
            parent_name = parent_ie.name
355
 
            yield parent_name, parent_ie
356
 
 
357
 
            # directory children in sorted order
358
 
            dn = []
359
 
            for ie in parent_ie.children.itervalues():
360
 
                if ie.kind == 'directory':
361
 
                    dn.append((ie.name, ie))
362
 
            dn.sort()
 
356
        accum = []
 
357
        def descend(parent_ie, parent_path):
 
358
            accum.append((parent_path, parent_ie))
363
359
            
364
 
            for name, child_ie in dn:
365
 
                for sub_name, sub_ie in descend(child_ie):
366
 
                    yield appendpath(parent_name, sub_name), sub_ie
 
360
            kids = [(ie.name, ie) for ie in parent_ie.children.itervalues() if ie.kind == 'directory']
 
361
            kids.sort()
367
362
 
368
 
        for name, ie in descend(self.root):
369
 
            yield name, ie
 
363
            for name, child_ie in kids:
 
364
                child_path = os.path.join(parent_path, name)
 
365
                descend(child_ie, child_path)
 
366
        descend(self.root, '')
 
367
        return accum
370
368
        
371
369
 
372
370
 
391
389
        >>> inv['123123'].name
392
390
        'hello.c'
393
391
        """
394
 
        if file_id == None:
395
 
            raise BzrError("can't look up file_id None")
396
 
            
397
392
        try:
398
393
            return self._byid[file_id]
399
394
        except KeyError:
400
 
            raise BzrError("file_id {%s} not in inventory" % file_id)
401
 
 
 
395
            if file_id == None:
 
396
                raise BzrError("can't look up file_id None")
 
397
            else:
 
398
                raise BzrError("file_id {%s} not in inventory" % file_id)
 
399
 
 
400
 
 
401
    def get_file_kind(self, file_id):
 
402
        return self._byid[file_id].kind
402
403
 
403
404
    def get_child(self, parent_id, filename):
404
405
        return self[parent_id].children.get(filename)
469
470
        del self[ie.parent_id].children[ie.name]
470
471
 
471
472
 
472
 
    def id_set(self):
473
 
        return Set(self._byid)
474
 
 
475
 
 
476
473
    def to_element(self):
477
474
        """Convert to XML Element"""
478
475
        e = Element('inventory')
501
498
    from_element = classmethod(from_element)
502
499
 
503
500
 
504
 
    def __cmp__(self, other):
 
501
    def __eq__(self, other):
505
502
        """Compare two sets by comparing their contents.
506
503
 
507
504
        >>> i1 = Inventory()
515
512
        >>> i1 == i2
516
513
        True
517
514
        """
518
 
        if self is other:
519
 
            return 0
520
 
        
521
515
        if not isinstance(other, Inventory):
522
516
            return NotImplemented
523
517
 
524
 
        if self.id_set() ^ other.id_set():
525
 
            return 1
526
 
 
527
 
        for file_id in self._byid:
528
 
            c = cmp(self[file_id], other[file_id])
529
 
            if c: return c
530
 
 
531
 
        return 0
 
518
        if len(self._byid) != len(other._byid):
 
519
            # shortcut: obviously not the same
 
520
            return False
 
521
 
 
522
        return self._byid == other._byid
 
523
 
 
524
 
 
525
    def __ne__(self, other):
 
526
        return not (self == other)
 
527
 
 
528
 
 
529
    def __hash__(self):
 
530
        raise ValueError('not hashable')
 
531
 
532
532
 
533
533
 
534
534
    def get_idpath(self, file_id):