Rev 2866: Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit. in http://people.ubuntu.com/~robertc/baz2.0/wt.update_to_one_parent_via_delta
Robert Collins
robertc at robertcollins.net
Thu Sep 27 10:04:36 BST 2007
At http://people.ubuntu.com/~robertc/baz2.0/wt.update_to_one_parent_via_delta
------------------------------------------------------------
revno: 2866
revision-id: robertc at robertcollins.net-20070927090426-u91r9ki6vrpjqcsd
parent: pqm at pqm.ubuntu.com-20070925205148-yd27v1odc65uql59
committer: Robert Collins <robertc at robertcollins.net>
branch nick: wt.update_to_one_parent_via_delta
timestamp: Thu 2007-09-27 19:04:26 +1000
message:
Create new mutable tree method update_to_one_parent_via_delta for eventual use by commit.
modified:
bzrlib/inventory.py inventory.py-20050309040759-6648b84ca2005b37
bzrlib/mutabletree.py mutabletree.py-20060906023413-4wlkalbdpsxi2r4y-2
bzrlib/tests/workingtree_implementations/test_inv.py test_inv.py-20070311221604-ighlq8tbn5xq0kuo-1
bzrlib/tests/workingtree_implementations/test_parents.py test_set_parents.py-20060807231740-yicmnlci1mj8smu1-1
=== modified file 'bzrlib/inventory.py'
--- a/bzrlib/inventory.py 2007-09-21 04:22:53 +0000
+++ b/bzrlib/inventory.py 2007-09-27 09:04:26 +0000
@@ -946,6 +946,69 @@
self._byid = {}
self.revision_id = revision_id
+ def __repr__(self):
+ return "<Inventory object at %x, contents=%r>" % (id(self), self._byid)
+
+ def apply_delta(self, delta):
+ """Apply a delta to this inventory.
+
+ :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
+ invalid result - such as supplying two changes which rename two
+ files, 'A' and 'B' with each other : [('A', 'B', 'A-id', a_entry),
+ ('B', 'A', 'B-id', b_entry)].
+
+ Each change is a tuple, of the form (old_path, new_path, file_id,
+ new_entry).
+
+ When new_path is None, the change indicates the removal of an entry
+ from the inventory and new_entry will be ignored (using None is
+ appropriate). If new_path is not None, then new_entry must be an
+ InventoryEntry instance, which will be incorporated into the
+ inventory (and replace any existing entry with the same file id).
+
+ When old_path is None, the change indicates the addition of
+ a new entry to the inventory.
+
+ When neither new_path nor old_path are None, the change is a
+ modification to an entry, such as a rename, reparent, kind change
+ etc.
+
+ The children attribute of new_entry is ignored. This is because
+ apply_inventory_delta preserves children automatically across
+ alterations to the parent of the children, and cases where the
+ parent id of a child is changing require the child to be passed in
+ as a separate change regardless. E.g. in the recursive deletion of
+ a directory - the directories children must be included in the
+ delta, or the final inventory will be invalid.
+ """
+ children = {}
+ # Remove all affected items which were in the original inventory,
+ # starting with the longest paths, thus ensuring parents are examined
+ # after their children, which means that everything we examine has no
+ # 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.
+ children[file_id] = getattr(self[file_id], 'children', {})
+ # 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)
+ # Insert all affected which should be in the new inventory, reattaching
+ # their children if they had any. This is done from shortest path to
+ # longest, ensuring that items which were modified and whose parents in
+ # the resulting inventory were also modified, are inserted after their
+ # parents.
+ for new_path, new_entry in sorted((np, e) for op, np, f, e in
+ delta if np is not None):
+ if new_entry.kind == 'directory':
+ new_entry.children = children.get(new_entry.file_id, {})
+ self.add(new_entry)
+
def _set_root(self, ie):
self.root = ie
self._byid = {self.root.file_id: self.root}
=== modified file 'bzrlib/mutabletree.py'
--- a/bzrlib/mutabletree.py 2007-08-07 20:45:21 +0000
+++ b/bzrlib/mutabletree.py 2007-09-27 09:04:26 +0000
@@ -29,6 +29,7 @@
bzrdir,
)
from bzrlib.osutils import dirname
+from bzrlib.revisiontree import RevisionTree
from bzrlib.trace import mutter, warning
""")
@@ -159,37 +160,14 @@
def apply_inventory_delta(self, changes):
"""Apply changes to the inventory as an atomic operation.
- The argument is a set of changes to apply. It must describe a
- valid result, but the order is not important. Specifically,
- intermediate stages *may* be invalid, such as when two files
- swap names.
-
- The changes should be structured as a list of tuples, of the form
- (old_path, new_path, file_id, new_entry). For creation, old_path
- must be None. For deletion, new_path and new_entry must be None.
- file_id is always non-None. For renames and other mutations, all
- values must be non-None.
-
- If the new_entry is a directory, its children should be an empty
- dict. Children are handled by apply_inventory_delta itself.
-
- :param changes: A list of tuples for the change to apply:
- [(old_path, new_path, file_id, new_inventory_entry), ...]
+ :param changes: An inventory delta to apply to the working tree's
+ inventory.
+ :return None:
+ :seealso Inventory.apply_delta: For details on the changes parameter.
"""
self.flush()
inv = self.inventory
- children = {}
- for old_path, file_id in sorted(((op, f) for op, np, f, e in changes
- if op is not None), reverse=True):
- if file_id not in inv:
- continue
- children[file_id] = getattr(inv[file_id], 'children', {})
- inv.remove_recursive_id(file_id)
- for new_path, new_entry in sorted((np, e) for op, np, f, e in
- changes if np is not None):
- if getattr(new_entry, 'children', None) is not None:
- new_entry.children = children.get(new_entry.file_id, {})
- inv.add(new_entry)
+ inv.apply_delta(changes)
self._write_inventory(inv)
@needs_write_lock
@@ -453,6 +431,35 @@
self.read_working_inventory()
return added, ignored
+ def update_to_one_parent_via_delta(self, new_revid, delta):
+ """Update the parents of this tree after a commit.
+
+ This gives the tree one parent, with revision id new_revid. The
+ inventory delta delta is applied ot the current basis tree to generate
+ the inventory for the parent new_revid, and all other parent trees are
+ discarded.
+
+ :param new_revid: The new revision id for the trees parent.
+ :param delta: An inventory delta (see apply_inventory_delta) describing
+ the changes from the current left most parent revision to new_revid.
+ """
+ # if the tree is updated by a pull to the branch, as happens in
+ # WorkingTree2, when there was no separation between branch and tree,
+ # then just clear merges, efficiency is not a concern for now as this
+ # is legacy environments only, and they are slow regardless.
+ if self.last_revision() == new_revid:
+ self.set_parent_ids([new_revid])
+ return
+ # generic implementation based on Inventory manipulation. See
+ # WorkingTree classes for optimised versions for specific format trees.
+ basis = self.basis_tree()
+ basis.lock_read()
+ inventory = basis.inventory
+ basis.unlock()
+ inventory.apply_delta(delta)
+ rev_tree = RevisionTree(self.branch.repository, inventory, new_revid)
+ self.set_parent_trees([(new_revid, rev_tree)])
+
class _FastPath(object):
"""A path object with fast accessors for things like basename."""
=== modified file 'bzrlib/tests/workingtree_implementations/test_inv.py'
--- a/bzrlib/tests/workingtree_implementations/test_inv.py 2007-08-29 16:09:51 +0000
+++ b/bzrlib/tests/workingtree_implementations/test_inv.py 2007-09-27 09:04:26 +0000
@@ -154,10 +154,36 @@
('foo/bar', None, 'bar-id', None)])
self.assertIs(None, wt.path2id('foo'))
+ def test_rename_dir_with_children(self):
+ wt = self.make_branch_and_tree('.')
+ wt.lock_write()
+ root_id = wt.get_root_id()
+ self.addCleanup(wt.unlock)
+ self.build_tree(['foo/', 'foo/bar'])
+ wt.add(['foo', 'foo/bar'],
+ ['foo-id', 'bar-id'])
+ wt.apply_inventory_delta([('foo', 'baz', 'foo-id',
+ inventory.InventoryDirectory('foo-id', 'baz', root_id))])
+ # foo/bar should have been followed the rename of its parent to baz/bar
+ self.assertEqual('baz/bar', wt.id2path('bar-id'))
+
+ def test_rename_dir_with_children_with_children(self):
+ wt = self.make_branch_and_tree('.')
+ wt.lock_write()
+ root_id = wt.get_root_id()
+ self.addCleanup(wt.unlock)
+ self.build_tree(['foo/', 'foo/bar/', 'foo/bar/baz'])
+ wt.add(['foo', 'foo/bar', 'foo/bar/baz'],
+ ['foo-id', 'bar-id', 'baz-id'])
+ wt.apply_inventory_delta([('foo', 'quux', 'foo-id',
+ inventory.InventoryDirectory('foo-id', 'quux', root_id))])
+ # foo/bar/baz should have been followed the rename of its parent's
+ # parent to quux/bar/baz
+ self.assertEqual('quux/bar/baz', wt.id2path('baz-id'))
+
def test_rename_file(self):
wt = self.make_branch_and_tree('.')
wt.lock_write()
- root_id = wt.get_root_id()
self.addCleanup(wt.unlock)
self.build_tree(['foo/', 'foo/bar', 'baz/'])
wt.add(['foo', 'foo/bar', 'baz'],
@@ -197,6 +223,8 @@
self.build_tree(['dir/', 'dir/child', 'other/'])
wt.add(['dir', 'dir/child', 'other'],
['dir-id', 'child-id', 'other-id'])
+ # this delta moves dir-id to dir2 and reparents
+ # child-id wto a parent of other-id
wt.apply_inventory_delta([('dir', 'dir2', 'dir-id',
inventory.InventoryDirectory('dir-id', 'dir2', root_id)),
('dir/child', 'other/child', 'child-id',
=== modified file 'bzrlib/tests/workingtree_implementations/test_parents.py'
--- a/bzrlib/tests/workingtree_implementations/test_parents.py 2007-07-12 07:22:52 +0000
+++ b/bzrlib/tests/workingtree_implementations/test_parents.py 2007-09-27 09:04:26 +0000
@@ -16,13 +16,23 @@
"""Tests of the parent related functions of WorkingTrees."""
+from errno import EEXIST
import os
from bzrlib import (
errors,
+ osutils,
revision as _mod_revision,
symbol_versioning,
)
+from bzrlib.inventory import (
+ Inventory,
+ InventoryFile,
+ InventoryDirectory,
+ InventoryLink,
+ )
+from bzrlib.revision import Revision
+from bzrlib.tests import SymlinkFeature, TestNotApplicable
from bzrlib.tests.workingtree_implementations import TestCaseWithWorkingTree
from bzrlib.uncommit import uncommit
@@ -237,3 +247,328 @@
first_revision = tree.commit('first post')
tree.add_parent_tree(('second', None))
self.assertConsistentParents([first_revision, 'second'], tree)
+
+
+class UpdateToOneParentViaDeltaTests(TestParents):
+ """Tests for the update_to_one_parent_via_delta call.
+
+ This is intuitively defined as 'apply an inventory delta to the basis and
+ discard other parents', but for trees that have an inventory that is not
+ managed as a tree-by-id, the implementation requires roughly duplicated
+ tests with those for apply_inventory_delta on the main tree.
+ """
+
+ def assertDeltaApplicationResultsInExpectedBasis(self, tree, revid, delta,
+ expected_inventory):
+ tree.update_to_one_parent_via_delta(revid, delta)
+ # check the last revision was adjusted to rev_id
+ self.assertEqual(revid, tree.last_revision())
+ # check the parents are what we expect
+ self.assertEqual([revid], tree.get_parent_ids())
+ # check that the basis tree has the inventory we expect from applying
+ # the delta.
+ result_basis = tree.basis_tree()
+ result_basis.lock_read()
+ self.addCleanup(result_basis.unlock)
+ self.assertEqual(expected_inventory, result_basis.inventory)
+
+ def make_inv_delta(self, old, new):
+ """Make an inventory delta from two inventories."""
+ old_ids = set(old._byid.iterkeys())
+ new_ids = set(new._byid.iterkeys())
+ adds = new_ids - old_ids
+ deletes = old_ids - new_ids
+ common = old_ids.intersection(new_ids)
+ delta = []
+ for file_id in deletes:
+ delta.append((old.id2path(file_id), None, file_id, None))
+ for file_id in adds:
+ delta.append((None, new.id2path(file_id), file_id, new[file_id]))
+ for file_id in common:
+ if old[file_id] != new[file_id]:
+ delta.append((old.id2path(file_id), new.id2path(file_id),
+ file_id, new[file_id]))
+ return delta
+
+ def fake_up_revision(self, tree, revid, shape):
+ tree.lock_write()
+ try:
+ tree.branch.repository.start_write_group()
+ try:
+ if shape.root.revision is None:
+ shape.root.revision = revid
+ sha1 = tree.branch.repository.add_inventory(revid, shape, [])
+ rev = Revision(timestamp=0,
+ timezone=None,
+ committer="Foo Bar <foo at example.com>",
+ message="Message",
+ inventory_sha1=sha1,
+ revision_id=revid)
+ tree.branch.repository.add_revision(revid, rev)
+ except:
+ tree.branch.repository.abort_write_group()
+ raise
+ else:
+ tree.branch.repository.commit_write_group()
+ finally:
+ tree.unlock()
+
+ def add_entry(self, inv, rev_id, entry):
+ entry.revision = rev_id
+ inv.add(entry)
+
+ def add_dir(self, inv, rev_id, file_id, parent_id, name):
+ new_dir = InventoryDirectory(file_id, name, parent_id)
+ self.add_entry(inv, rev_id, new_dir)
+
+ def add_file(self, inv, rev_id, file_id, parent_id, name, sha, size):
+ new_file = InventoryFile(file_id, name, parent_id)
+ new_file.text_sha1 = sha
+ new_file.text_size = size
+ self.add_entry(inv, rev_id, new_file)
+
+ def add_link(self, inv, rev_id, file_id, parent_id, name, target):
+ new_link = InventoryLink(file_id, name, parent_id)
+ new_link.symlink_target = target
+ self.add_entry(inv, rev_id, new_link)
+
+ def add_new_root(self, new_shape, old_revid, new_revid):
+ if self.bzrdir_format.repository_format.rich_root_data:
+ self.add_dir(new_shape, old_revid, 'root-id', None, '')
+ else:
+ self.add_dir(new_shape, new_revid, 'root-id', None, '')
+
+ def assertTransitionFromBasisToShape(self, basis_shape, basis_revid,
+ new_shape, new_revid, extra_parent=None):
+ delta = self.make_inv_delta(basis_shape, new_shape)
+ tree = self.make_branch_and_tree('tree')
+ # the shapes need to be in the tree's repository to be able to set them
+ # as a parent, but the file content is not needed.
+ if basis_revid is not None:
+ self.fake_up_revision(tree, basis_revid, basis_shape)
+ parents = [basis_revid]
+ if extra_parent is not None:
+ parents.append(extra_parent)
+ tree.set_parent_ids(parents)
+ self.fake_up_revision(tree, new_revid, new_shape)
+ self.assertDeltaApplicationResultsInExpectedBasis(tree, new_revid,
+ delta, new_shape)
+ osutils.rmtree('tree')
+
+ def test_no_parents_just_root(self):
+ """Test doing an empty commit - no parent, set a root only."""
+ basis_shape = Inventory(root_id=None) # empty tree
+ new_shape = Inventory() # tree with a root
+ self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
+ 'new_parent')
+
+ def test_no_parents_full_tree(self):
+ """Test doing a regular initial commit with files and dirs."""
+ basis_shape = Inventory(root_id=None) # empty tree
+ revid = 'new-parent'
+ new_shape = Inventory(root_id=None)
+ self.add_dir(new_shape, revid, 'root-id', None, '')
+ self.add_link(new_shape, revid, 'link-id', 'root-id', 'link', 'target')
+ self.add_file(new_shape, revid, 'file-id', 'root-id', 'file', '1' * 32,
+ 12)
+ self.add_dir(new_shape, revid, 'dir-id', 'root-id', 'dir')
+ self.add_file(new_shape, revid, 'subfile-id', 'dir-id', 'subfile',
+ '2' * 32, 24)
+ self.assertTransitionFromBasisToShape(basis_shape, None, new_shape,
+ revid)
+
+ def test_file_content_change(self):
+ old_revid = 'old-parent'
+ basis_shape = Inventory(root_id=None)
+ self.add_dir(basis_shape, old_revid, 'root-id', None, '')
+ self.add_file(basis_shape, old_revid, 'file-id', 'root-id', 'file',
+ '1' * 32, 12)
+ new_revid = 'new-parent'
+ new_shape = Inventory(root_id=None)
+ self.add_new_root(new_shape, old_revid, new_revid)
+ self.add_file(new_shape, new_revid, 'file-id', 'root-id', 'file',
+ '2' * 32, 24)
+ self.assertTransitionFromBasisToShape(basis_shape, old_revid,
+ new_shape, new_revid)
+
+ def test_link_content_change(self):
+ old_revid = 'old-parent'
+ basis_shape = Inventory(root_id=None)
+ self.add_dir(basis_shape, old_revid, 'root-id', None, '')
+ self.add_link(basis_shape, old_revid, 'link-id', 'root-id', 'link',
+ 'old-target')
+ new_revid = 'new-parent'
+ new_shape = Inventory(root_id=None)
+ self.add_new_root(new_shape, old_revid, new_revid)
+ self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
+ 'new-target')
+ self.assertTransitionFromBasisToShape(basis_shape, old_revid,
+ new_shape, new_revid)
+
+ def test_kind_changes(self):
+ def do_file(inv, revid):
+ self.add_file(inv, revid, 'path-id', 'root-id', 'path', '1' * 32,
+ 12)
+ def do_link(inv, revid):
+ self.add_link(inv, revid, 'path-id', 'root-id', 'path', 'target')
+ def do_dir(inv, revid):
+ self.add_dir(inv, revid, 'path-id', 'root-id', 'path')
+ for old_factory in (do_file, do_link, do_dir):
+ for new_factory in (do_file, do_link, do_dir):
+ if old_factory == new_factory:
+ continue
+ old_revid = 'old-parent'
+ basis_shape = Inventory(root_id=None)
+ self.add_dir(basis_shape, old_revid, 'root-id', None, '')
+ old_factory(basis_shape, old_revid)
+ new_revid = 'new-parent'
+ new_shape = Inventory(root_id=None)
+ self.add_new_root(new_shape, old_revid, new_revid)
+ new_factory(new_shape, new_revid)
+ self.assertTransitionFromBasisToShape(basis_shape, old_revid,
+ new_shape, new_revid)
+
+ def test_content_from_second_parent_is_dropped(self):
+ left_revid = 'left-parent'
+ basis_shape = Inventory(root_id=None)
+ self.add_dir(basis_shape, left_revid, 'root-id', None, '')
+ self.add_link(basis_shape, left_revid, 'link-id', 'root-id', 'link',
+ 'left-target')
+ # the right shape has content - file, link, subdir with a child,
+ # that should all be discarded by the call.
+ right_revid = 'right-parent'
+ right_shape = Inventory(root_id=None)
+ self.add_dir(right_shape, left_revid, 'root-id', None, '')
+ self.add_link(right_shape, right_revid, 'link-id', 'root-id', 'link',
+ 'left-target')
+ self.add_dir(right_shape, right_revid, 'subdir-id', 'root-id', 'dir')
+ self.add_file(right_shape, right_revid, 'file-id', 'subdir-id', 'file',
+ '2' * 32, 24)
+ new_revid = 'new-parent'
+ new_shape = Inventory(root_id=None)
+ self.add_new_root(new_shape, left_revid, new_revid)
+ self.add_link(new_shape, new_revid, 'link-id', 'root-id', 'link',
+ 'new-target')
+ self.assertTransitionFromBasisToShape(basis_shape, left_revid,
+ new_shape, new_revid, right_revid)
+
+ def test_parent_id_changed(self):
+ # test that when the only change to a entry is its parent id changing
+ # that it is handled correctly (that is it keeps the same path)
+ old_revid = 'old-parent'
+ basis_shape = Inventory(root_id=None)
+ self.add_dir(basis_shape, old_revid, 'root-id', None, '')
+ self.add_dir(basis_shape, old_revid, 'orig-parent-id', 'root-id', 'dir')
+ self.add_dir(basis_shape, old_revid, 'dir-id', 'orig-parent-id', 'dir')
+ new_revid = 'new-parent'
+ new_shape = Inventory(root_id=None)
+ self.add_new_root(new_shape, old_revid, new_revid)
+ self.add_dir(new_shape, new_revid, 'new-parent-id', 'root-id', 'dir')
+ self.add_dir(new_shape, new_revid, 'dir-id', 'new-parent-id', 'dir')
+ self.assertTransitionFromBasisToShape(basis_shape, old_revid,
+ new_shape, new_revid)
+
+ def test_name_changed(self):
+ # test that when the only change to a entry is its name changing
+ # that it is handled correctly (that is it keeps the same parent id)
+ old_revid = 'old-parent'
+ basis_shape = Inventory(root_id=None)
+ self.add_dir(basis_shape, old_revid, 'root-id', None, '')
+ self.add_dir(basis_shape, old_revid, 'parent-id', 'root-id', 'origdir')
+ self.add_dir(basis_shape, old_revid, 'dir-id', 'parent-id', 'olddir')
+ new_revid = 'new-parent'
+ new_shape = Inventory(root_id=None)
+ self.add_new_root(new_shape, old_revid, new_revid)
+ self.add_dir(new_shape, new_revid, 'parent-id', 'root-id', 'newdir')
+ self.add_dir(new_shape, new_revid, 'dir-id', 'parent-id', 'newdir')
+ self.assertTransitionFromBasisToShape(basis_shape, old_revid,
+ new_shape, new_revid)
+
+ def test_path_swap(self):
+ # test a A->B and B->A path swap.
+ old_revid = 'old-parent'
+ basis_shape = Inventory(root_id=None)
+ self.add_dir(basis_shape, old_revid, 'root-id', None, '')
+ self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
+ self.add_dir(basis_shape, old_revid, 'dir-id-B', 'root-id', 'B')
+ self.add_link(basis_shape, old_revid, 'link-id-C', 'root-id', 'C', 'C')
+ self.add_link(basis_shape, old_revid, 'link-id-D', 'root-id', 'D', 'D')
+ self.add_file(basis_shape, old_revid, 'file-id-E', 'root-id', 'E',
+ '1' * 32, 12)
+ self.add_file(basis_shape, old_revid, 'file-id-F', 'root-id', 'F',
+ '2' * 32, 24)
+ new_revid = 'new-parent'
+ new_shape = Inventory(root_id=None)
+ self.add_new_root(new_shape, old_revid, new_revid)
+ self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'B')
+ self.add_dir(new_shape, new_revid, 'dir-id-B', 'root-id', 'A')
+ self.add_link(new_shape, new_revid, 'link-id-C', 'root-id', 'D', 'C')
+ self.add_link(new_shape, new_revid, 'link-id-D', 'root-id', 'C', 'D')
+ self.add_file(new_shape, new_revid, 'file-id-E', 'root-id', 'F',
+ '1' * 32, 12)
+ self.add_file(new_shape, new_revid, 'file-id-F', 'root-id', 'E',
+ '2' * 32, 24)
+ self.assertTransitionFromBasisToShape(basis_shape, old_revid,
+ new_shape, new_revid)
+
+ def test_adds(self):
+ # test adding paths and dirs, including adding to a newly added dir.
+ old_revid = 'old-parent'
+ basis_shape = Inventory(root_id=None)
+ # with a root, so its a commit after the first.
+ self.add_dir(basis_shape, old_revid, 'root-id', None, '')
+ new_revid = 'new-parent'
+ new_shape = Inventory(root_id=None)
+ self.add_new_root(new_shape, old_revid, new_revid)
+ self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
+ self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
+ self.add_file(new_shape, new_revid, 'file-id-C', 'root-id', 'C',
+ '1' * 32, 12)
+ self.add_file(new_shape, new_revid, 'file-id-D', 'dir-id-A', 'D',
+ '2' * 32, 24)
+ self.assertTransitionFromBasisToShape(basis_shape, old_revid,
+ new_shape, new_revid)
+
+ def test_removes(self):
+ # test removing paths, including paths that are within other also
+ # removed paths.
+ old_revid = 'old-parent'
+ basis_shape = Inventory(root_id=None)
+ self.add_dir(basis_shape, old_revid, 'root-id', None, '')
+ self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
+ self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
+ self.add_file(basis_shape, old_revid, 'file-id-C', 'root-id', 'C',
+ '1' * 32, 12)
+ self.add_file(basis_shape, old_revid, 'file-id-D', 'dir-id-A', 'D',
+ '2' * 32, 24)
+ new_revid = 'new-parent'
+ new_shape = Inventory(root_id=None)
+ self.add_new_root(new_shape, old_revid, new_revid)
+ self.assertTransitionFromBasisToShape(basis_shape, old_revid,
+ new_shape, new_revid)
+
+ def test_move_to_added_dir(self):
+ old_revid = 'old-parent'
+ basis_shape = Inventory(root_id=None)
+ self.add_dir(basis_shape, old_revid, 'root-id', None, '')
+ self.add_link(basis_shape, old_revid, 'link-id-B', 'root-id', 'B', 'C')
+ new_revid = 'new-parent'
+ new_shape = Inventory(root_id=None)
+ self.add_new_root(new_shape, old_revid, new_revid)
+ self.add_dir(new_shape, new_revid, 'dir-id-A', 'root-id', 'A')
+ self.add_link(new_shape, new_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
+ self.assertTransitionFromBasisToShape(basis_shape, old_revid,
+ new_shape, new_revid)
+
+ def test_move_from_removed_dir(self):
+ old_revid = 'old-parent'
+ basis_shape = Inventory(root_id=None)
+ self.add_dir(basis_shape, old_revid, 'root-id', None, '')
+ self.add_dir(basis_shape, old_revid, 'dir-id-A', 'root-id', 'A')
+ self.add_link(basis_shape, old_revid, 'link-id-B', 'dir-id-A', 'B', 'C')
+ new_revid = 'new-parent'
+ new_shape = Inventory(root_id=None)
+ self.add_new_root(new_shape, old_revid, new_revid)
+ self.add_link(new_shape, new_revid, 'link-id-B', 'root-id', 'B', 'C')
+ self.assertTransitionFromBasisToShape(basis_shape, old_revid,
+ new_shape, new_revid)
More information about the bazaar-commits
mailing list