1744
2051
if not all_versioned:
1745
2052
raise errors.PathsNotVersionedError(specific_files)
1746
2053
# -- remove redundancy in supplied specific_files to prevent over-scanning --
1747
search_specific_files = set()
1748
2054
for path in specific_files:
1749
2055
other_specific_files = specific_files.difference(set([path]))
1750
2056
if not osutils.is_inside_any(other_specific_files, path):
1751
2057
# this is a top level path, we must check it.
1752
2058
search_specific_files.add(path)
1754
# compare source_index and target_index at or under each element of search_specific_files.
1755
# follow the following comparison table. Note that we only want to do diff operations when
1756
# the target is fdl because thats when the walkdirs logic will have exposed the pathinfo
1760
# Source | Target | disk | action
1761
# r | fdlt | | add source to search, add id path move and perform
1762
# | | | diff check on source-target
1763
# r | fdlt | a | dangling file that was present in the basis.
1765
# r | a | | add source to search
1767
# r | r | | this path is present in a non-examined tree, skip.
1768
# r | r | a | this path is present in a non-examined tree, skip.
1769
# a | fdlt | | add new id
1770
# a | fdlt | a | dangling locally added file, skip
1771
# a | a | | not present in either tree, skip
1772
# a | a | a | not present in any tree, skip
1773
# a | r | | not present in either tree at this path, skip as it
1774
# | | | may not be selected by the users list of paths.
1775
# a | r | a | not present in either tree at this path, skip as it
1776
# | | | may not be selected by the users list of paths.
1777
# fdlt | fdlt | | content in both: diff them
1778
# fdlt | fdlt | a | deleted locally, but not unversioned - show as deleted ?
1779
# fdlt | a | | unversioned: output deleted id for now
1780
# fdlt | a | a | unversioned and deleted: output deleted id
1781
# fdlt | r | | relocated in this tree, so add target to search.
1782
# | | | Dont diff, we will see an r,fd; pair when we reach
1783
# | | | this id at the other path.
1784
# fdlt | r | a | relocated in this tree, so add target to search.
1785
# | | | Dont diff, we will see an r,fd; pair when we reach
1786
# | | | this id at the other path.
1788
# for all search_indexs in each path at or under each element of
1789
# search_specific_files, if the detail is relocated: add the id, and add the
1790
# relocated path as one to search if its not searched already. If the
1791
# detail is not relocated, add the id.
1792
searched_specific_files = set()
1793
NULL_PARENT_DETAILS = dirstate.DirState.NULL_PARENT_DETAILS
1794
# Using a list so that we can access the values and change them in
1795
# nested scope. Each one is [path, file_id, entry]
1796
last_source_parent = [None, None, None]
1797
last_target_parent = [None, None, None]
1799
2060
use_filesystem_for_exec = (sys.platform != 'win32')
1801
def _process_entry(entry, path_info):
1802
"""Compare an entry and real disk to generate delta information.
1804
:param path_info: top_relpath, basename, kind, lstat, abspath for
1805
the path of entry. If None, then the path is considered absent.
1806
(Perhaps we should pass in a concrete entry for this ?)
1807
Basename is returned as a utf8 string because we expect this
1808
tuple will be ignored, and don't want to take the time to
1811
if source_index is None:
1812
source_details = NULL_PARENT_DETAILS
1814
source_details = entry[1][source_index]
1815
target_details = entry[1][target_index]
1816
target_minikind = target_details[0]
1817
if path_info is not None and target_minikind in 'fdlt':
1818
assert target_index == 0
1819
link_or_sha1 = state.update_entry(entry, abspath=path_info[4],
1820
stat_value=path_info[3])
1821
# The entry may have been modified by update_entry
1822
target_details = entry[1][target_index]
1823
target_minikind = target_details[0]
1826
source_minikind = source_details[0]
1827
if source_minikind in 'fdltr' and target_minikind in 'fdlt':
1828
# claimed content in both: diff
1829
# r | fdlt | | add source to search, add id path move and perform
1830
# | | | diff check on source-target
1831
# r | fdlt | a | dangling file that was present in the basis.
1833
if source_minikind in 'r':
1834
# add the source to the search path to find any children it
1835
# has. TODO ? : only add if it is a container ?
1836
if not osutils.is_inside_any(searched_specific_files,
1838
search_specific_files.add(source_details[1])
1839
# generate the old path; this is needed for stating later
1841
old_path = source_details[1]
1842
old_dirname, old_basename = os.path.split(old_path)
1843
path = pathjoin(entry[0][0], entry[0][1])
1844
old_entry = state._get_entry(source_index,
1846
# update the source details variable to be the real
1848
source_details = old_entry[1][source_index]
1849
source_minikind = source_details[0]
1851
old_dirname = entry[0][0]
1852
old_basename = entry[0][1]
1853
old_path = path = pathjoin(old_dirname, old_basename)
1854
if path_info is None:
1855
# the file is missing on disk, show as removed.
1856
content_change = True
1860
# source and target are both versioned and disk file is present.
1861
target_kind = path_info[2]
1862
if target_kind == 'directory':
1863
if source_minikind != 'd':
1864
content_change = True
1866
# directories have no fingerprint
1867
content_change = False
1869
elif target_kind == 'file':
1870
if source_minikind != 'f':
1871
content_change = True
1873
# We could check the size, but we already have the
1875
content_change = (link_or_sha1 != source_details[1])
1876
# Target details is updated at update_entry time
1877
if use_filesystem_for_exec:
1878
# We don't need S_ISREG here, because we are sure
1879
# we are dealing with a file.
1880
target_exec = bool(stat.S_IEXEC & path_info[3].st_mode)
1882
target_exec = target_details[3]
1883
elif target_kind == 'symlink':
1884
if source_minikind != 'l':
1885
content_change = True
1887
content_change = (link_or_sha1 != source_details[1])
1889
elif target_kind == 'tree-reference':
1890
if source_minikind != 't':
1891
content_change = True
1893
content_change = False
1896
raise Exception, "unknown kind %s" % path_info[2]
1897
# parent id is the entry for the path in the target tree
1898
if old_dirname == last_source_parent[0]:
1899
source_parent_id = last_source_parent[1]
1901
source_parent_entry = state._get_entry(source_index,
1902
path_utf8=old_dirname)
1903
source_parent_id = source_parent_entry[0][2]
1904
if source_parent_id == entry[0][2]:
1905
# This is the root, so the parent is None
1906
source_parent_id = None
1908
last_source_parent[0] = old_dirname
1909
last_source_parent[1] = source_parent_id
1910
last_source_parent[2] = source_parent_entry
1911
new_dirname = entry[0][0]
1912
if new_dirname == last_target_parent[0]:
1913
target_parent_id = last_target_parent[1]
1915
# TODO: We don't always need to do the lookup, because the
1916
# parent entry will be the same as the source entry.
1917
target_parent_entry = state._get_entry(target_index,
1918
path_utf8=new_dirname)
1919
target_parent_id = target_parent_entry[0][2]
1920
if target_parent_id == entry[0][2]:
1921
# This is the root, so the parent is None
1922
target_parent_id = None
1924
last_target_parent[0] = new_dirname
1925
last_target_parent[1] = target_parent_id
1926
last_target_parent[2] = target_parent_entry
1928
source_exec = source_details[3]
1929
return ((entry[0][2], (old_path, path), content_change,
1931
(source_parent_id, target_parent_id),
1932
(old_basename, entry[0][1]),
1933
(_minikind_to_kind[source_minikind], target_kind),
1934
(source_exec, target_exec)),)
1935
elif source_minikind in 'a' and target_minikind in 'fdlt':
1936
# looks like a new file
1937
if path_info is not None:
1938
path = pathjoin(entry[0][0], entry[0][1])
1939
# parent id is the entry for the path in the target tree
1940
# TODO: these are the same for an entire directory: cache em.
1941
parent_id = state._get_entry(target_index,
1942
path_utf8=entry[0][0])[0][2]
1943
if parent_id == entry[0][2]:
1945
if use_filesystem_for_exec:
1946
# We need S_ISREG here, because we aren't sure if this
1949
stat.S_ISREG(path_info[3].st_mode)
1950
and stat.S_IEXEC & path_info[3].st_mode)
1952
target_exec = target_details[3]
1953
return ((entry[0][2], (None, path), True,
1956
(None, entry[0][1]),
1957
(None, path_info[2]),
1958
(None, target_exec)),)
1960
# but its not on disk: we deliberately treat this as just
1961
# never-present. (Why ?! - RBC 20070224)
1963
elif source_minikind in 'fdlt' and target_minikind in 'a':
1964
# unversioned, possibly, or possibly not deleted: we dont care.
1965
# if its still on disk, *and* theres no other entry at this
1966
# path [we dont know this in this routine at the moment -
1967
# perhaps we should change this - then it would be an unknown.
1968
old_path = pathjoin(entry[0][0], entry[0][1])
1969
# parent id is the entry for the path in the target tree
1970
parent_id = state._get_entry(source_index, path_utf8=entry[0][0])[0][2]
1971
if parent_id == entry[0][2]:
1973
return ((entry[0][2], (old_path, None), True,
1976
(entry[0][1], None),
1977
(_minikind_to_kind[source_minikind], None),
1978
(source_details[3], None)),)
1979
elif source_minikind in 'fdlt' and target_minikind in 'r':
1980
# a rename; could be a true rename, or a rename inherited from
1981
# a renamed parent. TODO: handle this efficiently. Its not
1982
# common case to rename dirs though, so a correct but slow
1983
# implementation will do.
1984
if not osutils.is_inside_any(searched_specific_files, target_details[1]):
1985
search_specific_files.add(target_details[1])
1986
elif source_minikind in 'ra' and target_minikind in 'ra':
1987
# neither of the selected trees contain this file,
1988
# so skip over it. This is not currently directly tested, but
1989
# is indirectly via test_too_much.TestCommands.test_conflicts.
1992
raise AssertionError("don't know how to compare "
1993
"source_minikind=%r, target_minikind=%r"
1994
% (source_minikind, target_minikind))
1995
## import pdb;pdb.set_trace()
1998
while search_specific_files:
1999
# TODO: the pending list should be lexically sorted? the
2000
# interface doesn't require it.
2001
current_root = search_specific_files.pop()
2002
current_root_unicode = current_root.decode('utf8')
2003
searched_specific_files.add(current_root)
2004
# process the entries for this containing directory: the rest will be
2005
# found by their parents recursively.
2006
root_entries = _entries_for_path(current_root)
2007
root_abspath = self.target.abspath(current_root_unicode)
2009
root_stat = os.lstat(root_abspath)
2011
if e.errno == errno.ENOENT:
2012
# the path does not exist: let _process_entry know that.
2013
root_dir_info = None
2015
# some other random error: hand it up.
2018
root_dir_info = ('', current_root,
2019
osutils.file_kind_from_stat_mode(root_stat.st_mode), root_stat,
2021
if root_dir_info[2] == 'directory':
2022
if self.target._directory_is_tree_reference(
2023
current_root.decode('utf8')):
2024
root_dir_info = root_dir_info[:2] + \
2025
('tree-reference',) + root_dir_info[3:]
2027
if not root_entries and not root_dir_info:
2028
# this specified path is not present at all, skip it.
2030
path_handled = False
2031
for entry in root_entries:
2032
for result in _process_entry(entry, root_dir_info):
2033
# this check should probably be outside the loop: one
2034
# 'iterate two trees' api, and then _iter_changes filters
2035
# unchanged pairs. - RBC 20070226
2037
if (include_unchanged
2038
or result[2] # content change
2039
or result[3][0] != result[3][1] # versioned status
2040
or result[4][0] != result[4][1] # parent id
2041
or result[5][0] != result[5][1] # name
2042
or result[6][0] != result[6][1] # kind
2043
or result[7][0] != result[7][1] # executable
2046
(utf8_decode_or_none(result[1][0]),
2047
utf8_decode_or_none(result[1][1])),
2051
(utf8_decode_or_none(result[5][0]),
2052
utf8_decode_or_none(result[5][1])),
2056
if want_unversioned and not path_handled and root_dir_info:
2057
new_executable = bool(
2058
stat.S_ISREG(root_dir_info[3].st_mode)
2059
and stat.S_IEXEC & root_dir_info[3].st_mode)
2061
(None, current_root_unicode),
2065
(None, splitpath(current_root_unicode)[-1]),
2066
(None, root_dir_info[2]),
2067
(None, new_executable)
2069
initial_key = (current_root, '', '')
2070
block_index, _ = state._find_block_index_from_key(initial_key)
2071
if block_index == 0:
2072
# we have processed the total root already, but because the
2073
# initial key matched it we should skip it here.
2075
if root_dir_info and root_dir_info[2] == 'tree-reference':
2076
current_dir_info = None
2078
dir_iterator = osutils._walkdirs_utf8(root_abspath, prefix=current_root)
2080
current_dir_info = dir_iterator.next()
2082
# on win32, python2.4 has e.errno == ERROR_DIRECTORY, but
2083
# python 2.5 has e.errno == EINVAL,
2084
# and e.winerror == ERROR_DIRECTORY
2085
e_winerror = getattr(e, 'winerror', None)
2086
win_errors = (ERROR_DIRECTORY, ERROR_PATH_NOT_FOUND)
2087
# there may be directories in the inventory even though
2088
# this path is not a file on disk: so mark it as end of
2090
if e.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
2091
current_dir_info = None
2092
elif (sys.platform == 'win32'
2093
and (e.errno in win_errors
2094
or e_winerror in win_errors)):
2095
current_dir_info = None
2099
if current_dir_info[0][0] == '':
2100
# remove .bzr from iteration
2101
bzr_index = bisect_left(current_dir_info[1], ('.bzr',))
2102
assert current_dir_info[1][bzr_index][0] == '.bzr'
2103
del current_dir_info[1][bzr_index]
2104
# walk until both the directory listing and the versioned metadata
2106
if (block_index < len(state._dirblocks) and
2107
osutils.is_inside(current_root, state._dirblocks[block_index][0])):
2108
current_block = state._dirblocks[block_index]
2110
current_block = None
2111
while (current_dir_info is not None or
2112
current_block is not None):
2113
if (current_dir_info and current_block
2114
and current_dir_info[0][0] != current_block[0]):
2115
if current_dir_info[0][0] < current_block[0] :
2116
# filesystem data refers to paths not covered by the dirblock.
2117
# this has two possibilities:
2118
# A) it is versioned but empty, so there is no block for it
2119
# B) it is not versioned.
2120
# in either case it was processed by the containing directories walk:
2121
# if it is root/foo, when we walked root we emitted it,
2122
# or if we ere given root/foo to walk specifically, we
2123
# emitted it when checking the walk-root entries
2124
# advance the iterator and loop - we dont need to emit it.
2126
current_dir_info = dir_iterator.next()
2127
except StopIteration:
2128
current_dir_info = None
2130
# We have a dirblock entry for this location, but there
2131
# is no filesystem path for this. This is most likely
2132
# because a directory was removed from the disk.
2133
# We don't have to report the missing directory,
2134
# because that should have already been handled, but we
2135
# need to handle all of the files that are contained
2137
for current_entry in current_block[1]:
2138
# entry referring to file not present on disk.
2139
# advance the entry only, after processing.
2140
for result in _process_entry(current_entry, None):
2141
# this check should probably be outside the loop: one
2142
# 'iterate two trees' api, and then _iter_changes filters
2143
# unchanged pairs. - RBC 20070226
2144
if (include_unchanged
2145
or result[2] # content change
2146
or result[3][0] != result[3][1] # versioned status
2147
or result[4][0] != result[4][1] # parent id
2148
or result[5][0] != result[5][1] # name
2149
or result[6][0] != result[6][1] # kind
2150
or result[7][0] != result[7][1] # executable
2153
(utf8_decode_or_none(result[1][0]),
2154
utf8_decode_or_none(result[1][1])),
2158
(utf8_decode_or_none(result[5][0]),
2159
utf8_decode_or_none(result[5][1])),
2164
if (block_index < len(state._dirblocks) and
2165
osutils.is_inside(current_root,
2166
state._dirblocks[block_index][0])):
2167
current_block = state._dirblocks[block_index]
2169
current_block = None
2172
if current_block and entry_index < len(current_block[1]):
2173
current_entry = current_block[1][entry_index]
2175
current_entry = None
2176
advance_entry = True
2178
if current_dir_info and path_index < len(current_dir_info[1]):
2179
current_path_info = current_dir_info[1][path_index]
2180
if current_path_info[2] == 'directory':
2181
if self.target._directory_is_tree_reference(
2182
current_path_info[0].decode('utf8')):
2183
current_path_info = current_path_info[:2] + \
2184
('tree-reference',) + current_path_info[3:]
2186
current_path_info = None
2188
path_handled = False
2189
while (current_entry is not None or
2190
current_path_info is not None):
2191
if current_entry is None:
2192
# the check for path_handled when the path is adnvaced
2193
# will yield this path if needed.
2195
elif current_path_info is None:
2196
# no path is fine: the per entry code will handle it.
2197
for result in _process_entry(current_entry, current_path_info):
2198
# this check should probably be outside the loop: one
2199
# 'iterate two trees' api, and then _iter_changes filters
2200
# unchanged pairs. - RBC 20070226
2201
if (include_unchanged
2202
or result[2] # content change
2203
or result[3][0] != result[3][1] # versioned status
2204
or result[4][0] != result[4][1] # parent id
2205
or result[5][0] != result[5][1] # name
2206
or result[6][0] != result[6][1] # kind
2207
or result[7][0] != result[7][1] # executable
2210
(utf8_decode_or_none(result[1][0]),
2211
utf8_decode_or_none(result[1][1])),
2215
(utf8_decode_or_none(result[5][0]),
2216
utf8_decode_or_none(result[5][1])),
2220
elif current_entry[0][1] != current_path_info[1]:
2221
if current_path_info[1] < current_entry[0][1]:
2222
# extra file on disk: pass for now, but only
2223
# increment the path, not the entry
2224
advance_entry = False
2226
# entry referring to file not present on disk.
2227
# advance the entry only, after processing.
2228
for result in _process_entry(current_entry, None):
2229
# this check should probably be outside the loop: one
2230
# 'iterate two trees' api, and then _iter_changes filters
2231
# unchanged pairs. - RBC 20070226
2233
if (include_unchanged
2234
or result[2] # content change
2235
or result[3][0] != result[3][1] # versioned status
2236
or result[4][0] != result[4][1] # parent id
2237
or result[5][0] != result[5][1] # name
2238
or result[6][0] != result[6][1] # kind
2239
or result[7][0] != result[7][1] # executable
2242
(utf8_decode_or_none(result[1][0]),
2243
utf8_decode_or_none(result[1][1])),
2247
(utf8_decode_or_none(result[5][0]),
2248
utf8_decode_or_none(result[5][1])),
2252
advance_path = False
2254
for result in _process_entry(current_entry, current_path_info):
2255
# this check should probably be outside the loop: one
2256
# 'iterate two trees' api, and then _iter_changes filters
2257
# unchanged pairs. - RBC 20070226
2259
if (include_unchanged
2260
or result[2] # content change
2261
or result[3][0] != result[3][1] # versioned status
2262
or result[4][0] != result[4][1] # parent id
2263
or result[5][0] != result[5][1] # name
2264
or result[6][0] != result[6][1] # kind
2265
or result[7][0] != result[7][1] # executable
2268
(utf8_decode_or_none(result[1][0]),
2269
utf8_decode_or_none(result[1][1])),
2273
(utf8_decode_or_none(result[5][0]),
2274
utf8_decode_or_none(result[5][1])),
2278
if advance_entry and current_entry is not None:
2280
if entry_index < len(current_block[1]):
2281
current_entry = current_block[1][entry_index]
2283
current_entry = None
2285
advance_entry = True # reset the advance flaga
2286
if advance_path and current_path_info is not None:
2287
if not path_handled:
2288
# unversioned in all regards
2289
if want_unversioned:
2290
new_executable = bool(
2291
stat.S_ISREG(current_path_info[3].st_mode)
2292
and stat.S_IEXEC & current_path_info[3].st_mode)
2293
if want_unversioned:
2295
(None, utf8_decode_or_none(current_path_info[0])),
2299
(None, utf8_decode_or_none(current_path_info[1])),
2300
(None, current_path_info[2]),
2301
(None, new_executable))
2302
# dont descend into this unversioned path if it is
2304
if current_path_info[2] in ('directory'):
2305
del current_dir_info[1][path_index]
2307
# dont descend the disk iterator into any tree
2309
if current_path_info[2] == 'tree-reference':
2310
del current_dir_info[1][path_index]
2313
if path_index < len(current_dir_info[1]):
2314
current_path_info = current_dir_info[1][path_index]
2315
if current_path_info[2] == 'directory':
2316
if self.target._directory_is_tree_reference(
2317
current_path_info[0].decode('utf8')):
2318
current_path_info = current_path_info[:2] + \
2319
('tree-reference',) + current_path_info[3:]
2321
current_path_info = None
2322
path_handled = False
2324
advance_path = True # reset the advance flagg.
2325
if current_block is not None:
2327
if (block_index < len(state._dirblocks) and
2328
osutils.is_inside(current_root, state._dirblocks[block_index][0])):
2329
current_block = state._dirblocks[block_index]
2331
current_block = None
2332
if current_dir_info is not None:
2334
current_dir_info = dir_iterator.next()
2335
except StopIteration:
2336
current_dir_info = None
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()
2340
2067
def is_compatible(source, target):
2341
2068
# the target must be a dirstate working tree
2342
if not isinstance(target, WorkingTree4):
2069
if not isinstance(target, DirStateWorkingTree):
2344
# the source must be a revtreee or dirstate rev tree.
2071
# the source must be a revtree or dirstate rev tree.
2345
2072
if not isinstance(source,
2346
2073
(revisiontree.RevisionTree, DirStateRevisionTree)):
2348
2075
# the source revid must be in the target dirstate
2349
2076
if not (source._revision_id == NULL_REVISION or
2350
2077
source._revision_id in target.get_parent_ids()):
2351
# TODO: what about ghosts? it may well need to
2078
# TODO: what about ghosts? it may well need to
2352
2079
# check for them explicitly.