19
19
# TODO: Maybe store inventory_id in the file? Not really needed.
21
__copyright__ = "Copyright (C) 2005 Canonical Ltd."
22
21
__author__ = "Martin Pool <mbp@canonical.com>"
24
# This should really be an id randomly assigned when the tree is
25
# created, but it's not for now.
24
29
import sys, os.path, types, re
25
30
from sets import Set
60
65
>>> i = Inventory()
62
>>> i.add(InventoryEntry('123', 'src', kind='directory'))
63
>>> i.add(InventoryEntry('2323', 'hello.c', parent_id='123'))
68
>>> i.add(InventoryEntry('123', 'src', 'directory', ROOT_ID))
69
>>> i.add(InventoryEntry('2323', 'hello.c', 'file', parent_id='123'))
64
70
>>> for j in i.iter_entries():
67
('src', InventoryEntry('123', 'src', kind='directory', parent_id=None))
73
('src', InventoryEntry('123', 'src', kind='directory', parent_id='TREE_ROOT'))
68
74
('src/hello.c', InventoryEntry('2323', 'hello.c', kind='file', parent_id='123'))
69
>>> i.add(InventoryEntry('2323', 'bye.c', parent_id='123'))
75
>>> i.add(InventoryEntry('2323', 'bye.c', 'file', '123'))
70
76
Traceback (most recent call last):
72
78
BzrError: ('inventory already contains entry with id {2323}', [])
73
>>> i.add(InventoryEntry('2324', 'bye.c', parent_id='123'))
74
>>> i.add(InventoryEntry('2325', 'wibble', parent_id='123', kind='directory'))
79
>>> i.add(InventoryEntry('2324', 'bye.c', 'file', '123'))
80
>>> i.add(InventoryEntry('2325', 'wibble', 'directory', '123'))
75
81
>>> i.path2id('src/wibble')
79
>>> i.add(InventoryEntry('2326', 'wibble.c', parent_id='2325'))
85
>>> i.add(InventoryEntry('2326', 'wibble.c', 'file', '2325'))
81
87
InventoryEntry('2326', 'wibble.c', kind='file', parent_id='2325')
82
88
>>> for j in i.iter_entries():
95
101
But those depend on its position within a particular inventory, and
96
102
it would be nice not to need to hold the backpointer here.
98
def __init__(self, file_id, name, kind='file', text_id=None,
105
# TODO: split InventoryEntry into subclasses for files,
106
# directories, etc etc.
108
def __init__(self, file_id, name, kind, parent_id, text_id=None):
100
109
"""Create an InventoryEntry
102
111
The filename must be a single component, relative to the
103
112
parent directory; it cannot be a whole path or relative name.
105
>>> e = InventoryEntry('123', 'hello.c')
114
>>> e = InventoryEntry('123', 'hello.c', 'file', ROOT_ID)
110
>>> e = InventoryEntry('123', 'src/hello.c')
119
>>> e = InventoryEntry('123', 'src/hello.c', 'file', ROOT_ID)
111
120
Traceback (most recent call last):
112
121
BzrError: ("InventoryEntry name is not a simple filename: 'src/hello.c'", [])
138
149
other = InventoryEntry(self.file_id, self.name, self.kind,
139
self.text_id, self.parent_id)
150
self.parent_id, text_id=self.text_id)
140
151
other.text_sha1 = self.text_sha1
141
152
other.text_size = self.text_size
159
170
e.set('file_id', self.file_id)
160
171
e.set('kind', self.kind)
162
if self.text_size is not None:
173
if self.text_size != None:
163
174
e.set('text_size', '%d' % self.text_size)
165
for f in ['text_id', 'text_sha1', 'parent_id']:
176
for f in ['text_id', 'text_sha1']:
166
177
v = getattr(self, f)
181
# to be conservative, we don't externalize the root pointers
182
# for now, leaving them as null in the xml form. in a future
183
# version it will be implied by nested elements.
184
if self.parent_id != ROOT_ID:
185
assert isinstance(self.parent_id, basestring)
186
e.set('parent_id', self.parent_id)
175
193
def from_element(cls, elt):
176
194
assert elt.tag == 'entry'
177
self = cls(elt.get('file_id'), elt.get('name'), elt.get('kind'))
196
## original format inventories don't have a parent_id for
197
## nodes in the root directory, but it's cleaner to use one
199
parent_id = elt.get('parent_id')
200
if parent_id == None:
203
self = cls(elt.get('file_id'), elt.get('name'), elt.get('kind'), parent_id)
178
204
self.text_id = elt.get('text_id')
179
205
self.text_sha1 = elt.get('text_sha1')
180
self.parent_id = elt.get('parent_id')
182
207
## mutter("read inventoryentry: %r" % (elt.attrib))
291
316
The inventory is created with a default root directory, with
294
self.root = RootEntry(None)
295
self._byid = {None: self.root}
319
self.root = RootEntry(ROOT_ID)
320
self._byid = {self.root.file_id: self.root}
298
323
def __iter__(self):
362
387
"""Return the entry for given file_id.
364
389
>>> inv = Inventory()
365
>>> inv.add(InventoryEntry('123123', 'hello.c'))
390
>>> inv.add(InventoryEntry('123123', 'hello.c', 'file', ROOT_ID))
366
391
>>> inv['123123'].name
369
return self._byid[file_id]
395
bailout("can't look up file_id None")
398
return self._byid[file_id]
400
bailout("file_id {%s} not in inventory" % file_id)
372
403
def get_child(self, parent_id, filename):
385
416
parent = self._byid[entry.parent_id]
387
bailout("parent_id %r not in inventory" % entry.parent_id)
418
bailout("parent_id {%s} not in inventory" % entry.parent_id)
389
420
if parent.children.has_key(entry.name):
390
421
bailout("%s is already versioned" %
402
433
if len(parts) == 0:
403
434
bailout("cannot re-add root of inventory")
406
437
file_id = bzrlib.branch.gen_file_id(relpath)
408
439
parent_id = self.path2id(parts[:-1])
440
assert parent_id != None
409
441
ie = InventoryEntry(file_id, parts[-1],
410
442
kind=kind, parent_id=parent_id)
411
443
return self.add(ie)
454
486
"""Construct from XML Element
456
488
>>> inv = Inventory()
457
>>> inv.add(InventoryEntry('foo.c-123981239', 'foo.c'))
489
>>> inv.add(InventoryEntry('foo.c-123981239', 'foo.c', 'file', ROOT_ID))
458
490
>>> elt = inv.to_element()
459
491
>>> inv2 = Inventory.from_element(elt)
476
508
>>> i2 = Inventory()
479
>>> i1.add(InventoryEntry('123', 'foo'))
511
>>> i1.add(InventoryEntry('123', 'foo', 'file', ROOT_ID))
482
>>> i2.add(InventoryEntry('123', 'foo'))
514
>>> i2.add(InventoryEntry('123', 'foo', 'file', ROOT_ID))
505
537
The list contains one element for each directory followed by
506
538
the id of the file itself. So the length of the returned list
507
539
is equal to the depth of the file in the tree, counting the
508
root directory as depth 0.
540
root directory as depth 1.
511
543
while file_id != None:
512
ie = self._byid[file_id]
545
ie = self._byid[file_id]
547
bailout("file_id {%s} not found in inventory" % file_id)
513
548
p.insert(0, ie.file_id)
514
549
file_id = ie.parent_id
535
568
This returns the entry of the last component in the path,
536
569
which may be either a file or a directory.
571
Returns None iff the path is not found.
538
573
if isinstance(name, types.StringTypes):
539
574
name = splitpath(name)
576
mutter("lookup path %r" % name)
544
581
cie = parent.children[f]
545
582
assert cie.name == f
583
assert cie.parent_id == parent.file_id
548
586
# or raise an error?