Rev 2880: (robertc) Have CommitBuilder.record_entry_contents return inventory delta information. (Robert Collins). in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Wed Oct 3 08:01:17 BST 2007


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 2880
revision-id: pqm at pqm.ubuntu.com-20071003070115-95pox4ok1e47a7v0
parent: pqm at pqm.ubuntu.com-20071003050442-e0x9ofdfo0hwxnal
parent: robertc at robertcollins.net-20071003061506-2hbze42e1sokni8j
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2007-10-03 08:01:15 +0100
message:
  (robertc) Have CommitBuilder.record_entry_contents return inventory delta information. (Robert Collins).
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/commit.py               commit.py-20050511101309-79ec1a0168e0e825
  bzrlib/errors.py               errors.py-20050309040759-20512168c4e14fbd
  bzrlib/repository.py           rev_storage.py-20051111201905-119e9401e46257e3
  bzrlib/tests/repository_implementations/test_commit_builder.py test_commit_builder.py-20060606110838-76e3ra5slucqus81-1
    ------------------------------------------------------------
    revno: 2871.1.4
    merged: robertc at robertcollins.net-20071003061506-2hbze42e1sokni8j
    parent: robertc at robertcollins.net-20070928070556-lxsw7332u3kdltwz
    parent: pqm at pqm.ubuntu.com-20071003050442-e0x9ofdfo0hwxnal
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: commit-builder
    timestamp: Wed 2007-10-03 16:15:06 +1000
    message:
      Merge bzr.dev.
    ------------------------------------------------------------
    revno: 2871.1.3
    merged: robertc at robertcollins.net-20070928070556-lxsw7332u3kdltwz
    parent: robertc at robertcollins.net-20070928012059-sbuwqzv68dlbb9z9
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: commit-builder
    timestamp: Fri 2007-09-28 17:05:56 +1000
    message:
      * The CommitBuilder method ``record_entry_contents`` now returns summary
        information about the effect of the commit on the repository. This tuple
        contains an inventory delta item if the entry changed from the basis, and a
        boolean indicating whether a new file graph node was recorded.
        (Robert Collins)
    ------------------------------------------------------------
    revno: 2871.1.2
    merged: robertc at robertcollins.net-20070928012059-sbuwqzv68dlbb9z9
    parent: robertc at robertcollins.net-20070928005033-k2aj7takjzzfo3vn
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: commit-builder
    timestamp: Fri 2007-09-28 11:20:59 +1000
    message:
      * ``CommitBuilder.record_entry_contents`` now requires the root entry of a
        tree be supplied to it, previously failing to do so would trigger a
        deprecation warning. (Robert Collins)
=== modified file 'NEWS'
--- a/NEWS	2007-10-03 05:04:42 +0000
+++ b/NEWS	2007-10-03 06:15:06 +0000
@@ -119,6 +119,10 @@
 
   API BREAKS:
 
+   * ``CommitBuilder.record_entry_contents`` now requires the root entry of a
+     tree be supplied to it, previously failing to do so would trigger a
+     deprecation warning. (Robert Collins)
+
    * ``KnitVersionedFile.add*`` will no longer cache added records even when
      enable_cache() has been called - the caching feature is now exclusively for
      reading existing data. (Robert Collins)
@@ -183,6 +187,12 @@
    * Knit joining has been enhanced to support plain to annotated conversion
      and annotated to plain conversion. (Ian Clatworthy)
 
+   * The CommitBuilder method ``record_entry_contents`` now returns summary
+     information about the effect of the commit on the repository. This tuple
+     contains an inventory delta item if the entry changed from the basis, and a
+     boolean indicating whether a new file graph node was recorded.
+     (Robert Collins)
+
   TESTING:
 
    * When running bzr commands within the test suite, internal exceptions are

=== modified file 'bzrlib/commit.py'
--- a/bzrlib/commit.py	2007-10-02 01:06:55 +0000
+++ b/bzrlib/commit.py	2007-10-03 06:15:06 +0000
@@ -665,8 +665,9 @@
                 # required after that changes.
                 if len(self.parents) > 1:
                     ie.revision = None
-                if self.builder.record_entry_contents(ie, self.parent_invs, path,
-                    self.basis_tree, None):
+                delta, version_recorded = self.builder.record_entry_contents(
+                    ie, self.parent_invs, path, self.basis_tree, None)
+                if version_recorded:
                     self.any_entries_changed = True
 
         # note that deletes have occurred
@@ -784,8 +785,9 @@
         else:
             ie = existing_ie.copy()
             ie.revision = None
-        if self.builder.record_entry_contents(ie, self.parent_invs,
-            path, self.work_tree, content_summary):
+        delta, version_recorded = self.builder.record_entry_contents(ie,
+            self.parent_invs, path, self.work_tree, content_summary)
+        if version_recorded:
             self.any_entries_changed = True
         if report_changes:
             self._report_change(ie, path)

=== modified file 'bzrlib/errors.py'
--- a/bzrlib/errors.py	2007-10-02 06:13:56 +0000
+++ b/bzrlib/errors.py	2007-10-03 06:15:06 +0000
@@ -260,6 +260,12 @@
         self.revision_id = revision_id
 
 
+class RootMissing(InternalBzrError):
+
+    _fmt = ("The root entry of a tree must be the first entry supplied to "
+        "record_entry_contents.")
+
+
 class NoHelpTopic(BzrError):
 
     _fmt = ("No help could be found for '%(topic)s'. "

=== modified file 'bzrlib/repository.py'
--- a/bzrlib/repository.py	2007-10-03 04:23:01 +0000
+++ b/bzrlib/repository.py	2007-10-03 06:15:06 +0000
@@ -194,18 +194,23 @@
             commit.
         :param tree: The tree that is being committed.
         """
-        if ie.parent_id is not None:
-            # if ie is not root, add a root automatically.
-            symbol_versioning.warn('Root entry should be supplied to'
-                ' record_entry_contents, as of bzr 0.10.',
-                 DeprecationWarning, stacklevel=2)
-            self.record_entry_contents(tree.inventory.root.copy(), parent_invs,
-                                       '', tree, tree.path_content_summary(''))
+        # In this revision format, root entries have no knit or weave When
+        # serializing out to disk and back in root.revision is always
+        # _new_revision_id
+        ie.revision = self._new_revision_id
+
+    def _get_delta(self, ie, basis_inv, path):
+        """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)
+        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)
         else:
-            # In this revision format, root entries have no knit or weave When
-            # serializing out to disk and back in root.revision is always
-            # _new_revision_id
-            ie.revision = self._new_revision_id
+            # common, unaltered
+            return None
 
     def record_entry_contents(self, ie, parent_invs, path, tree,
         content_summary):
@@ -223,11 +228,16 @@
             content - stat, length, exec, sha/link target. This is only
             accessed when the entry has a revision of None - that is when it is
             a candidate to commit.
-        :return: True if a new version of the entry has been recorded.
-            (Committing a merge where a file was only changed on the other side
-            will not return True.)
+        :return: A tuple (change_delta, version_recorded). change_delta is 
+            an inventory_delta change for this entry against the basis tree of
+            the commit, or None if no change occured against the basis tree.
+            version_recorded is True if a new version of the entry has been
+            recorded. For instance, committing a merge where a file was only
+            changed on the other side will return (delta, False).
         """
         if self.new_inventory.root is None:
+            if ie.parent_id is not None:
+                raise errors.RootMissing()
             self._check_root(ie, parent_invs, tree)
         if ie.revision is None:
             kind = content_summary[0]
@@ -245,11 +255,38 @@
         assert ie.kind == kind
         self.new_inventory.add(ie)
 
+        # TODO: slow, take it out of the inner loop.
+        try:
+            basis_inv = parent_invs[0]
+        except IndexError:
+            basis_inv = Inventory(root_id=None)
+
         # ie.revision is always None if the InventoryEntry is considered
         # for committing. We may record the previous parents revision if the
         # content is actually unchanged against a sole head.
         if ie.revision is not None:
-            return ie.revision == self._new_revision_id and (path != '' or
+            if self._versioned_root or path != '':
+                # not considered for commit
+                delta = None
+            else:
+                # 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.
+                if ie.file_id not in basis_inv:
+                    # add
+                    delta = (None, path, ie.file_id, ie)
+                else:
+                    basis_id = basis_inv[ie.file_id]
+                    if basis_id.name != '':
+                        # not the root
+                        delta = (basis_inv.id2path(ie.file_id), path,
+                            ie.file_id, ie)
+                    else:
+                        # common, unaltered
+                        delta = None
+            # not considered for commit, OR, for non-rich-root 
+            return delta, ie.revision == self._new_revision_id and (path != '' or
                 self._versioned_root)
 
         # XXX: Friction: parent_candidates should return a list not a dict
@@ -301,7 +338,7 @@
                     ie.text_size = parent_entry.text_size
                     ie.text_sha1 = parent_entry.text_sha1
                     ie.executable = parent_entry.executable
-                    return
+                    return self._get_delta(ie, basis_inv, path), False
                 else:
                     # Either there is only a hash change(no hash cache entry,
                     # or same size content change), or there is no change on
@@ -326,13 +363,13 @@
                 ie.text_size = parent_entry.text_size
                 ie.text_sha1 = parent_entry.text_sha1
                 ie.executable = parent_entry.executable
-                return
+                return self._get_delta(ie, basis_inv, path), False
         elif kind == 'directory':
             if not store:
                 # all data is meta here, nothing specific to directory, so
                 # carry over:
                 ie.revision = parent_entry.revision
-                return
+                return self._get_delta(ie, basis_inv, path), False
             lines = []
             self._add_text_to_weave(ie.file_id, lines, heads, None)
         elif kind == 'symlink':
@@ -346,7 +383,7 @@
                 # unchanged, carry over.
                 ie.revision = parent_entry.revision
                 ie.symlink_target = parent_entry.symlink_target
-                return
+                return self._get_delta(ie, basis_inv, path), False
             ie.symlink_target = current_link_target
             lines = []
             self._add_text_to_weave(ie.file_id, lines, heads, None)
@@ -358,14 +395,14 @@
                 # unchanged, carry over.
                 ie.reference_revision = parent_entry.reference_revision
                 ie.revision = parent_entry.revision
-                return
+                return self._get_delta(ie, basis_inv, path), False
             ie.reference_revision = content_summary[3]
             lines = []
             self._add_text_to_weave(ie.file_id, lines, heads, None)
         else:
             raise NotImplementedError('unknown kind')
         ie.revision = self._new_revision_id
-        return True
+        return self._get_delta(ie, basis_inv, path), True
 
     def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
         versionedfile = self.repository.weave_store.get_weave_or_empty(
@@ -401,8 +438,6 @@
             commit.
         :param tree: The tree that is being committed.
         """
-        # ie must be root for this builder
-        assert ie.parent_id is None
 
 
 ######################################################################

=== modified file 'bzrlib/tests/repository_implementations/test_commit_builder.py'
--- a/bzrlib/tests/repository_implementations/test_commit_builder.py	2007-09-27 21:11:38 +0000
+++ b/bzrlib/tests/repository_implementations/test_commit_builder.py	2007-10-03 06:15:06 +0000
@@ -16,6 +16,7 @@
 
 """Tests for repository commit builder."""
 
+from copy import copy
 import errno
 import os
 import sys
@@ -121,23 +122,18 @@
         self.assertEqual(revision_id,
             tree.branch.repository.get_inventory(revision_id).revision_id)
 
-    def test_commit_without_root(self):
-        """This should cause a deprecation warning, not an assertion failure"""
+    def test_commit_without_root_errors(self):
         tree = self.make_branch_and_tree(".")
         tree.lock_write()
         try:
-            if tree.branch.repository.supports_rich_root():
-                raise tests.TestSkipped('Format requires root')
             self.build_tree(['foo'])
             tree.add('foo', 'foo-id')
             entry = tree.inventory['foo-id']
             builder = tree.branch.get_commit_builder([])
-            self.callDeprecated(['Root entry should be supplied to'
-                ' record_entry_contents, as of bzr 0.10.'],
+            self.assertRaises(errors.RootMissing,
                 builder.record_entry_contents, entry, [], 'foo', tree,
                     tree.path_content_summary('foo'))
-            builder.finish_inventory()
-            rev_id = builder.commit('foo bar')
+            builder.abort()
         finally:
             tree.unlock()
     
@@ -152,9 +148,11 @@
         try:
             ie = inventory.make_entry('directory', '', None,
                     tree.inventory.root.file_id)
-            self.assertFalse(builder.record_entry_contents(
+            delta, version_recorded = builder.record_entry_contents(
                 ie, [parent_tree.inventory], '', tree,
-                tree.path_content_summary('')))
+                tree.path_content_summary(''))
+            self.assertFalse(version_recorded)
+            self.assertEqual(None, delta)
             builder.abort()
         except:
             builder.abort()
@@ -229,7 +227,7 @@
     def _add_commit_check_unchanged(self, tree, name):
         tree.add([name], [name + 'id'])
         rev1 = tree.commit('')
-        rev2 = tree.commit('')
+        rev2 = self.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)
@@ -323,15 +321,37 @@
         tree.add([name], [name + 'id'])
         rev1 = tree.commit('')
         changer()
+        rev2 = self.mini_commit(tree, name, tree.id2path(name + 'id'))
+        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
+        self.assertEqual(rev1, tree1.inventory[name + 'id'].revision)
+        self.assertEqual(rev2, tree2.inventory[name + 'id'].revision)
+        self.assertFileAncestry([rev1, rev2], tree, name)
+
+    def mini_commit(self, tree, name, new_name, records_version=True,
+        delta_against_basis=True):
+        """Perform a miniature commit looking for record entry results.
+        
+        :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.
+        """
         tree.lock_write()
         try:
             # mini manual commit here so we can check the return of
             # record_entry_contents.
-            builder = tree.branch.get_commit_builder([tree.last_revision()])
+            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)
             # root
             builder.record_entry_contents(
                 inventory.make_entry('directory', '', None,
@@ -345,13 +365,25 @@
                 return builder.record_entry_contents(ie, parent_invs, path,
                     tree, tree.path_content_summary(path))
 
-            file_id = name + 'id'
+            file_id = tree.path2id(new_name)
             parent_id = tree.inventory[file_id].parent_id
             if parent_id != tree.inventory.root.file_id:
                 commit_id(parent_id)
             # because a change of some sort is meant to have occurred,
             # recording the entry must return True.
-            self.assertTrue(commit_id(file_id))
+            delta, version_recorded = commit_id(file_id)
+            if records_version:
+                self.assertTrue(version_recorded)
+            else:
+                self.assertFalse(version_recorded)
+            new_entry = builder.new_inventory[file_id]
+            if delta_against_basis:
+                expected_delta = (name, new_name, file_id, new_entry)
+            else:
+                expected_delta = None
+            if expected_delta != delta:
+                import pdb;pdb.set_trace()
+            self.assertEqual(expected_delta, delta)
             builder.finish_inventory()
             rev2 = builder.commit('')
             tree.set_parent_ids([rev2])
@@ -361,10 +393,7 @@
             raise
         else:
             tree.unlock()
-        tree1, tree2 = self._get_revtrees(tree, [rev1, rev2])
-        self.assertEqual(rev1, tree1.inventory[name + 'id'].revision)
-        self.assertEqual(rev2, tree2.inventory[name + 'id'].revision)
-        self.assertFileAncestry([rev1, rev2], tree, name)
+        return rev2
 
     def assertFileAncestry(self, ancestry, tree, name, alt_ancestry=None):
         # all the changes that have occured should be in the ancestry
@@ -413,7 +442,7 @@
         rev2 = self._rename_in_tree(tree1, name)
         rev3 = self._rename_in_tree(tree2, name)
         tree1.merge_from_branch(tree2.branch)
-        rev4 = tree1.commit('')
+        rev4 = self.mini_commit(tree1, 'new_' + name, 'new_' + name)
         tree3, = self._get_revtrees(tree1, [rev4])
         self.assertEqual(rev4, tree3.inventory[name + 'id'].revision)
         # TODO: change this to an assertFileGraph call to check the
@@ -445,7 +474,7 @@
         # change on the other side to merge back
         rev2 = self._rename_in_tree(tree2, name)
         tree1.merge_from_branch(tree2.branch)
-        rev3 = tree1.commit('')
+        rev3 = self.mini_commit(tree1, name, 'new_' + name, False)
         tree3, = self._get_revtrees(tree1, [rev2])
         self.assertEqual(rev2, tree3.inventory[name + 'id'].revision)
         self.assertFileAncestry([rev1, rev2], tree1, name)




More information about the bazaar-commits mailing list