27
27
# created, but it's not for now.
28
28
ROOT_ID = "TREE_ROOT"
34
from bzrlib.lazy_import import lazy_import
35
lazy_import(globals(), """
49
from bzrlib.errors import (
39
from bzrlib import errors, osutils
40
from bzrlib.osutils import (pumpfile, quotefn, splitpath, joinpath,
41
pathjoin, sha_strings)
42
from bzrlib.errors import (NotVersionedError, InvalidEntryName,
43
BzrError, BzrCheckError, BinaryFile)
53
44
from bzrlib.trace import mutter
90
81
InventoryDirectory('123', 'src', parent_id='TREE_ROOT', revision=None)
91
82
>>> i.add(InventoryFile('2323', 'hello.c', parent_id='123'))
92
83
InventoryFile('2323', 'hello.c', parent_id='123', sha1=None, len=None)
93
>>> shouldbe = {0: '', 1: 'src', 2: 'src/hello.c'}
84
>>> shouldbe = {0: '', 1: 'src', 2: pathjoin('src','hello.c')}
94
85
>>> for ix, j in enumerate(i.iter_entries()):
95
86
... print (j[0] == shouldbe[ix], j[1])
97
(True, InventoryDirectory('TREE_ROOT', '', parent_id=None, revision=None))
88
(True, RootEntry('TREE_ROOT', u'', parent_id=None, revision=None))
98
89
(True, InventoryDirectory('123', 'src', parent_id='TREE_ROOT', revision=None))
99
90
(True, InventoryFile('2323', 'hello.c', parent_id='123', sha1=None, len=None))
100
91
>>> i.add(InventoryFile('2323', 'bye.c', '123'))
255
246
def get_tar_item(self, root, dp, now, tree):
256
247
"""Get a tarfile item and a file stream for its content."""
257
item = tarfile.TarInfo(osutils.pathjoin(root, dp).encode('utf8'))
248
item = tarfile.TarInfo(pathjoin(root, dp))
258
249
# TODO: would be cool to actually set it to the timestamp of the
259
250
# revision it was last changed
290
281
assert isinstance(name, basestring), name
291
282
if '/' in name or '\\' in name:
292
raise errors.InvalidEntryName(name=name)
283
raise InvalidEntryName(name=name)
293
284
self.executable = False
294
285
self.revision = None
295
286
self.text_sha1 = None
304
295
"""Return a short kind indicator useful for appending to names."""
305
296
raise BzrError('unknown kind %r' % self.kind)
307
known_kinds = ('file', 'directory', 'symlink')
298
known_kinds = ('file', 'directory', 'symlink', 'root_directory')
309
300
def _put_in_tar(self, item, tree):
310
301
"""populate item for stashing in a tar, and return the content stream.
320
311
This is a template method - implement _put_on_disk in subclasses.
322
fullpath = osutils.pathjoin(dest, dp)
313
fullpath = pathjoin(dest, dp)
323
314
self._put_on_disk(fullpath, tree)
324
315
# mutter(" export {%s} kind %s to %s", self.file_id,
325
316
# self.kind, fullpath)
335
326
def versionable_kind(kind):
336
return (kind in ('file', 'directory', 'symlink'))
327
return kind in ('file', 'directory', 'symlink')
338
329
def check(self, checker, rev_id, inv, tree):
339
330
"""Check this inventory entry is intact.
518
509
def __init__(self, file_id):
519
510
self.file_id = file_id
520
511
self.children = {}
521
self.kind = 'directory'
512
self.kind = 'root_directory'
522
513
self.parent_id = None
524
515
self.revision = None
525
symbol_versioning.warn('RootEntry is deprecated as of bzr 0.10.'
526
' Please use InventoryDirectory instead.',
527
DeprecationWarning, stacklevel=2)
529
517
def __eq__(self, other):
530
518
if not isinstance(other, RootEntry):
654
642
text_diff(to_label, to_text,
655
643
from_label, from_text, output_to)
656
except errors.BinaryFile:
658
646
label_pair = (to_label, from_label)
686
674
def _put_on_disk(self, fullpath, tree):
687
675
"""See InventoryEntry._put_on_disk."""
688
osutils.pumpfile(tree.get_file(self.file_id), file(fullpath, 'wb'))
676
pumpfile(tree.get_file(self.file_id), file(fullpath, 'wb'))
689
677
if tree.is_executable(self.file_id):
690
678
os.chmod(fullpath, 0755)
858
846
>>> inv = Inventory('TREE_ROOT-12345678-12345678')
859
847
>>> inv.add(InventoryFile('123-123', 'hello.c', ROOT_ID))
860
Traceback (most recent call last):
861
BzrError: parent_id {TREE_ROOT} not in inventory
862
>>> inv.add(InventoryFile('123-123', 'hello.c', 'TREE_ROOT-12345678-12345678'))
863
848
InventoryFile('123-123', 'hello.c', parent_id='TREE_ROOT-12345678-12345678', sha1=None, len=None)
865
850
def __init__(self, root_id=ROOT_ID, revision_id=None):
872
857
The inventory is created with a default root directory, with
875
if root_id is not None:
876
self._set_root(InventoryDirectory(root_id, '', None))
860
# We are letting Branch.create() create a unique inventory
861
# root id. Rather than generating a random one here.
863
# root_id = bzrlib.branch.gen_file_id('TREE_ROOT')
864
self.root = RootEntry(root_id)
865
# FIXME: this isn't ever used, changing it to self.revision may break
866
# things. TODO make everything use self.revision_id
880
867
self.revision_id = revision_id
882
def _set_root(self, ie):
884
868
self._byid = {self.root.file_id: self.root}
903
887
def iter_entries(self, from_dir=None):
904
888
"""Return (path, entry) pairs, in order by name."""
905
889
if from_dir is None:
906
if self.root is None:
908
891
from_dir = self.root
909
892
yield '', self.root
910
893
elif isinstance(from_dir, basestring):
944
927
# if we finished all children, pop it off the stack
947
def iter_entries_by_dir(self, from_dir=None, specific_file_ids=None):
930
def iter_entries_by_dir(self, from_dir=None):
948
931
"""Iterate over the entries in a directory first order.
950
933
This returns all entries for a directory before returning
957
940
# TODO? Perhaps this should return the from_dir so that the root is
958
941
# yielded? or maybe an option?
959
942
if from_dir is None:
960
if self.root is None:
962
# Optimize a common case
963
if specific_file_ids is not None and len(specific_file_ids) == 1:
964
file_id = list(specific_file_ids)[0]
966
yield self.id2path(file_id), self[file_id]
968
944
from_dir = self.root
969
if (specific_file_ids is None or
970
self.root.file_id in specific_file_ids):
972
946
elif isinstance(from_dir, basestring):
973
947
from_dir = self._byid[from_dir]
975
if specific_file_ids is not None:
977
def add_ancestors(file_id):
978
if file_id not in self:
980
parent_id = self[file_id].parent_id
981
if parent_id is None:
983
if parent_id not in parents:
984
parents.add(parent_id)
985
add_ancestors(parent_id)
986
for file_id in specific_file_ids:
987
add_ancestors(file_id)
991
949
stack = [(u'', from_dir)]
998
956
child_relpath = cur_relpath + child_name
1000
if (specific_file_ids is None or
1001
child_ie.file_id in specific_file_ids):
1002
yield child_relpath, child_ie
958
yield child_relpath, child_ie
1004
960
if child_ie.kind == 'directory':
1005
if parents is None or child_ie.file_id in parents:
1006
child_dirs.append((child_relpath+'/', child_ie))
961
child_dirs.append((child_relpath+'/', child_ie))
1007
962
stack.extend(reversed(child_dirs))
1009
964
def entries(self):
1016
971
kids = dir_ie.children.items()
1018
973
for name, ie in kids:
1019
child_path = osutils.pathjoin(dir_path, name)
974
child_path = pathjoin(dir_path, name)
1020
975
accum.append((child_path, ie))
1021
976
if ie.kind == 'directory':
1022
977
descend(ie, child_path)
1037
992
for name, child_ie in kids:
1038
child_path = osutils.pathjoin(parent_path, name)
993
child_path = pathjoin(parent_path, name)
1039
994
descend(child_ie, child_path)
1040
995
descend(self.root, u'')
1051
1006
>>> '456' in inv
1054
return (file_id in self._byid)
1009
return file_id in self._byid
1056
1011
def __getitem__(self, file_id):
1057
1012
"""Return the entry for given file_id.
1066
1021
return self._byid[file_id]
1067
1022
except KeyError:
1068
# really we're passing an inventory, not a tree...
1069
raise errors.NoSuchId(self, file_id)
1024
raise BzrError("can't look up file_id None")
1026
raise BzrError("file_id {%s} not in inventory" % file_id)
1071
1028
def get_file_kind(self, file_id):
1072
1029
return self._byid[file_id].kind
1085
1042
if entry.file_id in self._byid:
1086
1043
raise BzrError("inventory already contains entry with id {%s}" % entry.file_id)
1088
if entry.parent_id is None:
1089
assert self.root is None and len(self._byid) == 0
1090
self._set_root(entry)
1045
if entry.parent_id == ROOT_ID or entry.parent_id is None:
1046
entry.parent_id = self.root.file_id
1093
1049
parent = self._byid[entry.parent_id]
1094
1050
except KeyError:
1097
1053
if entry.name in parent.children:
1098
1054
raise BzrError("%s is already versioned" %
1099
osutils.pathjoin(self.id2path(parent.file_id), entry.name))
1055
pathjoin(self.id2path(parent.file_id), entry.name))
1101
1057
self._byid[entry.file_id] = entry
1102
1058
parent.children[entry.name] = entry
1114
1070
if len(parts) == 0:
1115
1071
if file_id is None:
1116
file_id = generate_ids.gen_root_id()
1117
self.root = InventoryDirectory(file_id, '', None)
1072
file_id = bzrlib.workingtree.gen_root_id()
1073
self.root = RootEntry(file_id)
1118
1074
self._byid = {self.root.file_id: self.root}
1121
1077
parent_path = parts[:-1]
1122
1078
parent_id = self.path2id(parent_path)
1123
1079
if parent_id is None:
1124
raise errors.NotVersionedError(path=parent_path)
1080
raise NotVersionedError(path=parent_path)
1125
1081
ie = make_entry(kind, parts[-1], parent_id, file_id)
1126
1082
return self.add(ie)
1222
1178
Returns None IFF the path is not found.
1224
if isinstance(name, basestring):
1225
name = osutils.splitpath(name)
1180
if isinstance(name, types.StringTypes):
1181
name = splitpath(name)
1227
1183
# mutter("lookup path %r" % name)
1229
1185
parent = self.root
1234
children = getattr(parent, 'children', None)
1235
if children is None:
1188
cie = parent.children[f]
1238
1189
assert cie.name == f
1239
1190
assert cie.parent_id == parent.file_id
1248
1199
return bool(self.path2id(names))
1250
1201
def has_id(self, file_id):
1251
return (file_id in self._byid)
1253
def remove_recursive_id(self, file_id):
1254
"""Remove file_id, and children, from the inventory.
1256
:param file_id: A file_id to remove.
1258
to_find_delete = [self._byid[file_id]]
1260
while to_find_delete:
1261
ie = to_find_delete.pop()
1262
to_delete.append(ie.file_id)
1263
if ie.kind == 'directory':
1264
to_find_delete.extend(ie.children.values())
1265
for file_id in reversed(to_delete):
1267
del self._byid[file_id]
1268
if ie.parent_id is not None:
1269
del self[ie.parent_id].children[ie.name]
1202
return self._byid.has_key(file_id)
1271
1204
def rename(self, file_id, new_parent_id, new_name):
1272
1205
"""Move a file within the inventory.
1297
1230
file_ie.name = new_name
1298
1231
file_ie.parent_id = new_parent_id
1300
def is_root(self, file_id):
1301
return self.root is not None and file_id == self.root.file_id
1304
1234
def make_entry(kind, name, parent_id, file_id=None):
1305
1235
"""Create an inventory entry.
1310
1240
:param file_id: the file_id to use. if None, one will be created.
1312
1242
if file_id is None:
1313
file_id = generate_ids.gen_file_id(name)
1243
file_id = bzrlib.workingtree.gen_file_id(name)
1315
1245
norm_name, can_access = osutils.normalized_filename(name)
1316
1246
if norm_name != name: