At http://bazaar.launchpad.net/~lifeless/bzr/apply-inventory-delta
------------------------------------------------------------ revno: 4529 revision-id: [email protected] parent: [email protected] committer: Robert Collins <[email protected]> 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): -- bazaar-commits mailing list [email protected] https://lists.ubuntu.com/mailman/listinfo/bazaar-commits
