20
20
ROOT_ID = "TREE_ROOT"
23
import sys, os.path, types, re
26
29
from bzrlib.errors import BzrError, BzrCheckError
28
from bzrlib.osutils import uuid, quotefn, splitpath, joinpath, appendpath
31
from bzrlib.osutils import quotefn, splitpath, joinpath, appendpath
29
32
from bzrlib.trace import mutter
30
33
from bzrlib.errors import NotVersionedError
33
36
class InventoryEntry(object):
34
37
"""Description of a versioned file.
57
60
>>> i.add(InventoryEntry('123', 'src', 'directory', ROOT_ID))
61
InventoryEntry('123', 'src', kind='directory', parent_id='TREE_ROOT')
58
62
>>> i.add(InventoryEntry('2323', 'hello.c', 'file', parent_id='123'))
63
InventoryEntry('2323', 'hello.c', kind='file', parent_id='123')
59
64
>>> for j in i.iter_entries():
67
72
BzrError: inventory already contains entry with id {2323}
68
73
>>> i.add(InventoryEntry('2324', 'bye.c', 'file', '123'))
74
InventoryEntry('2324', 'bye.c', kind='file', parent_id='123')
69
75
>>> i.add(InventoryEntry('2325', 'wibble', 'directory', '123'))
76
InventoryEntry('2325', 'wibble', kind='directory', parent_id='123')
70
77
>>> i.path2id('src/wibble')
74
81
>>> i.add(InventoryEntry('2326', 'wibble.c', 'file', '2325'))
82
InventoryEntry('2326', 'wibble.c', kind='file', parent_id='2325')
76
84
InventoryEntry('2326', 'wibble.c', kind='file', parent_id='2325')
77
>>> for j in i.iter_entries():
79
... assert i.path2id(j[0])
85
>>> for path, entry in i.iter_entries():
86
... print path.replace('\\\\', '/') # for win32 os.sep
87
... assert i.path2id(path)
85
93
src/wibble/wibble.c
94
>>> i.id2path('2326').replace('\\\\', '/')
87
95
'src/wibble/wibble.c'
89
97
TODO: Maybe also keep the full path of the entry, and the children?
95
103
# directories, etc etc.
97
105
__slots__ = ['text_sha1', 'text_size', 'file_id', 'name', 'kind',
98
'text_id', 'parent_id', 'children', ]
106
'text_id', 'parent_id', 'children',
107
'text_version', 'entry_version', ]
100
110
def __init__(self, file_id, name, kind, parent_id, text_id=None):
101
111
"""Create an InventoryEntry
112
122
Traceback (most recent call last):
113
123
BzrCheckError: InventoryEntry name 'src/hello.c' is invalid
125
assert isinstance(name, basestring), name
115
126
if '/' in name or '\\' in name:
116
127
raise BzrCheckError('InventoryEntry name %r is invalid' % name)
129
self.text_version = None
130
self.entry_version = None
118
131
self.text_sha1 = None
119
132
self.text_size = None
121
133
self.file_id = file_id
160
def to_element(self):
161
"""Convert to XML element"""
162
from bzrlib.xml import Element
166
e.set('name', self.name)
167
e.set('file_id', self.file_id)
168
e.set('kind', self.kind)
170
if self.text_size != None:
171
e.set('text_size', '%d' % self.text_size)
173
for f in ['text_id', 'text_sha1']:
178
# to be conservative, we don't externalize the root pointers
179
# for now, leaving them as null in the xml form. in a future
180
# version it will be implied by nested elements.
181
if self.parent_id != ROOT_ID:
182
assert isinstance(self.parent_id, basestring)
183
e.set('parent_id', self.parent_id)
190
def from_element(cls, elt):
191
assert elt.tag == 'entry'
193
## original format inventories don't have a parent_id for
194
## nodes in the root directory, but it's cleaner to use one
196
parent_id = elt.get('parent_id')
197
if parent_id == None:
200
self = cls(elt.get('file_id'), elt.get('name'), elt.get('kind'), parent_id)
201
self.text_id = elt.get('text_id')
202
self.text_sha1 = elt.get('text_sha1')
204
## mutter("read inventoryentry: %r" % (elt.attrib))
206
v = elt.get('text_size')
207
self.text_size = v and int(v)
212
from_element = classmethod(from_element)
214
172
def __eq__(self, other):
215
173
if not isinstance(other, InventoryEntry):
216
174
return NotImplemented
221
179
and (self.text_size == other.text_size) \
222
180
and (self.text_id == other.text_id) \
223
181
and (self.parent_id == other.parent_id) \
224
and (self.kind == other.kind)
182
and (self.kind == other.kind) \
183
and (self.text_version == other.text_version) \
184
and (self.entry_version == other.entry_version)
227
187
def __ne__(self, other):
269
229
>>> inv = Inventory()
270
230
>>> inv.add(InventoryEntry('123-123', 'hello.c', 'file', ROOT_ID))
231
InventoryEntry('123-123', 'hello.c', kind='file', parent_id='TREE_ROOT')
271
232
>>> inv['123-123'].name
285
246
>>> inv = Inventory('TREE_ROOT-12345678-12345678')
286
247
>>> inv.add(InventoryEntry('123-123', 'hello.c', 'file', ROOT_ID))
248
InventoryEntry('123-123', 'hello.c', kind='file', parent_id='TREE_ROOT-12345678-12345678')
288
250
def __init__(self, root_id=ROOT_ID):
289
251
"""Create or read an inventory.
295
257
The inventory is created with a default root directory, with
298
# We are letting Branch(init=True) create a unique inventory
260
# We are letting Branch.initialize() create a unique inventory
299
261
# root id. Rather than generating a random one here.
300
262
#if root_id is None:
301
263
# root_id = bzrlib.branch.gen_file_id('TREE_ROOT')
372
334
>>> inv = Inventory()
373
335
>>> inv.add(InventoryEntry('123', 'foo.c', 'file', ROOT_ID))
336
InventoryEntry('123', 'foo.c', kind='file', parent_id='TREE_ROOT')
385
348
>>> inv = Inventory()
386
349
>>> inv.add(InventoryEntry('123123', 'hello.c', 'file', ROOT_ID))
350
InventoryEntry('123123', 'hello.c', kind='file', parent_id='TREE_ROOT')
387
351
>>> inv['123123'].name
407
371
"""Add entry to inventory.
409
373
To add a file to a branch ready to be committed, use Branch.add,
376
Returns the new entry object.
411
378
if entry.file_id in self._byid:
412
379
raise BzrError("inventory already contains entry with id {%s}" % entry.file_id)
426
393
self._byid[entry.file_id] = entry
427
394
parent.children[entry.name] = entry
430
398
def add_path(self, relpath, kind, file_id=None):
431
399
"""Add entry from a path.
433
The immediate parent must already be versioned"""
401
The immediate parent must already be versioned.
403
Returns the new entry object."""
434
404
from bzrlib.branch import gen_file_id
436
406
parts = bzrlib.osutils.splitpath(relpath)
456
426
>>> inv = Inventory()
457
427
>>> inv.add(InventoryEntry('123', 'foo.c', 'file', ROOT_ID))
428
InventoryEntry('123', 'foo.c', kind='file', parent_id='TREE_ROOT')
460
431
>>> del inv['123']
476
447
del self[ie.parent_id].children[ie.name]
479
def to_element(self):
480
"""Convert to XML Element"""
481
from bzrlib.xml import Element
483
e = Element('inventory')
485
if self.root.file_id not in (None, ROOT_ID):
486
e.set('file_id', self.root.file_id)
487
for path, ie in self.iter_entries():
488
e.append(ie.to_element())
492
def from_element(cls, elt):
493
"""Construct from XML Element
495
>>> inv = Inventory()
496
>>> inv.add(InventoryEntry('foo.c-123981239', 'foo.c', 'file', ROOT_ID))
497
>>> elt = inv.to_element()
498
>>> inv2 = Inventory.from_element(elt)
502
# XXXX: doctest doesn't run this properly under python2.3
503
assert elt.tag == 'inventory'
504
root_id = elt.get('file_id') or ROOT_ID
507
ie = InventoryEntry.from_element(e)
508
if ie.parent_id == ROOT_ID:
509
ie.parent_id = root_id
513
from_element = classmethod(from_element)
516
450
def __eq__(self, other):
517
451
"""Compare two sets by comparing their contents.
523
457
>>> i1.add(InventoryEntry('123', 'foo', 'file', ROOT_ID))
458
InventoryEntry('123', 'foo', kind='file', parent_id='TREE_ROOT')
526
461
>>> i2.add(InventoryEntry('123', 'foo', 'file', ROOT_ID))
462
InventoryEntry('123', 'foo', kind='file', parent_id='TREE_ROOT')