Rev 4529: Handle mismatches between inventory delta paths and actual paths found by traversing parent pointers. in http://bazaar.launchpad.net/~lifeless/bzr/apply-inventory-delta

Robert Collins robertc at robertcollins.net
Mon Jul 13 05:51:37 BST 2009


At http://bazaar.launchpad.net/~lifeless/bzr/apply-inventory-delta

------------------------------------------------------------
revno: 4529
revision-id: robertc at robertcollins.net-20090713045134-wwr4zgl1bne4ay66
parent: robertc at robertcollins.net-20090713040233-u4y2zlbahqwp21f2
committer: Robert Collins <robertc at robertcollins.net>
branch nick: apply-inventory-delta
timestamp: Mon 2009-07-13 14:51:34 +1000
message:
  Handle mismatches between inventory delta paths and actual paths found by traversing parent pointers.
=== modified file 'bzrlib/dirstate.py'
--- a/bzrlib/dirstate.py	2009-07-13 04:02:33 +0000
+++ b/bzrlib/dirstate.py	2009-07-13 04:51:34 +0000
@@ -204,6 +204,7 @@
 import bisect
 import binascii
 import errno
+import operator
 import os
 from stat import S_IEXEC
 import stat
@@ -1338,7 +1339,7 @@
                     insertions[child[0][2]] = (key, minikind, executable,
                                                fingerprint, new_child_path)
         try:
-            self._apply_removals(removals.values())
+            self._apply_removals(removals.iteritems())
             self._apply_insertions(insertions.values())
             # Validate parents
             self._after_delta_check_parents(parents, 0)
@@ -1352,11 +1353,23 @@
             raise errors.InconsistentDeltaDelta(delta, "error from _get_entry.")
 
     def _apply_removals(self, removals):
-        for path in sorted(removals, reverse=True):
+        for file_id, path in sorted(removals, reverse=True,
+            key=operator.itemgetter(1)):
             dirname, basename = osutils.split(path)
             block_i, entry_i, d_present, f_present = \
                 self._get_block_entry_index(dirname, basename, 0)
-            entry = self._dirblocks[block_i][1][entry_i]
+            try:
+                entry = self._dirblocks[block_i][1][entry_i]
+            except IndexError:
+                raise errors.InconsistentDelta(path, file_id,
+                    "Wrong path for old path.")
+            if not f_present or entry[1][0][0] in 'ar':
+                raise errors.InconsistentDelta(path, file_id,
+                    "Wrong path for old path.")
+            if file_id != entry[0][2]:
+                raise errors.InconsistentDelta(path, file_id,
+                    "Attempt to remove wrong has wrong id - found %r."
+                    % entry[0][2])
             self._make_absent(entry)
             # See if we have a malformed delta: deleting a directory must not
             # leave crud behind. This increases the number of bisects needed

=== modified file 'bzrlib/inventory.py'
--- a/bzrlib/inventory.py	2009-07-13 04:02:33 +0000
+++ b/bzrlib/inventory.py	2009-07-13 04:51:34 +0000
@@ -1089,6 +1089,9 @@
         See the inventory developers documentation for the theory behind
         inventory deltas.
 
+        If delta application fails the inventory is left in an indeterminate
+        state and must not be used.
+
         :param delta: A list of changes to apply. After all the changes are
             applied the final inventory must be internally consistent, but it
             is ok to supply changes which, if only half-applied would have an
@@ -1138,13 +1141,13 @@
         # modified children remaining by the time we examine it.
         for old_path, file_id in sorted(((op, f) for op, np, f, e in delta
                                         if op is not None), reverse=True):
-            if file_id not in self:
-                # adds come later
-                continue
             # Preserve unaltered children of file_id for later reinsertion.
             file_id_children = getattr(self[file_id], 'children', {})
             if len(file_id_children):
                 children[file_id] = file_id_children
+            if self.id2path(file_id) != old_path:
+                raise errors.InconsistentDelta(old_path, file_id,
+                    "Entry was at wrong other path %r." % self.id2path(file_id))
             # Remove file_id and the unaltered children. If file_id is not
             # being deleted it will be reinserted back later.
             self.remove_recursive_id(file_id)
@@ -1661,6 +1664,10 @@
                 old_key = None
             else:
                 old_key = (file_id,)
+                if self.id2path(file_id) != old_path:
+                    raise errors.InconsistentDelta(old_path, file_id,
+                        "Entry was at wrong other path %r." %
+                        self.id2path(file_id))
             id_to_entry_delta.append((old_key, new_key, new_value))
             if result.parent_id_basename_to_file_id is not None:
                 # parent_id, basename changes

=== modified file 'bzrlib/tests/test_inv.py'
--- a/bzrlib/tests/test_inv.py	2009-07-13 04:02:33 +0000
+++ b/bzrlib/tests/test_inv.py	2009-07-13 04:51:34 +0000
@@ -380,6 +380,50 @@
         self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
             inv, delta)
 
+    def test_old_parent_path_is_wrong(self):
+        inv = self.get_empty_inventory()
+        parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
+        parent1.revision = 'result'
+        parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
+        parent2.revision = 'result'
+        file1 = inventory.InventoryFile('id', 'path', 'p-2')
+        file1.revision = 'result'
+        file1.text_size = 0
+        file1.text_sha1 = ""
+        inv.add(parent1)
+        inv.add(parent2)
+        inv.add(file1)
+        # This delta claims that file1 was at dir/path, but actually it was at
+        # dir2/path if you follow the inventory parent structure.
+        delta = [(u'dir/path', None, 'id', None)]
+        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
+            inv, delta)
+
+    def test_old_parent_path_is_for_other_id(self):
+        inv = self.get_empty_inventory()
+        parent1 = inventory.InventoryDirectory('p-1', 'dir', inv.root.file_id)
+        parent1.revision = 'result'
+        parent2 = inventory.InventoryDirectory('p-2', 'dir2', inv.root.file_id)
+        parent2.revision = 'result'
+        file1 = inventory.InventoryFile('id', 'path', 'p-2')
+        file1.revision = 'result'
+        file1.text_size = 0
+        file1.text_sha1 = ""
+        file2 = inventory.InventoryFile('id2', 'path', 'p-1')
+        file2.revision = 'result'
+        file2.text_size = 0
+        file2.text_sha1 = ""
+        inv.add(parent1)
+        inv.add(parent2)
+        inv.add(file1)
+        inv.add(file2)
+        # This delta claims that file1 was at dir/path, but actually it was at
+        # dir2/path if you follow the inventory parent structure. At dir/path
+        # is another entry we should not delete.
+        delta = [(u'dir/path', None, 'id', None)]
+        self.assertRaises(errors.InconsistentDelta, self.apply_delta, self,
+            inv, delta)
+
 
 class TestInventoryEntry(TestCase):
 




More information about the bazaar-commits mailing list