2051
1743
if not all_versioned:
2052
1744
raise errors.PathsNotVersionedError(specific_files)
2053
1745
# -- remove redundancy in supplied specific_files to prevent over-scanning --
1746
search_specific_files = set()
2054
1747
for path in specific_files:
2055
1748
other_specific_files = specific_files.difference(set([path]))
2056
1749
if not osutils.is_inside_any(other_specific_files, path):
2057
1750
# this is a top level path, we must check it.
2058
1751
search_specific_files.add(path)
1753
# compare source_index and target_index at or under each element of search_specific_files.
1754
# follow the following comparison table. Note that we only want to do diff operations when
1755
# the target is fdl because thats when the walkdirs logic will have exposed the pathinfo
1759
# Source | Target | disk | action
1760
# r | fdlt | | add source to search, add id path move and perform
1761
# | | | diff check on source-target
1762
# r | fdlt | a | dangling file that was present in the basis.
1764
# r | a | | add source to search
1766
# r | r | | this path is present in a non-examined tree, skip.
1767
# r | r | a | this path is present in a non-examined tree, skip.
1768
# a | fdlt | | add new id
1769
# a | fdlt | a | dangling locally added file, skip
1770
# a | a | | not present in either tree, skip
1771
# a | a | a | not present in any tree, skip
1772
# a | r | | not present in either tree at this path, skip as it
1773
# | | | may not be selected by the users list of paths.
1774
# a | r | a | not present in either tree at this path, skip as it
1775
# | | | may not be selected by the users list of paths.
1776
# fdlt | fdlt | | content in both: diff them
1777
# fdlt | fdlt | a | deleted locally, but not unversioned - show as deleted ?
1778
# fdlt | a | | unversioned: output deleted id for now
1779
# fdlt | a | a | unversioned and deleted: output deleted id
1780
# fdlt | r | | relocated in this tree, so add target to search.
1781
# | | | Dont diff, we will see an r,fd; pair when we reach
1782
# | | | this id at the other path.
1783
# fdlt | r | a | relocated in this tree, so add target to search.
1784
# | | | Dont diff, we will see an r,fd; pair when we reach
1785
# | | | this id at the other path.
1787
# for all search_indexs in each path at or under each element of
1788
# search_specific_files, if the detail is relocated: add the id, and add the
1789
# relocated path as one to search if its not searched already. If the
1790
# detail is not relocated, add the id.
1791
searched_specific_files = set()
1792
NULL_PARENT_DETAILS = dirstate.DirState.NULL_PARENT_DETAILS
1793
# Using a list so that we can access the values and change them in
1794
# nested scope. Each one is [path, file_id, entry]
1795
last_source_parent = [None, None, None]
1796
last_target_parent = [None, None, None]
2060
1798
use_filesystem_for_exec = (sys.platform != 'win32')
2061
iter_changes = self.target._iter_changes(include_unchanged,
2062
use_filesystem_for_exec, search_specific_files, state,
2063
source_index, target_index, want_unversioned, self.target)
2064
return iter_changes.iter_changes()
1800
def _process_entry(entry, path_info):
1801
"""Compare an entry and real disk to generate delta information.
1803
:param path_info: top_relpath, basename, kind, lstat, abspath for
1804
the path of entry. If None, then the path is considered absent.
1805
(Perhaps we should pass in a concrete entry for this ?)
1806
Basename is returned as a utf8 string because we expect this
1807
tuple will be ignored, and don't want to take the time to
1810
if source_index is None:
1811
source_details = NULL_PARENT_DETAILS
1813
source_details = entry[1][source_index]
1814
target_details = entry[1][target_index]
1815
target_minikind = target_details[0]
1816
if path_info is not None and target_minikind in 'fdlt':
1817
assert target_index == 0
1818
link_or_sha1 = state.update_entry(entry, abspath=path_info[4],
1819
stat_value=path_info[3])
1820
# The entry may have been modified by update_entry
1821
target_details = entry[1][target_index]
1822
target_minikind = target_details[0]
1825
source_minikind = source_details[0]
1826
if source_minikind in 'fdltr' and target_minikind in 'fdlt':
1827
# claimed content in both: diff
1828
# r | fdlt | | add source to search, add id path move and perform
1829
# | | | diff check on source-target
1830
# r | fdlt | a | dangling file that was present in the basis.
1832
if source_minikind in 'r':
1833
# add the source to the search path to find any children it
1834
# has. TODO ? : only add if it is a container ?
1835
if not osutils.is_inside_any(searched_specific_files,
1837
search_specific_files.add(source_details[1])
1838
# generate the old path; this is needed for stating later
1840
old_path = source_details[1]
1841
old_dirname, old_basename = os.path.split(old_path)
1842
path = pathjoin(entry[0][0], entry[0][1])
1843
old_entry = state._get_entry(source_index,
1845
# update the source details variable to be the real
1847
source_details = old_entry[1][source_index]
1848
source_minikind = source_details[0]
1850
old_dirname = entry[0][0]
1851
old_basename = entry[0][1]
1852
old_path = path = pathjoin(old_dirname, old_basename)
1853
if path_info is None:
1854
# the file is missing on disk, show as removed.
1855
content_change = True
1859
# source and target are both versioned and disk file is present.
1860
target_kind = path_info[2]
1861
if target_kind == 'directory':
1862
if source_minikind != 'd':
1863
content_change = True
1865
# directories have no fingerprint
1866
content_change = False
1868
elif target_kind == 'file':
1869
if source_minikind != 'f':
1870
content_change = True
1872
# We could check the size, but we already have the
1874
content_change = (link_or_sha1 != source_details[1])
1875
# Target details is updated at update_entry time
1876
if use_filesystem_for_exec:
1877
# We don't need S_ISREG here, because we are sure
1878
# we are dealing with a file.
1879
target_exec = bool(stat.S_IEXEC & path_info[3].st_mode)
1881
target_exec = target_details[3]
1882
elif target_kind == 'symlink':
1883
if source_minikind != 'l':
1884
content_change = True
1886
content_change = (link_or_sha1 != source_details[1])
1888
elif target_kind == 'tree-reference':
1889
if source_minikind != 't':
1890
content_change = True
1892
content_change = False
1895
raise Exception, "unknown kind %s" % path_info[2]
1896
# parent id is the entry for the path in the target tree
1897
if old_dirname == last_source_parent[0]:
1898
source_parent_id = last_source_parent[1]
1900
source_parent_entry = state._get_entry(source_index,
1901
path_utf8=old_dirname)
1902
source_parent_id = source_parent_entry[0][2]
1903
if source_parent_id == entry[0][2]:
1904
# This is the root, so the parent is None
1905
source_parent_id = None
1907
last_source_parent[0] = old_dirname
1908
last_source_parent[1] = source_parent_id
1909
last_source_parent[2] = source_parent_entry
1910
new_dirname = entry[0][0]
1911
if new_dirname == last_target_parent[0]:
1912
target_parent_id = last_target_parent[1]
1914
# TODO: We don't always need to do the lookup, because the
1915
# parent entry will be the same as the source entry.
1916
target_parent_entry = state._get_entry(target_index,
1917
path_utf8=new_dirname)
1918
target_parent_id = target_parent_entry[0][2]
1919
if target_parent_id == entry[0][2]:
1920
# This is the root, so the parent is None
1921
target_parent_id = None
1923
last_target_parent[0] = new_dirname
1924
last_target_parent[1] = target_parent_id
1925
last_target_parent[2] = target_parent_entry
1927
source_exec = source_details[3]
1928
return ((entry[0][2], (old_path, path), content_change,
1930
(source_parent_id, target_parent_id),
1931
(old_basename, entry[0][1]),
1932
(_minikind_to_kind[source_minikind], target_kind),
1933
(source_exec, target_exec)),)
1934
elif source_minikind in 'a' and target_minikind in 'fdlt':
1935
# looks like a new file
1936
if path_info is not None:
1937
path = pathjoin(entry[0][0], entry[0][1])
1938
# parent id is the entry for the path in the target tree
1939
# TODO: these are the same for an entire directory: cache em.
1940
parent_id = state._get_entry(target_index,
1941
path_utf8=entry[0][0])[0][2]
1942
if parent_id == entry[0][2]:
1944
if use_filesystem_for_exec:
1945
# We need S_ISREG here, because we aren't sure if this
1948
stat.S_ISREG(path_info[3].st_mode)
1949
and stat.S_IEXEC & path_info[3].st_mode)
1951
target_exec = target_details[3]
1952
return ((entry[0][2], (None, path), True,
1955
(None, entry[0][1]),
1956
(None, path_info[2]),
1957
(None, target_exec)),)
1959
# but its not on disk: we deliberately treat this as just
1960
# never-present. (Why ?! - RBC 20070224)
1962
elif source_minikind in 'fdlt' and target_minikind in 'a':
1963
# unversioned, possibly, or possibly not deleted: we dont care.
1964
# if its still on disk, *and* theres no other entry at this
1965
# path [we dont know this in this routine at the moment -
1966
# perhaps we should change this - then it would be an unknown.
1967
old_path = pathjoin(entry[0][0], entry[0][1])
1968
# parent id is the entry for the path in the target tree
1969
parent_id = state._get_entry(source_index, path_utf8=entry[0][0])[0][2]
1970
if parent_id == entry[0][2]:
1972
return ((entry[0][2], (old_path, None), True,
1975
(entry[0][1], None),
1976
(_minikind_to_kind[source_minikind], None),
1977
(source_details[3], None)),)
1978
elif source_minikind in 'fdlt' and target_minikind in 'r':
1979
# a rename; could be a true rename, or a rename inherited from
1980
# a renamed parent. TODO: handle this efficiently. Its not
1981
# common case to rename dirs though, so a correct but slow
1982
# implementation will do.
1983
if not osutils.is_inside_any(searched_specific_files, target_details[1]):
1984
search_specific_files.add(target_details[1])
1985
elif source_minikind in 'ra' and target_minikind in 'ra':
1986
# neither of the selected trees contain this file,
1987
# so skip over it. This is not currently directly tested, but
1988
# is indirectly via test_too_much.TestCommands.test_conflicts.
1991
raise AssertionError("don't know how to compare "
1992
"source_minikind=%r, target_minikind=%r"
1993
% (source_minikind, target_minikind))
1994
## import pdb;pdb.set_trace()
1997
while search_specific_files:
1998
# TODO: the pending list should be lexically sorted? the
1999
# interface doesn't require it.
2000
current_root = search_specific_files.pop()
2001
current_root_unicode = current_root.decode('utf8')
2002
searched_specific_files.add(current_root)
2003
# process the entries for this containing directory: the rest will be
2004
# found by their parents recursively.
2005
root_entries = _entries_for_path(current_root)
2006
root_abspath = self.target.abspath(current_root_unicode)
2008
root_stat = os.lstat(root_abspath)
2010
if e.errno == errno.ENOENT:
2011
# the path does not exist: let _process_entry know that.
2012
root_dir_info = None
2014
# some other random error: hand it up.
2017
root_dir_info = ('', current_root,
2018
osutils.file_kind_from_stat_mode(root_stat.st_mode), root_stat,
2020
if root_dir_info[2] == 'directory':
2021
if self.target._directory_is_tree_reference(
2022
current_root.decode('utf8')):
2023
root_dir_info = root_dir_info[:2] + \
2024
('tree-reference',) + root_dir_info[3:]
2026
if not root_entries and not root_dir_info:
2027
# this specified path is not present at all, skip it.
2029
path_handled = False
2030
for entry in root_entries:
2031
for result in _process_entry(entry, root_dir_info):
2032
# this check should probably be outside the loop: one
2033
# 'iterate two trees' api, and then _iter_changes filters
2034
# unchanged pairs. - RBC 20070226
2036
if (include_unchanged
2037
or result[2] # content change
2038
or result[3][0] != result[3][1] # versioned status
2039
or result[4][0] != result[4][1] # parent id
2040
or result[5][0] != result[5][1] # name
2041
or result[6][0] != result[6][1] # kind
2042
or result[7][0] != result[7][1] # executable
2045
(utf8_decode_or_none(result[1][0]),
2046
utf8_decode_or_none(result[1][1])),
2050
(utf8_decode_or_none(result[5][0]),
2051
utf8_decode_or_none(result[5][1])),
2055
if want_unversioned and not path_handled and root_dir_info:
2056
new_executable = bool(
2057
stat.S_ISREG(root_dir_info[3].st_mode)
2058
and stat.S_IEXEC & root_dir_info[3].st_mode)
2060
(None, current_root_unicode),
2064
(None, splitpath(current_root_unicode)[-1]),
2065
(None, root_dir_info[2]),
2066
(None, new_executable)
2068
initial_key = (current_root, '', '')
2069
block_index, _ = state._find_block_index_from_key(initial_key)
2070
if block_index == 0:
2071
# we have processed the total root already, but because the
2072
# initial key matched it we should skip it here.
2074
if root_dir_info and root_dir_info[2] == 'tree-reference':
2075
current_dir_info = None
2077
dir_iterator = osutils._walkdirs_utf8(root_abspath, prefix=current_root)
2079
current_dir_info = dir_iterator.next()
2081
# on win32, python2.4 has e.errno == ERROR_DIRECTORY, but
2082
# python 2.5 has e.errno == EINVAL,
2083
# and e.winerror == ERROR_DIRECTORY
2084
e_winerror = getattr(e, 'winerror', None)
2085
# there may be directories in the inventory even though
2086
# this path is not a file on disk: so mark it as end of
2088
if e.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
2089
current_dir_info = None
2090
elif (sys.platform == 'win32'
2091
and ERROR_DIRECTORY in (e.errno, e_winerror)):
2092
current_dir_info = None
2096
if current_dir_info[0][0] == '':
2097
# remove .bzr from iteration
2098
bzr_index = bisect_left(current_dir_info[1], ('.bzr',))
2099
assert current_dir_info[1][bzr_index][0] == '.bzr'
2100
del current_dir_info[1][bzr_index]
2101
# walk until both the directory listing and the versioned metadata
2103
if (block_index < len(state._dirblocks) and
2104
osutils.is_inside(current_root, state._dirblocks[block_index][0])):
2105
current_block = state._dirblocks[block_index]
2107
current_block = None
2108
while (current_dir_info is not None or
2109
current_block is not None):
2110
if (current_dir_info and current_block
2111
and current_dir_info[0][0] != current_block[0]):
2112
if current_dir_info[0][0] < current_block[0] :
2113
# filesystem data refers to paths not covered by the dirblock.
2114
# this has two possibilities:
2115
# A) it is versioned but empty, so there is no block for it
2116
# B) it is not versioned.
2117
# in either case it was processed by the containing directories walk:
2118
# if it is root/foo, when we walked root we emitted it,
2119
# or if we ere given root/foo to walk specifically, we
2120
# emitted it when checking the walk-root entries
2121
# advance the iterator and loop - we dont need to emit it.
2123
current_dir_info = dir_iterator.next()
2124
except StopIteration:
2125
current_dir_info = None
2127
# We have a dirblock entry for this location, but there
2128
# is no filesystem path for this. This is most likely
2129
# because a directory was removed from the disk.
2130
# We don't have to report the missing directory,
2131
# because that should have already been handled, but we
2132
# need to handle all of the files that are contained
2134
for current_entry in current_block[1]:
2135
# entry referring to file not present on disk.
2136
# advance the entry only, after processing.
2137
for result in _process_entry(current_entry, None):
2138
# this check should probably be outside the loop: one
2139
# 'iterate two trees' api, and then _iter_changes filters
2140
# unchanged pairs. - RBC 20070226
2141
if (include_unchanged
2142
or result[2] # content change
2143
or result[3][0] != result[3][1] # versioned status
2144
or result[4][0] != result[4][1] # parent id
2145
or result[5][0] != result[5][1] # name
2146
or result[6][0] != result[6][1] # kind
2147
or result[7][0] != result[7][1] # executable
2150
(utf8_decode_or_none(result[1][0]),
2151
utf8_decode_or_none(result[1][1])),
2155
(utf8_decode_or_none(result[5][0]),
2156
utf8_decode_or_none(result[5][1])),
2161
if (block_index < len(state._dirblocks) and
2162
osutils.is_inside(current_root,
2163
state._dirblocks[block_index][0])):
2164
current_block = state._dirblocks[block_index]
2166
current_block = None
2169
if current_block and entry_index < len(current_block[1]):
2170
current_entry = current_block[1][entry_index]
2172
current_entry = None
2173
advance_entry = True
2175
if current_dir_info and path_index < len(current_dir_info[1]):
2176
current_path_info = current_dir_info[1][path_index]
2177
if current_path_info[2] == 'directory':
2178
if self.target._directory_is_tree_reference(
2179
current_path_info[0].decode('utf8')):
2180
current_path_info = current_path_info[:2] + \
2181
('tree-reference',) + current_path_info[3:]
2183
current_path_info = None
2185
path_handled = False
2186
while (current_entry is not None or
2187
current_path_info is not None):
2188
if current_entry is None:
2189
# the check for path_handled when the path is adnvaced
2190
# will yield this path if needed.
2192
elif current_path_info is None:
2193
# no path is fine: the per entry code will handle it.
2194
for result in _process_entry(current_entry, current_path_info):
2195
# this check should probably be outside the loop: one
2196
# 'iterate two trees' api, and then _iter_changes filters
2197
# unchanged pairs. - RBC 20070226
2198
if (include_unchanged
2199
or result[2] # content change
2200
or result[3][0] != result[3][1] # versioned status
2201
or result[4][0] != result[4][1] # parent id
2202
or result[5][0] != result[5][1] # name
2203
or result[6][0] != result[6][1] # kind
2204
or result[7][0] != result[7][1] # executable
2207
(utf8_decode_or_none(result[1][0]),
2208
utf8_decode_or_none(result[1][1])),
2212
(utf8_decode_or_none(result[5][0]),
2213
utf8_decode_or_none(result[5][1])),
2217
elif current_entry[0][1] != current_path_info[1]:
2218
if current_path_info[1] < current_entry[0][1]:
2219
# extra file on disk: pass for now, but only
2220
# increment the path, not the entry
2221
advance_entry = False
2223
# entry referring to file not present on disk.
2224
# advance the entry only, after processing.
2225
for result in _process_entry(current_entry, None):
2226
# this check should probably be outside the loop: one
2227
# 'iterate two trees' api, and then _iter_changes filters
2228
# unchanged pairs. - RBC 20070226
2230
if (include_unchanged
2231
or result[2] # content change
2232
or result[3][0] != result[3][1] # versioned status
2233
or result[4][0] != result[4][1] # parent id
2234
or result[5][0] != result[5][1] # name
2235
or result[6][0] != result[6][1] # kind
2236
or result[7][0] != result[7][1] # executable
2239
(utf8_decode_or_none(result[1][0]),
2240
utf8_decode_or_none(result[1][1])),
2244
(utf8_decode_or_none(result[5][0]),
2245
utf8_decode_or_none(result[5][1])),
2249
advance_path = False
2251
for result in _process_entry(current_entry, current_path_info):
2252
# this check should probably be outside the loop: one
2253
# 'iterate two trees' api, and then _iter_changes filters
2254
# unchanged pairs. - RBC 20070226
2256
if (include_unchanged
2257
or result[2] # content change
2258
or result[3][0] != result[3][1] # versioned status
2259
or result[4][0] != result[4][1] # parent id
2260
or result[5][0] != result[5][1] # name
2261
or result[6][0] != result[6][1] # kind
2262
or result[7][0] != result[7][1] # executable
2265
(utf8_decode_or_none(result[1][0]),
2266
utf8_decode_or_none(result[1][1])),
2270
(utf8_decode_or_none(result[5][0]),
2271
utf8_decode_or_none(result[5][1])),
2275
if advance_entry and current_entry is not None:
2277
if entry_index < len(current_block[1]):
2278
current_entry = current_block[1][entry_index]
2280
current_entry = None
2282
advance_entry = True # reset the advance flaga
2283
if advance_path and current_path_info is not None:
2284
if not path_handled:
2285
# unversioned in all regards
2286
if want_unversioned:
2287
new_executable = bool(
2288
stat.S_ISREG(current_path_info[3].st_mode)
2289
and stat.S_IEXEC & current_path_info[3].st_mode)
2290
if want_unversioned:
2292
(None, utf8_decode_or_none(current_path_info[0])),
2296
(None, utf8_decode_or_none(current_path_info[1])),
2297
(None, current_path_info[2]),
2298
(None, new_executable))
2299
# dont descend into this unversioned path if it is
2301
if current_path_info[2] in ('directory'):
2302
del current_dir_info[1][path_index]
2304
# dont descend the disk iterator into any tree
2306
if current_path_info[2] == 'tree-reference':
2307
del current_dir_info[1][path_index]
2310
if path_index < len(current_dir_info[1]):
2311
current_path_info = current_dir_info[1][path_index]
2312
if current_path_info[2] == 'directory':
2313
if self.target._directory_is_tree_reference(
2314
current_path_info[0].decode('utf8')):
2315
current_path_info = current_path_info[:2] + \
2316
('tree-reference',) + current_path_info[3:]
2318
current_path_info = None
2319
path_handled = False
2321
advance_path = True # reset the advance flagg.
2322
if current_block is not None:
2324
if (block_index < len(state._dirblocks) and
2325
osutils.is_inside(current_root, state._dirblocks[block_index][0])):
2326
current_block = state._dirblocks[block_index]
2328
current_block = None
2329
if current_dir_info is not None:
2331
current_dir_info = dir_iterator.next()
2332
except StopIteration:
2333
current_dir_info = None
2067
2337
def is_compatible(source, target):
2068
2338
# the target must be a dirstate working tree
2069
if not isinstance(target, DirStateWorkingTree):
2339
if not isinstance(target, WorkingTree4):
2071
# the source must be a revtree or dirstate rev tree.
2341
# the source must be a revtreee or dirstate rev tree.
2072
2342
if not isinstance(source,
2073
2343
(revisiontree.RevisionTree, DirStateRevisionTree)):
2075
2345
# the source revid must be in the target dirstate
2076
2346
if not (source._revision_id == NULL_REVISION or
2077
2347
source._revision_id in target.get_parent_ids()):
2078
# TODO: what about ghosts? it may well need to
2348
# TODO: what about ghosts? it may well need to
2079
2349
# check for them explicitly.