Rev 3777: Teach CommitBuilder to accumulate inventory deltas. in http://people.ubuntu.com/~robertc/baz2.0/commit-delta

Robert Collins robertc at robertcollins.net
Mon Oct 13 05:32:34 BST 2008


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

------------------------------------------------------------
revno: 3777
revision-id: robertc at robertcollins.net-20081013043229-dn4s7hfg6h6zcobm
parent: robertc at robertcollins.net-20081013002817-xxxsr37afvuhbzdx
committer: Robert Collins <robertc at robertcollins.net>
branch nick: commit-delta
timestamp: Mon 2008-10-13 15:32:29 +1100
message:
  Teach CommitBuilder to accumulate inventory deltas.
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  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 'NEWS'
--- a/NEWS	2008-10-13 00:28:17 +0000
+++ b/NEWS	2008-10-13 04:32:29 +0000
@@ -18,6 +18,11 @@
       allows adding an inventory via an inventory delta, which can be
       more efficient for some repository types. (Robert Collins)
 
+    * Repository ``CommitBuilder`` objects can now accumulate an inventory
+      delta. To enable this functionality call ``builder.recording_deletes``
+      and additionally call ``builder.record_delete`` when a delete
+      against the basis occurs. (Robert Collins)
+
 
 bzr 1.8rc1 2008-10-07
 ---------------------

=== modified file 'bzrlib/repository.py'
--- a/bzrlib/repository.py	2008-10-13 00:28:17 +0000
+++ b/bzrlib/repository.py	2008-10-13 04:32:29 +0000
@@ -116,6 +116,8 @@
 
         self._generate_revision_if_needed()
         self.__heads = graph.HeadsCache(repository.get_graph()).heads
+        self.basis_delta = []
+        self._recording_deletes = False
 
     def commit(self, message):
         """Make the actual commit.
@@ -211,15 +213,43 @@
         """Get a delta against the basis inventory for ie."""
         if ie.file_id not in basis_inv:
             # add
-            return (None, path, ie.file_id, ie)
+            result = (None, path, ie.file_id, ie)
+            self.basis_delta.append(result)
+            return result
         elif ie != basis_inv[ie.file_id]:
             # common but altered
             # TODO: avoid tis id2path call.
-            return (basis_inv.id2path(ie.file_id), path, ie.file_id, ie)
+            result = (basis_inv.id2path(ie.file_id), path, ie.file_id, ie)
+            self.basis_delta.append(result)
+            return result
         else:
             # common, unaltered
             return None
 
+    def record_delete(self, path, file_id):
+        """Record that a delete occured against a basis tree.
+
+        This is an optional API - when used it adds items to the basis_delta
+        being accumulated by the commit builder. It cannot be called unless the
+        method recording_deletes() has been called to inform the builder that a
+        delta is being supplied.
+
+        :param path: The path of the thing deleted.
+        :param file_id: The file id that was deleted.
+        """
+        if not self._recording_deletes:
+            raise AssertionError("recording deletes not activated.")
+        self.basis_delta.append((path, None, file_id, None))
+
+    def recording_deletes(self):
+        """Tell the commit builder that deletes are being notified.
+
+        This enables the accumulation of an inventory delta; for the resulting
+        commit to be valid deletes against the basis MUST be recorded via
+        builder.record_delete().
+        """
+        self._recording_deletes = True
+
     def record_entry_contents(self, ie, parent_invs, path, tree,
         content_summary):
         """Record the content of ie from tree into the commit if needed.
@@ -277,15 +307,19 @@
         if ie.revision is not None:
             if not self._versioned_root and path == '':
                 # repositories that do not version the root set the root's
-                # revision to the new commit even when no change occurs, and
-                # this masks when a change may have occurred against the basis,
-                # so calculate if one happened.
+                # revision to the new commit even when no change occurs (more
+                # specifically, they do not record a revision on the root; and
+                # the rev id is assigned to the root during deserialisation -
+                # this masks when a change may have occurred against the basis.
+                # To match this we always issue a delta, because the revision
+                # of the root will always be changing.
                 if ie.file_id in basis_inv:
                     delta = (basis_inv.id2path(ie.file_id), path,
                         ie.file_id, ie)
                 else:
                     # add
                     delta = (None, path, ie.file_id, ie)
+                self.basis_delta.append(delta)
                 return delta, False, None
             else:
                 # we don't need to commit this, because the caller already

=== modified file 'bzrlib/tests/per_repository/test_commit_builder.py'
--- a/bzrlib/tests/per_repository/test_commit_builder.py	2008-09-22 05:15:20 +0000
+++ b/bzrlib/tests/per_repository/test_commit_builder.py	2008-10-13 04:32:29 +0000
@@ -162,6 +162,7 @@
                 self.assertEqual(
                     ('', '', ie.file_id, ie),
                     delta)
+                self.assertEqual(delta, builder.basis_delta[-1])
             else:
                 self.assertEqual(None, delta)
             # Directories do not get hashed.
@@ -191,6 +192,57 @@
         # but thats all the current contract guarantees anyway.
         self.assertEqual(rev_id, tree.branch.repository.get_inventory(rev_id).revision_id)
 
+    def test_record_delete(self):
+        tree = self.make_branch_and_tree(".")
+        self.build_tree(["foo"])
+        tree.add(["foo"], ["foo-id"])
+        rev_id = tree.commit("added foo")
+        # Remove the inventory details for foo-id, because
+        # record_entry_contents ends up copying root verbatim.
+        tree.unversion(["foo-id"])
+        tree.lock_write()
+        try:
+            basis = tree.branch.repository.revision_tree(rev_id)
+            builder = tree.branch.get_commit_builder([rev_id])
+            try:
+                builder.recording_deletes()
+                if builder.record_root_entry is True:
+                    parent_invs = [basis.inventory]
+                    del basis.inventory.root.children['foo']
+                    builder.record_entry_contents(basis.inventory.root,
+                        parent_invs, '', tree, tree.path_content_summary(''))
+                builder.record_delete("foo", "foo-id")
+                self.assertEqual(("foo", None, "foo-id", None),
+                    builder.basis_delta[-1])
+                builder.finish_inventory()
+                rev_id2 = builder.commit('delete foo')
+            except:
+                tree.branch.repository.abort_write_group()
+                raise
+        finally:
+            tree.unlock()
+        rev_tree = builder.revision_tree()
+        rev_tree.lock_read()
+        self.addCleanup(rev_tree.unlock)
+        self.assertFalse(rev_tree.path2id('foo'))
+
+    def test_record_delete_without_notification(self):
+        tree = self.make_branch_and_tree(".")
+        self.build_tree(["foo"])
+        tree.add(["foo"], ["foo-id"])
+        rev_id = tree.commit("added foo")
+        tree.lock_write()
+        try:
+            builder = tree.branch.get_commit_builder([rev_id])
+            try:
+                self.record_root(builder, tree)
+                self.assertRaises(AssertionError,
+                    builder.record_delete, "foo", "foo-id")
+            finally:
+                tree.branch.repository.abort_write_group()
+        finally:
+            tree.unlock()
+
     def test_revision_tree(self):
         tree = self.make_branch_and_tree(".")
         tree.lock_write()
@@ -424,6 +476,7 @@
             new_entry = builder.new_inventory[file_id]
             if delta_against_basis:
                 expected_delta = (name, new_name, file_id, new_entry)
+                self.assertEqual(expected_delta, builder.basis_delta[-1])
             else:
                 expected_delta = None
             self.assertEqual(expected_delta, delta)




More information about the bazaar-commits mailing list