~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: Aaron Bentley
  • Date: 2005-09-10 22:55:35 UTC
  • mto: (1185.3.4)
  • mto: This revision was merged to the branch mainline in revision 1390.
  • Revision ID: aaron.bentley@utoronto.ca-20050910225535-3660c70bf335c618
Handled ancestors that are missing when finding a base

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 
23
23
import sys, os.path, types, re
24
24
 
25
 
try:
26
 
    from cElementTree import Element, ElementTree, SubElement
27
 
except ImportError:
28
 
    from elementtree.ElementTree import Element, ElementTree, SubElement
29
 
 
30
 
from xml import XMLMixin
31
 
from errors import bailout, BzrError, BzrCheckError
32
 
 
33
25
import bzrlib
 
26
from bzrlib.errors import BzrError, BzrCheckError
 
27
 
34
28
from bzrlib.osutils import uuid, quotefn, splitpath, joinpath, appendpath
35
29
from bzrlib.trace import mutter
 
30
from bzrlib.errors import NotVersionedError
 
31
        
36
32
 
37
 
class InventoryEntry(XMLMixin):
 
33
class InventoryEntry(object):
38
34
    """Description of a versioned file.
39
35
 
40
36
    An InventoryEntry has the following fields, which are also
59
55
    >>> i.path2id('')
60
56
    'TREE_ROOT'
61
57
    >>> i.add(InventoryEntry('123', 'src', 'directory', ROOT_ID))
 
58
    InventoryEntry('123', 'src', kind='directory', parent_id='TREE_ROOT')
62
59
    >>> i.add(InventoryEntry('2323', 'hello.c', 'file', parent_id='123'))
 
60
    InventoryEntry('2323', 'hello.c', kind='file', parent_id='123')
63
61
    >>> for j in i.iter_entries():
64
62
    ...   print j
65
63
    ... 
68
66
    >>> i.add(InventoryEntry('2323', 'bye.c', 'file', '123'))
69
67
    Traceback (most recent call last):
70
68
    ...
71
 
    BzrError: ('inventory already contains entry with id {2323}', [])
 
69
    BzrError: inventory already contains entry with id {2323}
72
70
    >>> i.add(InventoryEntry('2324', 'bye.c', 'file', '123'))
 
71
    InventoryEntry('2324', 'bye.c', kind='file', parent_id='123')
73
72
    >>> i.add(InventoryEntry('2325', 'wibble', 'directory', '123'))
 
73
    InventoryEntry('2325', 'wibble', kind='directory', parent_id='123')
74
74
    >>> i.path2id('src/wibble')
75
75
    '2325'
76
76
    >>> '2325' in i
77
77
    True
78
78
    >>> i.add(InventoryEntry('2326', 'wibble.c', 'file', '2325'))
 
79
    InventoryEntry('2326', 'wibble.c', kind='file', parent_id='2325')
79
80
    >>> i['2326']
80
81
    InventoryEntry('2326', 'wibble.c', kind='file', parent_id='2325')
81
82
    >>> for j in i.iter_entries():
98
99
    # TODO: split InventoryEntry into subclasses for files,
99
100
    # directories, etc etc.
100
101
 
101
 
    text_sha1 = None
102
 
    text_size = None
103
 
    
 
102
    __slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
 
103
                 'text_id', 'parent_id', 'children', ]
 
104
 
104
105
    def __init__(self, file_id, name, kind, parent_id, text_id=None):
105
106
        """Create an InventoryEntry
106
107
        
119
120
        if '/' in name or '\\' in name:
120
121
            raise BzrCheckError('InventoryEntry name %r is invalid' % name)
121
122
        
 
123
        self.text_sha1 = None
 
124
        self.text_size = None
 
125
    
122
126
        self.file_id = file_id
123
127
        self.name = name
124
128
        self.kind = kind
160
164
    
161
165
    def to_element(self):
162
166
        """Convert to XML element"""
 
167
        from bzrlib.xml import Element
 
168
        
163
169
        e = Element('entry')
164
170
 
165
171
        e.set('name', self.name)
248
254
 
249
255
 
250
256
 
251
 
class Inventory(XMLMixin):
 
257
class Inventory(object):
252
258
    """Inventory of versioned files in a tree.
253
259
 
254
260
    This describes which file_id is present at each point in the tree,
266
272
    inserted, other than through the Inventory API.
267
273
 
268
274
    >>> inv = Inventory()
269
 
    >>> inv.write_xml(sys.stdout)
270
 
    <inventory>
271
 
    </inventory>
272
275
    >>> inv.add(InventoryEntry('123-123', 'hello.c', 'file', ROOT_ID))
 
276
    InventoryEntry('123-123', 'hello.c', kind='file', parent_id='TREE_ROOT')
273
277
    >>> inv['123-123'].name
274
278
    'hello.c'
275
279
 
284
288
 
285
289
    >>> [x[0] for x in inv.iter_entries()]
286
290
    ['hello.c']
287
 
    
288
 
    >>> inv.write_xml(sys.stdout)
289
 
    <inventory>
290
 
    <entry file_id="123-123" kind="file" name="hello.c" />
291
 
    </inventory>
292
 
 
 
291
    >>> inv = Inventory('TREE_ROOT-12345678-12345678')
 
292
    >>> inv.add(InventoryEntry('123-123', 'hello.c', 'file', ROOT_ID))
 
293
    InventoryEntry('123-123', 'hello.c', kind='file', parent_id='TREE_ROOT-12345678-12345678')
293
294
    """
294
 
    def __init__(self):
 
295
    def __init__(self, root_id=ROOT_ID):
295
296
        """Create or read an inventory.
296
297
 
297
298
        If a working directory is specified, the inventory is read
301
302
        The inventory is created with a default root directory, with
302
303
        an id of None.
303
304
        """
304
 
        self.root = RootEntry(ROOT_ID)
 
305
        # We are letting Branch(init=True) create a unique inventory
 
306
        # root id. Rather than generating a random one here.
 
307
        #if root_id is None:
 
308
        #    root_id = bzrlib.branch.gen_file_id('TREE_ROOT')
 
309
        self.root = RootEntry(root_id)
305
310
        self._byid = {self.root.file_id: self.root}
306
311
 
307
312
 
373
378
 
374
379
        >>> inv = Inventory()
375
380
        >>> inv.add(InventoryEntry('123', 'foo.c', 'file', ROOT_ID))
 
381
        InventoryEntry('123', 'foo.c', kind='file', parent_id='TREE_ROOT')
376
382
        >>> '123' in inv
377
383
        True
378
384
        >>> '456' in inv
386
392
 
387
393
        >>> inv = Inventory()
388
394
        >>> inv.add(InventoryEntry('123123', 'hello.c', 'file', ROOT_ID))
 
395
        InventoryEntry('123123', 'hello.c', kind='file', parent_id='TREE_ROOT')
389
396
        >>> inv['123123'].name
390
397
        'hello.c'
391
398
        """
411
418
        To add  a file to a branch ready to be committed, use Branch.add,
412
419
        which calls this."""
413
420
        if entry.file_id in self._byid:
414
 
            bailout("inventory already contains entry with id {%s}" % entry.file_id)
 
421
            raise BzrError("inventory already contains entry with id {%s}" % entry.file_id)
 
422
 
 
423
        if entry.parent_id == ROOT_ID or entry.parent_id is None:
 
424
            entry.parent_id = self.root.file_id
415
425
 
416
426
        try:
417
427
            parent = self._byid[entry.parent_id]
418
428
        except KeyError:
419
 
            bailout("parent_id {%s} not in inventory" % entry.parent_id)
 
429
            raise BzrError("parent_id {%s} not in inventory" % entry.parent_id)
420
430
 
421
431
        if parent.children.has_key(entry.name):
422
 
            bailout("%s is already versioned" %
 
432
            raise BzrError("%s is already versioned" %
423
433
                    appendpath(self.id2path(parent.file_id), entry.name))
424
434
 
425
435
        self._byid[entry.file_id] = entry
426
436
        parent.children[entry.name] = entry
 
437
        return entry
427
438
 
428
439
 
429
440
    def add_path(self, relpath, kind, file_id=None):
430
441
        """Add entry from a path.
431
442
 
432
443
        The immediate parent must already be versioned"""
 
444
        from bzrlib.branch import gen_file_id
 
445
        
433
446
        parts = bzrlib.osutils.splitpath(relpath)
434
447
        if len(parts) == 0:
435
 
            bailout("cannot re-add root of inventory")
 
448
            raise BzrError("cannot re-add root of inventory")
436
449
 
437
450
        if file_id == None:
438
 
            file_id = bzrlib.branch.gen_file_id(relpath)
439
 
 
440
 
        parent_id = self.path2id(parts[:-1])
441
 
        assert parent_id != None
 
451
            file_id = gen_file_id(relpath)
 
452
 
 
453
        parent_path = parts[:-1]
 
454
        parent_id = self.path2id(parent_path)
 
455
        if parent_id == None:
 
456
            raise NotVersionedError(parent_path)
 
457
 
442
458
        ie = InventoryEntry(file_id, parts[-1],
443
459
                            kind=kind, parent_id=parent_id)
444
460
        return self.add(ie)
449
465
 
450
466
        >>> inv = Inventory()
451
467
        >>> inv.add(InventoryEntry('123', 'foo.c', 'file', ROOT_ID))
 
468
        InventoryEntry('123', 'foo.c', kind='file', parent_id='TREE_ROOT')
452
469
        >>> '123' in inv
453
470
        True
454
471
        >>> del inv['123']
472
489
 
473
490
    def to_element(self):
474
491
        """Convert to XML Element"""
 
492
        from bzrlib.xml import Element
 
493
        
475
494
        e = Element('inventory')
476
495
        e.text = '\n'
 
496
        if self.root.file_id not in (None, ROOT_ID):
 
497
            e.set('file_id', self.root.file_id)
477
498
        for path, ie in self.iter_entries():
478
499
            e.append(ie.to_element())
479
500
        return e
481
502
 
482
503
    def from_element(cls, elt):
483
504
        """Construct from XML Element
484
 
 
 
505
        
485
506
        >>> inv = Inventory()
486
507
        >>> inv.add(InventoryEntry('foo.c-123981239', 'foo.c', 'file', ROOT_ID))
 
508
        InventoryEntry('foo.c-123981239', 'foo.c', kind='file', parent_id='TREE_ROOT')
487
509
        >>> elt = inv.to_element()
488
510
        >>> inv2 = Inventory.from_element(elt)
489
511
        >>> inv2 == inv
490
512
        True
491
513
        """
 
514
        # XXXX: doctest doesn't run this properly under python2.3
492
515
        assert elt.tag == 'inventory'
493
 
        o = cls()
 
516
        root_id = elt.get('file_id') or ROOT_ID
 
517
        o = cls(root_id)
494
518
        for e in elt:
495
 
            o.add(InventoryEntry.from_element(e))
 
519
            ie = InventoryEntry.from_element(e)
 
520
            if ie.parent_id == ROOT_ID:
 
521
                ie.parent_id = root_id
 
522
            o.add(ie)
496
523
        return o
497
524
        
498
525
    from_element = classmethod(from_element)
506
533
        >>> i1 == i2
507
534
        True
508
535
        >>> i1.add(InventoryEntry('123', 'foo', 'file', ROOT_ID))
 
536
        InventoryEntry('123', 'foo', kind='file', parent_id='TREE_ROOT')
509
537
        >>> i1 == i2
510
538
        False
511
539
        >>> i2.add(InventoryEntry('123', 'foo', 'file', ROOT_ID))
 
540
        InventoryEntry('123', 'foo', kind='file', parent_id='TREE_ROOT')
512
541
        >>> i1 == i2
513
542
        True
514
543
        """
544
573
            try:
545
574
                ie = self._byid[file_id]
546
575
            except KeyError:
547
 
                bailout("file_id {%s} not found in inventory" % file_id)
 
576
                raise BzrError("file_id {%s} not found in inventory" % file_id)
548
577
            p.insert(0, ie.file_id)
549
578
            file_id = ie.parent_id
550
579
        return p
554
583
        """Return as a list the path to file_id."""
555
584
 
556
585
        # get all names, skipping root
557
 
        p = [self[fid].name for fid in self.get_idpath(file_id)[1:]]
 
586
        p = [self._byid[fid].name for fid in self.get_idpath(file_id)[1:]]
558
587
        return os.sep.join(p)
559
588
            
560
589
 
604
633
 
605
634
        This does not move the working file."""
606
635
        if not is_valid_name(new_name):
607
 
            bailout("not an acceptable filename: %r" % new_name)
 
636
            raise BzrError("not an acceptable filename: %r" % new_name)
608
637
 
609
638
        new_parent = self._byid[new_parent_id]
610
639
        if new_name in new_parent.children:
611
 
            bailout("%r already exists in %r" % (new_name, self.id2path(new_parent_id)))
 
640
            raise BzrError("%r already exists in %r" % (new_name, self.id2path(new_parent_id)))
612
641
 
613
642
        new_parent_idpath = self.get_idpath(new_parent_id)
614
643
        if file_id in new_parent_idpath:
615
 
            bailout("cannot move directory %r into a subdirectory of itself, %r"
 
644
            raise BzrError("cannot move directory %r into a subdirectory of itself, %r"
616
645
                    % (self.id2path(file_id), self.id2path(new_parent_id)))
617
646
 
618
647
        file_ie = self._byid[file_id]
629
658
 
630
659
 
631
660
 
632
 
_NAME_RE = re.compile(r'^[^/\\]+$')
 
661
_NAME_RE = None
633
662
 
634
663
def is_valid_name(name):
 
664
    global _NAME_RE
 
665
    if _NAME_RE == None:
 
666
        _NAME_RE = re.compile(r'^[^/\\]+$')
 
667
        
635
668
    return bool(_NAME_RE.match(name))