~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/dirstate.py

Merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
321
321
        # modified states.
322
322
        self._header_state = DirState.NOT_IN_MEMORY
323
323
        self._dirblock_state = DirState.NOT_IN_MEMORY
 
324
        # If true, an error has been detected while updating the dirstate, and 
 
325
        # for safety we're not going to commit to disk.
 
326
        self._changes_aborted = False
324
327
        self._dirblocks = []
325
328
        self._ghosts = []
326
329
        self._parents = []
1163
1166
            raise
1164
1167
        return result
1165
1168
 
 
1169
    def update_by_delta(self, delta):
 
1170
        """Apply an inventory delta to the dirstate for tree 0
 
1171
 
 
1172
        :param delta: An inventory delta.  See Inventory.apply_delta for
 
1173
            details.
 
1174
        """
 
1175
        self._read_dirblocks_if_needed()
 
1176
        insertions = {}
 
1177
        removals = {}
 
1178
        for old_path, new_path, file_id, inv_entry in sorted(delta,
 
1179
                                                             reverse=True):
 
1180
            assert file_id not in insertions
 
1181
            assert file_id not in removals
 
1182
            if old_path is not None:
 
1183
                old_path = old_path.encode('utf-8')
 
1184
                removals[file_id] = old_path
 
1185
            if new_path is not None:
 
1186
                new_path = new_path.encode('utf-8')
 
1187
                dirname, basename = osutils.split(new_path)
 
1188
                key = (dirname, basename, file_id)
 
1189
                minikind = DirState._kind_to_minikind[inv_entry.kind]
 
1190
                if minikind == 't':
 
1191
                    fingerprint = inv_entry.reference_revision
 
1192
                else:
 
1193
                    fingerprint = ''
 
1194
                insertions[file_id] = (key, minikind, inv_entry.executable,
 
1195
                                       fingerprint, new_path)
 
1196
            if None not in (old_path, new_path):
 
1197
                for child in self._iter_child_entries(0, old_path):
 
1198
                    if child[0][2] in insertions or child[0][2] in removals:
 
1199
                        continue
 
1200
                    child_dirname = child[0][0]
 
1201
                    child_basename = child[0][1]
 
1202
                    minikind = child[1][0][0]
 
1203
                    fingerprint = child[1][0][4]
 
1204
                    executable = child[1][0][3]
 
1205
                    old_child_path = osutils.pathjoin(child[0][0],
 
1206
                                                      child[0][1])
 
1207
                    removals[child[0][2]] = old_child_path
 
1208
                    child_suffix = child_dirname[len(old_path):]
 
1209
                    new_child_dirname = (new_path + child_suffix)
 
1210
                    key = (new_child_dirname, child_basename, child[0][2])
 
1211
                    new_child_path = os.path.join(new_child_dirname,
 
1212
                                                  child_basename)
 
1213
                    insertions[child[0][2]] = (key, minikind, executable,
 
1214
                                               fingerprint, new_child_path)
 
1215
        self._apply_removals(removals.values())
 
1216
        self._apply_insertions(insertions.values())
 
1217
 
 
1218
    def _apply_removals(self, removals):
 
1219
        for path in sorted(removals, reverse=True):
 
1220
            dirname, basename = osutils.split(path)
 
1221
            block_i, entry_i, d_present, f_present = \
 
1222
                self._get_block_entry_index(dirname, basename, 0)
 
1223
            entry = self._dirblocks[block_i][1][entry_i]
 
1224
            self._make_absent(entry)
 
1225
 
 
1226
    def _apply_insertions(self, adds):
 
1227
        for key, minikind, executable, fingerprint, path_utf8 in sorted(adds):
 
1228
            self.update_minimal(key, minikind, executable, fingerprint,
 
1229
                                path_utf8=path_utf8)
 
1230
 
1166
1231
    def update_basis_by_delta(self, delta, new_revid):
1167
1232
        """Update the parents of this tree after a commit.
1168
1233
 
1247
1312
                        source_path = entry[0][0] + '/' + entry[0][1]
1248
1313
                    else:
1249
1314
                        source_path = entry[0][1]
1250
 
                    target_path = new_path_utf8 + source_path[len(old_path):]
 
1315
                    if new_path_utf8:
 
1316
                        target_path = new_path_utf8 + source_path[len(old_path):]
 
1317
                    else:
 
1318
                        assert len(old_path) > 0, ("cannot rename directory to"
 
1319
                                                   " itself")
 
1320
                        target_path = source_path[len(old_path) + 1:]
1251
1321
                    adds.append((None, target_path, entry[0][2], entry[1][1], False))
1252
1322
                    deletes.append(
1253
1323
                        (source_path, target_path, entry[0][2], None, False))
1292
1362
            assert old_path is None
1293
1363
            # the entry for this file_id must be in tree 0.
1294
1364
            entry = self._get_entry(0, file_id, new_path)
1295
 
            if entry[0][2] != file_id:
1296
 
                raise errors.BzrError('dirstate: cannot apply delta, working'
1297
 
                    ' tree does not contain new entry %r %r' %
1298
 
                    (new_path, file_id))
 
1365
            if entry[0] is None or entry[0][2] != file_id:
 
1366
                self._changes_aborted = True
 
1367
                raise errors.InconsistentDelta(new_path, file_id,
 
1368
                    'working tree does not contain new entry')
1299
1369
            if real_add and entry[1][1][0] not in absent:
1300
 
                raise errors.BzrError('dirstate: inconsistent delta, with '
1301
 
                    'tree 0. %r %r' % (new_path, file_id))
 
1370
                self._changes_aborted = True
 
1371
                raise errors.InconsistentDelta(new_path, file_id,
 
1372
                    'The entry was considered to be a genuinely new record,'
 
1373
                    ' but there was already an old record for it.')
1302
1374
            # We don't need to update the target of an 'r' because the handling
1303
1375
            # of renames turns all 'r' situations into a delete at the original
1304
1376
            # location.
1315
1387
            assert old_path == new_path
1316
1388
            # the entry for this file_id must be in tree 0.
1317
1389
            entry = self._get_entry(0, file_id, new_path)
1318
 
            if entry[0][2] != file_id:
1319
 
                raise errors.BzrError('dirstate: cannot apply delta, working'
1320
 
                    ' tree does not contain new entry %r %r' %
1321
 
                    (new_path, file_id))
 
1390
            if entry[0] is None or entry[0][2] != file_id:
 
1391
                self._changes_aborted = True
 
1392
                raise errors.InconsistentDelta(new_path, file_id,
 
1393
                    'working tree does not contain new entry')
1322
1394
            if (entry[1][0][0] in absent or
1323
1395
                entry[1][1][0] in absent):
1324
 
                raise errors.BzrError('dirstate: inconsistent delta, with '
1325
 
                    'tree 0. %r %r' % (new_path, file_id))
 
1396
                self._changes_aborted = True
 
1397
                raise errors.InconsistentDelta(new_path, file_id,
 
1398
                    'changed considered absent')
1326
1399
            entry[1][1] = new_details
1327
1400
 
1328
1401
    def _update_basis_apply_deletes(self, deletes):
1348
1421
            block_index, entry_index, dir_present, file_present = \
1349
1422
                self._get_block_entry_index(dirname, basename, 1)
1350
1423
            if not file_present:
1351
 
                raise errors.BzrError('dirstate: cannot apply delta, basis'
1352
 
                    ' tree does not contain new entry %r %r' %
1353
 
                    (old_path, file_id))
 
1424
                self._changes_aborted = True
 
1425
                raise errors.InconsistentDelta(old_path, file_id,
 
1426
                    'basis tree does not contain removed entry')
1354
1427
            entry = self._dirblocks[block_index][1][entry_index]
1355
1428
            if entry[0][2] != file_id:
1356
 
                raise errors.BzrError('mismatched file_id in tree 1 %r %r' %
1357
 
                    (old_path, file_id))
 
1429
                self._changes_aborted = True
 
1430
                raise errors.InconsistentDelta(old_path, file_id,
 
1431
                    'mismatched file_id in tree 1')
1358
1432
            if real_delete:
1359
1433
                if entry[1][0][0] != 'a':
1360
 
                    raise errors.BzrError('dirstate: inconsistent delta, with '
1361
 
                        'tree 0. %r %r' % (old_path, file_id))
 
1434
                    self._changes_aborted = True
 
1435
                    raise errors.InconsistentDelta(old_path, file_id,
 
1436
                            'This was marked as a real delete, but the WT state'
 
1437
                            ' claims that it still exists and is versioned.')
1362
1438
                del self._dirblocks[block_index][1][entry_index]
1363
1439
            else:
1364
1440
                if entry[1][0][0] == 'a':
1365
 
                    raise errors.BzrError('dirstate: inconsistent delta, with '
1366
 
                        'tree 0. %r %r' % (old_path, file_id))
 
1441
                    self._changes_aborted = True
 
1442
                    raise errors.InconsistentDelta(old_path, file_id,
 
1443
                        'The entry was considered a rename, but the source path'
 
1444
                        ' is marked as absent.')
 
1445
                    # For whatever reason, we were asked to rename an entry
 
1446
                    # that was originally marked as deleted. This could be
 
1447
                    # because we are renaming the parent directory, and the WT
 
1448
                    # current state has the file marked as deleted.
1367
1449
                elif entry[1][0][0] == 'r':
1368
1450
                    # implement the rename
1369
1451
                    del self._dirblocks[block_index][1][entry_index]
1666
1748
            assert entry[0][2] and entry[1][tree_index][0] not in ('a', 'r'), 'unversioned entry?!?!'
1667
1749
            if fileid_utf8:
1668
1750
                if entry[0][2] != fileid_utf8:
 
1751
                    self._changes_aborted = True
1669
1752
                    raise errors.BzrError('integrity error ? : mismatching'
1670
1753
                                          ' tree_index, file_id and path')
1671
1754
            return entry
1954
2037
        start over, to allow for fine grained read lock duration, so 'status'
1955
2038
        wont block 'commit' - for example.
1956
2039
        """
 
2040
        if self._changes_aborted:
 
2041
            # Should this be a warning? For now, I'm expecting that places that
 
2042
            # mark it inconsistent will warn, making a warning here redundant.
 
2043
            trace.mutter('Not saving DirState because '
 
2044
                    '_changes_aborted is set.')
 
2045
            return
1957
2046
        if (self._header_state == DirState.IN_MEMORY_MODIFIED or
1958
2047
            self._dirblock_state == DirState.IN_MEMORY_MODIFIED):
1959
2048
 
2598
2687
        """Forget all state information about the dirstate."""
2599
2688
        self._header_state = DirState.NOT_IN_MEMORY
2600
2689
        self._dirblock_state = DirState.NOT_IN_MEMORY
 
2690
        self._changes_aborted = False
2601
2691
        self._parents = []
2602
2692
        self._ghosts = []
2603
2693
        self._dirblocks = []