~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/inventory.py

  • Committer: aaron.bentley at utoronto
  • Date: 2005-09-04 02:59:56 UTC
  • mfrom: (1172)
  • mto: (1185.3.4)
  • mto: This revision was merged to the branch mainline in revision 1178.
  • Revision ID: aaron.bentley@utoronto.ca-20050904025956-776ba4f07de97700
Merged mpool's latest changes (~0.0.7)

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 bzrlib.xml import XMLMixin
 
25
import bzrlib
31
26
from bzrlib.errors import BzrError, BzrCheckError
32
27
 
33
 
import bzrlib
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
    ... 
70
68
    ...
71
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
        """
413
420
        if entry.file_id in self._byid:
414
421
            raise BzrError("inventory already contains entry with id {%s}" % entry.file_id)
415
422
 
 
423
        if entry.parent_id == ROOT_ID or entry.parent_id is None:
 
424
            entry.parent_id = self.root.file_id
 
425
 
416
426
        try:
417
427
            parent = self._byid[entry.parent_id]
418
428
        except KeyError:
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
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
        """
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
 
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))