Rev 3710: First cut - make it work - at updating the tree stat cache during commit. in http://people.ubuntu.com/~robertc/baz2.0/commit-dirstate
Robert Collins
robertc at robertcollins.net
Fri Sep 19 07:53:50 BST 2008
At http://people.ubuntu.com/~robertc/baz2.0/commit-dirstate
------------------------------------------------------------
revno: 3710
revision-id: robertc at robertcollins.net-20080919065341-5t5w1p2gi926nfia
parent: pqm at pqm.ubuntu.com-20080916010540-7l7uexkq5aelzv5p
committer: Robert Collins <robertc at robertcollins.net>
branch nick: commit-dirstate
timestamp: Fri 2008-09-19 16:53:41 +1000
message:
First cut - make it work - at updating the tree stat cache during commit.
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/commit.py commit.py-20050511101309-79ec1a0168e0e825
bzrlib/dirstate.py dirstate.py-20060728012006-d6mvoihjb3je9peu-1
bzrlib/mutabletree.py mutabletree.py-20060906023413-4wlkalbdpsxi2r4y-2
bzrlib/repository.py rev_storage.py-20051111201905-119e9401e46257e3
bzrlib/tests/per_repository/test_commit_builder.py test_commit_builder.py-20060606110838-76e3ra5slucqus81-1
bzrlib/tests/test_dirstate.py test_dirstate.py-20060728012006-d6mvoihjb3je9peu-2
bzrlib/tests/test_workingtree_4.py test_workingtree_4.p-20070223025758-531n3tznl3zacv2o-1
bzrlib/workingtree_4.py workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
=== modified file 'NEWS'
--- a/NEWS 2008-09-16 00:35:25 +0000
+++ b/NEWS 2008-09-19 06:53:41 +0000
@@ -13,6 +13,9 @@
IMPROVEMENTS:
+ * File changes during a commit will update the tree stat cache.
+ (Robert Collins)
+
BUG FIXES:
* Branching from a shared repository on a smart server into a new
=== modified file 'bzrlib/commit.py'
--- a/bzrlib/commit.py 2008-08-05 04:10:43 +0000
+++ b/bzrlib/commit.py 2008-09-19 06:53:41 +0000
@@ -690,11 +690,12 @@
# required after that changes.
if len(self.parents) > 1:
ie.revision = None
- delta, version_recorded = self.builder.record_entry_contents(
+ 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
- if delta: self._basis_delta.append(delta)
+ if delta:
+ self._basis_delta.append(delta)
def _report_and_accumulate_deletes(self):
# XXX: Could the list of deleted paths and ids be instead taken from
@@ -843,14 +844,18 @@
else:
ie = existing_ie.copy()
ie.revision = None
- delta, version_recorded = self.builder.record_entry_contents(ie,
- self.parent_invs, path, self.work_tree, content_summary)
+ # For carried over entries we don't care about the fs hash - the repo
+ # isn't generating a sha, so we're not saving computation time.
+ delta, version_recorded, fs_hash = self.builder.record_entry_contents(
+ ie, self.parent_invs, path, self.work_tree, content_summary)
if delta:
self._basis_delta.append(delta)
if version_recorded:
self.any_entries_changed = True
if report_changes:
self._report_change(ie, path)
+ if fs_hash:
+ self.work_tree._observed_sha1(ie.file_id, path, fs_hash)
return ie
def _report_change(self, ie, path):
=== modified file 'bzrlib/dirstate.py'
--- a/bzrlib/dirstate.py 2008-07-30 08:55:10 +0000
+++ b/bzrlib/dirstate.py 2008-09-19 06:53:41 +0000
@@ -1472,6 +1472,29 @@
# it is being resurrected here, so blank it out temporarily.
self._dirblocks[block_index][1][entry_index][1][1] = null
+ def _observed_sha1(self, entry, sha1, stat_value,
+ _stat_to_minikind=_stat_to_minikind, _pack_stat=pack_stat):
+ """Note the sha1 of a file.
+
+ :param entry: The entry the sha1 is for.
+ :param sha1: The observed sha1.
+ :param stat_value: The os.lstat for the file.
+ """
+ try:
+ minikind = _stat_to_minikind[stat_value.st_mode & 0170000]
+ except KeyError:
+ # Unhandled kind
+ return None
+ packed_stat = _pack_stat(stat_value)
+ if minikind == 'f':
+ if self._cutoff_time is None:
+ self._sha_cutoff_time()
+ if (stat_value.st_mtime < self._cutoff_time
+ and stat_value.st_ctime < self._cutoff_time):
+ entry[1][0] = ('f', sha1, entry[1][0][2], entry[1][0][3],
+ packed_stat)
+ self._dirblock_state = DirState.IN_MEMORY_MODIFIED
+
def update_entry(self, entry, abspath, stat_value,
_stat_to_minikind=_stat_to_minikind,
_pack_stat=pack_stat):
=== modified file 'bzrlib/mutabletree.py'
--- a/bzrlib/mutabletree.py 2008-05-08 04:33:38 +0000
+++ b/bzrlib/mutabletree.py 2008-09-19 06:53:41 +0000
@@ -247,6 +247,26 @@
"""
raise NotImplementedError(self.mkdir)
+ def _observed_sha1(self, file_id, path, sha1):
+ """Tell the tree we have observed a paths sha1.
+
+ The intent of this function is to allow trees that have a hashcache to
+ update the hashcache during commit. If the observed file is too new to
+ be safely hash-cached the tree will ignore it; this will likewise mean
+ that a file changed subsequent to the file's being read and sha'd will
+ not lead to a false cache entry. A file move could cause this, and
+ in future work it would be better to pass the cache fingerprint around
+ so that its never separated from the sha, and we can supply the
+ fingerprint back to the tree during this code path.
+
+ The default implementation does nothing.
+
+ :param file_id: The file id
+ :param path: The file path
+ :param sha1: The sha 1 that was observed.
+ :return: None
+ """
+
@needs_write_lock
def put_file_bytes_non_atomic(self, file_id, bytes):
"""Update the content of a file in the tree.
=== modified file 'bzrlib/repository.py'
--- a/bzrlib/repository.py 2008-09-04 14:22:34 +0000
+++ b/bzrlib/repository.py 2008-09-19 06:53:41 +0000
@@ -241,12 +241,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: 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.
+ :return: A tuple (change_delta, version_recorded, fs_hash).
+ 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).
+ fs_hash is either None, or the hash of the path. (Currently we
+ use sha1 hashes of the entire file content, and only calculate
+ these for regular files).
"""
if self.new_inventory.root is None:
if ie.parent_id is not None:
@@ -287,7 +291,7 @@
else:
# add
delta = (None, path, ie.file_id, ie)
- return delta, False
+ return delta, False, None
else:
# we don't need to commit this, because the caller already
# determined that an existing revision of this file is
@@ -298,7 +302,7 @@
raise AssertionError("Impossible situation, a skipped "
"inventory entry (%r) claims to be modified in this "
"commit (%r).", (ie, self._new_revision_id))
- return None, False
+ return None, False, None
# XXX: Friction: parent_candidates should return a list not a dict
# so that we don't have to walk the inventories again.
parent_candiate_entries = ie.parent_candidates(parent_invs)
@@ -350,7 +354,7 @@
ie.text_size = parent_entry.text_size
ie.text_sha1 = parent_entry.text_sha1
ie.executable = parent_entry.executable
- return self._get_delta(ie, basis_inv, path), False
+ return self._get_delta(ie, basis_inv, path), False, None
else:
# Either there is only a hash change(no hash cache entry,
# or same size content change), or there is no change on
@@ -375,13 +379,13 @@
ie.text_size = parent_entry.text_size
ie.text_sha1 = parent_entry.text_sha1
ie.executable = parent_entry.executable
- return self._get_delta(ie, basis_inv, path), False
+ return self._get_delta(ie, basis_inv, path), False, None
elif kind == 'directory':
if not store:
# all data is meta here, nothing specific to directory, so
# carry over:
ie.revision = parent_entry.revision
- return self._get_delta(ie, basis_inv, path), False
+ return self._get_delta(ie, basis_inv, path), False, None
lines = []
self._add_text_to_weave(ie.file_id, lines, heads, None)
elif kind == 'symlink':
@@ -395,7 +399,7 @@
# unchanged, carry over.
ie.revision = parent_entry.revision
ie.symlink_target = parent_entry.symlink_target
- return self._get_delta(ie, basis_inv, path), False
+ return self._get_delta(ie, basis_inv, path), False, None
ie.symlink_target = current_link_target
lines = []
self._add_text_to_weave(ie.file_id, lines, heads, None)
@@ -407,14 +411,14 @@
# unchanged, carry over.
ie.reference_revision = parent_entry.reference_revision
ie.revision = parent_entry.revision
- return self._get_delta(ie, basis_inv, path), False
+ return self._get_delta(ie, basis_inv, path), False, None
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 self._get_delta(ie, basis_inv, path), True
+ return self._get_delta(ie, basis_inv, path), True, ie.text_sha1
def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
# Note: as we read the content directly from the tree, we know its not
=== modified file 'bzrlib/tests/per_repository/test_commit_builder.py'
--- a/bzrlib/tests/per_repository/test_commit_builder.py 2008-09-04 20:32:04 +0000
+++ b/bzrlib/tests/per_repository/test_commit_builder.py 2008-09-19 06:53:41 +0000
@@ -151,7 +151,7 @@
try:
ie = inventory.make_entry('directory', '', None,
tree.get_root_id())
- delta, version_recorded = builder.record_entry_contents(
+ delta, version_recorded, fs_hash = builder.record_entry_contents(
ie, [parent_tree.inventory], '', tree,
tree.path_content_summary(''))
self.assertFalse(version_recorded)
@@ -164,6 +164,8 @@
delta)
else:
self.assertEqual(None, delta)
+ # Directories do not get hashed.
+ self.assertEqual(None, fs_hash)
builder.abort()
except:
builder.abort()
@@ -288,10 +290,12 @@
os.symlink('target', 'link')
self._add_commit_check_unchanged(tree, 'link')
- def _add_commit_renamed_check_changed(self, tree, name):
+ def _add_commit_renamed_check_changed(self, tree, name,
+ expect_fs_hash=False):
def rename():
tree.rename_one(name, 'new_' + name)
- self._add_commit_change_check_changed(tree, name, rename)
+ self._add_commit_change_check_changed(tree, name, rename,
+ expect_fs_hash=expect_fs_hash)
def test_last_modified_revision_after_rename_dir_changes(self):
# renaming a dir changes the last modified.
@@ -303,7 +307,8 @@
# renaming a file changes the last modified.
tree = self.make_branch_and_tree('.')
self.build_tree(['file'])
- self._add_commit_renamed_check_changed(tree, 'file')
+ self._add_commit_renamed_check_changed(tree, 'file',
+ expect_fs_hash=True)
def test_last_modified_revision_after_rename_link_changes(self):
# renaming a link changes the last modified.
@@ -312,12 +317,14 @@
os.symlink('target', 'link')
self._add_commit_renamed_check_changed(tree, 'link')
- def _add_commit_reparent_check_changed(self, tree, name):
+ def _add_commit_reparent_check_changed(self, tree, name,
+ expect_fs_hash=False):
self.build_tree(['newparent/'])
tree.add(['newparent'])
def reparent():
tree.rename_one(name, 'newparent/new_' + name)
- self._add_commit_change_check_changed(tree, name, reparent)
+ self._add_commit_change_check_changed(tree, name, reparent,
+ expect_fs_hash=expect_fs_hash)
def test_last_modified_revision_after_reparent_dir_changes(self):
# reparenting a dir changes the last modified.
@@ -329,7 +336,8 @@
# reparenting a file changes the last modified.
tree = self.make_branch_and_tree('.')
self.build_tree(['file'])
- self._add_commit_reparent_check_changed(tree, 'file')
+ self._add_commit_reparent_check_changed(tree, 'file',
+ expect_fs_hash=True)
def test_last_modified_revision_after_reparent_link_changes(self):
# reparenting a link changes the last modified.
@@ -338,11 +346,13 @@
os.symlink('target', 'link')
self._add_commit_reparent_check_changed(tree, 'link')
- def _add_commit_change_check_changed(self, tree, name, changer):
+ def _add_commit_change_check_changed(self, tree, name, changer,
+ expect_fs_hash=False):
tree.add([name], [name + 'id'])
rev1 = tree.commit('')
changer()
- rev2 = self.mini_commit(tree, name, tree.id2path(name + 'id'))
+ rev2 = self.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)
self.assertEqual(rev2, tree2.inventory[name + 'id'].revision)
@@ -353,7 +363,7 @@
self.assertFileGraph(expected_graph, tree, (file_id, rev2))
def mini_commit(self, tree, name, new_name, records_version=True,
- delta_against_basis=True):
+ delta_against_basis=True, expect_fs_hash=False):
"""Perform a miniature commit looking for record entry results.
:param tree: The tree to commit.
@@ -363,6 +373,8 @@
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: True or false to indicate whether we expect a
+ file hash to be returned from the record_entry_contents call.
"""
tree.lock_write()
try:
@@ -396,18 +408,20 @@
commit_id(parent_id)
# because a change of some sort is meant to have occurred,
# recording the entry must return True.
- delta, version_recorded = commit_id(file_id)
+ delta, version_recorded, fs_hash = commit_id(file_id)
if records_version:
self.assertTrue(version_recorded)
else:
self.assertFalse(version_recorded)
+ if expect_fs_hash:
+ self.assertEqual(tree.get_file_sha1(file_id), fs_hash)
+ else:
+ self.assertEqual(None, fs_hash)
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('')
@@ -434,7 +448,8 @@
self.build_tree(['file'])
def change_file():
tree.put_file_bytes_non_atomic('fileid', 'new content')
- self._add_commit_change_check_changed(tree, 'file', change_file)
+ self._add_commit_change_check_changed(tree, 'file', change_file,
+ expect_fs_hash=True)
def test_last_modified_revision_after_content_link_changes(self):
# changing a link changes the last modified.
@@ -455,13 +470,14 @@
tree.rename_one(name, 'new_' + name)
return tree.commit('')
- def _commit_sprout_rename_merge(self, tree1, name):
+ def _commit_sprout_rename_merge(self, tree1, name, expect_fs_hash=False):
rev1, tree2 = self._commit_sprout(tree1, name)
# change both sides equally
rev2 = self._rename_in_tree(tree1, name)
rev3 = self._rename_in_tree(tree2, name)
tree1.merge_from_branch(tree2.branch)
- rev4 = self.mini_commit(tree1, 'new_' + name, 'new_' + name)
+ rev4 = self.mini_commit(tree1, 'new_' + name, 'new_' + name,
+ expect_fs_hash=expect_fs_hash)
tree3, = self._get_revtrees(tree1, [rev4])
self.assertEqual(rev4, tree3.inventory[name + 'id'].revision)
file_id = name + 'id'
@@ -482,7 +498,7 @@
# merge a file changes the last modified.
tree1 = self.make_branch_and_tree('t1')
self.build_tree(['t1/file'])
- self._commit_sprout_rename_merge(tree1, 'file')
+ self._commit_sprout_rename_merge(tree1, 'file', expect_fs_hash=True)
def test_last_modified_revision_after_merge_link_changes(self):
# merge a link changes the last modified.
@@ -534,7 +550,7 @@
self.requireFeature(tests.SymlinkFeature)
os.symlink('target', name)
- def _check_kind_change(self, make_before, make_after):
+ def _check_kind_change(self, make_before, make_after, expect_fs_hash=False):
tree = self.make_branch_and_tree('.')
path = 'name'
make_before(path)
@@ -543,16 +559,19 @@
osutils.delete_any(path)
make_after(path)
- self._add_commit_change_check_changed(tree, path, change_kind)
+ self._add_commit_change_check_changed(tree, path, change_kind,
+ expect_fs_hash=expect_fs_hash)
def test_last_modified_dir_file(self):
- self._check_kind_change(self.make_dir, self.make_file)
+ self._check_kind_change(self.make_dir, self.make_file,
+ expect_fs_hash=True)
def test_last_modified_dir_link(self):
self._check_kind_change(self.make_dir, self.make_link)
def test_last_modified_link_file(self):
- self._check_kind_change(self.make_link, self.make_file)
+ self._check_kind_change(self.make_link, self.make_file,
+ expect_fs_hash=True)
def test_last_modified_link_dir(self):
self._check_kind_change(self.make_link, self.make_dir)
=== modified file 'bzrlib/tests/test_dirstate.py'
--- a/bzrlib/tests/test_dirstate.py 2008-09-01 14:03:34 +0000
+++ b/bzrlib/tests/test_dirstate.py 2008-09-19 06:53:41 +0000
@@ -1677,6 +1677,28 @@
entry = state._get_entry(0, path_utf8='a')
return state, entry
+ def test_observed_sha1_cachable(self):
+ state, entry = self.get_state_with_a()
+ atime = time.time() - 10
+ self.build_tree(['a'])
+ statvalue = os.lstat('a')
+ statvalue = _FakeStat(statvalue.st_size, atime, atime,
+ statvalue.st_dev, statvalue.st_ino, statvalue.st_mode)
+ state._observed_sha1(entry, "foo", statvalue)
+ self.assertEqual('foo', entry[1][0][1])
+ packed_stat = dirstate.pack_stat(statvalue)
+ self.assertEqual(packed_stat, entry[1][0][4])
+
+ def test_observed_sha1_not_cachable(self):
+ state, entry = self.get_state_with_a()
+ oldval = entry[1][0][1]
+ oldstat = entry[1][0][4]
+ self.build_tree(['a'])
+ statvalue = os.lstat('a')
+ state._observed_sha1(entry, "foo", statvalue)
+ self.assertEqual(oldval, entry[1][0][1])
+ self.assertEqual(oldstat, entry[1][0][4])
+
def test_update_entry(self):
state, entry = self.get_state_with_a()
self.build_tree(['a'])
=== modified file 'bzrlib/tests/test_workingtree_4.py'
--- a/bzrlib/tests/test_workingtree_4.py 2008-07-08 14:55:19 +0000
+++ b/bzrlib/tests/test_workingtree_4.py 2008-09-19 06:53:41 +0000
@@ -18,6 +18,7 @@
"""Tests for WorkingTreeFormat4"""
import os
+import time
from bzrlib import (
bzrdir,
@@ -577,98 +578,54 @@
self.assertEqual([], changes)
self.assertEqual(['', 'versioned', 'versioned2'], returned)
-
-class TestCorruptDirstate(TestCaseWithTransport):
- """Tests for how we handle when the dirstate has been corrupted."""
-
- def create_wt4(self):
- control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
- control.create_repository()
- control.create_branch()
- tree = workingtree_4.WorkingTreeFormat4().initialize(control)
+ def get_tree_with_cachable_file_foo(self):
+ tree = self.make_branch_and_tree('.')
+ self.build_tree(['foo'])
+ tree.add(['foo'], ['foo-id'])
+ # a 4 second old timestamp is always hashable - sucks to delay
+ # the test suite, but not testing this is worse.
+ time.sleep(4)
return tree
- def test_invalid_rename(self):
- tree = self.create_wt4()
- # Create a corrupted dirstate
+ def test_commit_updates_hash_cache(self):
+ tree = self.get_tree_with_cachable_file_foo()
+ revid = tree.commit('a commit')
+ # tree's dirstate should now have a valid stat entry for foo.
+ tree.lock_read()
+ entry = tree._get_entry(path='foo')
+ expected_sha1 = osutils.sha_file_by_name('foo')
+ self.assertEqual(expected_sha1, entry[1][0][1])
+
+ def test_observed_sha1_cachable(self):
+ tree = self.get_tree_with_cachable_file_foo()
+ expected_sha1 = osutils.sha_file_by_name('foo')
tree.lock_write()
try:
- tree.commit('init') # We need a parent, or we always compare with NULL
- state = tree.current_dirstate()
- state._read_dirblocks_if_needed()
- # Now add in an invalid entry, a rename with a dangling pointer
- state._dirblocks[1][1].append((('', 'foo', 'foo-id'),
- [('f', '', 0, False, ''),
- ('r', 'bar', 0 , False, '')]))
- self.assertListRaises(errors.CorruptDirstate,
- tree.iter_changes, tree.basis_tree())
+ tree._observed_sha1("foo-id", "foo", expected_sha1)
+ self.assertEqual(expected_sha1,
+ tree._get_entry(path="foo")[1][0][1])
finally:
tree.unlock()
-
- def get_simple_dirblocks(self, state):
- """Extract the simple information from the DirState.
-
- This returns the dirblocks, only with the sha1sum and stat details
- filtered out.
- """
- simple_blocks = []
- for block in state._dirblocks:
- simple_block = (block[0], [])
- for entry in block[1]:
- # Include the key for each entry, and for each parent include
- # just the minikind, so we know if it was
- # present/absent/renamed/etc
- simple_block[1].append((entry[0], [i[0] for i in entry[1]]))
- simple_blocks.append(simple_block)
- return simple_blocks
-
- def test_update_basis_with_invalid_delta(self):
- """When given an invalid delta, it should abort, and not be saved."""
- self.build_tree(['dir/', 'dir/file'])
- tree = self.create_wt4()
- tree.lock_write()
+ tree = tree.bzrdir.open_workingtree()
+ tree.lock_read()
self.addCleanup(tree.unlock)
- tree.add(['dir', 'dir/file'], ['dir-id', 'file-id'])
- first_revision_id = tree.commit('init')
-
- root_id = tree.path2id('')
- state = tree.current_dirstate()
- state._read_dirblocks_if_needed()
- self.assertEqual([
- ('', [(('', '', root_id), ['d', 'd'])]),
- ('', [(('', 'dir', 'dir-id'), ['d', 'd'])]),
- ('dir', [(('dir', 'file', 'file-id'), ['f', 'f'])]),
- ], self.get_simple_dirblocks(state))
-
- tree.remove(['dir/file'])
- self.assertEqual([
- ('', [(('', '', root_id), ['d', 'd'])]),
- ('', [(('', 'dir', 'dir-id'), ['d', 'd'])]),
- ('dir', [(('dir', 'file', 'file-id'), ['a', 'f'])]),
- ], self.get_simple_dirblocks(state))
- # Make sure the removal is written to disk
- tree.flush()
-
- # self.assertRaises(Exception, tree.update_basis_by_delta,
- new_dir = inventory.InventoryDirectory('dir-id', 'new-dir', root_id)
- new_dir.revision = 'new-revision-id'
- new_file = inventory.InventoryFile('file-id', 'new-file', root_id)
- new_file.revision = 'new-revision-id'
- self.assertRaises(errors.InconsistentDelta,
- tree.update_basis_by_delta, 'new-revision-id',
- [('dir', 'new-dir', 'dir-id', new_dir),
- ('dir/file', 'new-dir/new-file', 'file-id', new_file),
- ])
- del state
-
- # Now when we re-read the file it should not have been modified
- tree.unlock()
+ self.assertEqual(expected_sha1, tree._get_entry(path="foo")[1][0][1])
+
+ def test_observed_sha1_new_file(self):
+ tree = self.make_branch_and_tree('.')
+ self.build_tree(['foo'])
+ tree.add(['foo'], ['foo-id'])
tree.lock_read()
- self.assertEqual(first_revision_id, tree.last_revision())
- state = tree.current_dirstate()
- state._read_dirblocks_if_needed()
- self.assertEqual([
- ('', [(('', '', root_id), ['d', 'd'])]),
- ('', [(('', 'dir', 'dir-id'), ['d', 'd'])]),
- ('dir', [(('dir', 'file', 'file-id'), ['a', 'f'])]),
- ], self.get_simple_dirblocks(state))
+ try:
+ current_sha1 = tree._get_entry(path="foo")[1][0][1]
+ finally:
+ tree.unlock()
+ tree.lock_write()
+ try:
+ tree._observed_sha1("foo-id", "foo",
+ osutils.sha_file_by_name('foo'))
+ # Must not have changed
+ self.assertEqual(current_sha1,
+ tree._get_entry(path="foo")[1][0][1])
+ finally:
+ tree.unlock()
=== modified file 'bzrlib/workingtree_4.py'
--- a/bzrlib/workingtree_4.py 2008-09-01 14:03:34 +0000
+++ b/bzrlib/workingtree_4.py 2008-09-19 06:53:41 +0000
@@ -548,6 +548,13 @@
# path is missing on disk.
continue
+ def _observed_sha1(self, file_id, path, sha1):
+ """See MutableTree._observed_sha1."""
+ state = self.current_dirstate()
+ entry = self._get_entry(file_id=file_id, path=path)
+ statvalue = os.lstat(self.abspath(path))
+ state._observed_sha1(entry, sha1, statvalue)
+
def kind(self, file_id):
"""Return the kind of a file.
More information about the bazaar-commits
mailing list