Rev 3786: CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes. in http://people.ubuntu.com/~robertc/baz2.0/commit-iterchanges

Robert Collins robertc at robertcollins.net
Tue Nov 18 06:39:59 GMT 2008


At http://people.ubuntu.com/~robertc/baz2.0/commit-iterchanges

------------------------------------------------------------
revno: 3786
revision-id: robertc at robertcollins.net-20081118063955-rosaxti87kco7tlk
parent: robertc at robertcollins.net-20081118042340-iwb4oeouxbkfmrxk
committer: Robert Collins <robertc at robertcollins.net>
branch nick: commit-iterchanges
timestamp: Tue 2008-11-18 17:39:55 +1100
message:
  CommitBuilder handles renamed directory and unmodified entries with single parents, for record_iter_changes.
modified:
  bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
  bzrlib/tests/per_repository/test_commit_builder.py test_commit_builder.py-20060606110838-76e3ra5slucqus81-1
=== modified file 'bzrlib/repository.py'
--- a/bzrlib/repository.py	2008-11-18 04:08:30 +0000
+++ b/bzrlib/repository.py	2008-11-18 06:39:55 +0000
@@ -510,11 +510,13 @@
         self._any_changes = True
         return self._get_delta(ie, basis_inv, path), True, fingerprint
 
-    def record_iter_changes(self, tree, basis_revision_id, iter_changes,
-        _entry_factory=entry_factory):
+    def record_iter_changes(self, tree, basis_tree, basis_revision_id,
+        iter_changes, _entry_factory=entry_factory):
         """Record a new tree via iter_changes.
 
         :param tree: The tree to obtain text contents from for changed objects.
+        :param basis_tree: The basis tree this commit is being performed
+            against.
         :param basis_revision_id: The revision id of the tree the iter_changes
             has been generated against.
         :param iter_changes: An iter_changes iterator.
@@ -526,20 +528,76 @@
         # deltas between all the parent inventories. We use inventory delta's 
         # between the inventory objects because iter_changes masks
         # last-changed-field only changes.
+        basis_inv = basis_tree.inventory
         # file_id -> change map, change is fileid, paths, changed, versioneds,
         # parents, names, kinds, executables
+        merged_ids = {}
+        if len(self.parents) > 1:
+            revtrees = list(self.repository.revision_trees(self.parents))
+            repo_basis = revtrees[0]
+            for revtree in revtrees[1:]:
+                for change in _make_delta(basis_tree, revtree):
+                    if change[1] is None:
+                        # Deleted
+                        continue
+                    if change[2] not in merged_ids:
+                        if change[0] is not None:
+                            merged_ids[change[2]] = set([change[3].revision,
+                                repo_basis[change[2]].revision])
+                        else:
+                            merged_ids[change[2]] = set([change[3].revision])
+                    else:
+                        merged_ids[change[2]].add(change[3].revision)
+        else:
+            merged_ids = {}
         changes= {}
         for change in iter_changes:
-            changes[change[0]] = change
+            changes[change[0]] = change, merged_ids.get(
+                change[0], set([basis_inv[change[0]].revision]))
+        unchanged_merged = set(merged_ids) - set(changes)
+        # changes contains tuples with the change and a set of inventory
+        # candidates for the file.
         # inv delta is:
         # old_path, new_path, file_id, new_inventory_entry
         seen_root = False # Is the root in the basis delta?
         inv_delta = self.basis_delta
         modified_rev = self._new_revision_id
-        for change in changes.values():
+        for change, head_candidates in changes.values():
             if change[3][1]: # versioned in target.
+                kind = change[6][1]
+                heads = self._heads(change[0], head_candidates)
+                # Populate the entry
+                if change[2]:
+                    # From disk.
+                    if kind == 'file':
+                        import pdb;pdb.set_trace()
+                    elif kind == 'symlink':
+                        import pdb;pdb.set_trace()
+                    elif kind == 'directory':
+                        # Nothing to set.
+                        import pdb;pdb.set_trace()
+                        pass
+                    elif kind == 'tree-reference':
+                        import pdb;pdb.set_trace()
+                    else:
+                        raise AssertionError('unknown kind %r' % kind)
+                else:
+                    # From basis.
+                    if kind == 'file':
+                        import pdb;pdb.set_trace()
+                    elif kind == 'symlink':
+                        import pdb;pdb.set_trace()
+                    elif kind == 'directory':
+                        # Nothing to set.
+                        self._add_text_to_weave(change[0], [], heads, None)
+                        pass
+                    elif kind == 'tree-reference':
+                        import pdb;pdb.set_trace()
+                    else:
+                        raise AssertionError('unknown kind %r' % kind)
                 entry = _entry_factory[change[6][1]](
                     change[0], change[5][1], change[4][1])
+                
                 entry.revision = modified_rev
             else:
                 entry = None

=== modified file 'bzrlib/tests/per_repository/test_commit_builder.py'
--- a/bzrlib/tests/per_repository/test_commit_builder.py	2008-11-18 04:23:40 +0000
+++ b/bzrlib/tests/per_repository/test_commit_builder.py	2008-11-18 06:39:55 +0000
@@ -76,8 +76,8 @@
         try:
             builder = tree.branch.get_commit_builder([])
             try:
-                builder.record_iter_changes(tree, tree.last_revision(),
-                    tree.iter_changes(tree.basis_tree()))
+                builder.record_iter_changes(tree, tree.basis_tree(),
+                    tree.last_revision(), tree.iter_changes(tree.basis_tree()))
                 builder.finish_inventory()
             except:
                 builder.abort()
@@ -104,8 +104,8 @@
         try:
             builder = tree.branch.get_commit_builder([])
             try:
-                builder.record_iter_changes(tree, tree.last_revision(),
-                    tree.iter_changes(tree.basis_tree()))
+                builder.record_iter_changes(tree, tree.basis_tree(),
+                    tree.last_revision(), tree.iter_changes(tree.basis_tree()))
                 builder.finish_inventory()
             except:
                 builder.abort()
@@ -180,8 +180,8 @@
                 return
             self.assertFalse(builder.random_revid)
             try:
-                builder.record_iter_changes(tree, tree.last_revision(),
-                    tree.iter_changes(tree.basis_tree()))
+                builder.record_iter_changes(tree, tree.basis_tree(),
+                    tree.last_revision(), tree.iter_changes(tree.basis_tree()))
                 builder.finish_inventory()
             except:
                 builder.abort()
@@ -260,7 +260,7 @@
         self.addCleanup(parent_tree.unlock)
         builder = tree.branch.get_commit_builder([old_revision_id])
         try:
-            builder.record_iter_changes(tree, old_revision_id, [])
+            builder.record_iter_changes(tree, parent_tree, old_revision_id, [])
             # Regardless of repository root behaviour we should consider this a
             # pointless commit.
             self.assertFalse(builder.any_changes())
@@ -340,7 +340,8 @@
                 delete_change = ('foo-id', ('foo', None), True, (True, False),
                     (tree.path2id(''), None), ('foo', None), ('file', None),
                     (False, None))
-                builder.record_iter_changes(tree, rev_id, [delete_change])
+                builder.record_iter_changes(tree, tree.basis_tree(), rev_id,
+                    [delete_change])
                 self.assertEqual(("foo", None, "foo-id", None),
                     builder.basis_delta[0])
                 self.assertTrue(builder.any_changes())
@@ -395,7 +396,8 @@
         try:
             builder = tree.branch.get_commit_builder([])
             try:
-                builder.record_iter_changes(tree, _mod_revision.NULL_REVISION,
+                builder.record_iter_changes(tree, tree.basis_tree(),
+                    _mod_revision.NULL_REVISION,
                     tree.iter_changes(tree.basis_tree()))
                 builder.finish_inventory()
                 rev_id = builder.commit('foo bar')
@@ -413,6 +415,9 @@
     def test_root_entry_has_revision(self):
         # test the root revision created and put in the basis
         # has the right rev id.
+        # XXX: RBC 20081118 - this test is too big, it depends on the exact
+        # behaviour of tree methods and so on; it should be written to the
+        # commit builder interface directly.
         tree = self.make_branch_and_tree('.')
         rev_id = tree.commit('message')
         basis_tree = tree.basis_tree()
@@ -444,10 +449,12 @@
         else:
             self.assertEqual(rev2, tree2.inventory.root.revision)
 
-    def _add_commit_check_unchanged(self, tree, name):
+    def _add_commit_check_unchanged(self, tree, name, mini_commit=None):
         tree.add([name], [name + 'id'])
         rev1 = tree.commit('')
-        rev2 = self.mini_commit(tree, name, name, False, False)
+        if mini_commit is None:
+            mini_commit = self.mini_commit
+        rev2 = mini_commit(tree, name, name, False, False)
         tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
         self.assertEqual(rev1, tree1.inventory[name + 'id'].revision)
         self.assertEqual(rev1, tree2.inventory[name + 'id'].revision)
@@ -462,6 +469,13 @@
         self.build_tree(['dir/'])
         self._add_commit_check_unchanged(tree, 'dir')
 
+    def test_last_modified_revision_after_commit_dir_unchanged_ric(self):
+        # committing without changing a dir does not change the last modified.
+        tree = self.make_branch_and_tree('.')
+        self.build_tree(['dir/'])
+        self._add_commit_check_unchanged(tree, 'dir',
+            mini_commit=self.mini_commit_record_iter_changes)
+
     def test_last_modified_revision_after_commit_dir_contents_unchanged(self):
         # committing without changing a dir does not change the last modified
         # of the dir even the dirs contents are changed.
@@ -486,6 +500,13 @@
         self.build_tree(['file'])
         self._add_commit_check_unchanged(tree, 'file')
 
+    def test_last_modified_revision_after_commit_file_unchanged_ric(self):
+        # committing without changing a file does not change the last modified.
+        tree = self.make_branch_and_tree('.')
+        self.build_tree(['file'])
+        self._add_commit_check_unchanged(tree, 'file',
+            mini_commit=self.mini_commit_record_iter_changes)
+
     def test_last_modified_revision_after_commit_link_unchanged(self):
         # committing without changing a link does not change the last modified.
         self.requireFeature(tests.SymlinkFeature)
@@ -493,12 +514,22 @@
         os.symlink('target', 'link')
         self._add_commit_check_unchanged(tree, 'link')
 
+    def test_last_modified_revision_after_commit_link_unchanged_ric(self):
+        # committing without changing a link does not change the last modified.
+        self.requireFeature(tests.SymlinkFeature)
+        tree = self.make_branch_and_tree('.')
+        os.symlink('target', 'link')
+        self._add_commit_check_unchanged(tree, 'link',
+            mini_commit=self.mini_commit_record_iter_changes)
+
     def _add_commit_renamed_check_changed(self, tree, name,
-        expect_fs_hash=False):
+        expect_fs_hash=False, mini_commit=None):
         def rename():
             tree.rename_one(name, 'new_' + name)
+        if mini_commit is None:
+            mini_commit = self.mini_commit
         self._add_commit_change_check_changed(tree, name, rename,
-            expect_fs_hash=expect_fs_hash)
+            expect_fs_hash=expect_fs_hash, mini_commit=mini_commit)
 
     def test_last_modified_revision_after_rename_dir_changes(self):
         # renaming a dir changes the last modified.
@@ -506,6 +537,13 @@
         self.build_tree(['dir/'])
         self._add_commit_renamed_check_changed(tree, 'dir')
 
+    def test_last_modified_revision_after_rename_dir_changes_ric(self):
+        # renaming a dir changes the last modified.
+        tree = self.make_branch_and_tree('.')
+        self.build_tree(['dir/'])
+        self._add_commit_renamed_check_changed(tree, 'dir',
+            mini_commit=self.mini_commit_record_iter_changes)
+
     def test_last_modified_revision_after_rename_file_changes(self):
         # renaming a file changes the last modified.
         tree = self.make_branch_and_tree('.')
@@ -550,11 +588,13 @@
         self._add_commit_reparent_check_changed(tree, 'link')
 
     def _add_commit_change_check_changed(self, tree, name, changer,
-        expect_fs_hash=False):
+        expect_fs_hash=False, mini_commit=None):
         tree.add([name], [name + 'id'])
         rev1 = tree.commit('')
         changer()
-        rev2 = self.mini_commit(tree, name, tree.id2path(name + 'id'),
+        if mini_commit is None:
+            mini_commit = self.mini_commit
+        rev2 = mini_commit(tree, name, tree.id2path(name + 'id'),
             expect_fs_hash=expect_fs_hash)
         tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
         self.assertEqual(rev1, tree1.inventory[name + 'id'].revision)
@@ -642,6 +682,65 @@
             tree.unlock()
         return rev2
 
+    def mini_commit_record_iter_changes(self, tree, name, new_name,
+        records_version=True, delta_against_basis=True, expect_fs_hash=False):
+        """Perform a miniature commit looking for record entry results.
+
+        This version uses the record_iter_changes interface.
+        
+        :param tree: The tree to commit.
+        :param name: The path in the basis tree of the tree being committed.
+        :param new_name: The path in the tree being committed.
+        :param records_version: True if the commit of new_name is expected to
+            record a new version.
+        :param delta_against_basis: True of the commit of new_name is expected
+            to have a delta against the basis.
+        :param expect_fs_hash: ignored, present for compatibility with test
+            driver code for 'mini_commit'.
+        """
+        tree.lock_write()
+        try:
+            # mini manual commit here so we can check the return of
+            # record_entry_contents.
+            parent_ids = tree.get_parent_ids()
+            builder = tree.branch.get_commit_builder(parent_ids)
+            parent_tree = tree.basis_tree()
+            parent_tree.lock_read()
+            self.addCleanup(parent_tree.unlock)
+            parent_invs = [parent_tree.inventory]
+            for parent_id in parent_ids[1:]:
+                parent_invs.append(tree.branch.repository.revision_tree(
+                    parent_id).inventory)
+            changes = list(tree.iter_changes(parent_tree))
+            builder.record_iter_changes(tree, parent_tree, parent_ids[0],
+                changes)
+            delta = builder.basis_delta
+            delta_dict = dict((change[2], change) for change in delta)
+            version_recorded = tree.path2id(new_name) in delta_dict
+            if records_version:
+                self.assertTrue(version_recorded)
+            else:
+                self.assertFalse(version_recorded)
+            builder.finish_inventory()
+            new_inventory = builder.revision_tree().inventory
+            file_id = tree.path2id(new_name)
+            new_entry = new_inventory[file_id]
+            if delta_against_basis:
+                expected_delta = (name, new_name, file_id, new_entry)
+                self.assertEqual(expected_delta, delta_dict[file_id])
+            else:
+                expected_delta = None
+                self.assertFalse(version_recorded)
+            rev2 = builder.commit('')
+            tree.set_parent_ids([rev2])
+        except:
+            builder.abort()
+            tree.unlock()
+            raise
+        else:
+            tree.unlock()
+        return rev2
+
     def assertFileGraph(self, expected_graph, tree, tip):
         # all the changes that have occured should be in the ancestry
         # (closest to a public per-file graph API we have today)




More information about the bazaar-commits mailing list