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