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