At http://bazaar.launchpad.net/~lifeless/bzr/apply-inventory-delta
------------------------------------------------------------ revno: 4528 revision-id: [email protected] parent: [email protected] committer: Robert Collins <[email protected]> branch nick: apply-inventory-delta timestamp: Mon 2009-07-13 14:02:33 +1000 message: Handle deltas with new paths not matching the actual path. === modified file 'bzrlib/dirstate.py' --- a/bzrlib/dirstate.py 2009-07-13 01:16:24 +0000 +++ b/bzrlib/dirstate.py 2009-07-13 04:02:33 +0000 @@ -1297,6 +1297,7 @@ inventory._check_delta_unique_new_paths( inventory._check_delta_ids_match_entry(delta))), reverse=True): if (file_id in insertions) or (file_id in removals): + self._changes_aborted = True raise errors.InconsistentDelta(old_path or new_path, file_id, "repeated file_id") if old_path is not None: @@ -1336,10 +1337,19 @@ child_basename) insertions[child[0][2]] = (key, minikind, executable, fingerprint, new_child_path) - self._apply_removals(removals.values()) - self._apply_insertions(insertions.values()) - # Validate parents - self._after_delta_check_parents(parents, 0) + try: + self._apply_removals(removals.values()) + self._apply_insertions(insertions.values()) + # Validate parents + self._after_delta_check_parents(parents, 0) + except errors.BzrError, e: + if 'integrity error' not in str(e): + raise + # _get_entry raises BzrError when a request is inconsistent; we + # want such errors to be shown as InconsistentDelta - and that + # fits the behaviour we trigger. + self._changes_aborted = True + raise errors.InconsistentDeltaDelta(delta, "error from _get_entry.") def _apply_removals(self, removals): for path in sorted(removals, reverse=True): === modified file 'bzrlib/inventory.py' --- a/bzrlib/inventory.py 2009-07-10 05:18:29 +0000 +++ b/bzrlib/inventory.py 2009-07-13 04:02:33 +0000 @@ -1169,6 +1169,9 @@ except AttributeError: raise errors.InconsistentDelta(new_path, new_entry.file_id, "Parent is not a directory.") + if self.id2path(new_entry.file_id) != new_path: + raise errors.InconsistentDelta(new_path, new_entry.file_id, + "New path is not consistent with parent path.") if len(children): # Get the parent id that was deleted parent_id, children = children.popitem() @@ -1254,12 +1257,11 @@ To add a file to a branch ready to be committed, use Branch.add, which calls this. - Returns the new entry object. + :return: entry """ if entry.file_id in self._byid: raise errors.DuplicateFileId(entry.file_id, self._byid[entry.file_id]) - if entry.parent_id is None: self.root = entry else: @@ -1590,6 +1592,7 @@ copied to and updated for the result. :return: The new CHKInventory. """ + split = osutils.split result = CHKInventory(self._search_key_name) if propagate_caches: # Just propagate the path-to-fileid cache for now @@ -1630,7 +1633,8 @@ inventory_delta = _check_delta_unique_new_paths(inventory_delta) # Check for entries that don't match the fileid inventory_delta = _check_delta_ids_match_entry(inventory_delta) - # All changed entries need to have their parents be directories. + # All changed entries need to have their parents be directories and be + # at the right path. This set contains (path, id) tuples. parents = set() for old_path, new_path, file_id, entry in inventory_delta: # file id changes @@ -1652,7 +1656,7 @@ # Update caches. It's worth doing this whether # we're propagating the old caches or not. result._path_to_fileid_cache[new_path] = file_id - parents.add(entry.parent_id) + parents.add((split(new_path)[0], entry.parent_id)) if old_path is None: old_key = None else: @@ -1678,8 +1682,8 @@ result.id_to_entry.apply_delta(id_to_entry_delta) if parent_id_basename_delta: result.parent_id_basename_to_file_id.apply_delta(parent_id_basename_delta) - parents.discard(None) - for parent in parents: + parents.discard(('', None)) + for parent_path, parent in parents: try: if result[parent].kind != 'directory': raise errors.InconsistentDelta(result.id2path(parent), parent, @@ -1687,6 +1691,9 @@ except errors.NoSuchId: raise errors.InconsistentDelta("<unknown>", parent, "Parent is not present in resulting inventory.") + if result.path2id(parent_path) != parent: + raise errors.InconsistentDelta(parent_path, parent, + "Parent has wrong path %r." % result.path2id(parent_path)) return result @classmethod === modified file 'bzrlib/tests/test_inv.py' --- a/bzrlib/tests/test_inv.py 2009-07-13 01:16:24 +0000 +++ b/bzrlib/tests/test_inv.py 2009-07-13 04:02:33 +0000 @@ -54,10 +54,12 @@ 'apply_delta':apply_inventory_Repository_add_inventory_by_delta, 'format':format})) for format in workingtree_formats(): - scenarios.append((str(format.__class__.__name__), { + scenarios.append( + (str(format.__class__.__name__) + ".update_basis_by_delta", { 'apply_delta':apply_inventory_WT_basis, 'format':format})) - scenarios.append((str(format.__class__.__name__), { + scenarios.append( + (str(format.__class__.__name__) + ".apply_inventory_delta", { 'apply_delta':apply_inventory_WT, 'format':format})) return multiply_tests(to_adapt, scenarios, result) @@ -360,6 +362,24 @@ self.assertRaises(errors.InconsistentDelta, self.apply_delta, self, inv, delta) + def test_new_parent_path_has_wrong_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 = "" + inv.add(parent1) + inv.add(parent2) + # This delta claims that file1 is at dir/path, but actually its at + # dir2/path if you follow the inventory parent structure. + delta = [(None, u'dir/path', 'id', file1)] + self.assertRaises(errors.InconsistentDelta, self.apply_delta, self, + inv, delta) + class TestInventoryEntry(TestCase): === modified file 'doc/developers/inventory.txt' --- a/doc/developers/inventory.txt 2009-07-02 07:22:27 +0000 +++ b/doc/developers/inventory.txt 2009-07-13 04:02:33 +0000 @@ -554,6 +554,7 @@ (Wrong path) - An entry whose parent is not a directory. (Under non-directory). - An entry that is internally inconsistent. + - An entry that is already present in the tree (Duplicate id) Known causes of inconsistency: - A 'new' entry which the inventory already has - when this is a directory -- bazaar-commits mailing list [email protected] https://lists.ubuntu.com/mailman/listinfo/bazaar-commits
