Rev 3222: Tweaked fix for #187169: if an illegal update is made to the dirstate, don't allow it to be written out. in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Wed Feb 13 03:19:11 GMT 2008


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

------------------------------------------------------------
revno: 3222
revision-id:pqm at pqm.ubuntu.com-20080213031902-4xhmwps49vclfhh6
parent: pqm at pqm.ubuntu.com-20080208064756-bg0zu0y4e789j50r
parent: mbp at sourcefrog.net-20080213014247-buajf9y91w5xuaa8
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Wed 2008-02-13 03:19:02 +0000
message:
  Tweaked fix for #187169: if an illegal update is made to the dirstate, don't allow it to be written out.
modified:
  bzrlib/bundle/commands.py      __init__.py-20050617152058-1b6530d9ab85c11c
  bzrlib/dirstate.py             dirstate.py-20060728012006-d6mvoihjb3je9peu-1
  bzrlib/errors.py               errors.py-20050309040759-20512168c4e14fbd
  bzrlib/tests/blackbox/test_bundle_info.py test_bundle_info.py-20070816181255-eiuodwxuqu7w7gxf-1
  bzrlib/tests/test_dirstate.py  test_dirstate.py-20060728012006-d6mvoihjb3je9peu-2
  bzrlib/tests/test_errors.py    test_errors.py-20060210110251-41aba2deddf936a8
  bzrlib/tests/test_workingtree_4.py test_workingtree_4.p-20070223025758-531n3tznl3zacv2o-1
  bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
    ------------------------------------------------------------
    revno: 3221.1.8
    revision-id:mbp at sourcefrog.net-20080213014247-buajf9y91w5xuaa8
    parent: mbp at sourcefrog.net-20080213013527-ml201846kikzon42
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: prepare-1.2rc1
    timestamp: Wed 2008-02-13 12:42:47 +1100
    message:
      Update error format in test_inconsistent_delta
    modified:
      bzrlib/tests/test_errors.py    test_errors.py-20060210110251-41aba2deddf936a8
    ------------------------------------------------------------
    revno: 3221.1.7
    revision-id:mbp at sourcefrog.net-20080213013527-ml201846kikzon42
    parent: mbp at sourcefrog.net-20080213013133-x41bshcx7i5ozpo1
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: prepare-1.2rc1
    timestamp: Wed 2008-02-13 12:35:27 +1100
    message:
      Update and rename test for Dirstate._changes_aborted
    modified:
      bzrlib/tests/test_dirstate.py  test_dirstate.py-20060728012006-d6mvoihjb3je9peu-2
    ------------------------------------------------------------
    revno: 3221.1.6
    revision-id:mbp at sourcefrog.net-20080213013133-x41bshcx7i5ozpo1
    parent: mbp at sourcefrog.net-20080212021404-j0okfr3zla2sv351
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: prepare-1.2rc1
    timestamp: Wed 2008-02-13 12:31:33 +1100
    message:
      Better error message from bundle-info
    modified:
      bzrlib/bundle/commands.py      __init__.py-20050617152058-1b6530d9ab85c11c
      bzrlib/tests/blackbox/test_bundle_info.py test_bundle_info.py-20070816181255-eiuodwxuqu7w7gxf-1
    ------------------------------------------------------------
    revno: 3221.1.5
    revision-id:mbp at sourcefrog.net-20080212021404-j0okfr3zla2sv351
    parent: mbp at sourcefrog.net-20080212021343-3r7l8xi9lsrt4gzu
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: prepare-1.2rc1
    timestamp: Tue 2008-02-12 13:14:04 +1100
    message:
      Fix incorrect indent in dirstate error handling
    modified:
      bzrlib/dirstate.py             dirstate.py-20060728012006-d6mvoihjb3je9peu-1
    ------------------------------------------------------------
    revno: 3221.1.4
    revision-id:mbp at sourcefrog.net-20080212021343-3r7l8xi9lsrt4gzu
    parent: mbp at sourcefrog.net-20080211112411-j07kfvsserr68aha
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: prepare-1.2rc1
    timestamp: Tue 2008-02-12 13:13:43 +1100
    message:
      Fix format string for InconsistentDelta
    modified:
      bzrlib/errors.py               errors.py-20050309040759-20512168c4e14fbd
    ------------------------------------------------------------
    revno: 3221.1.3
    revision-id:mbp at sourcefrog.net-20080211112411-j07kfvsserr68aha
    parent: mbp at sourcefrog.net-20080211111556-qs2fntphg1ytv6ne
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: prepare-1.2rc1
    timestamp: Mon 2008-02-11 22:24:11 +1100
    message:
      Review cleanups for CorruptDirstate: use the path everywhere rather than the object, and use more standard phrasing.
    modified:
      bzrlib/errors.py               errors.py-20050309040759-20512168c4e14fbd
      bzrlib/tests/test_errors.py    test_errors.py-20060210110251-41aba2deddf936a8
      bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
    ------------------------------------------------------------
    revno: 3221.1.2
    revision-id:mbp at sourcefrog.net-20080211111556-qs2fntphg1ytv6ne
    parent: mbp at sourcefrog.net-20080211110224-u78ya6vu5wmmswju
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: prepare-1.2rc1
    timestamp: Mon 2008-02-11 22:15:56 +1100
    message:
      Mark problems in a Dirstate that should prevent saving with a _changes_aborted flag rather than _consistency
    modified:
      bzrlib/dirstate.py             dirstate.py-20060728012006-d6mvoihjb3je9peu-1
    ------------------------------------------------------------
    revno: 3221.1.1
    revision-id:mbp at sourcefrog.net-20080211110224-u78ya6vu5wmmswju
    parent: pqm at pqm.ubuntu.com-20080208064756-bg0zu0y4e789j50r
    parent: john at arbash-meinel.com-20080204211249-cb49xtg70j5okbw7
    committer: Martin Pool <mbp at sourcefrog.net>
    branch nick: prepare-1.2rc1
    timestamp: Mon 2008-02-11 22:02:24 +1100
    message:
      Merge John's invalid-dirstate rollback patch
    modified:
      bzrlib/dirstate.py             dirstate.py-20060728012006-d6mvoihjb3je9peu-1
      bzrlib/errors.py               errors.py-20050309040759-20512168c4e14fbd
      bzrlib/tests/test_dirstate.py  test_dirstate.py-20060728012006-d6mvoihjb3je9peu-2
      bzrlib/tests/test_errors.py    test_errors.py-20060210110251-41aba2deddf936a8
      bzrlib/tests/test_workingtree_4.py test_workingtree_4.p-20070223025758-531n3tznl3zacv2o-1
      bzrlib/workingtree_4.py        workingtree_4.py-20070208044105-5fgpc5j3ljlh5q6c-1
    ------------------------------------------------------------
    revno: 3207.2.2
    revision-id:john at arbash-meinel.com-20080204211249-cb49xtg70j5okbw7
    parent: jameinel at samus-20080130201600-rhtzr6r55k604rbb
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: dirstate_187169
    timestamp: Mon 2008-02-04 15:12:49 -0600
    message:
      Fix bug #187169, when an invalid delta is supplied to update_basis_by_delta
    modified:
      bzrlib/dirstate.py             dirstate.py-20060728012006-d6mvoihjb3je9peu-1
      bzrlib/errors.py               errors.py-20050309040759-20512168c4e14fbd
      bzrlib/tests/test_dirstate.py  test_dirstate.py-20060728012006-d6mvoihjb3je9peu-2
      bzrlib/tests/test_errors.py    test_errors.py-20060210110251-41aba2deddf936a8
      bzrlib/tests/test_workingtree_4.py test_workingtree_4.p-20070223025758-531n3tznl3zacv2o-1
    ------------------------------------------------------------
    revno: 3207.2.1
    revision-id:jameinel at samus-20080130201600-rhtzr6r55k604rbb
    parent: pqm at pqm.ubuntu.com-20080130100306-p0uqnxt3hodnyiej
    committer: jameinel <jameinel at SAMUS>
    branch nick: dirstate_187169
    timestamp: Wed 2008-01-30 14:16:00 -0600
    message:
      Add a test that _iter_changes raises a clearer error when we encounter an invalid rename.
    modified:
      bzrlib/errors.py               errors.py-20050309040759-20512168c4e14fbd
      bzrlib/tests/test_errors.py    test_errors.py-20060210110251-41aba2deddf936a8
      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 'bzrlib/bundle/commands.py'
--- a/bzrlib/bundle/commands.py	2007-10-16 16:02:01 +0000
+++ b/bzrlib/bundle/commands.py	2008-02-13 01:31:33 +0000
@@ -59,8 +59,8 @@
             bundle_info = read_bundle(bundle_file)
         else:
             if verbose:
-                raise errors.BzrCommandError('Verbose requires a merge'
-                                             ' directive')
+                raise errors.BzrCommandError('--verbose requires a merge'
+                    ' directive')
         reader_method = getattr(bundle_info, 'get_bundle_reader', None)
         if reader_method is None:
             raise errors.BzrCommandError('Bundle format not supported')

=== modified file 'bzrlib/dirstate.py'
--- a/bzrlib/dirstate.py	2007-12-19 08:12:34 +0000
+++ b/bzrlib/dirstate.py	2008-02-12 02:14:04 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006, 2007 Canonical Ltd
+# Copyright (C) 2006, 2007, 2008 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
@@ -321,6 +321,9 @@
         # modified states.
         self._header_state = DirState.NOT_IN_MEMORY
         self._dirblock_state = DirState.NOT_IN_MEMORY
+        # If true, an error has been detected while updating the dirstate, and 
+        # for safety we're not going to commit to disk.
+        self._changes_aborted = False
         self._dirblocks = []
         self._ghosts = []
         self._parents = []
@@ -1292,13 +1295,15 @@
             assert old_path is None
             # the entry for this file_id must be in tree 0.
             entry = self._get_entry(0, file_id, new_path)
-            if entry[0][2] != file_id:
-                raise errors.BzrError('dirstate: cannot apply delta, working'
-                    ' tree does not contain new entry %r %r' %
-                    (new_path, file_id))
+            if entry[0] is None or entry[0][2] != file_id:
+                self._changes_aborted = True
+                raise errors.InconsistentDelta(new_path, file_id,
+                    'working tree does not contain new entry')
             if real_add and entry[1][1][0] not in absent:
-                raise errors.BzrError('dirstate: inconsistent delta, with '
-                    'tree 0. %r %r' % (new_path, file_id))
+                self._changes_aborted = True
+                raise errors.InconsistentDelta(new_path, file_id,
+                    'The entry was considered to be a genuinely new record,'
+                    ' but there was already an old record for it.')
             # We don't need to update the target of an 'r' because the handling
             # of renames turns all 'r' situations into a delete at the original
             # location.
@@ -1315,14 +1320,15 @@
             assert old_path == new_path
             # the entry for this file_id must be in tree 0.
             entry = self._get_entry(0, file_id, new_path)
-            if entry[0][2] != file_id:
-                raise errors.BzrError('dirstate: cannot apply delta, working'
-                    ' tree does not contain new entry %r %r' %
-                    (new_path, file_id))
+            if entry[0] is None or entry[0][2] != file_id:
+                self._changes_aborted = True
+                raise errors.InconsistentDelta(new_path, file_id,
+                    'working tree does not contain new entry')
             if (entry[1][0][0] in absent or
                 entry[1][1][0] in absent):
-                raise errors.BzrError('dirstate: inconsistent delta, with '
-                    'tree 0. %r %r' % (new_path, file_id))
+                self._changes_aborted = True
+                raise errors.InconsistentDelta(new_path, file_id,
+                    'changed considered absent')
             entry[1][1] = new_details
 
     def _update_basis_apply_deletes(self, deletes):
@@ -1348,22 +1354,31 @@
             block_index, entry_index, dir_present, file_present = \
                 self._get_block_entry_index(dirname, basename, 1)
             if not file_present:
-                raise errors.BzrError('dirstate: cannot apply delta, basis'
-                    ' tree does not contain new entry %r %r' %
-                    (old_path, file_id))
+                self._changes_aborted = True
+                raise errors.InconsistentDelta(old_path, file_id,
+                    'basis tree does not contain removed entry')
             entry = self._dirblocks[block_index][1][entry_index]
             if entry[0][2] != file_id:
-                raise errors.BzrError('mismatched file_id in tree 1 %r %r' %
-                    (old_path, file_id))
+                self._changes_aborted = True
+                raise errors.InconsistentDelta(old_path, file_id,
+                    'mismatched file_id in tree 1')
             if real_delete:
                 if entry[1][0][0] != 'a':
-                    raise errors.BzrError('dirstate: inconsistent delta, with '
-                        'tree 0. %r %r' % (old_path, file_id))
+                    self._changes_aborted = True
+                    raise errors.InconsistentDelta(old_path, file_id,
+                            'This was marked as a real delete, but the WT state'
+                            ' claims that it still exists and is versioned.')
                 del self._dirblocks[block_index][1][entry_index]
             else:
                 if entry[1][0][0] == 'a':
-                    raise errors.BzrError('dirstate: inconsistent delta, with '
-                        'tree 0. %r %r' % (old_path, file_id))
+                    self._changes_aborted = True
+                    raise errors.InconsistentDelta(old_path, file_id,
+                        'The entry was considered a rename, but the source path'
+                        ' is marked as absent.')
+                    # For whatever reason, we were asked to rename an entry
+                    # that was originally marked as deleted. This could be
+                    # because we are renaming the parent directory, and the WT
+                    # current state has the file marked as deleted.
                 elif entry[1][0][0] == 'r':
                     # implement the rename
                     del self._dirblocks[block_index][1][entry_index]
@@ -1666,6 +1681,7 @@
             assert entry[0][2] and entry[1][tree_index][0] not in ('a', 'r'), 'unversioned entry?!?!'
             if fileid_utf8:
                 if entry[0][2] != fileid_utf8:
+                    self._changes_aborted = True
                     raise errors.BzrError('integrity error ? : mismatching'
                                           ' tree_index, file_id and path')
             return entry
@@ -1954,6 +1970,12 @@
         start over, to allow for fine grained read lock duration, so 'status'
         wont block 'commit' - for example.
         """
+        if self._changes_aborted:
+            # Should this be a warning? For now, I'm expecting that places that
+            # mark it inconsistent will warn, making a warning here redundant.
+            trace.mutter('Not saving DirState because '
+                    '_changes_aborted is set.')
+            return
         if (self._header_state == DirState.IN_MEMORY_MODIFIED or
             self._dirblock_state == DirState.IN_MEMORY_MODIFIED):
 
@@ -2598,6 +2620,7 @@
         """Forget all state information about the dirstate."""
         self._header_state = DirState.NOT_IN_MEMORY
         self._dirblock_state = DirState.NOT_IN_MEMORY
+        self._changes_aborted = False
         self._parents = []
         self._ghosts = []
         self._dirblocks = []

=== modified file 'bzrlib/errors.py'
--- a/bzrlib/errors.py	2008-02-06 16:38:04 +0000
+++ b/bzrlib/errors.py	2008-02-12 02:13:43 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2005, 2006, 2007 Canonical Ltd
+# Copyright (C) 2005, 2006, 2007, 2008 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
@@ -1981,6 +1981,17 @@
     _fmt = "Format error in conflict listings"
 
 
+class CorruptDirstate(BzrError):
+
+    _fmt = ("Inconsistency in dirstate file %(dirstate_path)s.\n"
+            "Error: %(description)s")
+
+    def __init__(self, dirstate_path, description):
+        BzrError.__init__(self)
+        self.dirstate_path = dirstate_path
+        self.description = description
+
+
 class CorruptRepository(BzrError):
 
     _fmt = ("An error has been detected in the repository %(repo_path)s.\n"
@@ -1991,6 +2002,19 @@
         self.repo_path = repo.bzrdir.root_transport.base
 
 
+class InconsistentDelta(BzrError):
+    """Used when we get a delta that is not valid."""
+
+    _fmt = ("An inconsistent delta was supplied involving %(path)r,"
+            " %(file_id)r\nreason: %(reason)s")
+
+    def __init__(self, path, file_id, reason):
+        BzrError.__init__(self)
+        self.path = path
+        self.file_id = file_id
+        self.reason = reason
+
+
 class UpgradeRequired(BzrError):
 
     _fmt = "To use this feature you must upgrade your branch at %(path)s."

=== modified file 'bzrlib/tests/blackbox/test_bundle_info.py'
--- a/bzrlib/tests/blackbox/test_bundle_info.py	2007-08-16 18:23:13 +0000
+++ b/bzrlib/tests/blackbox/test_bundle_info.py	2008-02-13 01:31:33 +0000
@@ -38,7 +38,7 @@
         self.assertContainsRe(info, 'file: 1 .0 multiparent.')
         self.assertContainsRe(info, 'nicks: source')
         self.assertNotContainsRe(info, 'foo')
-        self.run_bzr_error(['Verbose requires a merge directive'],
+        self.run_bzr_error(['--verbose requires a merge directive'],
                            'bundle-info -v bundle')
         target = self.make_branch('target')
         md = merge_directive.MergeDirective2.from_objects(

=== modified file 'bzrlib/tests/test_dirstate.py'
--- a/bzrlib/tests/test_dirstate.py	2007-11-21 22:16:20 +0000
+++ b/bzrlib/tests/test_dirstate.py	2008-02-13 01:35:27 +0000
@@ -646,6 +646,45 @@
         finally:
             state.unlock()
 
+    def test_save_refuses_if_changes_aborted(self):
+        self.build_tree(['a-file', 'a-dir/'])
+        state = dirstate.DirState.initialize('dirstate')
+        try:
+            # No stat and no sha1 sum.
+            state.add('a-file', 'a-file-id', 'file', None, '')
+            state.save()
+        finally:
+            state.unlock()
+
+        # The dirstate should include TREE_ROOT and 'a-file' and nothing else
+        expected_blocks = [
+            ('', [(('', '', 'TREE_ROOT'),
+                   [('d', '', 0, False, dirstate.DirState.NULLSTAT)])]),
+            ('', [(('', 'a-file', 'a-file-id'),
+                   [('f', '', 0, False, dirstate.DirState.NULLSTAT)])]),
+        ]
+
+        state = dirstate.DirState.on_file('dirstate')
+        state.lock_write()
+        try:
+            state._read_dirblocks_if_needed()
+            self.assertEqual(expected_blocks, state._dirblocks)
+
+            # Now modify the state, but mark it as inconsistent
+            state.add('a-dir', 'a-dir-id', 'directory', None, '')
+            state._changes_aborted = True
+            state.save()
+        finally:
+            state.unlock()
+
+        state = dirstate.DirState.on_file('dirstate')
+        state.lock_read()
+        try:
+            state._read_dirblocks_if_needed()
+            self.assertEqual(expected_blocks, state._dirblocks)
+        finally:
+            state.unlock()
+
 
 class TestDirStateInitialize(TestCaseWithDirState):
 

=== modified file 'bzrlib/tests/test_errors.py'
--- a/bzrlib/tests/test_errors.py	2008-01-25 10:46:10 +0000
+++ b/bzrlib/tests/test_errors.py	2008-02-13 01:42:47 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2006, 2007 Canonical Ltd
+# Copyright (C) 2006, 2007, 2008 Canonical Ltd
 #   Authors: Robert Collins <robert.collins at canonical.com>
 #            and others
 #
@@ -29,6 +29,13 @@
 
 class TestErrors(TestCaseWithTransport):
 
+    def test_corrupt_dirstate(self):
+        error = errors.CorruptDirstate('path/to/dirstate', 'the reason why')
+        self.assertEqualDiff(
+            "Inconsistency in dirstate file path/to/dirstate.\n"
+            "Error: the reason why",
+            str(error))
+
     def test_disabled_method(self):
         error = errors.DisabledMethod("class name")
         self.assertEqualDiff(
@@ -51,6 +58,13 @@
             'It supports versions "(4, 5, 6)" to "(7, 8, 9)".',
             str(error))
 
+    def test_inconsistent_delta(self):
+        error = errors.InconsistentDelta('path', 'file-id', 'reason for foo')
+        self.assertEqualDiff(
+            "An inconsistent delta was supplied involving 'path', 'file-id'\n"
+            "reason: reason for foo",
+            str(error))
+
     def test_in_process_transport(self):
         error = errors.InProcessTransport('fpp')
         self.assertEqualDiff(

=== modified file 'bzrlib/tests/test_workingtree_4.py'
--- a/bzrlib/tests/test_workingtree_4.py	2007-09-17 05:33:56 +0000
+++ b/bzrlib/tests/test_workingtree_4.py	2008-02-04 21:12:49 +0000
@@ -574,3 +574,99 @@
         changes = [c[1] for c in tree._iter_changes(basis)]
         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)
+        return tree
+
+    def test_invalid_rename(self):
+        tree = self.create_wt4()
+        # Create a corrupted dirstate
+        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())
+        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()
+        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()
+        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))

=== modified file 'bzrlib/workingtree_4.py'
--- a/bzrlib/workingtree_4.py	2007-12-29 21:28:32 +0000
+++ b/bzrlib/workingtree_4.py	2008-02-11 11:24:11 +0000
@@ -1951,6 +1951,11 @@
                                                  path_utf8=old_path)
                     # update the source details variable to be the real
                     # location.
+                    if old_entry == (None, None):
+                        raise errors.CorruptDirstate(state._filename,
+                            "entry '%s/%s' is considered renamed from %r"
+                            " but source does not exist\n"
+                            "entry: %s" % (entry[0][0], entry[0][1], old_path, entry))
                     source_details = old_entry[1][source_index]
                     source_minikind = source_details[0]
                 else:




More information about the bazaar-commits mailing list