~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/dirstate.py

(jameinel) (bug #780544) when updating the WT,
 allow it to be done with a fast delta,
 rather than setting the state from scratch. (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
388
388
    # A pack_stat (the x's) that is just noise and will never match the output
389
389
    # of base64 encode.
390
390
    NULLSTAT = 'x' * 32
391
 
    NULL_PARENT_DETAILS = ('a', '', 0, False, '')
 
391
    NULL_PARENT_DETAILS = static_tuple.StaticTuple('a', '', 0, False, '')
392
392
 
393
393
    HEADER_FORMAT_2 = '#bazaar dirstate flat format 2\n'
394
394
    HEADER_FORMAT_3 = '#bazaar dirstate flat format 3\n'
1378
1378
        delta = sorted(self._check_delta_is_valid(delta), reverse=True)
1379
1379
        for old_path, new_path, file_id, inv_entry in delta:
1380
1380
            if (file_id in insertions) or (file_id in removals):
1381
 
                raise errors.InconsistentDelta(old_path or new_path, file_id,
 
1381
                self._raise_invalid(old_path or new_path, file_id,
1382
1382
                    "repeated file_id")
1383
1383
            if old_path is not None:
1384
1384
                old_path = old_path.encode('utf-8')
1387
1387
                new_ids.add(file_id)
1388
1388
            if new_path is not None:
1389
1389
                if inv_entry is None:
1390
 
                    raise errors.InconsistentDelta(new_path, file_id,
 
1390
                    self._raise_invalid(new_path, file_id,
1391
1391
                        "new_path with no entry")
1392
1392
                new_path = new_path.encode('utf-8')
1393
1393
                dirname_utf8, basename = osutils.split(new_path)
1434
1434
            # _get_entry raises BzrError when a request is inconsistent; we
1435
1435
            # want such errors to be shown as InconsistentDelta - and that 
1436
1436
            # fits the behaviour we trigger.
1437
 
            raise errors.InconsistentDeltaDelta(delta, "error from _get_entry.")
 
1437
            raise errors.InconsistentDeltaDelta(delta,
 
1438
                "error from _get_entry. %s" % (e,))
1438
1439
 
1439
1440
    def _apply_removals(self, removals):
1440
1441
        for file_id, path in sorted(removals, reverse=True,
1445
1446
            try:
1446
1447
                entry = self._dirblocks[block_i][1][entry_i]
1447
1448
            except IndexError:
1448
 
                self._changes_aborted = True
1449
 
                raise errors.InconsistentDelta(path, file_id,
 
1449
                self._raise_invalid(path, file_id,
1450
1450
                    "Wrong path for old path.")
1451
1451
            if not f_present or entry[1][0][0] in 'ar':
1452
 
                self._changes_aborted = True
1453
 
                raise errors.InconsistentDelta(path, file_id,
 
1452
                self._raise_invalid(path, file_id,
1454
1453
                    "Wrong path for old path.")
1455
1454
            if file_id != entry[0][2]:
1456
 
                self._changes_aborted = True
1457
 
                raise errors.InconsistentDelta(path, file_id,
 
1455
                self._raise_invalid(path, file_id,
1458
1456
                    "Attempt to remove path has wrong id - found %r."
1459
1457
                    % entry[0][2])
1460
1458
            self._make_absent(entry)
1470
1468
                # be due to it being in a parent tree, or a corrupt delta.
1471
1469
                for child_entry in self._dirblocks[block_i][1]:
1472
1470
                    if child_entry[1][0][0] not in ('r', 'a'):
1473
 
                        self._changes_aborted = True
1474
 
                        raise errors.InconsistentDelta(path, entry[0][2],
 
1471
                        self._raise_invalid(path, entry[0][2],
1475
1472
                            "The file id was deleted but its children were "
1476
1473
                            "not deleted.")
1477
1474
 
1481
1478
                self.update_minimal(key, minikind, executable, fingerprint,
1482
1479
                                    path_utf8=path_utf8)
1483
1480
        except errors.NotVersionedError:
1484
 
            self._changes_aborted = True
1485
 
            raise errors.InconsistentDelta(path_utf8.decode('utf8'), key[2],
 
1481
            self._raise_invalid(path_utf8.decode('utf8'), key[2],
1486
1482
                "Missing parent")
1487
1483
 
1488
1484
    def update_basis_by_delta(self, delta, new_revid):
1540
1536
        new_ids = set()
1541
1537
        for old_path, new_path, file_id, inv_entry in delta:
1542
1538
            if inv_entry is not None and file_id != inv_entry.file_id:
1543
 
                raise errors.InconsistentDelta(new_path, file_id,
 
1539
                self._raise_invalid(new_path, file_id,
1544
1540
                    "mismatched entry file_id %r" % inv_entry)
1545
 
            if new_path is not None:
 
1541
            if new_path is None:
 
1542
                new_path_utf8 = None
 
1543
            else:
1546
1544
                if inv_entry is None:
1547
 
                    raise errors.InconsistentDelta(new_path, file_id,
 
1545
                    self._raise_invalid(new_path, file_id,
1548
1546
                        "new_path with no entry")
1549
1547
                new_path_utf8 = encode(new_path)
1550
1548
                # note the parent for validation
1552
1550
                if basename_utf8:
1553
1551
                    parents.add((dirname_utf8, inv_entry.parent_id))
1554
1552
            if old_path is None:
1555
 
                adds.append((None, encode(new_path), file_id,
 
1553
                old_path_utf8 = None
 
1554
            else:
 
1555
                old_path_utf8 = encode(old_path)
 
1556
            if old_path is None:
 
1557
                adds.append((None, new_path_utf8, file_id,
1556
1558
                    inv_to_entry(inv_entry), True))
1557
1559
                new_ids.add(file_id)
1558
1560
            elif new_path is None:
1559
 
                deletes.append((encode(old_path), None, file_id, None, True))
1560
 
            elif (old_path, new_path) != root_only:
 
1561
                deletes.append((old_path_utf8, None, file_id, None, True))
 
1562
            elif (old_path, new_path) == root_only:
 
1563
                # change things in-place
 
1564
                # Note: the case of a parent directory changing its file_id
 
1565
                #       tends to break optimizations here, because officially
 
1566
                #       the file has actually been moved, it just happens to
 
1567
                #       end up at the same path. If we can figure out how to
 
1568
                #       handle that case, we can avoid a lot of add+delete
 
1569
                #       pairs for objects that stay put.
 
1570
                # elif old_path == new_path:
 
1571
                changes.append((old_path_utf8, new_path_utf8, file_id,
 
1572
                                inv_to_entry(inv_entry)))
 
1573
            else:
1561
1574
                # Renames:
1562
1575
                # Because renames must preserve their children we must have
1563
1576
                # processed all relocations and removes before hand. The sort
1573
1586
                self._update_basis_apply_deletes(deletes)
1574
1587
                deletes = []
1575
1588
                # Split into an add/delete pair recursively.
1576
 
                adds.append((None, new_path_utf8, file_id,
1577
 
                    inv_to_entry(inv_entry), False))
 
1589
                adds.append((old_path_utf8, new_path_utf8, file_id,
 
1590
                             inv_to_entry(inv_entry), False))
1578
1591
                # Expunge deletes that we've seen so that deleted/renamed
1579
1592
                # children of a rename directory are handled correctly.
1580
 
                new_deletes = reversed(list(self._iter_child_entries(1,
1581
 
                    encode(old_path))))
 
1593
                new_deletes = reversed(list(
 
1594
                    self._iter_child_entries(1, old_path_utf8)))
1582
1595
                # Remove the current contents of the tree at orig_path, and
1583
1596
                # reinsert at the correct new path.
1584
1597
                for entry in new_deletes:
1585
 
                    if entry[0][0]:
1586
 
                        source_path = entry[0][0] + '/' + entry[0][1]
 
1598
                    child_dirname, child_basename, child_file_id = entry[0]
 
1599
                    if child_dirname:
 
1600
                        source_path = child_dirname + '/' + child_basename
1587
1601
                    else:
1588
 
                        source_path = entry[0][1]
 
1602
                        source_path = child_basename
1589
1603
                    if new_path_utf8:
1590
1604
                        target_path = new_path_utf8 + source_path[len(old_path):]
1591
1605
                    else:
1592
1606
                        if old_path == '':
1593
1607
                            raise AssertionError("cannot rename directory to"
1594
 
                                " itself")
 
1608
                                                 " itself")
1595
1609
                        target_path = source_path[len(old_path) + 1:]
1596
1610
                    adds.append((None, target_path, entry[0][2], entry[1][1], False))
1597
1611
                    deletes.append(
1598
1612
                        (source_path, target_path, entry[0][2], None, False))
1599
 
                deletes.append(
1600
 
                    (encode(old_path), new_path, file_id, None, False))
1601
 
            else:
1602
 
                # changes to just the root should not require remove/insertion
1603
 
                # of everything.
1604
 
                changes.append((encode(old_path), encode(new_path), file_id,
1605
 
                    inv_to_entry(inv_entry)))
 
1613
                deletes.append((old_path_utf8, new_path, file_id, None, False))
1606
1614
        self._check_delta_ids_absent(new_ids, delta, 1)
1607
1615
        try:
1608
1616
            # Finish expunging deletes/first half of renames.
1618
1626
            if 'integrity error' not in str(e):
1619
1627
                raise
1620
1628
            # _get_entry raises BzrError when a request is inconsistent; we
1621
 
            # want such errors to be shown as InconsistentDelta - and that 
1622
 
            # fits the behaviour we trigger. Partof this is driven by dirstate
1623
 
            # only supporting deltas that turn the basis into a closer fit to
1624
 
            # the active tree.
1625
 
            raise errors.InconsistentDeltaDelta(delta, "error from _get_entry.")
 
1629
            # want such errors to be shown as InconsistentDelta - and that
 
1630
            # fits the behaviour we trigger.
 
1631
            raise errors.InconsistentDeltaDelta(delta,
 
1632
                "error from _get_entry. %s" % (e,))
1626
1633
 
1627
1634
        self._mark_modified(header_modified=True)
1628
1635
        self._id_index = None
1644
1651
                if entry[0][2] != file_id:
1645
1652
                    # Different file_id, so not what we want.
1646
1653
                    continue
1647
 
                # NB: No changes made before this helper is called, so no need
1648
 
                # to set the _changes_aborted flag.
1649
 
                raise errors.InconsistentDelta(
1650
 
                    ("%s/%s" % key[0:2]).decode('utf8'), file_id,
 
1654
                self._raise_invalid(("%s/%s" % key[0:2]).decode('utf8'), file_id,
1651
1655
                    "This file_id is new in the delta but already present in "
1652
1656
                    "the target")
1653
1657
 
 
1658
    def _raise_invalid(self, path, file_id, reason):
 
1659
        self._changes_aborted = True
 
1660
        raise errors.InconsistentDelta(path, file_id, reason)
 
1661
 
1654
1662
    def _update_basis_apply_adds(self, adds):
1655
1663
        """Apply a sequence of adds to tree 1 during update_basis_by_delta.
1656
1664
 
1664
1672
        """
1665
1673
        # Adds are accumulated partly from renames, so can be in any input
1666
1674
        # order - sort it.
1667
 
        adds.sort()
 
1675
        # TODO: we may want to sort in dirblocks order. That way each entry
 
1676
        #       will end up in the same directory, allowing the _get_entry
 
1677
        #       fast-path for looking up 2 items in the same dir work.
 
1678
        adds.sort(key=lambda x: x[1])
1668
1679
        # adds is now in lexographic order, which places all parents before
1669
1680
        # their children, so we can process it linearly.
1670
1681
        absent = 'ar'
 
1682
        st = static_tuple.StaticTuple
1671
1683
        for old_path, new_path, file_id, new_details, real_add in adds:
1672
 
            # the entry for this file_id must be in tree 0.
1673
 
            entry = self._get_entry(0, file_id, new_path)
1674
 
            if entry[0] is None:
1675
 
                # new_path is not versioned in the active WT state,
1676
 
                # but we are adding it to the basis tree state, we
1677
 
                # need to create a new entry record for it.
1678
 
                dirname, basename = osutils.split(new_path)
1679
 
                entry_key = (dirname, basename, file_id)
1680
 
                _, block = self._find_block(entry_key, add_if_missing=True)
1681
 
                index, _ = self._find_entry_index(entry_key, block)
1682
 
                entry = (entry_key, [DirState.NULL_PARENT_DETAILS]*2)
1683
 
                block.insert(index, entry)
1684
 
            elif entry[0][2] != file_id:
1685
 
                self._changes_aborted = True
1686
 
                raise errors.InconsistentDelta(new_path, file_id,
1687
 
                    'working tree does not contain new entry')
1688
 
            if real_add and entry[1][1][0] not in absent:
1689
 
                self._changes_aborted = True
1690
 
                raise errors.InconsistentDelta(new_path, file_id,
1691
 
                    'The entry was considered to be a genuinely new record,'
1692
 
                    ' but there was already an old record for it.')
1693
 
            # We don't need to update the target of an 'r' because the handling
1694
 
            # of renames turns all 'r' situations into a delete at the original
1695
 
            # location.
1696
 
            entry[1][1] = new_details
 
1684
            dirname, basename = osutils.split(new_path)
 
1685
            entry_key = st(dirname, basename, file_id)
 
1686
            block_index, present = self._find_block_index_from_key(entry_key)
 
1687
            if not present:
 
1688
                self._raise_invalid(new_path, file_id,
 
1689
                    "Unable to find block for this record."
 
1690
                    " Was the parent added?")
 
1691
            block = self._dirblocks[block_index][1]
 
1692
            entry_index, present = self._find_entry_index(entry_key, block)
 
1693
            if real_add:
 
1694
                if old_path is not None:
 
1695
                    self._raise_invalid(new_path, file_id,
 
1696
                        'considered a real add but still had old_path at %s'
 
1697
                        % (old_path,))
 
1698
            if present:
 
1699
                entry = block[entry_index]
 
1700
                basis_kind = entry[1][1][0]
 
1701
                if basis_kind == 'a':
 
1702
                    entry[1][1] = new_details
 
1703
                elif basis_kind == 'r':
 
1704
                    raise NotImplementedError()
 
1705
                else:
 
1706
                    self._raise_invalid(new_path, file_id,
 
1707
                        "An entry was marked as a new add"
 
1708
                        " but the basis target already existed")
 
1709
            else:
 
1710
                # The exact key was not found in the block. However, we need to
 
1711
                # check if there is a key next to us that would have matched.
 
1712
                # We only need to check 2 locations, because there are only 2
 
1713
                # trees present.
 
1714
                for maybe_index in range(entry_index-1, entry_index+1):
 
1715
                    if maybe_index < 0 or maybe_index >= len(block):
 
1716
                        continue
 
1717
                    maybe_entry = block[maybe_index]
 
1718
                    if maybe_entry[0][:2] != (dirname, basename):
 
1719
                        # Just a random neighbor
 
1720
                        continue
 
1721
                    if maybe_entry[0][2] == file_id:
 
1722
                        raise AssertionError(
 
1723
                            '_find_entry_index didnt find a key match'
 
1724
                            ' but walking the data did, for %s'
 
1725
                            % (entry_key,))
 
1726
                    basis_kind = maybe_entry[1][1][0]
 
1727
                    if basis_kind not in 'ar':
 
1728
                        self._raise_invalid(new_path, file_id,
 
1729
                            "we have an add record for path, but the path"
 
1730
                            " is already present with another file_id %s"
 
1731
                            % (maybe_entry[0][2],))
 
1732
 
 
1733
                entry = (entry_key, [DirState.NULL_PARENT_DETAILS,
 
1734
                                     new_details])
 
1735
                block.insert(entry_index, entry)
 
1736
 
 
1737
            active_kind = entry[1][0][0]
 
1738
            if active_kind == 'a':
 
1739
                # The active record shows up as absent, this could be genuine,
 
1740
                # or it could be present at some other location. We need to
 
1741
                # verify.
 
1742
                id_index = self._get_id_index()
 
1743
                # The id_index may not be perfectly accurate for tree1, because
 
1744
                # we haven't been keeping it updated. However, it should be
 
1745
                # fine for tree0, and that gives us enough info for what we
 
1746
                # need
 
1747
                keys = id_index.get(file_id, ())
 
1748
                for key in keys:
 
1749
                    block_i, entry_i, d_present, f_present = \
 
1750
                        self._get_block_entry_index(key[0], key[1], 0)
 
1751
                    if not f_present:
 
1752
                        continue
 
1753
                    active_entry = self._dirblocks[block_i][1][entry_i]
 
1754
                    if (active_entry[0][2] != file_id):
 
1755
                        # Some other file is at this path, we don't need to
 
1756
                        # link it.
 
1757
                        continue
 
1758
                    real_active_kind = active_entry[1][0][0]
 
1759
                    if real_active_kind in 'ar':
 
1760
                        # We found a record, which was not *this* record,
 
1761
                        # which matches the file_id, but is not actually
 
1762
                        # present. Something seems *really* wrong.
 
1763
                        self._raise_invalid(new_path, file_id,
 
1764
                            "We found a tree0 entry that doesnt make sense")
 
1765
                    # Now, we've found a tree0 entry which matches the file_id
 
1766
                    # but is at a different location. So update them to be
 
1767
                    # rename records.
 
1768
                    active_dir, active_name = active_entry[0][:2]
 
1769
                    if active_dir:
 
1770
                        active_path = active_dir + '/' + active_name
 
1771
                    else:
 
1772
                        active_path = active_name
 
1773
                    active_entry[1][1] = st('r', new_path, 0, False, '')
 
1774
                    entry[1][0] = st('r', active_path, 0, False, '')
 
1775
            elif active_kind == 'r':
 
1776
                raise NotImplementedError()
 
1777
 
 
1778
            new_kind = new_details[0]
 
1779
            if new_kind == 'd':
 
1780
                self._ensure_block(block_index, entry_index, new_path)
1697
1781
 
1698
1782
    def _update_basis_apply_changes(self, changes):
1699
1783
        """Apply a sequence of changes to tree 1 during update_basis_by_delta.
1704
1788
        absent = 'ar'
1705
1789
        for old_path, new_path, file_id, new_details in changes:
1706
1790
            # the entry for this file_id must be in tree 0.
1707
 
            entry = self._get_entry(0, file_id, new_path)
1708
 
            if entry[0] is None or entry[0][2] != file_id:
1709
 
                self._changes_aborted = True
1710
 
                raise errors.InconsistentDelta(new_path, file_id,
1711
 
                    'working tree does not contain new entry')
1712
 
            if (entry[1][0][0] in absent or
1713
 
                entry[1][1][0] in absent):
1714
 
                self._changes_aborted = True
1715
 
                raise errors.InconsistentDelta(new_path, file_id,
1716
 
                    'changed considered absent')
 
1791
            entry = self._get_entry(1, file_id, new_path)
 
1792
            if entry[0] is None or entry[1][1][0] in 'ar':
 
1793
                self._raise_invalid(new_path, file_id,
 
1794
                    'changed entry considered not present')
1717
1795
            entry[1][1] = new_details
1718
1796
 
1719
1797
    def _update_basis_apply_deletes(self, deletes):
1731
1809
        null = DirState.NULL_PARENT_DETAILS
1732
1810
        for old_path, new_path, file_id, _, real_delete in deletes:
1733
1811
            if real_delete != (new_path is None):
1734
 
                self._changes_aborted = True
1735
 
                raise AssertionError("bad delete delta")
 
1812
                self._raise_invalid(old_path, file_id, "bad delete delta")
1736
1813
            # the entry for this file_id must be in tree 1.
1737
1814
            dirname, basename = osutils.split(old_path)
1738
1815
            block_index, entry_index, dir_present, file_present = \
1739
1816
                self._get_block_entry_index(dirname, basename, 1)
1740
1817
            if not file_present:
1741
 
                self._changes_aborted = True
1742
 
                raise errors.InconsistentDelta(old_path, file_id,
 
1818
                self._raise_invalid(old_path, file_id,
1743
1819
                    'basis tree does not contain removed entry')
1744
1820
            entry = self._dirblocks[block_index][1][entry_index]
 
1821
            # The state of the entry in the 'active' WT
 
1822
            active_kind = entry[1][0][0]
1745
1823
            if entry[0][2] != file_id:
1746
 
                self._changes_aborted = True
1747
 
                raise errors.InconsistentDelta(old_path, file_id,
 
1824
                self._raise_invalid(old_path, file_id,
1748
1825
                    'mismatched file_id in tree 1')
1749
 
            if real_delete:
1750
 
                if entry[1][0][0] == 'a':
1751
 
                    # The file was marked as deleted in the active
1752
 
                    # state, and it is now deleted in the basis state,
1753
 
                    # so just remove the record entirely
1754
 
                    del self._dirblocks[block_index][1][entry_index]
1755
 
                else:
1756
 
                    # The basis entry needs to be marked deleted
1757
 
                    entry[1][1] = null
1758
 
                # If we are deleting a directory, we need to make sure
1759
 
                # that all of its children are already deleted
 
1826
            dir_block = ()
 
1827
            old_kind = entry[1][1][0]
 
1828
            if active_kind in 'ar':
 
1829
                # The active tree doesn't have this file_id.
 
1830
                # The basis tree is changing this record. If this is a
 
1831
                # rename, then we don't want the record here at all
 
1832
                # anymore. If it is just an in-place change, we want the
 
1833
                # record here, but we'll add it if we need to. So we just
 
1834
                # delete it
 
1835
                if active_kind == 'r':
 
1836
                    active_path = entry[1][0][1]
 
1837
                    active_entry = self._get_entry(0, file_id, active_path)
 
1838
                    if active_entry[1][1][0] != 'r':
 
1839
                        self._raise_invalid(old_path, file_id,
 
1840
                            "Dirstate did not have matching rename entries")
 
1841
                    elif active_entry[1][0][0] in 'ar':
 
1842
                        self._raise_invalid(old_path, file_id,
 
1843
                            "Dirstate had a rename pointing at an inactive"
 
1844
                            " tree0")
 
1845
                    active_entry[1][1] = null
 
1846
                del self._dirblocks[block_index][1][entry_index]
 
1847
                if old_kind == 'd':
 
1848
                    # This was a directory, and the active tree says it
 
1849
                    # doesn't exist, and now the basis tree says it doesn't
 
1850
                    # exist. Remove its dirblock if present
 
1851
                    (dir_block_index,
 
1852
                     present) = self._find_block_index_from_key(
 
1853
                        (old_path, '', ''))
 
1854
                    if present:
 
1855
                        dir_block = self._dirblocks[dir_block_index][1]
 
1856
                        if not dir_block:
 
1857
                            # This entry is empty, go ahead and just remove it
 
1858
                            del self._dirblocks[dir_block_index]
 
1859
            else:
 
1860
                # There is still an active record, so just mark this
 
1861
                # removed.
 
1862
                entry[1][1] = null
1760
1863
                block_i, entry_i, d_present, f_present = \
1761
 
                    self._get_block_entry_index(old_path, '', 0)
 
1864
                    self._get_block_entry_index(old_path, '', 1)
1762
1865
                if d_present:
1763
 
                    # The dir block is still present in the dirstate; this could
1764
 
                    # be due to it being in a parent tree, or a corrupt delta.
1765
 
                    for child_entry in self._dirblocks[block_i][1]:
1766
 
                        if child_entry[1][1][0] not in ('r', 'a'):
1767
 
                            self._changes_aborted = True
1768
 
                            raise errors.InconsistentDelta(old_path, entry[0][2],
1769
 
                                "The file id was deleted but its children were "
1770
 
                                "not deleted.")
1771
 
            else:
1772
 
                if entry[1][0][0] == 'a':
1773
 
                    self._changes_aborted = True
1774
 
                    raise errors.InconsistentDelta(old_path, file_id,
1775
 
                        'The entry was considered a rename, but the source path'
1776
 
                        ' is marked as absent.')
1777
 
                    # For whatever reason, we were asked to rename an entry
1778
 
                    # that was originally marked as deleted. This could be
1779
 
                    # because we are renaming the parent directory, and the WT
1780
 
                    # current state has the file marked as deleted.
1781
 
                elif entry[1][0][0] == 'r':
1782
 
                    # implement the rename
1783
 
                    del self._dirblocks[block_index][1][entry_index]
1784
 
                else:
1785
 
                    # it is being resurrected here, so blank it out temporarily.
1786
 
                    # should be equivalent to entry[1][1] = null
1787
 
                    self._dirblocks[block_index][1][entry_index][1][1] = null
 
1866
                    dir_block = self._dirblocks[block_i][1]
 
1867
            for child_entry in dir_block:
 
1868
                child_basis_kind = child_entry[1][1][0]
 
1869
                if child_basis_kind not in 'ar':
 
1870
                    self._raise_invalid(old_path, file_id,
 
1871
                        "The file id was deleted but its children were "
 
1872
                        "not deleted.")
1788
1873
 
1789
1874
    def _after_delta_check_parents(self, parents, index):
1790
1875
        """Check that parents required by the delta are all intact.
1799
1884
            # has the right file id.
1800
1885
            entry = self._get_entry(index, file_id, dirname_utf8)
1801
1886
            if entry[1] is None:
1802
 
                self._changes_aborted = True
1803
 
                raise errors.InconsistentDelta(dirname_utf8.decode('utf8'),
 
1887
                self._raise_invalid(dirname_utf8.decode('utf8'),
1804
1888
                    file_id, "This parent is not present.")
1805
1889
            # Parents of things must be directories
1806
1890
            if entry[1][index][0] != 'd':
1807
 
                self._changes_aborted = True
1808
 
                raise errors.InconsistentDelta(dirname_utf8.decode('utf8'),
 
1891
                self._raise_invalid(dirname_utf8.decode('utf8'),
1809
1892
                    file_id, "This parent is not a directory.")
1810
1893
 
1811
1894
    def _observed_sha1(self, entry, sha1, stat_value,
2037
2120
            entry_index += 1
2038
2121
        return block_index, entry_index, True, False
2039
2122
 
2040
 
    def _get_entry(self, tree_index, fileid_utf8=None, path_utf8=None, include_deleted=False):
 
2123
    def _get_entry(self, tree_index, fileid_utf8=None, path_utf8=None,
 
2124
                   include_deleted=False):
2041
2125
        """Get the dirstate entry for path in tree tree_index.
2042
2126
 
2043
2127
        If either file_id or path is supplied, it is used as the key to lookup.
2242
2326
 
2243
2327
    def _get_id_index(self):
2244
2328
        """Get an id index of self._dirblocks.
2245
 
        
 
2329
 
2246
2330
        This maps from file_id => [(directory, name, file_id)] entries where
2247
2331
        that file_id appears in one of the trees.
2248
2332
        """
2262
2346
        # such, we use a simple tuple, and do our own uniqueness checks. While
2263
2347
        # the 'in' check is O(N) since N is nicely bounded it shouldn't ever
2264
2348
        # cause quadratic failure.
2265
 
        # TODO: This should use StaticTuple
2266
2349
        file_id = entry_key[2]
2267
2350
        entry_key = static_tuple.StaticTuple.from_sequence(entry_key)
2268
2351
        if file_id not in id_index:
2943
3026
                            # This entry has the same path (but a different id) as
2944
3027
                            # the new entry we're adding, and is present in ths
2945
3028
                            # tree.
2946
 
                            raise errors.InconsistentDelta(
 
3029
                            self._raise_invalid(
2947
3030
                                ("%s/%s" % key[0:2]).decode('utf8'), key[2],
2948
3031
                                "Attempt to add item at path already occupied by "
2949
3032
                                "id %r" % entry[0][2])
3707
3790
            raise AssertionError("don't know how to compare "
3708
3791
                "source_minikind=%r, target_minikind=%r"
3709
3792
                % (source_minikind, target_minikind))
3710
 
            ## import pdb;pdb.set_trace()
3711
3793
        return None, None
3712
3794
 
3713
3795
    def __iter__(self):