435
def snapshot(self, revision, path, previous_entries,
436
work_tree, commit_builder):
437
"""Make a snapshot of this entry which may or may not have changed.
439
This means that all its fields are populated, that it has its
440
text stored in the text store or weave.
442
:return: True if anything was recorded
444
# cannot be unchanged unless there is only one parent file rev.
445
self._read_tree_state(path, work_tree)
446
if len(previous_entries) == 1:
447
parent_ie = previous_entries.values()[0]
448
if self._unchanged(parent_ie):
449
self.revision = parent_ie.revision
451
self.revision = revision
452
return self._snapshot_text(previous_entries, work_tree, commit_builder)
454
def _snapshot_text(self, file_parents, work_tree, commit_builder):
455
"""Record the 'text' of this entry, whatever form that takes.
457
:return: True if anything was recorded
459
raise NotImplementedError(self._snapshot_text)
461
435
def __eq__(self, other):
462
436
if not isinstance(other, InventoryEntry):
463
437
return NotImplemented
716
685
def _forget_tree_state(self):
717
686
self.text_sha1 = None
719
def snapshot(self, revision, path, previous_entries,
720
work_tree, commit_builder):
721
"""See InventoryEntry.snapshot."""
722
# Note: We use a custom implementation of this method for files
723
# because it's a performance critical part of commit.
725
# If this is the initial commit for this file, we know the sha is
726
# coming later so skip calculating it now (in _read_tree_state())
727
if len(previous_entries) == 0:
728
self.executable = work_tree.is_executable(self.file_id, path=path)
730
self._read_tree_state(path, work_tree)
732
# If nothing is changed from the sole parent, there's nothing to do
733
if len(previous_entries) == 1:
734
parent_ie = previous_entries.values()[0]
735
if self._unchanged(parent_ie):
736
self.revision = parent_ie.revision
739
# Add the file to the repository
740
self.revision = revision
741
def get_content_byte_lines():
742
return work_tree.get_file(self.file_id, path).readlines()
743
self.text_sha1, self.text_size = commit_builder.modified_file_text(
744
self.file_id, previous_entries, get_content_byte_lines,
745
self.text_sha1, self.text_size)
748
688
def _unchanged(self, previous_ie):
749
689
"""See InventoryEntry._unchanged."""
750
690
compatible = super(InventoryFile, self)._unchanged(previous_ie)
940
877
self.revision_id = revision_id
880
return "<Inventory object at %x, contents=%r>" % (id(self), self._byid)
882
def apply_delta(self, delta):
883
"""Apply a delta to this inventory.
885
:param delta: A list of changes to apply. After all the changes are
886
applied the final inventory must be internally consistent, but it
887
is ok to supply changes which, if only half-applied would have an
888
invalid result - such as supplying two changes which rename two
889
files, 'A' and 'B' with each other : [('A', 'B', 'A-id', a_entry),
890
('B', 'A', 'B-id', b_entry)].
892
Each change is a tuple, of the form (old_path, new_path, file_id,
895
When new_path is None, the change indicates the removal of an entry
896
from the inventory and new_entry will be ignored (using None is
897
appropriate). If new_path is not None, then new_entry must be an
898
InventoryEntry instance, which will be incorporated into the
899
inventory (and replace any existing entry with the same file id).
901
When old_path is None, the change indicates the addition of
902
a new entry to the inventory.
904
When neither new_path nor old_path are None, the change is a
905
modification to an entry, such as a rename, reparent, kind change
908
The children attribute of new_entry is ignored. This is because
909
this method preserves children automatically across alterations to
910
the parent of the children, and cases where the parent id of a
911
child is changing require the child to be passed in as a separate
912
change regardless. E.g. in the recursive deletion of a directory -
913
the directory's children must be included in the delta, or the
914
final inventory will be invalid.
917
# Remove all affected items which were in the original inventory,
918
# starting with the longest paths, thus ensuring parents are examined
919
# after their children, which means that everything we examine has no
920
# modified children remaining by the time we examine it.
921
for old_path, file_id in sorted(((op, f) for op, np, f, e in delta
922
if op is not None), reverse=True):
923
if file_id not in self:
926
# Preserve unaltered children of file_id for later reinsertion.
927
children[file_id] = getattr(self[file_id], 'children', {})
928
# Remove file_id and the unaltered children. If file_id is not
929
# being deleted it will be reinserted back later.
930
self.remove_recursive_id(file_id)
931
# Insert all affected which should be in the new inventory, reattaching
932
# their children if they had any. This is done from shortest path to
933
# longest, ensuring that items which were modified and whose parents in
934
# the resulting inventory were also modified, are inserted after their
936
for new_path, new_entry in sorted((np, e) for op, np, f, e in
937
delta if np is not None):
938
if new_entry.kind == 'directory':
939
new_entry.children = children.get(new_entry.file_id, {})
942
942
def _set_root(self, ie):
944
944
self._byid = {self.root.file_id: self.root}
1418
1425
file_id = generate_ids.gen_file_id(name)
1420
1427
file_id = osutils.safe_file_id(file_id)
1428
name = ensure_normalized_name(name)
1430
factory = entry_factory[kind]
1432
raise BzrError("unknown kind %r" % kind)
1433
return factory(file_id, name, parent_id)
1436
def ensure_normalized_name(name):
1439
:raises InvalidNormalization: When name is not normalized, and cannot be
1440
accessed on this platform by the normalized path.
1441
:return: The NFC/NFKC normalised version of name.
1422
1443
#------- This has been copied to bzrlib.dirstate.DirState.add, please
1423
1444
# keep them synchronised.
1424
1445
# we dont import normalized_filename directly because we want to be