~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/workingtree_4.py

merge some of dirstate, update comparison tests to keep tree roots the same unless they're meant to differ

Show diffs side-by-side

added added

removed removed

Lines of Context:
718
718
 
719
719
    def paths2ids(self, paths, trees=[], require_versioned=True):
720
720
        """See Tree.paths2ids().
721
 
        
 
721
 
722
722
        This specialisation fast-paths the case where all the trees are in the
723
723
        dirstate.
724
724
        """
1433
1433
        target.branch.repository.fetch(source.branch.repository, revid)
1434
1434
        target.set_parent_ids([revid])
1435
1435
        return target.basis_tree(), target
 
1436
 
1436
1437
    _matching_from_tree_format = WorkingTreeFormat4()
1437
1438
    _matching_to_tree_format = WorkingTreeFormat4()
1438
1439
    _test_mutable_trees_to_test_trees = make_source_parent_tree
1456
1457
            versioned in one of source, target, extra_trees or
1457
1458
            PathsNotVersionedError is raised.
1458
1459
        """
 
1460
        utf8_decode = cache_utf8._utf8_decode
 
1461
        _minikind_to_kind = dirstate.DirState._minikind_to_kind
1459
1462
        # NB: show_status depends on being able to pass in non-versioned files
1460
1463
        # and report them as unknown
1461
1464
        # TODO: handle extra trees in the dirstate.
1586
1589
        # detail is not relocated, add the id.
1587
1590
        searched_specific_files = set()
1588
1591
        NULL_PARENT_DETAILS = dirstate.DirState.NULL_PARENT_DETAILS
 
1592
        # Using a list so that we can access the values and change them in
 
1593
        # nested scope. Each one is [path, file_id, entry]
 
1594
        last_source_parent = [None, None, None]
 
1595
        last_target_parent = [None, None, None]
 
1596
 
1589
1597
        def _process_entry(entry, path_info):
1590
1598
            """Compare an entry and real disk to generate delta information.
1591
1599
 
1599
1607
            else:
1600
1608
                source_details = entry[1][source_index]
1601
1609
            target_details = entry[1][target_index]
1602
 
            if source_details[0] in 'rfdl' and target_details[0] in 'fdl':
 
1610
            source_minikind = source_details[0]
 
1611
            target_minikind = target_details[0]
 
1612
            if source_minikind in 'fdlr' and target_minikind in 'fdl':
1603
1613
                # claimed content in both: diff
1604
1614
                #   r    | fdl    |      | add source to search, add id path move and perform
1605
1615
                #        |        |      | diff check on source-target
1606
 
                #   r    | fdl    |  a   | dangling file that was present in the basis. 
 
1616
                #   r    | fdl    |  a   | dangling file that was present in the basis.
1607
1617
                #        |        |      | ???
1608
 
                if source_details[0] in 'r':
 
1618
                if source_minikind in 'r':
1609
1619
                    # add the source to the search path to find any children it
1610
1620
                    # has.  TODO ? : only add if it is a container ?
1611
 
                    if not osutils.is_inside_any(searched_specific_files, source_details[1]):
 
1621
                    if not osutils.is_inside_any(searched_specific_files,
 
1622
                                                 source_details[1]):
1612
1623
                        search_specific_files.add(source_details[1])
1613
1624
                    # generate the old path; this is needed for stating later
1614
1625
                    # as well.
1615
1626
                    old_path = source_details[1]
1616
1627
                    old_dirname, old_basename = os.path.split(old_path)
1617
 
                    path = os.path.join(*entry[0][0:2])
1618
 
                    old_entry = state._get_entry(source_index, path_utf8=old_path)
 
1628
                    path = os.path.join(entry[0][0], entry[0][1])
 
1629
                    old_entry = state._get_entry(source_index,
 
1630
                                                 path_utf8=old_path)
1619
1631
                    # update the source details variable to be the real
1620
1632
                    # location.
1621
1633
                    source_details = old_entry[1][source_index]
 
1634
                    source_minikind = source_details[0]
1622
1635
                else:
1623
 
                    old_path = path = os.path.join(*entry[0][0:2])
1624
 
                    old_dirname, old_basename = entry[0][0:2]
 
1636
                    old_dirname = entry[0][0]
 
1637
                    old_basename = entry[0][1]
 
1638
                    old_path = path = os.path.join(old_dirname, old_basename)
1625
1639
                if path_info is None:
1626
1640
                    # the file is missing on disk, show as removed.
1627
 
                    old_path = os.path.join(*entry[0][0:2])
 
1641
                    old_path = os.path.join(entry[0][0], entry[0][1])
1628
1642
                    content_change = True
1629
1643
                    target_kind = None
1630
1644
                    target_exec = False
1631
1645
                else:
1632
1646
                    # source and target are both versioned and disk file is present.
1633
1647
                    target_kind = path_info[2]
1634
 
                    if path_info[2][0] == 'd':
1635
 
                        if source_details[0][0] != 'd':
 
1648
                    if target_kind == 'directory':
 
1649
                        if source_minikind != 'd':
1636
1650
                            content_change = True
1637
1651
                        else:
1638
1652
                            # directories have no fingerprint
1639
1653
                            content_change = False
1640
1654
                        target_exec = False
1641
 
                    elif path_info[2][0] == 'f':
1642
 
                        if source_details[0][0] != 'f':
 
1655
                    elif target_kind == 'file':
 
1656
                        if source_minikind != 'f':
1643
1657
                            content_change = True
1644
1658
                        else:
1645
1659
                            # has it changed? fast path: size, slow path: sha1.
1647
1661
                                content_change = True
1648
1662
                            else:
1649
1663
                                # maybe the same. Get the hash
1650
 
                                new_hash = self.target._hashcache.get_sha1(path, path_info[3])
 
1664
                                new_hash = self.target._hashcache.get_sha1(
 
1665
                                                            path, path_info[3])
1651
1666
                                content_change = (new_hash != source_details[1])
1652
1667
                        target_exec = bool(
1653
1668
                            stat.S_ISREG(path_info[3].st_mode)
1654
1669
                            and stat.S_IEXEC & path_info[3].st_mode)
1655
 
                    elif path_info[2][0] == 's':
1656
 
                        if source_details[0][0] != 'l':
 
1670
                    elif target_kind == 'symlink':
 
1671
                        if source_minikind != 'l':
1657
1672
                            content_change = True
1658
1673
                        else:
1659
 
                            # TODO: check symlink supported for windows users and grab
1660
 
                            # from target state here.
1661
 
                            content_change = os.readlink(path_info[4]) != source_details[1]
 
1674
                            # TODO: check symlink supported for windows users
 
1675
                            # and grab from target state here.
 
1676
                            link_target = os.readlink(path_info[4])
 
1677
                            content_change = (link_target != source_details[1])
1662
1678
                        target_exec = False
1663
1679
                    else:
1664
1680
                        raise Exception, "unknown kind %s" % path_info[2]
1665
1681
                # parent id is the entry for the path in the target tree
1666
 
                # TODO: the target is the same for an entire directory: cache em.
1667
 
                source_parent_id = state._get_entry(source_index, path_utf8=old_dirname)[0][2]
1668
 
                if source_parent_id == entry[0][2]:
1669
 
                    source_parent_id = None
1670
 
                target_parent_id = state._get_entry(target_index, path_utf8=entry[0][0])[0][2]
1671
 
                if target_parent_id == entry[0][2]:
1672
 
                    target_parent_id = None
 
1682
                if old_dirname == last_source_parent[0]:
 
1683
                    source_parent_id = last_source_parent[1]
 
1684
                else:
 
1685
                    source_parent_entry = state._get_entry(source_index,
 
1686
                                                           path_utf8=old_dirname)
 
1687
                    source_parent_id = source_parent_entry[0][2]
 
1688
                    if source_parent_id == entry[0][2]:
 
1689
                        # This is the root, so the parent is None
 
1690
                        source_parent_id = None
 
1691
                    else:
 
1692
                        last_source_parent[0] = old_dirname
 
1693
                        last_source_parent[1] = source_parent_id
 
1694
                        last_source_parent[2] = source_parent_entry
 
1695
 
 
1696
                new_dirname = entry[0][0]
 
1697
                if new_dirname == last_target_parent[0]:
 
1698
                    target_parent_id = last_target_parent[1]
 
1699
                else:
 
1700
                    # TODO: We don't always need to do the lookup, because the
 
1701
                    #       parent entry will be the same as the source entry.
 
1702
                    target_parent_entry = state._get_entry(target_index,
 
1703
                                                           path_utf8=new_dirname)
 
1704
                    target_parent_id = target_parent_entry[0][2]
 
1705
                    if target_parent_id == entry[0][2]:
 
1706
                        # This is the root, so the parent is None
 
1707
                        target_parent_id = None
 
1708
                    else:
 
1709
                        last_target_parent[0] = new_dirname
 
1710
                        last_target_parent[1] = target_parent_id
 
1711
                        last_target_parent[2] = target_parent_entry
 
1712
 
1673
1713
                source_exec = source_details[3]
1674
 
                path_unicode = path.decode('utf8')
 
1714
                path_unicode = utf8_decode(path)[0]
1675
1715
                return ((entry[0][2], path_unicode, content_change,
1676
1716
                        (True, True),
1677
1717
                        (source_parent_id, target_parent_id),
1678
1718
                        (old_basename, entry[0][1]),
1679
 
                        (dirstate.DirState._minikind_to_kind[source_details[0]], target_kind),
 
1719
                        (_minikind_to_kind[source_minikind], target_kind),
1680
1720
                        (source_exec, target_exec)),)
1681
 
            elif source_details[0] in 'a' and target_details[0] in 'fdl':
 
1721
            elif source_minikind in 'a' and target_minikind in 'fdl':
1682
1722
                # looks like a new file
1683
1723
                if path_info is not None:
1684
1724
                    path = os.path.join(*entry[0][0:2])
1691
1731
                    new_executable = bool(
1692
1732
                        stat.S_ISREG(path_info[3].st_mode)
1693
1733
                        and stat.S_IEXEC & path_info[3].st_mode)
1694
 
                    path_unicode = path.decode('utf8')
 
1734
                    path_unicode = utf8_decode(path)[0]
1695
1735
                    return ((entry[0][2], path_unicode, True,
1696
1736
                            (False, True),
1697
1737
                            (None, parent_id),
1702
1742
                    # but its not on disk: we deliberately treat this as just
1703
1743
                    # never-present. (Why ?! - RBC 20070224)
1704
1744
                    pass
1705
 
            elif source_details[0] in 'fdl' and target_details[0] in 'a':
 
1745
            elif source_minikind in 'fdl' and target_minikind in 'a':
1706
1746
                # unversioned, possibly, or possibly not deleted: we dont care.
1707
1747
                # if its still on disk, *and* theres no other entry at this
1708
1748
                # path [we dont know this in this routine at the moment -
1712
1752
                parent_id = state._get_entry(source_index, path_utf8=entry[0][0])[0][2]
1713
1753
                if parent_id == entry[0][2]:
1714
1754
                    parent_id = None
1715
 
                old_path_unicode = old_path.decode('utf8')
 
1755
                old_path_unicode = utf8_decode(old_path)[0]
1716
1756
                return ((entry[0][2], old_path_unicode, True,
1717
1757
                        (True, False),
1718
1758
                        (parent_id, None),
1719
1759
                        (entry[0][1], None),
1720
 
                        (dirstate.DirState._minikind_to_kind[source_details[0]], None),
 
1760
                        (_minikind_to_kind[source_minikind], None),
1721
1761
                        (source_details[3], None)),)
1722
 
            elif source_details[0] in 'fdl' and target_details[0] in 'r':
 
1762
            elif source_minikind in 'fdl' and target_minikind in 'r':
1723
1763
                # a rename; could be a true rename, or a rename inherited from
1724
1764
                # a renamed parent. TODO: handle this efficiently. Its not
1725
1765
                # common case to rename dirs though, so a correct but slow
1758
1798
                    # this check should probably be outside the loop: one
1759
1799
                    # 'iterate two trees' api, and then _iter_changes filters
1760
1800
                    # unchanged pairs. - RBC 20070226
1761
 
                    if include_unchanged or result[2] or True in map(lambda x:x[0]!=x[1], result[3:8]):
 
1801
                    if (include_unchanged
 
1802
                        or result[2]                    # content change
 
1803
                        or result[3][0] != result[3][1] # versioned status
 
1804
                        or result[4][0] != result[4][1] # parent id
 
1805
                        or result[5][0] != result[5][1] # name
 
1806
                        or result[6][0] != result[6][1] # kind
 
1807
                        or result[7][0] != result[7][1] # executable
 
1808
                        ):
1762
1809
                        yield result
1763
 
            dir_iterator = osutils.walkdirs(root_abspath, prefix=current_root)
 
1810
            dir_iterator = osutils._walkdirs_utf8(root_abspath, prefix=current_root)
1764
1811
            initial_key = (current_root, '', '')
1765
1812
            block_index, _ = state._find_block_index_from_key(initial_key)
1766
1813
            if block_index == 0:
1767
1814
                # we have processed the total root already, but because the
1768
 
                # initial key matched it we sould skip it here.
 
1815
                # initial key matched it we should skip it here.
1769
1816
                block_index +=1
1770
1817
            try:
1771
1818
                current_dir_info = dir_iterator.next()
1783
1830
                    bzr_index = bisect_left(current_dir_info[1], ('.bzr',))
1784
1831
                    assert current_dir_info[1][bzr_index][0] == '.bzr'
1785
1832
                    del current_dir_info[1][bzr_index]
1786
 
                # convert the unicode relpaths in the dir index to uf8 for
1787
 
                # comparison with dirstate data.
1788
 
                # TODO: keep the utf8 version around for giving to the caller.
1789
 
                current_dir_info = ((current_dir_info[0][0].encode('utf8'), current_dir_info[0][1]),
1790
 
                    [(line[0].encode('utf8'), line[1].encode('utf8')) + line[2:] for line in current_dir_info[1]])
1791
1833
            # walk until both the directory listing and the versioned metadata
1792
1834
            # are exhausted. TODO: reevaluate this, perhaps we should stop when
1793
1835
            # the versioned data runs out.
1797
1839
            else:
1798
1840
                current_block = None
1799
1841
            while (current_dir_info is not None or
1800
 
                current_block is not None):
1801
 
                if current_dir_info and current_block and current_dir_info[0][0] != current_block[0]:
1802
 
                    if current_block[0] < current_dir_info[0][0]:
1803
 
                        # extra dir on disk: pass for now? should del from info ?
1804
 
                        import pdb;pdb.set_trace()
1805
 
                        print 'unversioned dir'
1806
 
                    else:
1807
 
                        # directory data refers to paths not covered by the dirblock.
 
1842
                   current_block is not None):
 
1843
                if (current_dir_info and current_block
 
1844
                    and current_dir_info[0][0] != current_block[0]):
 
1845
                    if current_dir_info[0][0] < current_block[0] :
 
1846
                        # import pdb; pdb.set_trace()
 
1847
                        # print 'unversioned dir'
 
1848
                        # filesystem data refers to paths not covered by the dirblock.
1808
1849
                        # this has two possibilities:
1809
1850
                        # A) it is versioned but empty, so there is no block for it
1810
1851
                        # B) it is not versioned.
1811
1852
                        # in either case it was processed by the containing directories walk:
1812
1853
                        # if it is root/foo, when we walked root we emitted it,
1813
1854
                        # or if we ere given root/foo to walk specifically, we
1814
 
                        # emitted it when checking the walk-root entries 
 
1855
                        # emitted it when checking the walk-root entries
1815
1856
                        # advance the iterator and loop - we dont need to emit it.
1816
1857
                        try:
1817
1858
                            current_dir_info = dir_iterator.next()
1818
 
                            # convert the unicode relpaths in the dir index to uf8 for
1819
 
                            # comparison with dirstate data.
1820
 
                            # TODO: keep the utf8 version around for giving to the caller.
1821
 
                            current_dir_info = ((current_dir_info[0][0].encode('utf8'), current_dir_info[0][1]),
1822
 
                                [(line[0].encode('utf8'), line[1].encode('utf8')) + line[2:] for line in current_dir_info[1]])
1823
1859
                        except StopIteration:
1824
1860
                            current_dir_info = None
 
1861
                    else:
 
1862
                        # We have a dirblock entry for this location, but there
 
1863
                        # is no filesystem path for this. This is most likely
 
1864
                        # because a directory was removed from the disk.
 
1865
                        # We don't have to report the missing directory,
 
1866
                        # because that should have already been handled, but we
 
1867
                        # need to handle all of the files that are contained
 
1868
                        # within.
 
1869
                        for current_entry in current_block[1]:
 
1870
                            # entry referring to file not present on disk.
 
1871
                            # advance the entry only, after processing.
 
1872
                            for result in _process_entry(current_entry, None):
 
1873
                                # this check should probably be outside the loop: one
 
1874
                                # 'iterate two trees' api, and then _iter_changes filters
 
1875
                                # unchanged pairs. - RBC 20070226
 
1876
                                if (include_unchanged
 
1877
                                    or result[2]                    # content change
 
1878
                                    or result[3][0] != result[3][1] # versioned status
 
1879
                                    or result[4][0] != result[4][1] # parent id
 
1880
                                    or result[5][0] != result[5][1] # name
 
1881
                                    or result[6][0] != result[6][1] # kind
 
1882
                                    or result[7][0] != result[7][1] # executable
 
1883
                                    ):
 
1884
                                    yield result
 
1885
                        block_index +=1
 
1886
                        if (block_index < len(state._dirblocks) and
 
1887
                            osutils.is_inside(current_root,
 
1888
                                              state._dirblocks[block_index][0])):
 
1889
                            current_block = state._dirblocks[block_index]
 
1890
                        else:
 
1891
                            current_block = None
1825
1892
                    continue
1826
1893
                entry_index = 0
1827
1894
                if current_block and entry_index < len(current_block[1]):
1844
1911
                        new_executable = bool(
1845
1912
                            stat.S_ISREG(current_path_info[3].st_mode)
1846
1913
                            and stat.S_IEXEC & current_path_info[3].st_mode)
1847
 
                        yield (None, current_path_info[0], True, (False, False), (None, None), (None, current_path_info[1]), (None, current_path_info[2]), (None, new_executable))
 
1914
                        yield (None, current_path_info[0], True,
 
1915
                               (False, False),
 
1916
                               (None, None),
 
1917
                               (None, current_path_info[1]),
 
1918
                               (None, current_path_info[2]),
 
1919
                               (None, new_executable))
1848
1920
                    elif current_path_info is None:
1849
1921
                        # no path is fine: the per entry code will handle it.
1850
1922
                        for result in _process_entry(current_entry, current_path_info):
1851
1923
                            # this check should probably be outside the loop: one
1852
1924
                            # 'iterate two trees' api, and then _iter_changes filters
1853
1925
                            # unchanged pairs. - RBC 20070226
1854
 
                            if include_unchanged or result[2] or True in map(lambda x:x[0]!=x[1], result[3:8]):
 
1926
                            if (include_unchanged
 
1927
                                or result[2]                    # content change
 
1928
                                or result[3][0] != result[3][1] # versioned status
 
1929
                                or result[4][0] != result[4][1] # parent id
 
1930
                                or result[5][0] != result[5][1] # name
 
1931
                                or result[6][0] != result[6][1] # kind
 
1932
                                or result[7][0] != result[7][1] # executable
 
1933
                                ):
1855
1934
                                yield result
1856
1935
                    elif current_entry[0][1] != current_path_info[1]:
1857
1936
                        if current_path_info[1] < current_entry[0][1]:
1858
 
                            # extra file on disk: pass for now
1859
 
                            import pdb;pdb.set_trace()
1860
 
                            print 'unversioned file'
 
1937
                            # extra file on disk: pass for now, but only
 
1938
                            # increment the path, not the entry
 
1939
                            # import pdb; pdb.set_trace()
 
1940
                            # print 'unversioned file'
 
1941
                            advance_entry = False
1861
1942
                        else:
1862
1943
                            # entry referring to file not present on disk.
1863
1944
                            # advance the entry only, after processing.
1865
1946
                                # this check should probably be outside the loop: one
1866
1947
                                # 'iterate two trees' api, and then _iter_changes filters
1867
1948
                                # unchanged pairs. - RBC 20070226
1868
 
                                if include_unchanged or result[2] or True in map(lambda x:x[0]!=x[1], result[3:8]):
 
1949
                                if (include_unchanged
 
1950
                                    or result[2]                    # content change
 
1951
                                    or result[3][0] != result[3][1] # versioned status
 
1952
                                    or result[4][0] != result[4][1] # parent id
 
1953
                                    or result[5][0] != result[5][1] # name
 
1954
                                    or result[6][0] != result[6][1] # kind
 
1955
                                    or result[7][0] != result[7][1] # executable
 
1956
                                    ):
1869
1957
                                    yield result
1870
1958
                            advance_path = False
1871
1959
                    else:
1873
1961
                            # this check should probably be outside the loop: one
1874
1962
                            # 'iterate two trees' api, and then _iter_changes filters
1875
1963
                            # unchanged pairs. - RBC 20070226
1876
 
                            if include_unchanged or result[2] or True in map(lambda x:x[0]!=x[1], result[3:8]):
 
1964
                            if (include_unchanged
 
1965
                                or result[2]                    # content change
 
1966
                                or result[3][0] != result[3][1] # versioned status
 
1967
                                or result[4][0] != result[4][1] # parent id
 
1968
                                or result[5][0] != result[5][1] # name
 
1969
                                or result[6][0] != result[6][1] # kind
 
1970
                                or result[7][0] != result[7][1] # executable
 
1971
                                ):
1877
1972
                                yield result
1878
1973
                    if advance_entry and current_entry is not None:
1879
1974
                        entry_index += 1
1901
1996
                if current_dir_info is not None:
1902
1997
                    try:
1903
1998
                        current_dir_info = dir_iterator.next()
1904
 
                        # convert the unicode relpaths in the dir index to uf8 for
1905
 
                        # comparison with dirstate data.
1906
 
                        # TODO: keep the utf8 version around for giving to the caller.
1907
 
                        current_dir_info = ((current_dir_info[0][0].encode('utf8'), current_dir_info[0][1]),
1908
 
                            [(line[0].encode('utf8'), line[1].encode('utf8')) + line[2:] for line in current_dir_info[1]])
1909
1999
                    except StopIteration:
1910
2000
                        current_dir_info = None
1911
2001