126
127
'text_id', 'parent_id', 'children', 'executable',
129
def _add_text_to_weave(self, new_lines, parents, weave_store, transaction):
130
versionedfile = weave_store.get_weave_or_empty(self.file_id,
132
versionedfile.add_lines(self.revision, parents, new_lines)
133
versionedfile.clear_cache()
135
130
def detect_changes(self, old_entry):
136
131
"""Return a (text_modified, meta_modified) from this to old_entry.
426
423
self.revision = parent_ie.revision
427
424
return "unchanged"
428
425
return self._snapshot_into_revision(revision, previous_entries,
429
work_tree, weave_store, transaction)
426
work_tree, commit_builder)
431
428
def _snapshot_into_revision(self, revision, previous_entries, work_tree,
432
weave_store, transaction):
433
430
"""Record this revision unconditionally into a store.
435
432
The entry's last-changed revision property (`revision`) is updated to
442
439
mutter('new revision {%s} for {%s}', revision, self.file_id)
443
440
self.revision = revision
444
self._snapshot_text(previous_entries, work_tree, weave_store,
441
self._snapshot_text(previous_entries, work_tree, commit_builder)
447
def _snapshot_text(self, file_parents, work_tree, weave_store, transaction):
443
def _snapshot_text(self, file_parents, work_tree, commit_builder):
448
444
"""Record the 'text' of this entry, whatever form that takes.
450
446
This default implementation simply adds an empty text.
452
mutter('storing file {%s} in revision {%s}',
453
self.file_id, self.revision)
454
self._add_text_to_weave([], file_parents.keys(), weave_store, transaction)
448
raise NotImplementedError(self._snapshot_text)
456
450
def __eq__(self, other):
457
451
if not isinstance(other, InventoryEntry):
671
669
def _read_tree_state(self, path, work_tree):
672
670
"""See InventoryEntry._read_tree_state."""
673
self.text_sha1 = work_tree.get_file_sha1(self.file_id)
674
self.executable = work_tree.is_executable(self.file_id)
671
self.text_sha1 = work_tree.get_file_sha1(self.file_id, path=path)
672
# FIXME: 20050930 probe for the text size when getting sha1
673
# in _read_tree_state
674
self.executable = work_tree.is_executable(self.file_id, path=path)
676
676
def __repr__(self):
677
677
return ("%s(%r, %r, parent_id=%r, sha1=%r, len=%s)"
686
686
self.text_sha1 = None
687
687
self.executable = None
689
def _snapshot_text(self, file_parents, work_tree, versionedfile_store, transaction):
689
def _snapshot_text(self, file_parents, work_tree, commit_builder):
690
690
"""See InventoryEntry._snapshot_text."""
691
mutter('storing text of file {%s} in revision {%s} into %r',
692
self.file_id, self.revision, versionedfile_store)
693
# special case to avoid diffing on renames or
695
if (len(file_parents) == 1
696
and self.text_sha1 == file_parents.values()[0].text_sha1
697
and self.text_size == file_parents.values()[0].text_size):
698
previous_ie = file_parents.values()[0]
699
versionedfile = versionedfile_store.get_weave(self.file_id, transaction)
700
versionedfile.clone_text(self.revision, previous_ie.revision, file_parents.keys())
702
new_lines = work_tree.get_file(self.file_id).readlines()
703
self._add_text_to_weave(new_lines, file_parents.keys(), versionedfile_store,
705
self.text_sha1 = sha_strings(new_lines)
706
self.text_size = sum(map(len, new_lines))
691
def get_content_byte_lines():
692
return work_tree.get_file(self.file_id).readlines()
693
self.text_sha1, self.text_size = commit_builder.modified_file_text(
694
self.file_id, file_parents, get_content_byte_lines, self.text_sha1, self.text_size)
709
696
def _unchanged(self, previous_ie):
710
697
"""See InventoryEntry._unchanged."""
875
866
other.add(entry.copy())
879
869
def __iter__(self):
880
870
return iter(self._byid)
883
872
def __len__(self):
884
873
"""Returns number of entries."""
885
874
return len(self._byid)
888
876
def iter_entries(self, from_dir=None):
889
877
"""Return (path, entry) pairs, in order by name."""
893
elif isinstance(from_dir, basestring):
894
from_dir = self._byid[from_dir]
896
kids = from_dir.children.items()
898
for name, ie in kids:
900
if ie.kind == 'directory':
901
for cn, cie in self.iter_entries(from_dir=ie.file_id):
902
yield pathjoin(name, cn), cie
881
elif isinstance(from_dir, basestring):
882
from_dir = self._byid[from_dir]
884
# unrolling the recursive called changed the time from
885
# 440ms/663ms (inline/total) to 116ms/116ms
886
children = from_dir.children.items()
888
children = collections.deque(children)
889
stack = [(u'', children)]
891
from_dir_relpath, children = stack[-1]
894
name, ie = children.popleft()
896
# we know that from_dir_relpath never ends in a slash
897
# and 'f' doesn't begin with one, we can do a string op, rather
898
# than the checks of pathjoin(), though this means that all paths
900
path = from_dir_relpath + '/' + name
904
if ie.kind != 'directory':
907
# But do this child first
908
new_children = ie.children.items()
910
new_children = collections.deque(new_children)
911
stack.append((path, new_children))
912
# Break out of inner loop, so that we start outer loop with child
915
# if we finished all children, pop it off the stack
918
def iter_entries_by_dir(self, from_dir=None):
919
"""Iterate over the entries in a directory first order.
921
This returns all entries for a directory before returning
922
the entries for children of a directory. This is not
923
lexicographically sorted order, and is a hybrid between
924
depth-first and breadth-first.
926
:return: This yields (path, entry) pairs
928
# TODO? Perhaps this should return the from_dir so that the root is
929
# yielded? or maybe an option?
933
elif isinstance(from_dir, basestring):
934
from_dir = self._byid[from_dir]
936
stack = [(u'', from_dir)]
938
cur_relpath, cur_dir = stack.pop()
941
for child_name, child_ie in sorted(cur_dir.children.iteritems()):
943
child_relpath = cur_relpath + child_name
945
yield child_relpath, child_ie
947
if child_ie.kind == 'directory':
948
child_dirs.append((child_relpath+'/', child_ie))
949
stack.extend(reversed(child_dirs))
905
951
def entries(self):
906
952
"""Return list of (path, ie) for all entries except the root.
966
1008
return self._byid[file_id]
967
1009
except KeyError:
969
1011
raise BzrError("can't look up file_id None")
971
1013
raise BzrError("file_id {%s} not in inventory" % file_id)
974
1015
def get_file_kind(self, file_id):
975
1016
return self._byid[file_id].kind
977
1018
def get_child(self, parent_id, filename):
978
1019
return self[parent_id].children.get(filename)
981
1021
def add(self, entry):
982
1022
"""Add entry to inventory.