/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/dirstate.py

  • Committer: Robert Collins
  • Date: 2009-07-22 00:34:44 UTC
  • mfrom: (4555 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4557.
  • Revision ID: robertc@robertcollins.net-20090722003444-vcci9yub1ypc5n6k
Fix NEWS conflicts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
204
204
import bisect
205
205
import binascii
206
206
import errno
 
207
import operator
207
208
import os
208
209
from stat import S_IEXEC
209
210
import stat
1277
1278
    def update_by_delta(self, delta):
1278
1279
        """Apply an inventory delta to the dirstate for tree 0
1279
1280
 
 
1281
        This is the workhorse for apply_inventory_delta in dirstate based
 
1282
        trees.
 
1283
 
1280
1284
        :param delta: An inventory delta.  See Inventory.apply_delta for
1281
1285
            details.
1282
1286
        """
1283
1287
        self._read_dirblocks_if_needed()
 
1288
        encode = cache_utf8.encode
1284
1289
        insertions = {}
1285
1290
        removals = {}
1286
 
        for old_path, new_path, file_id, inv_entry in sorted(delta, reverse=True):
 
1291
        # Accumulate parent references (path_utf8, id), to check for parentless
 
1292
        # items or items placed under files/links/tree-references. We get
 
1293
        # references from every item in the delta that is not a deletion and
 
1294
        # is not itself the root.
 
1295
        parents = set()
 
1296
        # Added ids must not be in the dirstate already. This set holds those
 
1297
        # ids.
 
1298
        new_ids = set()
 
1299
        # This loop transforms the delta to single atomic operations that can
 
1300
        # be executed and validated.
 
1301
        for old_path, new_path, file_id, inv_entry in sorted(
 
1302
            inventory._check_delta_unique_old_paths(
 
1303
            inventory._check_delta_unique_new_paths(
 
1304
            inventory._check_delta_ids_match_entry(
 
1305
            inventory._check_delta_ids_are_valid(
 
1306
            inventory._check_delta_new_path_entry_both_or_None(delta))))),
 
1307
            reverse=True):
1287
1308
            if (file_id in insertions) or (file_id in removals):
1288
1309
                raise errors.InconsistentDelta(old_path or new_path, file_id,
1289
1310
                    "repeated file_id")
1290
1311
            if old_path is not None:
1291
1312
                old_path = old_path.encode('utf-8')
1292
1313
                removals[file_id] = old_path
 
1314
            else:
 
1315
                new_ids.add(file_id)
1293
1316
            if new_path is not None:
 
1317
                if inv_entry is None:
 
1318
                    raise errors.InconsistentDelta(new_path, file_id,
 
1319
                        "new_path with no entry")
1294
1320
                new_path = new_path.encode('utf-8')
1295
 
                dirname, basename = osutils.split(new_path)
1296
 
                key = (dirname, basename, file_id)
 
1321
                dirname_utf8, basename = osutils.split(new_path)
 
1322
                if basename:
 
1323
                    parents.add((dirname_utf8, inv_entry.parent_id))
 
1324
                key = (dirname_utf8, basename, file_id)
1297
1325
                minikind = DirState._kind_to_minikind[inv_entry.kind]
1298
1326
                if minikind == 't':
1299
1327
                    fingerprint = inv_entry.reference_revision
1321
1349
                                                  child_basename)
1322
1350
                    insertions[child[0][2]] = (key, minikind, executable,
1323
1351
                                               fingerprint, new_child_path)
1324
 
        self._apply_removals(removals.values())
1325
 
        self._apply_insertions(insertions.values())
 
1352
        self._check_delta_ids_absent(new_ids, delta, 0)
 
1353
        try:
 
1354
            self._apply_removals(removals.iteritems())
 
1355
            self._apply_insertions(insertions.values())
 
1356
            # Validate parents
 
1357
            self._after_delta_check_parents(parents, 0)
 
1358
        except errors.BzrError, e:
 
1359
            self._changes_aborted = True
 
1360
            if 'integrity error' not in str(e):
 
1361
                raise
 
1362
            # _get_entry raises BzrError when a request is inconsistent; we
 
1363
            # want such errors to be shown as InconsistentDelta - and that 
 
1364
            # fits the behaviour we trigger.
 
1365
            raise errors.InconsistentDeltaDelta(delta, "error from _get_entry.")
1326
1366
 
1327
1367
    def _apply_removals(self, removals):
1328
 
        for path in sorted(removals, reverse=True):
 
1368
        for file_id, path in sorted(removals, reverse=True,
 
1369
            key=operator.itemgetter(1)):
1329
1370
            dirname, basename = osutils.split(path)
1330
1371
            block_i, entry_i, d_present, f_present = \
1331
1372
                self._get_block_entry_index(dirname, basename, 0)
1332
 
            entry = self._dirblocks[block_i][1][entry_i]
 
1373
            try:
 
1374
                entry = self._dirblocks[block_i][1][entry_i]
 
1375
            except IndexError:
 
1376
                self._changes_aborted = True
 
1377
                raise errors.InconsistentDelta(path, file_id,
 
1378
                    "Wrong path for old path.")
 
1379
            if not f_present or entry[1][0][0] in 'ar':
 
1380
                self._changes_aborted = True
 
1381
                raise errors.InconsistentDelta(path, file_id,
 
1382
                    "Wrong path for old path.")
 
1383
            if file_id != entry[0][2]:
 
1384
                self._changes_aborted = True
 
1385
                raise errors.InconsistentDelta(path, file_id,
 
1386
                    "Attempt to remove path has wrong id - found %r."
 
1387
                    % entry[0][2])
1333
1388
            self._make_absent(entry)
1334
1389
            # See if we have a malformed delta: deleting a directory must not
1335
1390
            # leave crud behind. This increases the number of bisects needed
1343
1398
                # be due to it being in a parent tree, or a corrupt delta.
1344
1399
                for child_entry in self._dirblocks[block_i][1]:
1345
1400
                    if child_entry[1][0][0] not in ('r', 'a'):
 
1401
                        self._changes_aborted = True
1346
1402
                        raise errors.InconsistentDelta(path, entry[0][2],
1347
1403
                            "The file id was deleted but its children were "
1348
1404
                            "not deleted.")
1349
1405
 
1350
1406
    def _apply_insertions(self, adds):
1351
 
        for key, minikind, executable, fingerprint, path_utf8 in sorted(adds):
1352
 
            self.update_minimal(key, minikind, executable, fingerprint,
1353
 
                                path_utf8=path_utf8)
 
1407
        try:
 
1408
            for key, minikind, executable, fingerprint, path_utf8 in sorted(adds):
 
1409
                self.update_minimal(key, minikind, executable, fingerprint,
 
1410
                                    path_utf8=path_utf8)
 
1411
        except errors.NotVersionedError:
 
1412
            self._changes_aborted = True
 
1413
            raise errors.InconsistentDelta(path_utf8.decode('utf8'), key[2],
 
1414
                "Missing parent")
1354
1415
 
1355
1416
    def update_basis_by_delta(self, delta, new_revid):
1356
1417
        """Update the parents of this tree after a commit.
1400
1461
        # At the same time, to reduce interface friction we convert the input
1401
1462
        # inventory entries to dirstate.
1402
1463
        root_only = ('', '')
1403
 
        # Accumulate parent references (path and id), to check for parentless
 
1464
        # Accumulate parent references (path_utf8, id), to check for parentless
1404
1465
        # items or items placed under files/links/tree-references. We get
1405
1466
        # references from every item in the delta that is not a deletion and
1406
1467
        # is not itself the root.
1407
1468
        parents = set()
 
1469
        # Added ids must not be in the dirstate already. This set holds those
 
1470
        # ids.
 
1471
        new_ids = set()
1408
1472
        for old_path, new_path, file_id, inv_entry in delta:
1409
1473
            if inv_entry is not None and file_id != inv_entry.file_id:
1410
1474
                raise errors.InconsistentDelta(new_path, file_id,
1411
1475
                    "mismatched entry file_id %r" % inv_entry)
1412
1476
            if new_path is not None:
 
1477
                if inv_entry is None:
 
1478
                    raise errors.InconsistentDelta(new_path, file_id,
 
1479
                        "new_path with no entry")
1413
1480
                new_path_utf8 = encode(new_path)
1414
1481
                # note the parent for validation
1415
1482
                dirname_utf8, basename_utf8 = osutils.split(new_path_utf8)
1418
1485
            if old_path is None:
1419
1486
                adds.append((None, encode(new_path), file_id,
1420
1487
                    inv_to_entry(inv_entry), True))
 
1488
                new_ids.add(file_id)
1421
1489
            elif new_path is None:
1422
1490
                deletes.append((encode(old_path), None, file_id, None, True))
1423
1491
            elif (old_path, new_path) != root_only:
1466
1534
                # of everything.
1467
1535
                changes.append((encode(old_path), encode(new_path), file_id,
1468
1536
                    inv_to_entry(inv_entry)))
1469
 
 
 
1537
        self._check_delta_ids_absent(new_ids, delta, 1)
1470
1538
        try:
1471
1539
            # Finish expunging deletes/first half of renames.
1472
1540
            self._update_basis_apply_deletes(deletes)
1475
1543
            # Apply in-situ changes.
1476
1544
            self._update_basis_apply_changes(changes)
1477
1545
            # Validate parents
1478
 
            self._update_basis_check_parents(parents)
 
1546
            self._after_delta_check_parents(parents, 1)
1479
1547
        except errors.BzrError, e:
 
1548
            self._changes_aborted = True
1480
1549
            if 'integrity error' not in str(e):
1481
1550
                raise
1482
1551
            # _get_entry raises BzrError when a request is inconsistent; we
1484
1553
            # fits the behaviour we trigger. Partof this is driven by dirstate
1485
1554
            # only supporting deltas that turn the basis into a closer fit to
1486
1555
            # the active tree.
1487
 
            self._changes_aborted = True
1488
1556
            raise errors.InconsistentDeltaDelta(delta, "error from _get_entry.")
1489
1557
 
1490
1558
        self._dirblock_state = DirState.IN_MEMORY_MODIFIED
1492
1560
        self._id_index = None
1493
1561
        return
1494
1562
 
 
1563
    def _check_delta_ids_absent(self, new_ids, delta, tree_index):
 
1564
        """Check that none of the file_ids in new_ids are present in a tree."""
 
1565
        if not new_ids:
 
1566
            return
 
1567
        id_index = self._get_id_index()
 
1568
        for file_id in new_ids:
 
1569
            for key in id_index.get(file_id, []):
 
1570
                block_i, entry_i, d_present, f_present = \
 
1571
                    self._get_block_entry_index(key[0], key[1], tree_index)
 
1572
                if not f_present:
 
1573
                    # In a different tree
 
1574
                    continue
 
1575
                entry = self._dirblocks[block_i][1][entry_i]
 
1576
                if entry[0][2] != file_id:
 
1577
                    # Different file_id, so not what we want.
 
1578
                    continue
 
1579
                # NB: No changes made before this helper is called, so no need
 
1580
                # to set the _changes_aborted flag.
 
1581
                raise errors.InconsistentDelta(
 
1582
                    ("%s/%s" % key[0:2]).decode('utf8'), file_id,
 
1583
                    "This file_id is new in the delta but already present in "
 
1584
                    "the target")
 
1585
 
1495
1586
    def _update_basis_apply_adds(self, adds):
1496
1587
        """Apply a sequence of adds to tree 1 during update_basis_by_delta.
1497
1588
 
1562
1653
        null = DirState.NULL_PARENT_DETAILS
1563
1654
        for old_path, new_path, file_id, _, real_delete in deletes:
1564
1655
            if real_delete != (new_path is None):
 
1656
                self._changes_aborted = True
1565
1657
                raise AssertionError("bad delete delta")
1566
1658
            # the entry for this file_id must be in tree 1.
1567
1659
            dirname, basename = osutils.split(old_path)
1600
1692
                    # it is being resurrected here, so blank it out temporarily.
1601
1693
                    self._dirblocks[block_index][1][entry_index][1][1] = null
1602
1694
 
1603
 
    def _update_basis_check_parents(self, parents):
1604
 
        """Check that parents required by the delta are all intact."""
 
1695
    def _after_delta_check_parents(self, parents, index):
 
1696
        """Check that parents required by the delta are all intact.
 
1697
        
 
1698
        :param parents: An iterable of (path_utf8, file_id) tuples which are
 
1699
            required to be present in tree 'index' at path_utf8 with id file_id
 
1700
            and be a directory.
 
1701
        :param index: The column in the dirstate to check for parents in.
 
1702
        """
1605
1703
        for dirname_utf8, file_id in parents:
1606
1704
            # Get the entry - the ensures that file_id, dirname_utf8 exists and
1607
1705
            # has the right file id.
1608
 
            entry = self._get_entry(1, file_id, dirname_utf8)
 
1706
            entry = self._get_entry(index, file_id, dirname_utf8)
1609
1707
            if entry[1] is None:
1610
1708
                self._changes_aborted = True
1611
1709
                raise errors.InconsistentDelta(dirname_utf8.decode('utf8'),
1612
1710
                    file_id, "This parent is not present.")
1613
1711
            # Parents of things must be directories
1614
 
            if entry[1][1][0] != 'd':
 
1712
            if entry[1][index][0] != 'd':
1615
1713
                self._changes_aborted = True
1616
1714
                raise errors.InconsistentDelta(dirname_utf8.decode('utf8'),
1617
1715
                    file_id, "This parent is not a directory.")
2486
2584
                        new_path_utf8.decode('utf8'))
2487
2585
                self.update_minimal(new_entry_key, current_new_minikind,
2488
2586
                    executable=current_new[1].executable,
2489
 
                    path_utf8=new_path_utf8, fingerprint=fingerprint)
 
2587
                    path_utf8=new_path_utf8, fingerprint=fingerprint,
 
2588
                    fullscan=True)
2490
2589
                current_new = advance(new_iterator)
2491
2590
            elif not current_new:
2492
2591
                # new is finished
2511
2610
                            new_path_utf8.decode('utf8'))
2512
2611
                    self.update_minimal(current_old[0], current_new_minikind,
2513
2612
                        executable=current_new[1].executable,
2514
 
                        path_utf8=new_path_utf8, fingerprint=fingerprint)
 
2613
                        path_utf8=new_path_utf8, fingerprint=fingerprint,
 
2614
                        fullscan=True)
2515
2615
                # both sides are dealt with, move on
2516
2616
                current_old = advance(old_iterator)
2517
2617
                current_new = advance(new_iterator)
2525
2625
                        new_path_utf8.decode('utf8'))
2526
2626
                self.update_minimal(new_entry_key, current_new_minikind,
2527
2627
                    executable=current_new[1].executable,
2528
 
                    path_utf8=new_path_utf8, fingerprint=fingerprint)
 
2628
                    path_utf8=new_path_utf8, fingerprint=fingerprint,
 
2629
                    fullscan=True)
2529
2630
                current_new = advance(new_iterator)
2530
2631
            else:
2531
2632
                # we've advanced past the place where the old key would be,
2595
2696
        return last_reference
2596
2697
 
2597
2698
    def update_minimal(self, key, minikind, executable=False, fingerprint='',
2598
 
                       packed_stat=None, size=0, path_utf8=None):
 
2699
        packed_stat=None, size=0, path_utf8=None, fullscan=False):
2599
2700
        """Update an entry to the state in tree 0.
2600
2701
 
2601
2702
        This will either create a new entry at 'key' or update an existing one.
2612
2713
        :param size: Size information for new entry
2613
2714
        :param path_utf8: key[0] + '/' + key[1], just passed in to avoid doing
2614
2715
                extra computation.
 
2716
        :param fullscan: If True then a complete scan of the dirstate is being
 
2717
            done and checking for duplicate rows should not be done. This
 
2718
            should only be set by set_state_from_inventory and similar methods.
2615
2719
 
2616
2720
        If packed_stat and fingerprint are not given, they're invalidated in
2617
2721
        the entry.
2626
2730
        new_details = (minikind, fingerprint, size, executable, packed_stat)
2627
2731
        id_index = self._get_id_index()
2628
2732
        if not present:
 
2733
            # New record. Check there isn't a entry at this path already.
 
2734
            if not fullscan:
 
2735
                low_index, _ = self._find_entry_index(key[0:2] + ('',), block)
 
2736
                while low_index < len(block):
 
2737
                    entry = block[low_index]
 
2738
                    if entry[0][0:2] == key[0:2]:
 
2739
                        if entry[1][0][0] not in 'ar':
 
2740
                            # This entry has the same path (but a different id) as
 
2741
                            # the new entry we're adding, and is present in ths
 
2742
                            # tree.
 
2743
                            raise errors.InconsistentDelta(
 
2744
                                ("%s/%s" % key[0:2]).decode('utf8'), key[2],
 
2745
                                "Attempt to add item at path already occupied by "
 
2746
                                "id %r" % entry[0][2])
 
2747
                        low_index += 1
 
2748
                    else:
 
2749
                        break
2629
2750
            # new entry, synthesis cross reference here,
2630
2751
            existing_keys = id_index.setdefault(key[2], set())
2631
2752
            if not existing_keys: