23
23
import sys, os.path, types, re
26
from cElementTree import Element, ElementTree, SubElement
28
from elementtree.ElementTree import Element, ElementTree, SubElement
30
from xml import XMLMixin
31
from errors import bailout, BzrError, BzrCheckError
26
from bzrlib.errors import BzrError, BzrCheckError
34
28
from bzrlib.osutils import uuid, quotefn, splitpath, joinpath, appendpath
35
29
from bzrlib.trace import mutter
30
from bzrlib.errors import NotVersionedError
37
class InventoryEntry(XMLMixin):
33
class InventoryEntry(object):
38
34
"""Description of a versioned file.
40
36
An InventoryEntry has the following fields, which are also
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():
68
66
>>> i.add(InventoryEntry('2323', 'bye.c', 'file', '123'))
69
67
Traceback (most recent call last):
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')
78
78
>>> i.add(InventoryEntry('2326', 'wibble.c', 'file', '2325'))
79
InventoryEntry('2326', 'wibble.c', kind='file', parent_id='2325')
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.
102
__slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
103
'text_id', 'parent_id', 'children', ]
104
105
def __init__(self, file_id, name, kind, parent_id, text_id=None):
105
106
"""Create an InventoryEntry
266
272
inserted, other than through the Inventory API.
268
274
>>> inv = Inventory()
269
>>> inv.write_xml(sys.stdout)
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
285
289
>>> [x[0] for x in inv.iter_entries()]
288
>>> inv.write_xml(sys.stdout)
290
<entry file_id="123-123" kind="file" name="hello.c" />
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')
295
def __init__(self, root_id=ROOT_ID):
295
296
"""Create or read an inventory.
297
298
If a working directory is specified, the inventory is read
301
302
The inventory is created with a default root directory, with
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.
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}
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')
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
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)
423
if entry.parent_id == ROOT_ID or entry.parent_id is None:
424
entry.parent_id = self.root.file_id
417
427
parent = self._byid[entry.parent_id]
419
bailout("parent_id {%s} not in inventory" % entry.parent_id)
429
raise BzrError("parent_id {%s} not in inventory" % entry.parent_id)
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))
425
435
self._byid[entry.file_id] = entry
426
436
parent.children[entry.name] = entry
429
440
def add_path(self, relpath, kind, file_id=None):
430
441
"""Add entry from a path.
432
443
The immediate parent must already be versioned"""
444
from bzrlib.branch import gen_file_id
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")
437
450
if file_id == None:
438
file_id = bzrlib.branch.gen_file_id(relpath)
440
parent_id = self.path2id(parts[:-1])
441
assert parent_id != None
451
file_id = gen_file_id(relpath)
453
parent_path = parts[:-1]
454
parent_id = self.path2id(parent_path)
455
if parent_id == None:
456
raise NotVersionedError(parent_path)
442
458
ie = InventoryEntry(file_id, parts[-1],
443
459
kind=kind, parent_id=parent_id)
444
460
return self.add(ie)
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')
454
471
>>> del inv['123']
473
490
def to_element(self):
474
491
"""Convert to XML Element"""
492
from bzrlib.xml import Element
475
494
e = Element('inventory')
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())
482
503
def from_element(cls, elt):
483
504
"""Construct from XML Element
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)
514
# XXXX: doctest doesn't run this properly under python2.3
492
515
assert elt.tag == 'inventory'
516
root_id = elt.get('file_id') or ROOT_ID
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
498
525
from_element = classmethod(from_element)
508
535
>>> i1.add(InventoryEntry('123', 'foo', 'file', ROOT_ID))
536
InventoryEntry('123', 'foo', kind='file', parent_id='TREE_ROOT')
511
539
>>> i2.add(InventoryEntry('123', 'foo', 'file', ROOT_ID))
540
InventoryEntry('123', 'foo', kind='file', parent_id='TREE_ROOT')
554
583
"""Return as a list the path to file_id."""
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)
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)
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)))
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)))
618
647
file_ie = self._byid[file_id]