~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/dirstate.py

Fix WorkingTree4._iter_changes with pending merges and deleted files

Show diffs side-by-side

added added

removed removed

Lines of Context:
1692
1692
        if (self._header_state == DirState.IN_MEMORY_MODIFIED or
1693
1693
            self._dirblock_state == DirState.IN_MEMORY_MODIFIED):
1694
1694
 
1695
 
            grabbed_write_lock = False
1696
 
            if self._lock_state != 'w':
1697
 
                grabbed_write_lock, new_lock = self._lock_token.temporary_write_lock()
1698
 
                # Switch over to the new lock, as the old one may be closed.
1699
 
                # TODO: jam 20070315 We should validate the disk file has
1700
 
                #       not changed contents. Since temporary_write_lock may
1701
 
                #       not be an atomic operation.
1702
 
                self._lock_token = new_lock
1703
 
                self._state_file = new_lock.f
1704
 
                if not grabbed_write_lock:
1705
 
                    # We couldn't grab a write lock, so we switch back to a read one
 
1695
            if self._lock_state == 'w':
 
1696
                out_file = self._state_file
 
1697
                wlock = None
 
1698
            else:
 
1699
                # Try to grab a write lock so that we can update the file.
 
1700
                try:
 
1701
                    wlock = lock.WriteLock(self._filename)
 
1702
                except (errors.LockError, errors.LockContention), e:
 
1703
                    # We couldn't grab the lock, so just leave things dirty in
 
1704
                    # memory.
1706
1705
                    return
 
1706
                except IOError, e:
 
1707
                    # This may be a read-only tree, or someone else may have a
 
1708
                    # ReadLock. so handle the case when we cannot grab a write
 
1709
                    # lock
 
1710
                    if e.errno in (errno.ENOENT, errno.EPERM, errno.EACCES,
 
1711
                                   errno.EAGAIN):
 
1712
                        # Ignore these errors and just don't save anything
 
1713
                        return
 
1714
                    raise
 
1715
                out_file = wlock.f
1707
1716
            try:
1708
 
                self._state_file.seek(0)
1709
 
                self._state_file.writelines(self.get_lines())
1710
 
                self._state_file.truncate()
1711
 
                self._state_file.flush()
 
1717
                out_file.seek(0)
 
1718
                out_file.writelines(self.get_lines())
 
1719
                out_file.truncate()
 
1720
                out_file.flush()
1712
1721
                self._header_state = DirState.IN_MEMORY_UNMODIFIED
1713
1722
                self._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
1714
1723
            finally:
1715
 
                if grabbed_write_lock:
1716
 
                    self._lock_token = self._lock_token.restore_read_lock()
1717
 
                    self._state_file = self._lock_token.f
1718
 
                    # TODO: jam 20070315 We should validate the disk file has
1719
 
                    #       not changed contents. Since restore_read_lock may
1720
 
                    #       not be an atomic operation.
 
1724
                if wlock is not None:
 
1725
                    wlock.unlock()
1721
1726
 
1722
1727
    def _set_data(self, parent_ids, dirblocks):
1723
1728
        """Set the full dirstate data in memory.
2167
2172
 
2168
2173
        This can be useful in debugging; it shouldn't be necessary in 
2169
2174
        normal code.
2170
 
 
2171
 
        This must be called with a lock held.
2172
2175
        """
2173
 
        # NOTE: This must always raise AssertionError not just assert,
2174
 
        # otherwise it may not behave properly under python -O
2175
 
        #
2176
 
        # TODO: All entries must have some content that's not 'a' or 'r',
2177
 
        # otherwise it could just be removed.
2178
 
        #
2179
 
        # TODO: All relocations must point directly to a real entry.
2180
 
        #
2181
 
        # TODO: No repeated keys.
2182
 
        #
2183
 
        # -- mbp 20070325
2184
2176
        from pprint import pformat
2185
 
        self._read_dirblocks_if_needed()
2186
2177
        if len(self._dirblocks) > 0:
2187
 
            if not self._dirblocks[0][0] == '':
2188
 
                raise AssertionError(
 
2178
            assert self._dirblocks[0][0] == '', \
2189
2179
                    "dirblocks don't start with root block:\n" + \
2190
 
                    pformat(dirblocks))
 
2180
                    pformat(dirblocks)
2191
2181
        if len(self._dirblocks) > 1:
2192
 
            if not self._dirblocks[1][0] == '':
2193
 
                raise AssertionError(
 
2182
            assert self._dirblocks[1][0] == '', \
2194
2183
                    "dirblocks missing root directory:\n" + \
2195
 
                    pformat(dirblocks))
 
2184
                    pformat(dirblocks)
2196
2185
        # the dirblocks are sorted by their path components, name, and dir id
2197
2186
        dir_names = [d[0].split('/')
2198
2187
                for d in self._dirblocks[1:]]
2205
2194
        for dirblock in self._dirblocks:
2206
2195
            # within each dirblock, the entries are sorted by filename and
2207
2196
            # then by id.
2208
 
            for entry in dirblock[1]:
2209
 
                if dirblock[0] != entry[0][0]:
2210
 
                    raise AssertionError(
2211
 
                        "entry key for %r"
2212
 
                        "doesn't match directory name in\n%r" %
2213
 
                        (entry, pformat(dirblock)))
2214
 
            if dirblock[1] != sorted(dirblock[1]):
2215
 
                raise AssertionError(
2216
 
                    "dirblock for %r is not sorted:\n%s" % \
2217
 
                    (dirblock[0], pformat(dirblock)))
2218
 
 
2219
 
        # For each file id, for each tree: either
2220
 
        # the file id is not present at all; all rows with that id in the
2221
 
        # key have it marked as 'absent'
2222
 
        # OR the file id is present under exactly one name; any other entries 
2223
 
        # that mention that id point to the correct name.
2224
 
        #
2225
 
        # We check this with a dict per tree pointing either to the present
2226
 
        # name, or None if absent.
2227
 
        tree_count = self._num_present_parents() + 1
2228
 
        id_path_maps = [dict() for i in range(tree_count)]
2229
 
        # Make sure that all renamed entries point to the correct location.
2230
 
        for entry in self._iter_entries():
2231
 
            file_id = entry[0][2]
2232
 
            this_path = osutils.pathjoin(entry[0][0], entry[0][1])
2233
 
            if len(entry[1]) != tree_count:
2234
 
                raise AssertionError(
2235
 
                "wrong number of entry details for row\n%s" \
2236
 
                ",\nexpected %d" % \
2237
 
                (pformat(entry), tree_count))
2238
 
            for tree_index, tree_state in enumerate(entry[1]):
2239
 
                this_tree_map = id_path_maps[tree_index]
2240
 
                minikind = tree_state[0]
2241
 
                # have we seen this id before in this column?
2242
 
                if file_id in this_tree_map:
2243
 
                    previous_path = this_tree_map[file_id]
2244
 
                    # any later mention of this file must be consistent with
2245
 
                    # what was said before
2246
 
                    if minikind == 'a':
2247
 
                        if previous_path is not None:
2248
 
                            raise AssertionError(
2249
 
                            "file %s is absent in row %r but also present " \
2250
 
                            "at %r"% \
2251
 
                            (file_id, entry, previous_path))
2252
 
                    elif minikind == 'r':
2253
 
                        target_location = tree_state[1]
2254
 
                        if previous_path != target_location:
2255
 
                            raise AssertionError(
2256
 
                            "file %s relocation in row %r but also at %r" \
2257
 
                            % (file_id, entry, previous_path))
2258
 
                    else:
2259
 
                        # a file, directory, etc - may have been previously
2260
 
                        # pointed to by a relocation, which must point here
2261
 
                        if previous_path != this_path:
2262
 
                            raise AssertionError(
2263
 
                            "entry %r inconsistent with previous path %r" % \
2264
 
                            (entry, previous_path))
2265
 
                else:
2266
 
                    if minikind == 'a':
2267
 
                        # absent; should not occur anywhere else
2268
 
                        this_tree_map[file_id] = None
2269
 
                    elif minikind == 'r':
2270
 
                        # relocation, must occur at expected location 
2271
 
                        this_tree_map[file_id] = tree_state[1]
2272
 
                    else:
2273
 
                        this_tree_map[file_id] = this_path
 
2197
            assert dirblock[1] == sorted(dirblock[1]), \
 
2198
                "dirblock for %r is not sorted:\n%s" % \
 
2199
                (dirblock[0], pformat(dirblock))
2274
2200
 
2275
2201
    def _wipe_state(self):
2276
2202
        """Forget all state information about the dirstate."""
2371
2297
    # well within the noise margin
2372
2298
 
2373
2299
    # base64.encode always adds a final newline, so strip it off
2374
 
    return _encode(_pack('>LLLLLL'
 
2300
    return _encode(_pack('>llllll'
2375
2301
        , st.st_size, int(st.st_mtime), int(st.st_ctime)
2376
 
        , st.st_dev, st.st_ino & 0xFFFFFFFF, st.st_mode))[:-1]
 
2302
        , st.st_dev, st.st_ino, st.st_mode))[:-1]