~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: Martin Pool
  • Date: 2005-07-04 12:26:02 UTC
  • Revision ID: mbp@sourcefrog.net-20050704122602-69901910521e62c3
- check command checks that all inventory-ids are the same as in the revision.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
 
23
23
import sys, os.path, types, re
24
 
from sets import Set
25
 
 
26
 
try:
27
 
    from cElementTree import Element, ElementTree, SubElement
28
 
except ImportError:
29
 
    from elementtree.ElementTree import Element, ElementTree, SubElement
30
 
 
31
 
from xml import XMLMixin
32
 
from errors import bailout, BzrError, BzrCheckError
33
24
 
34
25
import bzrlib
 
26
from bzrlib.errors import BzrError, BzrCheckError
 
27
 
35
28
from bzrlib.osutils import uuid, quotefn, splitpath, joinpath, appendpath
36
29
from bzrlib.trace import mutter
37
30
 
38
 
class InventoryEntry(XMLMixin):
 
31
class InventoryEntry(object):
39
32
    """Description of a versioned file.
40
33
 
41
34
    An InventoryEntry has the following fields, which are also
69
62
    >>> i.add(InventoryEntry('2323', 'bye.c', 'file', '123'))
70
63
    Traceback (most recent call last):
71
64
    ...
72
 
    BzrError: ('inventory already contains entry with id {2323}', [])
 
65
    BzrError: inventory already contains entry with id {2323}
73
66
    >>> i.add(InventoryEntry('2324', 'bye.c', 'file', '123'))
74
67
    >>> i.add(InventoryEntry('2325', 'wibble', 'directory', '123'))
75
68
    >>> i.path2id('src/wibble')
145
138
                               self.parent_id, text_id=self.text_id)
146
139
        other.text_sha1 = self.text_sha1
147
140
        other.text_size = self.text_size
 
141
        # note that children are *not* copied; they're pulled across when
 
142
        # others are added
148
143
        return other
149
144
 
150
145
 
159
154
    
160
155
    def to_element(self):
161
156
        """Convert to XML element"""
 
157
        from bzrlib.xml import Element
 
158
        
162
159
        e = Element('entry')
163
160
 
164
161
        e.set('name', self.name)
209
206
 
210
207
    from_element = classmethod(from_element)
211
208
 
212
 
    def __cmp__(self, other):
213
 
        if self is other:
214
 
            return 0
 
209
    def __eq__(self, other):
215
210
        if not isinstance(other, InventoryEntry):
216
211
            return NotImplemented
217
212
 
218
 
        return cmp(self.file_id, other.file_id) \
219
 
               or cmp(self.name, other.name) \
220
 
               or cmp(self.text_sha1, other.text_sha1) \
221
 
               or cmp(self.text_size, other.text_size) \
222
 
               or cmp(self.text_id, other.text_id) \
223
 
               or cmp(self.parent_id, other.parent_id) \
224
 
               or cmp(self.kind, other.kind)
 
213
        return (self.file_id == other.file_id) \
 
214
               and (self.name == other.name) \
 
215
               and (self.text_sha1 == other.text_sha1) \
 
216
               and (self.text_size == other.text_size) \
 
217
               and (self.text_id == other.text_id) \
 
218
               and (self.parent_id == other.parent_id) \
 
219
               and (self.kind == other.kind)
 
220
 
 
221
 
 
222
    def __ne__(self, other):
 
223
        return not (self == other)
 
224
 
 
225
    def __hash__(self):
 
226
        raise ValueError('not hashable')
225
227
 
226
228
 
227
229
 
233
235
        self.parent_id = None
234
236
        self.name = ''
235
237
 
236
 
    def __cmp__(self, other):
237
 
        if self is other:
238
 
            return 0
 
238
    def __eq__(self, other):
239
239
        if not isinstance(other, RootEntry):
240
240
            return NotImplemented
241
 
        return cmp(self.file_id, other.file_id) \
242
 
               or cmp(self.children, other.children)
243
 
 
244
 
 
245
 
 
246
 
class Inventory(XMLMixin):
 
241
        
 
242
        return (self.file_id == other.file_id) \
 
243
               and (self.children == other.children)
 
244
 
 
245
 
 
246
 
 
247
class Inventory(object):
247
248
    """Inventory of versioned files in a tree.
248
249
 
249
250
    This describes which file_id is present at each point in the tree,
261
262
    inserted, other than through the Inventory API.
262
263
 
263
264
    >>> inv = Inventory()
264
 
    >>> inv.write_xml(sys.stdout)
265
 
    <inventory>
266
 
    </inventory>
267
265
    >>> inv.add(InventoryEntry('123-123', 'hello.c', 'file', ROOT_ID))
268
266
    >>> inv['123-123'].name
269
267
    'hello.c'
279
277
 
280
278
    >>> [x[0] for x in inv.iter_entries()]
281
279
    ['hello.c']
282
 
    
283
 
    >>> inv.write_xml(sys.stdout)
284
 
    <inventory>
285
 
    <entry file_id="123-123" kind="file" name="hello.c" />
286
 
    </inventory>
287
 
 
288
280
    """
289
281
    def __init__(self):
290
282
        """Create or read an inventory.
324
316
            if ie.kind == 'directory':
325
317
                for cn, cie in self.iter_entries(from_dir=ie.file_id):
326
318
                    yield os.path.join(name, cn), cie
327
 
                    
 
319
 
 
320
 
 
321
    def entries(self):
 
322
        """Return list of (path, ie) for all entries except the root.
 
323
 
 
324
        This may be faster than iter_entries.
 
325
        """
 
326
        accum = []
 
327
        def descend(dir_ie, dir_path):
 
328
            kids = dir_ie.children.items()
 
329
            kids.sort()
 
330
            for name, ie in kids:
 
331
                child_path = os.path.join(dir_path, name)
 
332
                accum.append((child_path, ie))
 
333
                if ie.kind == 'directory':
 
334
                    descend(ie, child_path)
 
335
 
 
336
        descend(self.root, '')
 
337
        return accum
328
338
 
329
339
 
330
340
    def directories(self):
331
 
        """Return (path, entry) pairs for all directories.
 
341
        """Return (path, entry) pairs for all directories, including the root.
332
342
        """
333
 
        def descend(parent_ie):
334
 
            parent_name = parent_ie.name
335
 
            yield parent_name, parent_ie
336
 
 
337
 
            # directory children in sorted order
338
 
            dn = []
339
 
            for ie in parent_ie.children.itervalues():
340
 
                if ie.kind == 'directory':
341
 
                    dn.append((ie.name, ie))
342
 
            dn.sort()
 
343
        accum = []
 
344
        def descend(parent_ie, parent_path):
 
345
            accum.append((parent_path, parent_ie))
343
346
            
344
 
            for name, child_ie in dn:
345
 
                for sub_name, sub_ie in descend(child_ie):
346
 
                    yield appendpath(parent_name, sub_name), sub_ie
 
347
            kids = [(ie.name, ie) for ie in parent_ie.children.itervalues() if ie.kind == 'directory']
 
348
            kids.sort()
347
349
 
348
 
        for name, ie in descend(self.root):
349
 
            yield name, ie
 
350
            for name, child_ie in kids:
 
351
                child_path = os.path.join(parent_path, name)
 
352
                descend(child_ie, child_path)
 
353
        descend(self.root, '')
 
354
        return accum
350
355
        
351
356
 
352
357
 
393
398
        To add  a file to a branch ready to be committed, use Branch.add,
394
399
        which calls this."""
395
400
        if entry.file_id in self._byid:
396
 
            bailout("inventory already contains entry with id {%s}" % entry.file_id)
 
401
            raise BzrError("inventory already contains entry with id {%s}" % entry.file_id)
397
402
 
398
403
        try:
399
404
            parent = self._byid[entry.parent_id]
400
405
        except KeyError:
401
 
            bailout("parent_id {%s} not in inventory" % entry.parent_id)
 
406
            raise BzrError("parent_id {%s} not in inventory" % entry.parent_id)
402
407
 
403
408
        if parent.children.has_key(entry.name):
404
 
            bailout("%s is already versioned" %
 
409
            raise BzrError("%s is already versioned" %
405
410
                    appendpath(self.id2path(parent.file_id), entry.name))
406
411
 
407
412
        self._byid[entry.file_id] = entry
412
417
        """Add entry from a path.
413
418
 
414
419
        The immediate parent must already be versioned"""
 
420
        from bzrlib.errors import NotVersionedError
 
421
        
415
422
        parts = bzrlib.osutils.splitpath(relpath)
416
423
        if len(parts) == 0:
417
 
            bailout("cannot re-add root of inventory")
 
424
            raise BzrError("cannot re-add root of inventory")
418
425
 
419
426
        if file_id == None:
420
 
            file_id = bzrlib.branch.gen_file_id(relpath)
421
 
 
422
 
        parent_id = self.path2id(parts[:-1])
423
 
        assert parent_id != None
 
427
            from bzrlib.branch import gen_file_id
 
428
            file_id = gen_file_id(relpath)
 
429
 
 
430
        parent_path = parts[:-1]
 
431
        parent_id = self.path2id(parent_path)
 
432
        if parent_id == None:
 
433
            raise NotVersionedError(parent_path)
 
434
 
424
435
        ie = InventoryEntry(file_id, parts[-1],
425
436
                            kind=kind, parent_id=parent_id)
426
437
        return self.add(ie)
452
463
        del self[ie.parent_id].children[ie.name]
453
464
 
454
465
 
455
 
    def id_set(self):
456
 
        return Set(self._byid)
457
 
 
458
 
 
459
466
    def to_element(self):
460
467
        """Convert to XML Element"""
 
468
        from bzrlib.xml import Element
 
469
        
461
470
        e = Element('inventory')
462
471
        e.text = '\n'
463
472
        for path, ie in self.iter_entries():
484
493
    from_element = classmethod(from_element)
485
494
 
486
495
 
487
 
    def __cmp__(self, other):
 
496
    def __eq__(self, other):
488
497
        """Compare two sets by comparing their contents.
489
498
 
490
499
        >>> i1 = Inventory()
498
507
        >>> i1 == i2
499
508
        True
500
509
        """
501
 
        if self is other:
502
 
            return 0
503
 
        
504
510
        if not isinstance(other, Inventory):
505
511
            return NotImplemented
506
512
 
507
 
        if self.id_set() ^ other.id_set():
508
 
            return 1
509
 
 
510
 
        for file_id in self._byid:
511
 
            c = cmp(self[file_id], other[file_id])
512
 
            if c: return c
513
 
 
514
 
        return 0
 
513
        if len(self._byid) != len(other._byid):
 
514
            # shortcut: obviously not the same
 
515
            return False
 
516
 
 
517
        return self._byid == other._byid
 
518
 
 
519
 
 
520
    def __ne__(self, other):
 
521
        return not (self == other)
 
522
 
 
523
 
 
524
    def __hash__(self):
 
525
        raise ValueError('not hashable')
 
526
 
515
527
 
516
528
 
517
529
    def get_idpath(self, file_id):
527
539
            try:
528
540
                ie = self._byid[file_id]
529
541
            except KeyError:
530
 
                bailout("file_id {%s} not found in inventory" % file_id)
 
542
                raise BzrError("file_id {%s} not found in inventory" % file_id)
531
543
            p.insert(0, ie.file_id)
532
544
            file_id = ie.parent_id
533
545
        return p
587
599
 
588
600
        This does not move the working file."""
589
601
        if not is_valid_name(new_name):
590
 
            bailout("not an acceptable filename: %r" % new_name)
 
602
            raise BzrError("not an acceptable filename: %r" % new_name)
591
603
 
592
604
        new_parent = self._byid[new_parent_id]
593
605
        if new_name in new_parent.children:
594
 
            bailout("%r already exists in %r" % (new_name, self.id2path(new_parent_id)))
 
606
            raise BzrError("%r already exists in %r" % (new_name, self.id2path(new_parent_id)))
595
607
 
596
608
        new_parent_idpath = self.get_idpath(new_parent_id)
597
609
        if file_id in new_parent_idpath:
598
 
            bailout("cannot move directory %r into a subdirectory of itself, %r"
 
610
            raise BzrError("cannot move directory %r into a subdirectory of itself, %r"
599
611
                    % (self.id2path(file_id), self.id2path(new_parent_id)))
600
612
 
601
613
        file_ie = self._byid[file_id]