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]
1589
1597
def _process_entry(entry, path_info):
1590
1598
"""Compare an entry and real disk to generate delta information.
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.
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,
1612
1623
search_specific_files.add(source_details[1])
1613
1624
# generate the old path; this is needed for stating later
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,
1619
1631
# update the source details variable to be the real
1621
1633
source_details = old_entry[1][source_index]
1634
source_minikind = source_details[0]
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
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
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
1645
1659
# has it changed? fast path: size, slow path: sha1.
1647
1661
content_change = True
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(
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
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
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]
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
1692
last_source_parent[0] = old_dirname
1693
last_source_parent[1] = source_parent_id
1694
last_source_parent[2] = source_parent_entry
1696
new_dirname = entry[0][0]
1697
if new_dirname == last_target_parent[0]:
1698
target_parent_id = last_target_parent[1]
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
1709
last_target_parent[0] = new_dirname
1710
last_target_parent[1] = target_parent_id
1711
last_target_parent[2] = target_parent_entry
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,
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])
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,
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
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
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.
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'
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.
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
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
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
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]
1891
current_block = None
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,
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
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
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
1870
1958
advance_path = False
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
1878
1973
if advance_entry and current_entry is not None:
1879
1974
entry_index += 1