Rev 2862: (robertc) Introduce new method Tree.path_content_summary for use in commit refactoring. (Robert Collins) in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Tue Sep 25 10:20:17 BST 2007


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

------------------------------------------------------------
revno: 2862
revision-id: pqm at pqm.ubuntu.com-20070925092014-7t2piu8gmm5hvbks
parent: pqm at pqm.ubuntu.com-20070925083651-rthie4089wg1wf6o
parent: robertc at robertcollins.net-20070925084129-ca0kd25h23dmunrs
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Tue 2007-09-25 10:20:14 +0100
message:
  (robertc) Introduce new method Tree.path_content_summary for use in commit refactoring. (Robert Collins)
added:
  bzrlib/tests/tree_implementations/test_path_content_summary.py test_path_content_su-20070904100855-3vrwedz6akn34kl5-1
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/revisiontree.py         revisiontree.py-20060724012533-bg8xyryhxd0o0i0h-1
  bzrlib/tests/tree_implementations/__init__.py __init__.py-20060717075546-420s7b0bj9hzeowi-2
  bzrlib/tree.py                 tree.py-20050309040759-9d5f2496be663e77
  bzrlib/workingtree.py          workingtree.py-20050511021032-29b6ec0a681e02e3
  bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
    ------------------------------------------------------------
    revno: 2776.1.9
    merged: robertc at robertcollins.net-20070925084129-ca0kd25h23dmunrs
    parent: robertc at robertcollins.net-20070925055031-ybtg84jah7lei4gc
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: workingtree
    timestamp: Tue 2007-09-25 18:41:29 +1000
    message:
      Review feedback.
    ------------------------------------------------------------
    revno: 2776.1.8
    merged: robertc at robertcollins.net-20070925055031-ybtg84jah7lei4gc
    parent: robertc at robertcollins.net-20070904100858-971b6sssddwfmwrw
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: workingtree
    timestamp: Tue 2007-09-25 15:50:31 +1000
    message:
      Retrieve the sha from the dirstate for path_content_summary on hash cache hits; slight performance hit but a big win for incremental commits.
    ------------------------------------------------------------
    revno: 2776.1.7
    merged: robertc at robertcollins.net-20070904100858-971b6sssddwfmwrw
    parent: robertc at robertcollins.net-20070904041207-zb3l8hfco0sp6hu8
    committer: Robert Collins <robertc at robertcollins.net>
    branch nick: workingtree
    timestamp: Tue 2007-09-04 20:08:58 +1000
    message:
      * New method on ``bzrlib.tree.Tree`` ``path_content_summary`` provides a
       tuple containing the key information about a path for commit processing
       to complete. (Robert Collins)
=== added file 'bzrlib/tests/tree_implementations/test_path_content_summary.py'
--- a/bzrlib/tests/tree_implementations/test_path_content_summary.py	1970-01-01 00:00:00 +0000
+++ b/bzrlib/tests/tree_implementations/test_path_content_summary.py	2007-09-04 10:08:58 +0000
@@ -0,0 +1,99 @@
+# Copyright (C) 2007 Canonical Ltd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+"""Test that all Tree's implement path_content_summary."""
+
+import os
+
+from bzrlib.osutils import supports_executable
+from bzrlib.tests import SymlinkFeature, TestSkipped, TestNotApplicable
+from bzrlib.tests.tree_implementations import TestCaseWithTree
+
+
+class TestPathContentSummary(TestCaseWithTree):
+
+    def _convert_tree(self, tree):
+        result = TestCaseWithTree._convert_tree(self, tree)
+        result.lock_read()
+        self.addCleanup(result.unlock)
+        return result
+
+    def test_symlink_content_summary(self):
+        self.requireFeature(SymlinkFeature)
+        tree = self.make_branch_and_tree('tree')
+        os.symlink('target', 'tree/path')
+        tree.add(['path'])
+        summary = self._convert_tree(tree).path_content_summary('path')
+        self.assertEqual(('symlink', None, None, 'target'), summary)
+
+    def test_missing_content_summary(self):
+        tree = self.make_branch_and_tree('tree')
+        summary = self._convert_tree(tree).path_content_summary('path')
+        self.assertEqual(('missing', None, None, None), summary)
+
+    def test_file_content_summary_executable(self):
+        if not supports_executable():
+            raise TestNotApplicable()
+        tree = self.make_branch_and_tree('tree')
+        self.build_tree(['tree/path'])
+        tree.add(['path'])
+        current_mode = os.stat('tree/path').st_mode
+        os.chmod('tree/path', current_mode | 0100)
+        summary = self._convert_tree(tree).path_content_summary('path')
+        self.assertEqual(4, len(summary))
+        self.assertEqual('file', summary[0])
+        # size must be known
+        self.assertEqual(22, summary[1])
+        # executable
+        self.assertEqual(True, summary[2])
+        # may have hash,
+        self.assertSubset((summary[3],),
+            (None, '0c352290ae1c26ca7f97d5b2906c4624784abd60'))
+
+    def test_file_content_summary_non_exec(self):
+        tree = self.make_branch_and_tree('tree')
+        self.build_tree(['tree/path'])
+        tree.add(['path'])
+        summary = self._convert_tree(tree).path_content_summary('path')
+        self.assertEqual(4, len(summary))
+        self.assertEqual('file', summary[0])
+        # size must be known
+        self.assertEqual(22, summary[1])
+        # not executable
+        if supports_executable:
+            self.assertEqual(False, summary[2])
+        else:
+            self.assertEqual(None, summary[2])
+        # may have hash,
+        self.assertSubset((summary[3],),
+            (None, '0c352290ae1c26ca7f97d5b2906c4624784abd60'))
+
+    def test_dir_content_summary(self):
+        tree = self.make_branch_and_tree('tree')
+        self.build_tree(['tree/path/'])
+        tree.add(['path'])
+        summary = self._convert_tree(tree).path_content_summary('path')
+        self.assertEqual(('directory', None, None, None), summary)
+
+    def test_tree_content_summary(self):
+        tree = self.make_branch_and_tree('tree')
+        subtree = self.make_branch_and_tree('tree/path')
+        tree.add(['path'])
+        if not tree.branch.repository._format.supports_tree_reference:
+            raise TestSkipped("Tree references not supported.")
+        summary = self._convert_tree(tree).path_content_summary('path')
+        self.assertEqual(4, len(summary))
+        self.assertEqual('tree-reference', summary[0])

=== modified file 'NEWS'
--- a/NEWS	2007-09-25 06:43:45 +0000
+++ b/NEWS	2007-09-25 09:20:14 +0000
@@ -421,6 +421,10 @@
      incremental addition of data to a file without requiring that all the
      data be buffered in memory. (Robert Collins)
 
+   * New method on ``bzrlib.tree.Tree`` ``path_content_summary`` provides a
+     tuple containing the key information about a path for commit processing
+     to complete. (Robert Collins)
+
    * New methods on ``bzrlib.knit.KnitVersionedFile``:
      ``get_data_stream(versions)``, ``insert_data_stream(stream)`` and
      ``get_format_signature()``.  These provide some infrastructure for

=== modified file 'bzrlib/revisiontree.py'
--- a/bzrlib/revisiontree.py	2007-08-30 02:00:37 +0000
+++ b/bzrlib/revisiontree.py	2007-09-04 10:08:58 +0000
@@ -153,6 +153,20 @@
         file_id = osutils.safe_file_id(file_id)
         return self._inventory[file_id].kind
 
+    def path_content_summary(self, path):
+        """See Tree.path_content_summary."""
+        id = self.inventory.path2id(path)
+        if id is None:
+            return ('missing', None, None, None)
+        entry = self._inventory[id]
+        kind = entry.kind
+        if kind == 'file':
+            return (kind, entry.text_size, entry.executable, entry.text_sha1)
+        elif kind == 'symlink':
+            return (kind, None, None, entry.symlink_target)
+        else:
+            return (kind, None, None, None)
+
     def _comparison_data(self, entry, path):
         if entry is None:
             return None, False, None

=== modified file 'bzrlib/tests/tree_implementations/__init__.py'
--- a/bzrlib/tests/tree_implementations/__init__.py	2007-06-28 07:15:28 +0000
+++ b/bzrlib/tests/tree_implementations/__init__.py	2007-09-04 10:08:58 +0000
@@ -327,6 +327,7 @@
         'bzrlib.tests.tree_implementations.test_get_symlink_target',
         'bzrlib.tests.tree_implementations.test_inv',
         'bzrlib.tests.tree_implementations.test_list_files',
+        'bzrlib.tests.tree_implementations.test_path_content_summary',
         'bzrlib.tests.tree_implementations.test_revision_tree',
         'bzrlib.tests.tree_implementations.test_test_trees',
         'bzrlib.tests.tree_implementations.test_tree',

=== modified file 'bzrlib/tree.py'
--- a/bzrlib/tree.py	2007-09-14 01:14:26 +0000
+++ b/bzrlib/tree.py	2007-09-25 09:20:14 +0000
@@ -188,6 +188,20 @@
         raise NotImplementedError("Tree subclass %s must implement kind"
             % self.__class__.__name__)
 
+    def path_content_summary(self, path):
+        """Get a summary of the information about path.
+        
+        :param path: A relative path within the tree.
+        :return: A tuple containing kind, size, exec, sha1-or-link.
+            Kind is always present (see tree.kind()).
+            size is present if kind is file, None otherwise.
+            exec is None unless kind is file and the platform supports the 'x'
+                bit.
+            sha1-or-link is the link target if kind is symlink, or the sha1 if
+                it can be obtained without reading the file.
+        """
+        raise NotImplementedError(self.path_content_summary)
+
     def get_reference_revision(self, file_id, path=None):
         raise NotImplementedError("Tree subclass %s must implement "
                                   "get_reference_revision"

=== modified file 'bzrlib/workingtree.py'
--- a/bzrlib/workingtree.py	2007-09-13 22:41:38 +0000
+++ b/bzrlib/workingtree.py	2007-09-25 09:20:14 +0000
@@ -702,6 +702,40 @@
         if updated:
             self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
 
+    def path_content_summary(self, path, _lstat=osutils.lstat,
+        _mapper=osutils.file_kind_from_stat_mode):
+        """See Tree.path_content_summary."""
+        abspath = self.abspath(path)
+        try:
+            stat_result = _lstat(abspath)
+        except OSError, e:
+            if getattr(e, 'errno', None) == errno.ENOENT:
+                # no file.
+                return ('missing', None, None, None)
+            # propagate other errors
+            raise
+        kind = _mapper(stat_result.st_mode)
+        if kind == 'file':
+            size = stat_result.st_size
+            # try for a stat cache lookup
+            if not supports_executable():
+                executable = None # caller can decide policy.
+            else:
+                mode = stat_result.st_mode
+                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
+            return (kind, size, executable, self._sha_from_stat(
+                path, stat_result))
+        elif kind == 'directory':
+            # perhaps it looks like a plain directory, but it's really a
+            # reference.
+            if self._directory_is_tree_reference(path):
+                kind = 'tree-reference'
+            return kind, None, None, None
+        elif kind == 'symlink':
+            return ('symlink', None, None, os.readlink(abspath))
+        else:
+            return (kind, None, None, None)
+
     @deprecated_method(zero_eleven)
     @needs_read_lock
     def pending_merges(self):
@@ -799,6 +833,16 @@
                 yield Stanza(file_id=file_id.decode('utf8'), hash=hash)
         self._put_rio('merge-hashes', iter_stanzas(), MERGE_MODIFIED_HEADER_1)
 
+    def _sha_from_stat(self, path, stat_result):
+        """Get a sha digest from the tree's stat cache.
+
+        The default implementation assumes no stat cache is present.
+
+        :param path: The path.
+        :param stat_result: The stat result being looked up.
+        """
+        return None
+
     def _put_rio(self, filename, stanzas, header):
         self._must_be_locked()
         my_file = rio_file(stanzas, header)
@@ -943,6 +987,21 @@
             other_tree.unlock()
         other_tree.bzrdir.retire_bzrdir()
 
+    def _directory_is_tree_reference(self, relpath):
+        # as a special case, if a directory contains control files then 
+        # it's a tree reference, except that the root of the tree is not
+        return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
+        # TODO: We could ask all the control formats whether they
+        # recognize this directory, but at the moment there's no cheap api
+        # to do that.  Since we probably can only nest bzr checkouts and
+        # they always use this name it's ok for now.  -- mbp 20060306
+        #
+        # FIXME: There is an unhandled case here of a subdirectory
+        # containing .bzr but not a branch; that will probably blow up
+        # when you try to commit it.  It might happen if there is a
+        # checkout in a subdirectory.  This can be avoided by not adding
+        # it.  mbp 20070306
+
     @needs_tree_write_lock
     def extract(self, file_id, format=None):
         """Extract a subtree from this tree.

=== modified file 'bzrlib/workingtree_4.py'
--- a/bzrlib/workingtree_4.py	2007-09-20 07:03:33 +0000
+++ b/bzrlib/workingtree_4.py	2007-09-25 09:20:14 +0000
@@ -49,7 +49,6 @@
     errors,
     generate_ids,
     globbing,
-    hashcache,
     ignores,
     merge,
     osutils,
@@ -271,21 +270,6 @@
         self._dirstate = dirstate.DirState.on_file(local_path)
         return self._dirstate
 
-    def _directory_is_tree_reference(self, relpath):
-        # as a special case, if a directory contains control files then 
-        # it's a tree reference, except that the root of the tree is not
-        return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
-        # TODO: We could ask all the control formats whether they
-        # recognize this directory, but at the moment there's no cheap api
-        # to do that.  Since we probably can only nest bzr checkouts and
-        # they always use this name it's ok for now.  -- mbp 20060306
-        #
-        # FIXME: There is an unhandled case here of a subdirectory
-        # containing .bzr but not a branch; that will probably blow up
-        # when you try to commit it.  It might happen if there is a
-        # checkout in a subdirectory.  This can be avoided by not adding
-        # it.  mbp 20070306
-
     def filter_unversioned_files(self, paths):
         """Filter out paths that are versioned.
 
@@ -1107,6 +1091,24 @@
         if state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED:
             self._make_dirty(reset_inventory=True)
 
+    def _sha_from_stat(self, path, stat_result):
+        """Get a sha digest from the tree's stat cache.
+
+        The default implementation assumes no stat cache is present.
+
+        :param path: The path.
+        :param stat_result: The stat result being looked up.
+        """
+        state = self.current_dirstate()
+        # XXX: should we make the path be passed in as utf8 ?
+        entry = state._get_entry(0, path_utf8=cache_utf8.encode(path))
+        tree_details = entry[1][0]
+        packed_stat = dirstate.pack_stat(stat_result)
+        if tree_details[4] == packed_stat:
+            return tree_details[1]
+        else:
+            return None
+
     @needs_read_lock
     def supports_tree_reference(self):
         return self._repo_supports_tree_reference
@@ -1569,6 +1571,20 @@
     def kind(self, file_id):
         return self.inventory[file_id].kind
 
+    def path_content_summary(self, path):
+        """See Tree.path_content_summary."""
+        id = self.inventory.path2id(path)
+        if id is None:
+            return ('missing', None, None, None)
+        entry = self._inventory[id]
+        kind = entry.kind
+        if kind == 'file':
+            return (kind, entry.text_size, entry.executable, entry.text_sha1)
+        elif kind == 'symlink':
+            return (kind, None, None, entry.symlink_target)
+        else:
+            return (kind, None, None, None)
+
     def is_executable(self, file_id, path=None):
         ie = self.inventory[file_id]
         if ie.kind != "file":




More information about the bazaar-commits mailing list