~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/dirstate.py

Better DirState._validate and tests for it.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2170
2170
 
2171
2171
        This must be called with a lock held.
2172
2172
        """
 
2173
        # TODO: All entries must have some content that's not 'a' or 'r',
 
2174
        # otherwise it could just be removed.
 
2175
        #
 
2176
        # TODO: All relocations must point directly to a real entry.
 
2177
        #
 
2178
        # TODO: No repeated keys.
 
2179
        #
 
2180
        # -- mbp 20070325
2173
2181
        from pprint import pformat
2174
 
        # TODO: All entries should have the same length?
 
2182
        self._read_dirblocks_if_needed()
2175
2183
        if len(self._dirblocks) > 0:
2176
2184
            assert self._dirblocks[0][0] == '', \
2177
2185
                    "dirblocks don't start with root block:\n" + \
2209
2217
        #
2210
2218
        # We check this with a dict per tree pointing either to the present
2211
2219
        # name, or None if absent.
2212
 
 
2213
 
        all_file_ids = set()
 
2220
        tree_count = self._num_present_parents() + 1
 
2221
        id_path_maps = [dict() for i in range(tree_count)]
2214
2222
        # Make sure that all renamed entries point to the correct location.
2215
2223
        for entry in self._iter_entries():
2216
2224
            file_id = entry[0][2]
2217
 
            all_file_ids.add(file_id)
 
2225
            this_path = osutils.pathjoin(entry[0][0], entry[0][1])
 
2226
            assert len(entry[1]) == tree_count, \
 
2227
                "wrong number of entry details for row\n%s" \
 
2228
                ",\nexpected %d" % \
 
2229
                (pformat(entry), tree_count)
2218
2230
            for tree_index, tree_state in enumerate(entry[1]):
2219
 
                if tree_state[0] == 'r': # Renamed entry
2220
 
                    target_location = tree_state[1]
2221
 
                    other_entry = self._get_entry(tree_index,
2222
 
                                                  path_utf8=target_location)
2223
 
                    this_path = osutils.pathjoin(entry[0][0], entry[0][1])
2224
 
                    other_path = osutils.pathjoin(other_entry[0][0],
2225
 
                                                  other_entry[0][1])
2226
 
                    assert entry[0][2] == other_entry[0][2], \
2227
 
                        ('A rename entry points to a record with a different'
2228
 
                         ' file id. %s => %s'
2229
 
                         % (pformat(entry), pformat(other_entry)))
2230
 
                    # there must be 'rename' pointers between all occurrences
2231
 
                    # of this file_id in all trees.
2232
 
                    #
2233
 
                    # TODO: If there's only a single tree (no basis
2234
 
                    # revisions), there must not be any rename markers.
2235
 
                    #
2236
 
                    # TODO: If there's more than 2 trees, we should still be
2237
 
                    # able to check that all the renames line up but it'll be
2238
 
                    # more complicated.
2239
 
                    #
2240
 
                    # these are disabled because i think the assertions
2241
 
                    # they encode are wrong: there is no necessary link
2242
 
                    # between the rename pointers in one tree and in another.
2243
 
                    # -- mbp 20070325
2244
 
                    #
2245
 
                    ## if len(entry[1]) == 2: # Check the rename is symmetric
2246
 
                    ##     if tree_index == 0:
2247
 
                    ##         other_index = 1
2248
 
                    ##     else:
2249
 
                    ##         other_index = 0
2250
 
                    ##     assert other_entry[1][other_index][0] == 'r', \
2251
 
                    ##         ('a rename points to a record which '
2252
 
                    ##          'does not have a reverse rename pointer '
2253
 
                    ##          '\n%s => %s'
2254
 
                    ##          % (pformat(entry), pformat(other_entry)))
2255
 
                    ##     assert other_entry[1][other_index][1] == this_path, \
2256
 
                    ##         ('a rename points to a record which points to a'
2257
 
                    ##          ' different location.\n'
2258
 
                    ##          '%s => %s\n'
2259
 
                    ##          'this_path=%s\n'
2260
 
                    ##          % (pformat(entry), pformat(other_entry),
2261
 
                    ##             this_path))
 
2231
                this_tree_map = id_path_maps[tree_index]
 
2232
                minikind = tree_state[0]
 
2233
                # have we seen this id before in this column?
 
2234
                if file_id in this_tree_map:
 
2235
                    previous_path = this_tree_map[file_id]
 
2236
                    # any later mention of this file must be consistent with
 
2237
                    # what was said before
 
2238
                    if minikind == 'a':
 
2239
                        assert previous_path is None, \
 
2240
                            "file %s is absent in row %r but also present " \
 
2241
                            "at %r"% \
 
2242
                            (file_id, entry, previous_path)
 
2243
                    elif minikind == 'r':
 
2244
                        target_location = tree_state[1]
 
2245
                        assert previous_path == target_location, \
 
2246
                            "file %s relocation in row %r but also at %r" \
 
2247
                            % (file_id, entry, previous_path)
 
2248
                    else:
 
2249
                        # a file, directory, etc - may have been previously
 
2250
                        # pointed to by a relocation, which must point here
 
2251
                        assert previous_path == this_path, \
 
2252
                            "entry %r inconsistent with previous path %r" % \
 
2253
                            (entry, previous_path)
 
2254
                else:
 
2255
                    if minikind == 'a':
 
2256
                        # absent; should not occur anywhere else
 
2257
                        this_tree_map[file_id] = None
 
2258
                    elif minikind == 'r':
 
2259
                        # relocation, must occur at expected location 
 
2260
                        this_tree_map[file_id] = tree_state[1]
 
2261
                    else:
 
2262
                        this_tree_map[file_id] = this_path
2262
2263
 
2263
2264
    def _wipe_state(self):
2264
2265
        """Forget all state information about the dirstate."""