Rev 3705: Refactor to allow a pluggable dirstate update_entry with interface tests. in http://people.ubuntu.com/~robertc/baz2.0/readdir
Robert Collins
robertc at robertcollins.net
Sat Sep 13 08:59:12 BST 2008
At http://people.ubuntu.com/~robertc/baz2.0/readdir
------------------------------------------------------------
revno: 3705
revision-id: robertc at robertcollins.net-20080913075905-4q1okaa2yntxxop2
parent: robertc at robertcollins.net-20080913043902-o8cuque1ozwpvk6x
committer: Robert Collins <robertc at robertcollins.net>
branch nick: update_entry
timestamp: Sat 2008-09-13 17:59:05 +1000
message:
Refactor to allow a pluggable dirstate update_entry with interface tests.
modified:
bzrlib/_dirstate_helpers_c.pyx dirstate_helpers.pyx-20070503201057-u425eni465q4idwn-3
bzrlib/dirstate.py dirstate.py-20060728012006-d6mvoihjb3je9peu-1
bzrlib/tests/test__dirstate_helpers.py test_dirstate_helper-20070504035751-jsbn00xodv0y1eve-2
bzrlib/tests/test_dirstate.py test_dirstate.py-20060728012006-d6mvoihjb3je9peu-2
bzrlib/workingtree_4.py workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
=== modified file 'bzrlib/_dirstate_helpers_c.pyx'
--- a/bzrlib/_dirstate_helpers_c.pyx 2008-09-02 19:15:57 +0000
+++ b/bzrlib/_dirstate_helpers_c.pyx 2008-09-13 07:59:05 +0000
@@ -19,8 +19,8 @@
This is the python implementation for DirState functions.
"""
-from bzrlib import errors
-from bzrlib.dirstate import DirState
+from bzrlib import errors, osutils
+from bzrlib.dirstate import DirState, pack_stat
# Give Pyrex some function definitions for it to understand.
@@ -719,3 +719,80 @@
reader._parse_dirblocks()
state._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
+
+
+_stat_to_minikind = DirState._stat_to_minikind
+
+
+def update_entry(self, entry, abspath, stat_value,
+ _stat_to_minikind=_stat_to_minikind,
+ _pack_stat=pack_stat):
+ """Update the entry based on what is actually on disk.
+
+ :param entry: This is the dirblock entry for the file in question.
+ :param abspath: The path on disk for this file.
+ :param stat_value: (optional) if we already have done a stat on the
+ file, re-use it.
+ :return: The sha1 hexdigest of the file (40 bytes) or link target of a
+ symlink.
+ """
+ try:
+ minikind = _stat_to_minikind[stat_value.st_mode & 0170000]
+ except KeyError:
+ # Unhandled kind
+ return None
+ packed_stat = _pack_stat(stat_value)
+ (saved_minikind, saved_link_or_sha1, saved_file_size,
+ saved_executable, saved_packed_stat) = entry[1][0]
+
+ if (minikind == saved_minikind
+ and packed_stat == saved_packed_stat):
+ # The stat hasn't changed since we saved, so we can re-use the
+ # saved sha hash.
+ if minikind == 'd':
+ return None
+
+ # size should also be in packed_stat
+ if saved_file_size == stat_value.st_size:
+ return saved_link_or_sha1
+
+ # If we have gotten this far, that means that we need to actually
+ # process this entry.
+ link_or_sha1 = None
+ if minikind == 'f':
+ link_or_sha1 = self._sha1_file(abspath)
+ executable = self._is_executable(stat_value.st_mode,
+ saved_executable)
+ 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', link_or_sha1, stat_value.st_size,
+ executable, packed_stat)
+ else:
+ entry[1][0] = ('f', '', stat_value.st_size,
+ executable, DirState.NULLSTAT)
+ elif minikind == 'd':
+ link_or_sha1 = None
+ entry[1][0] = ('d', '', 0, False, packed_stat)
+ if saved_minikind != 'd':
+ # This changed from something into a directory. Make sure we
+ # have a directory block for it. This doesn't happen very
+ # often, so this doesn't have to be super fast.
+ block_index, entry_index, dir_present, file_present = \
+ self._get_block_entry_index(entry[0][0], entry[0][1], 0)
+ self._ensure_block(block_index, entry_index,
+ osutils.pathjoin(entry[0][0], entry[0][1]))
+ elif minikind == 'l':
+ link_or_sha1 = self._read_link(abspath, saved_link_or_sha1)
+ 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] = ('l', link_or_sha1, stat_value.st_size,
+ False, packed_stat)
+ else:
+ entry[1][0] = ('l', '', stat_value.st_size,
+ False, DirState.NULLSTAT)
+ self._dirblock_state = DirState.IN_MEMORY_MODIFIED
+ return link_or_sha1
=== modified file 'bzrlib/dirstate.py'
--- a/bzrlib/dirstate.py 2008-07-30 08:55:10 +0000
+++ b/bzrlib/dirstate.py 2008-09-13 07:59:05 +0000
@@ -1472,79 +1472,6 @@
# it is being resurrected here, so blank it out temporarily.
self._dirblocks[block_index][1][entry_index][1][1] = null
- def update_entry(self, entry, abspath, stat_value,
- _stat_to_minikind=_stat_to_minikind,
- _pack_stat=pack_stat):
- """Update the entry based on what is actually on disk.
-
- :param entry: This is the dirblock entry for the file in question.
- :param abspath: The path on disk for this file.
- :param stat_value: (optional) if we already have done a stat on the
- file, re-use it.
- :return: The sha1 hexdigest of the file (40 bytes) or link target of a
- symlink.
- """
- try:
- minikind = _stat_to_minikind[stat_value.st_mode & 0170000]
- except KeyError:
- # Unhandled kind
- return None
- packed_stat = _pack_stat(stat_value)
- (saved_minikind, saved_link_or_sha1, saved_file_size,
- saved_executable, saved_packed_stat) = entry[1][0]
-
- if (minikind == saved_minikind
- and packed_stat == saved_packed_stat):
- # The stat hasn't changed since we saved, so we can re-use the
- # saved sha hash.
- if minikind == 'd':
- return None
-
- # size should also be in packed_stat
- if saved_file_size == stat_value.st_size:
- return saved_link_or_sha1
-
- # If we have gotten this far, that means that we need to actually
- # process this entry.
- link_or_sha1 = None
- if minikind == 'f':
- link_or_sha1 = self._sha1_file(abspath)
- executable = self._is_executable(stat_value.st_mode,
- saved_executable)
- 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', link_or_sha1, stat_value.st_size,
- executable, packed_stat)
- else:
- entry[1][0] = ('f', '', stat_value.st_size,
- executable, DirState.NULLSTAT)
- elif minikind == 'd':
- link_or_sha1 = None
- entry[1][0] = ('d', '', 0, False, packed_stat)
- if saved_minikind != 'd':
- # This changed from something into a directory. Make sure we
- # have a directory block for it. This doesn't happen very
- # often, so this doesn't have to be super fast.
- block_index, entry_index, dir_present, file_present = \
- self._get_block_entry_index(entry[0][0], entry[0][1], 0)
- self._ensure_block(block_index, entry_index,
- osutils.pathjoin(entry[0][0], entry[0][1]))
- elif minikind == 'l':
- link_or_sha1 = self._read_link(abspath, saved_link_or_sha1)
- 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] = ('l', link_or_sha1, stat_value.st_size,
- False, packed_stat)
- else:
- entry[1][0] = ('l', '', stat_value.st_size,
- False, DirState.NULLSTAT)
- self._dirblock_state = DirState.IN_MEMORY_MODIFIED
- return link_or_sha1
-
def _sha_cutoff_time(self):
"""Return cutoff time.
@@ -2774,6 +2701,82 @@
raise errors.ObjectNotLocked(self)
+def py_update_entry(self, entry, abspath, stat_value,
+ _stat_to_minikind=DirState._stat_to_minikind,
+ _pack_stat=pack_stat):
+ """Update the entry based on what is actually on disk.
+
+ :param entry: This is the dirblock entry for the file in question.
+ :param abspath: The path on disk for this file.
+ :param stat_value: (optional) if we already have done a stat on the
+ file, re-use it.
+ :return: The sha1 hexdigest of the file (40 bytes) or link target of a
+ symlink.
+ """
+ try:
+ minikind = _stat_to_minikind[stat_value.st_mode & 0170000]
+ except KeyError:
+ # Unhandled kind
+ return None
+ packed_stat = _pack_stat(stat_value)
+ (saved_minikind, saved_link_or_sha1, saved_file_size,
+ saved_executable, saved_packed_stat) = entry[1][0]
+
+ if (minikind == saved_minikind
+ and packed_stat == saved_packed_stat):
+ # The stat hasn't changed since we saved, so we can re-use the
+ # saved sha hash.
+ if minikind == 'd':
+ return None
+
+ # size should also be in packed_stat
+ if saved_file_size == stat_value.st_size:
+ return saved_link_or_sha1
+
+ # If we have gotten this far, that means that we need to actually
+ # process this entry.
+ link_or_sha1 = None
+ if minikind == 'f':
+ link_or_sha1 = self._sha1_file(abspath)
+ executable = self._is_executable(stat_value.st_mode,
+ saved_executable)
+ 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', link_or_sha1, stat_value.st_size,
+ executable, packed_stat)
+ else:
+ entry[1][0] = ('f', '', stat_value.st_size,
+ executable, DirState.NULLSTAT)
+ elif minikind == 'd':
+ link_or_sha1 = None
+ entry[1][0] = ('d', '', 0, False, packed_stat)
+ if saved_minikind != 'd':
+ # This changed from something into a directory. Make sure we
+ # have a directory block for it. This doesn't happen very
+ # often, so this doesn't have to be super fast.
+ block_index, entry_index, dir_present, file_present = \
+ self._get_block_entry_index(entry[0][0], entry[0][1], 0)
+ self._ensure_block(block_index, entry_index,
+ osutils.pathjoin(entry[0][0], entry[0][1]))
+ elif minikind == 'l':
+ link_or_sha1 = self._read_link(abspath, saved_link_or_sha1)
+ 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] = ('l', link_or_sha1, stat_value.st_size,
+ False, packed_stat)
+ else:
+ entry[1][0] = ('l', '', stat_value.st_size,
+ False, DirState.NULLSTAT)
+ self._dirblock_state = DirState.IN_MEMORY_MODIFIED
+ return link_or_sha1
+
+update_entry = py_update_entry
+
+
# Try to load the compiled form if possible
try:
from bzrlib._dirstate_helpers_c import (
@@ -2782,6 +2785,7 @@
_bisect_path_left_c as _bisect_path_left,
_bisect_path_right_c as _bisect_path_right,
cmp_by_dirs_c as cmp_by_dirs,
+ update_entry as update_entry,
)
except ImportError:
from bzrlib._dirstate_helpers_py import (
=== modified file 'bzrlib/tests/test__dirstate_helpers.py'
--- a/bzrlib/tests/test__dirstate_helpers.py 2008-09-02 18:51:03 +0000
+++ b/bzrlib/tests/test__dirstate_helpers.py 2008-09-13 07:59:05 +0000
@@ -24,6 +24,9 @@
errors,
tests,
)
+from bzrlib.tests import (
+ SymlinkFeature,
+ )
from bzrlib.tests import test_dirstate
@@ -765,3 +768,344 @@
from bzrlib._dirstate_helpers_py import _read_dirblocks_py
self.assertIs(_read_dirblocks_py, dirstate._read_dirblocks)
+ def test_update_entry(self):
+ if CompiledDirstateHelpersFeature.available():
+ from bzrlib._dirstate_helpers_c import update_entry
+ self.assertIs(update_entry, dirstate.update_entry)
+ else:
+ from bzrlib.dirstate import py_update_entry
+ self.assertIs(py_update_entry, dirstate.py_update_entry)
+
+
+class TestUpdateEntry(test_dirstate.TestCaseWithDirState):
+ """Test the DirState.update_entry functions"""
+
+ def get_state_with_a(self):
+ """Create a DirState tracking a single object named 'a'"""
+ state = test_dirstate.InstrumentedDirState.initialize('dirstate')
+ self.addCleanup(state.unlock)
+ state.add('a', 'a-id', 'file', None, '')
+ entry = state._get_entry(0, path_utf8='a')
+ self.set_update_entry()
+ return state, entry
+
+ def set_update_entry(self):
+ self.update_entry = dirstate.py_update_entry
+
+ def test_update_entry(self):
+ state, entry = self.get_state_with_a()
+ self.build_tree(['a'])
+ # Add one where we don't provide the stat or sha already
+ self.assertEqual(('', 'a', 'a-id'), entry[0])
+ self.assertEqual([('f', '', 0, False, dirstate.DirState.NULLSTAT)],
+ entry[1])
+ # Flush the buffers to disk
+ state.save()
+ self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
+ state._dirblock_state)
+
+ stat_value = os.lstat('a')
+ packed_stat = dirstate.pack_stat(stat_value)
+ link_or_sha1 = self.update_entry(state, entry, abspath='a',
+ stat_value=stat_value)
+ self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
+ link_or_sha1)
+
+ # The dirblock entry should not cache the file's sha1
+ self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
+ entry[1])
+ self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
+ state._dirblock_state)
+ mode = stat_value.st_mode
+ self.assertEqual([('sha1', 'a'), ('is_exec', mode, False)], state._log)
+
+ state.save()
+ self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
+ state._dirblock_state)
+
+ # If we do it again right away, we don't know if the file has changed
+ # so we will re-read the file. Roll the clock back so the file is
+ # guaranteed to look too new.
+ state.adjust_time(-10)
+
+ link_or_sha1 = self.update_entry(state, entry, abspath='a',
+ stat_value=stat_value)
+ self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
+ ('sha1', 'a'), ('is_exec', mode, False),
+ ], state._log)
+ self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
+ link_or_sha1)
+ self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
+ state._dirblock_state)
+ self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
+ entry[1])
+ state.save()
+
+ # However, if we move the clock forward so the file is considered
+ # "stable", it should just cache the value.
+ state.adjust_time(+20)
+ link_or_sha1 = self.update_entry(state, entry, abspath='a',
+ stat_value=stat_value)
+ self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
+ link_or_sha1)
+ self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
+ ('sha1', 'a'), ('is_exec', mode, False),
+ ('sha1', 'a'), ('is_exec', mode, False),
+ ], state._log)
+ self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
+ entry[1])
+
+ # Subsequent calls will just return the cached value
+ link_or_sha1 = self.update_entry(state, entry, abspath='a',
+ stat_value=stat_value)
+ self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
+ link_or_sha1)
+ self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
+ ('sha1', 'a'), ('is_exec', mode, False),
+ ('sha1', 'a'), ('is_exec', mode, False),
+ ], state._log)
+ self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
+ entry[1])
+
+ def test_update_entry_symlink(self):
+ """Update entry should read symlinks."""
+ self.requireFeature(SymlinkFeature)
+ state, entry = self.get_state_with_a()
+ state.save()
+ self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
+ state._dirblock_state)
+ os.symlink('target', 'a')
+
+ state.adjust_time(-10) # Make the symlink look new
+ stat_value = os.lstat('a')
+ packed_stat = dirstate.pack_stat(stat_value)
+ link_or_sha1 = self.update_entry(state, entry, abspath='a',
+ stat_value=stat_value)
+ self.assertEqual('target', link_or_sha1)
+ self.assertEqual([('read_link', 'a', '')], state._log)
+ # Dirblock is not updated (the link is too new)
+ self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
+ entry[1])
+ self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
+ state._dirblock_state)
+
+ # Because the stat_value looks new, we should re-read the target
+ link_or_sha1 = self.update_entry(state, entry, abspath='a',
+ stat_value=stat_value)
+ self.assertEqual('target', link_or_sha1)
+ self.assertEqual([('read_link', 'a', ''),
+ ('read_link', 'a', ''),
+ ], state._log)
+ self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
+ entry[1])
+ state.adjust_time(+20) # Skip into the future, all files look old
+ link_or_sha1 = self.update_entry(state, entry, abspath='a',
+ stat_value=stat_value)
+ self.assertEqual('target', link_or_sha1)
+ # We need to re-read the link because only now can we cache it
+ self.assertEqual([('read_link', 'a', ''),
+ ('read_link', 'a', ''),
+ ('read_link', 'a', ''),
+ ], state._log)
+ self.assertEqual([('l', 'target', 6, False, packed_stat)],
+ entry[1])
+
+ # Another call won't re-read the link
+ self.assertEqual([('read_link', 'a', ''),
+ ('read_link', 'a', ''),
+ ('read_link', 'a', ''),
+ ], state._log)
+ link_or_sha1 = self.update_entry(state, entry, abspath='a',
+ stat_value=stat_value)
+ self.assertEqual('target', link_or_sha1)
+ self.assertEqual([('l', 'target', 6, False, packed_stat)],
+ entry[1])
+
+ def do_update_entry(self, state, entry, abspath):
+ stat_value = os.lstat(abspath)
+ return self.update_entry(state, entry, abspath, stat_value)
+
+ def test_update_entry_dir(self):
+ state, entry = self.get_state_with_a()
+ self.build_tree(['a/'])
+ self.assertIs(None, self.do_update_entry(state, entry, 'a'))
+
+ def test_update_entry_dir_unchanged(self):
+ state, entry = self.get_state_with_a()
+ self.build_tree(['a/'])
+ state.adjust_time(+20)
+ self.assertIs(None, self.do_update_entry(state, entry, 'a'))
+ self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
+ state._dirblock_state)
+ state.save()
+ self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
+ state._dirblock_state)
+ self.assertIs(None, self.do_update_entry(state, entry, 'a'))
+ self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
+ state._dirblock_state)
+
+ def test_update_entry_file_unchanged(self):
+ state, entry = self.get_state_with_a()
+ self.build_tree(['a'])
+ sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
+ state.adjust_time(+20)
+ self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
+ self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
+ state._dirblock_state)
+ state.save()
+ self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
+ state._dirblock_state)
+ self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
+ self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
+ state._dirblock_state)
+
+ def create_and_test_file(self, state, entry):
+ """Create a file at 'a' and verify the state finds it.
+
+ The state should already be versioning *something* at 'a'. This makes
+ sure that state.update_entry recognizes it as a file.
+ """
+ self.build_tree(['a'])
+ stat_value = os.lstat('a')
+ packed_stat = dirstate.pack_stat(stat_value)
+
+ link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
+ self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
+ link_or_sha1)
+ self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
+ entry[1])
+ return packed_stat
+
+ def create_and_test_dir(self, state, entry):
+ """Create a directory at 'a' and verify the state finds it.
+
+ The state should already be versioning *something* at 'a'. This makes
+ sure that state.update_entry recognizes it as a directory.
+ """
+ self.build_tree(['a/'])
+ stat_value = os.lstat('a')
+ packed_stat = dirstate.pack_stat(stat_value)
+
+ link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
+ self.assertIs(None, link_or_sha1)
+ self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
+
+ return packed_stat
+
+ def create_and_test_symlink(self, state, entry):
+ """Create a symlink at 'a' and verify the state finds it.
+
+ The state should already be versioning *something* at 'a'. This makes
+ sure that state.update_entry recognizes it as a symlink.
+
+ This should not be called if this platform does not have symlink
+ support.
+ """
+ # caller should care about skipping test on platforms without symlinks
+ os.symlink('path/to/foo', 'a')
+
+ stat_value = os.lstat('a')
+ packed_stat = dirstate.pack_stat(stat_value)
+
+ link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
+ self.assertEqual('path/to/foo', link_or_sha1)
+ self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
+ entry[1])
+ return packed_stat
+
+ def test_update_file_to_dir(self):
+ """If a file changes to a directory we return None for the sha.
+ We also update the inventory record.
+ """
+ state, entry = self.get_state_with_a()
+ # The file sha1 won't be cached unless the file is old
+ state.adjust_time(+10)
+ self.create_and_test_file(state, entry)
+ os.remove('a')
+ self.create_and_test_dir(state, entry)
+
+ def test_update_file_to_symlink(self):
+ """File becomes a symlink"""
+ self.requireFeature(SymlinkFeature)
+ state, entry = self.get_state_with_a()
+ # The file sha1 won't be cached unless the file is old
+ state.adjust_time(+10)
+ self.create_and_test_file(state, entry)
+ os.remove('a')
+ self.create_and_test_symlink(state, entry)
+
+ def test_update_dir_to_file(self):
+ """Directory becoming a file updates the entry."""
+ state, entry = self.get_state_with_a()
+ # The file sha1 won't be cached unless the file is old
+ state.adjust_time(+10)
+ self.create_and_test_dir(state, entry)
+ os.rmdir('a')
+ self.create_and_test_file(state, entry)
+
+ def test_update_dir_to_symlink(self):
+ """Directory becomes a symlink"""
+ self.requireFeature(SymlinkFeature)
+ state, entry = self.get_state_with_a()
+ # The symlink target won't be cached if it isn't old
+ state.adjust_time(+10)
+ self.create_and_test_dir(state, entry)
+ os.rmdir('a')
+ self.create_and_test_symlink(state, entry)
+
+ def test_update_symlink_to_file(self):
+ """Symlink becomes a file"""
+ self.requireFeature(SymlinkFeature)
+ state, entry = self.get_state_with_a()
+ # The symlink and file info won't be cached unless old
+ state.adjust_time(+10)
+ self.create_and_test_symlink(state, entry)
+ os.remove('a')
+ self.create_and_test_file(state, entry)
+
+ def test_update_symlink_to_dir(self):
+ """Symlink becomes a directory"""
+ self.requireFeature(SymlinkFeature)
+ state, entry = self.get_state_with_a()
+ # The symlink target won't be cached if it isn't old
+ state.adjust_time(+10)
+ self.create_and_test_symlink(state, entry)
+ os.remove('a')
+ self.create_and_test_dir(state, entry)
+
+ def test__is_executable_win32(self):
+ state, entry = self.get_state_with_a()
+ self.build_tree(['a'])
+
+ # Make sure we are using the win32 implementation of _is_executable
+ state._is_executable = state._is_executable_win32
+
+ # The file on disk is not executable, but we are marking it as though
+ # it is. With _is_executable_win32 we ignore what is on disk.
+ entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
+
+ stat_value = os.lstat('a')
+ packed_stat = dirstate.pack_stat(stat_value)
+
+ state.adjust_time(-10) # Make sure everything is new
+ self.update_entry(state, entry, abspath='a', stat_value=stat_value)
+
+ # The row is updated, but the executable bit stays set.
+ self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
+ entry[1])
+
+ # Make the disk object look old enough to cache
+ state.adjust_time(+20)
+ digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
+ self.update_entry(state, entry, abspath='a', stat_value=stat_value)
+ self.assertEqual([('f', digest, 14, True, packed_stat)], entry[1])
+
+
+class TestCompiledUpdateEntry(TestUpdateEntry):
+ """Test the pyrex implementation of _read_dirblocks"""
+
+ _test_needs_features = [CompiledDirstateHelpersFeature]
+
+ def set_update_entry(self):
+ from bzrlib._dirstate_helpers_c import update_entry
+ self.update_entry = update_entry
=== 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-13 07:59:05 +0000
@@ -1666,326 +1666,6 @@
self.st_mode = mode
-class TestUpdateEntry(TestCaseWithDirState):
- """Test the DirState.update_entry functions"""
-
- def get_state_with_a(self):
- """Create a DirState tracking a single object named 'a'"""
- state = InstrumentedDirState.initialize('dirstate')
- self.addCleanup(state.unlock)
- state.add('a', 'a-id', 'file', None, '')
- entry = state._get_entry(0, path_utf8='a')
- return state, entry
-
- def test_update_entry(self):
- state, entry = self.get_state_with_a()
- self.build_tree(['a'])
- # Add one where we don't provide the stat or sha already
- self.assertEqual(('', 'a', 'a-id'), entry[0])
- self.assertEqual([('f', '', 0, False, dirstate.DirState.NULLSTAT)],
- entry[1])
- # Flush the buffers to disk
- state.save()
- self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
- state._dirblock_state)
-
- stat_value = os.lstat('a')
- packed_stat = dirstate.pack_stat(stat_value)
- link_or_sha1 = state.update_entry(entry, abspath='a',
- stat_value=stat_value)
- self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
- link_or_sha1)
-
- # The dirblock entry should not cache the file's sha1
- self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
- entry[1])
- self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
- state._dirblock_state)
- mode = stat_value.st_mode
- self.assertEqual([('sha1', 'a'), ('is_exec', mode, False)], state._log)
-
- state.save()
- self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
- state._dirblock_state)
-
- # If we do it again right away, we don't know if the file has changed
- # so we will re-read the file. Roll the clock back so the file is
- # guaranteed to look too new.
- state.adjust_time(-10)
-
- link_or_sha1 = state.update_entry(entry, abspath='a',
- stat_value=stat_value)
- self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
- ('sha1', 'a'), ('is_exec', mode, False),
- ], state._log)
- self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
- link_or_sha1)
- self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
- state._dirblock_state)
- self.assertEqual([('f', '', 14, False, dirstate.DirState.NULLSTAT)],
- entry[1])
- state.save()
-
- # However, if we move the clock forward so the file is considered
- # "stable", it should just cache the value.
- state.adjust_time(+20)
- link_or_sha1 = state.update_entry(entry, abspath='a',
- stat_value=stat_value)
- self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
- link_or_sha1)
- self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
- ('sha1', 'a'), ('is_exec', mode, False),
- ('sha1', 'a'), ('is_exec', mode, False),
- ], state._log)
- self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
- entry[1])
-
- # Subsequent calls will just return the cached value
- link_or_sha1 = state.update_entry(entry, abspath='a',
- stat_value=stat_value)
- self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
- link_or_sha1)
- self.assertEqual([('sha1', 'a'), ('is_exec', mode, False),
- ('sha1', 'a'), ('is_exec', mode, False),
- ('sha1', 'a'), ('is_exec', mode, False),
- ], state._log)
- self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
- entry[1])
-
- def test_update_entry_symlink(self):
- """Update entry should read symlinks."""
- self.requireFeature(SymlinkFeature)
- state, entry = self.get_state_with_a()
- state.save()
- self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
- state._dirblock_state)
- os.symlink('target', 'a')
-
- state.adjust_time(-10) # Make the symlink look new
- stat_value = os.lstat('a')
- packed_stat = dirstate.pack_stat(stat_value)
- link_or_sha1 = state.update_entry(entry, abspath='a',
- stat_value=stat_value)
- self.assertEqual('target', link_or_sha1)
- self.assertEqual([('read_link', 'a', '')], state._log)
- # Dirblock is not updated (the link is too new)
- self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
- entry[1])
- self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
- state._dirblock_state)
-
- # Because the stat_value looks new, we should re-read the target
- link_or_sha1 = state.update_entry(entry, abspath='a',
- stat_value=stat_value)
- self.assertEqual('target', link_or_sha1)
- self.assertEqual([('read_link', 'a', ''),
- ('read_link', 'a', ''),
- ], state._log)
- self.assertEqual([('l', '', 6, False, dirstate.DirState.NULLSTAT)],
- entry[1])
- state.adjust_time(+20) # Skip into the future, all files look old
- link_or_sha1 = state.update_entry(entry, abspath='a',
- stat_value=stat_value)
- self.assertEqual('target', link_or_sha1)
- # We need to re-read the link because only now can we cache it
- self.assertEqual([('read_link', 'a', ''),
- ('read_link', 'a', ''),
- ('read_link', 'a', ''),
- ], state._log)
- self.assertEqual([('l', 'target', 6, False, packed_stat)],
- entry[1])
-
- # Another call won't re-read the link
- self.assertEqual([('read_link', 'a', ''),
- ('read_link', 'a', ''),
- ('read_link', 'a', ''),
- ], state._log)
- link_or_sha1 = state.update_entry(entry, abspath='a',
- stat_value=stat_value)
- self.assertEqual('target', link_or_sha1)
- self.assertEqual([('l', 'target', 6, False, packed_stat)],
- entry[1])
-
- def do_update_entry(self, state, entry, abspath):
- stat_value = os.lstat(abspath)
- return state.update_entry(entry, abspath, stat_value)
-
- def test_update_entry_dir(self):
- state, entry = self.get_state_with_a()
- self.build_tree(['a/'])
- self.assertIs(None, self.do_update_entry(state, entry, 'a'))
-
- def test_update_entry_dir_unchanged(self):
- state, entry = self.get_state_with_a()
- self.build_tree(['a/'])
- state.adjust_time(+20)
- self.assertIs(None, self.do_update_entry(state, entry, 'a'))
- self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
- state._dirblock_state)
- state.save()
- self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
- state._dirblock_state)
- self.assertIs(None, self.do_update_entry(state, entry, 'a'))
- self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
- state._dirblock_state)
-
- def test_update_entry_file_unchanged(self):
- state, entry = self.get_state_with_a()
- self.build_tree(['a'])
- sha1sum = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
- state.adjust_time(+20)
- self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
- self.assertEqual(dirstate.DirState.IN_MEMORY_MODIFIED,
- state._dirblock_state)
- state.save()
- self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
- state._dirblock_state)
- self.assertEqual(sha1sum, self.do_update_entry(state, entry, 'a'))
- self.assertEqual(dirstate.DirState.IN_MEMORY_UNMODIFIED,
- state._dirblock_state)
-
- def create_and_test_file(self, state, entry):
- """Create a file at 'a' and verify the state finds it.
-
- The state should already be versioning *something* at 'a'. This makes
- sure that state.update_entry recognizes it as a file.
- """
- self.build_tree(['a'])
- stat_value = os.lstat('a')
- packed_stat = dirstate.pack_stat(stat_value)
-
- link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
- self.assertEqual('b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6',
- link_or_sha1)
- self.assertEqual([('f', link_or_sha1, 14, False, packed_stat)],
- entry[1])
- return packed_stat
-
- def create_and_test_dir(self, state, entry):
- """Create a directory at 'a' and verify the state finds it.
-
- The state should already be versioning *something* at 'a'. This makes
- sure that state.update_entry recognizes it as a directory.
- """
- self.build_tree(['a/'])
- stat_value = os.lstat('a')
- packed_stat = dirstate.pack_stat(stat_value)
-
- link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
- self.assertIs(None, link_or_sha1)
- self.assertEqual([('d', '', 0, False, packed_stat)], entry[1])
-
- return packed_stat
-
- def create_and_test_symlink(self, state, entry):
- """Create a symlink at 'a' and verify the state finds it.
-
- The state should already be versioning *something* at 'a'. This makes
- sure that state.update_entry recognizes it as a symlink.
-
- This should not be called if this platform does not have symlink
- support.
- """
- # caller should care about skipping test on platforms without symlinks
- os.symlink('path/to/foo', 'a')
-
- stat_value = os.lstat('a')
- packed_stat = dirstate.pack_stat(stat_value)
-
- link_or_sha1 = self.do_update_entry(state, entry, abspath='a')
- self.assertEqual('path/to/foo', link_or_sha1)
- self.assertEqual([('l', 'path/to/foo', 11, False, packed_stat)],
- entry[1])
- return packed_stat
-
- def test_update_file_to_dir(self):
- """If a file changes to a directory we return None for the sha.
- We also update the inventory record.
- """
- state, entry = self.get_state_with_a()
- # The file sha1 won't be cached unless the file is old
- state.adjust_time(+10)
- self.create_and_test_file(state, entry)
- os.remove('a')
- self.create_and_test_dir(state, entry)
-
- def test_update_file_to_symlink(self):
- """File becomes a symlink"""
- self.requireFeature(SymlinkFeature)
- state, entry = self.get_state_with_a()
- # The file sha1 won't be cached unless the file is old
- state.adjust_time(+10)
- self.create_and_test_file(state, entry)
- os.remove('a')
- self.create_and_test_symlink(state, entry)
-
- def test_update_dir_to_file(self):
- """Directory becoming a file updates the entry."""
- state, entry = self.get_state_with_a()
- # The file sha1 won't be cached unless the file is old
- state.adjust_time(+10)
- self.create_and_test_dir(state, entry)
- os.rmdir('a')
- self.create_and_test_file(state, entry)
-
- def test_update_dir_to_symlink(self):
- """Directory becomes a symlink"""
- self.requireFeature(SymlinkFeature)
- state, entry = self.get_state_with_a()
- # The symlink target won't be cached if it isn't old
- state.adjust_time(+10)
- self.create_and_test_dir(state, entry)
- os.rmdir('a')
- self.create_and_test_symlink(state, entry)
-
- def test_update_symlink_to_file(self):
- """Symlink becomes a file"""
- self.requireFeature(SymlinkFeature)
- state, entry = self.get_state_with_a()
- # The symlink and file info won't be cached unless old
- state.adjust_time(+10)
- self.create_and_test_symlink(state, entry)
- os.remove('a')
- self.create_and_test_file(state, entry)
-
- def test_update_symlink_to_dir(self):
- """Symlink becomes a directory"""
- self.requireFeature(SymlinkFeature)
- state, entry = self.get_state_with_a()
- # The symlink target won't be cached if it isn't old
- state.adjust_time(+10)
- self.create_and_test_symlink(state, entry)
- os.remove('a')
- self.create_and_test_dir(state, entry)
-
- def test__is_executable_win32(self):
- state, entry = self.get_state_with_a()
- self.build_tree(['a'])
-
- # Make sure we are using the win32 implementation of _is_executable
- state._is_executable = state._is_executable_win32
-
- # The file on disk is not executable, but we are marking it as though
- # it is. With _is_executable_win32 we ignore what is on disk.
- entry[1][0] = ('f', '', 0, True, dirstate.DirState.NULLSTAT)
-
- stat_value = os.lstat('a')
- packed_stat = dirstate.pack_stat(stat_value)
-
- state.adjust_time(-10) # Make sure everything is new
- state.update_entry(entry, abspath='a', stat_value=stat_value)
-
- # The row is updated, but the executable bit stays set.
- self.assertEqual([('f', '', 14, True, dirstate.DirState.NULLSTAT)],
- entry[1])
-
- # Make the disk object look old enough to cache
- state.adjust_time(+20)
- digest = 'b50e5406bb5e153ebbeb20268fcf37c87e1ecfb6'
- state.update_entry(entry, abspath='a', stat_value=stat_value)
- self.assertEqual([('f', digest, 14, True, packed_stat)], entry[1])
-
-
class TestPackStat(TestCaseWithTransport):
def assertPackStat(self, expected, stat_value):
=== 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-13 07:59:05 +0000
@@ -409,8 +409,8 @@
return None
else:
raise
- link_or_sha1 = state.update_entry(entry, file_abspath,
- stat_value=stat_value)
+ link_or_sha1 = dirstate.update_entry(state, entry, file_abspath,
+ stat_value=stat_value)
if entry[1][0][0] == 'f':
return link_or_sha1
return None
@@ -1856,6 +1856,7 @@
"revision {%s} is not stored in {%s}, but %s "
"can only be used for trees stored in the dirstate"
% (self.source._revision_id, self.target, self.iter_changes))
+ update_entry = dirstate.update_entry
target_index = 0
if self.source._revision_id == NULL_REVISION:
source_index = None
@@ -2009,8 +2010,8 @@
if path_info is not None and target_minikind in 'fdlt':
if not (target_index == 0):
raise AssertionError()
- link_or_sha1 = state.update_entry(entry, abspath=path_info[4],
- stat_value=path_info[3])
+ link_or_sha1 = update_entry(state, entry,
+ abspath=path_info[4], stat_value=path_info[3])
# The entry may have been modified by update_entry
target_details = entry[1][target_index]
target_minikind = target_details[0]
More information about the bazaar-commits
mailing list