1760
2039
if not found_versioned:
1761
2040
# none of the indexes was not 'absent' at all ids for this
1763
all_versioned = False
1765
if not all_versioned:
1766
raise errors.PathsNotVersionedError(specific_files)
2042
not_versioned.append(path)
2043
if len(not_versioned) > 0:
2044
raise errors.PathsNotVersionedError(not_versioned)
1767
2045
# -- remove redundancy in supplied specific_files to prevent over-scanning --
1768
search_specific_files = set()
1769
for path in specific_files:
1770
other_specific_files = specific_files.difference(set([path]))
1771
if not osutils.is_inside_any(other_specific_files, path):
1772
# this is a top level path, we must check it.
1773
search_specific_files.add(path)
1775
# compare source_index and target_index at or under each element of search_specific_files.
1776
# follow the following comparison table. Note that we only want to do diff operations when
1777
# the target is fdl because thats when the walkdirs logic will have exposed the pathinfo
1781
# Source | Target | disk | action
1782
# r | fdlt | | add source to search, add id path move and perform
1783
# | | | diff check on source-target
1784
# r | fdlt | a | dangling file that was present in the basis.
1786
# r | a | | add source to search
1788
# r | r | | this path is present in a non-examined tree, skip.
1789
# r | r | a | this path is present in a non-examined tree, skip.
1790
# a | fdlt | | add new id
1791
# a | fdlt | a | dangling locally added file, skip
1792
# a | a | | not present in either tree, skip
1793
# a | a | a | not present in any tree, skip
1794
# a | r | | not present in either tree at this path, skip as it
1795
# | | | may not be selected by the users list of paths.
1796
# a | r | a | not present in either tree at this path, skip as it
1797
# | | | may not be selected by the users list of paths.
1798
# fdlt | fdlt | | content in both: diff them
1799
# fdlt | fdlt | a | deleted locally, but not unversioned - show as deleted ?
1800
# fdlt | a | | unversioned: output deleted id for now
1801
# fdlt | a | a | unversioned and deleted: output deleted id
1802
# fdlt | r | | relocated in this tree, so add target to search.
1803
# | | | Dont diff, we will see an r,fd; pair when we reach
1804
# | | | this id at the other path.
1805
# fdlt | r | a | relocated in this tree, so add target to search.
1806
# | | | Dont diff, we will see an r,fd; pair when we reach
1807
# | | | this id at the other path.
1809
# for all search_indexs in each path at or under each element of
1810
# search_specific_files, if the detail is relocated: add the id, and add the
1811
# relocated path as one to search if its not searched already. If the
1812
# detail is not relocated, add the id.
1813
searched_specific_files = set()
1814
NULL_PARENT_DETAILS = dirstate.DirState.NULL_PARENT_DETAILS
1815
# Using a list so that we can access the values and change them in
1816
# nested scope. Each one is [path, file_id, entry]
1817
last_source_parent = [None, None]
1818
last_target_parent = [None, None]
2046
search_specific_files = osutils.minimum_path_selection(specific_files)
1820
2048
use_filesystem_for_exec = (sys.platform != 'win32')
1822
# Just a sentry, so that _process_entry can say that this
1823
# record is handled, but isn't interesting to process (unchanged)
1824
uninteresting = object()
1827
old_dirname_to_file_id = {}
1828
new_dirname_to_file_id = {}
1829
# TODO: jam 20070516 - Avoid the _get_entry lookup overhead by
1830
# keeping a cache of directories that we have seen.
1832
def _process_entry(entry, path_info):
1833
"""Compare an entry and real disk to generate delta information.
1835
:param path_info: top_relpath, basename, kind, lstat, abspath for
1836
the path of entry. If None, then the path is considered absent.
1837
(Perhaps we should pass in a concrete entry for this ?)
1838
Basename is returned as a utf8 string because we expect this
1839
tuple will be ignored, and don't want to take the time to
1841
:return: None if these don't match
1842
A tuple of information about the change, or
1843
the object 'uninteresting' if these match, but are
1844
basically identical.
1846
if source_index is None:
1847
source_details = NULL_PARENT_DETAILS
1849
source_details = entry[1][source_index]
1850
target_details = entry[1][target_index]
1851
target_minikind = target_details[0]
1852
if path_info is not None and target_minikind in 'fdlt':
1853
assert target_index == 0
1854
link_or_sha1 = state.update_entry(entry, abspath=path_info[4],
1855
stat_value=path_info[3])
1856
# The entry may have been modified by update_entry
1857
target_details = entry[1][target_index]
1858
target_minikind = target_details[0]
1861
file_id = entry[0][2]
1862
source_minikind = source_details[0]
1863
if source_minikind in 'fdltr' and target_minikind in 'fdlt':
1864
# claimed content in both: diff
1865
# r | fdlt | | add source to search, add id path move and perform
1866
# | | | diff check on source-target
1867
# r | fdlt | a | dangling file that was present in the basis.
1869
if source_minikind in 'r':
1870
# add the source to the search path to find any children it
1871
# has. TODO ? : only add if it is a container ?
1872
if not osutils.is_inside_any(searched_specific_files,
1874
search_specific_files.add(source_details[1])
1875
# generate the old path; this is needed for stating later
1877
old_path = source_details[1]
1878
old_dirname, old_basename = os.path.split(old_path)
1879
path = pathjoin(entry[0][0], entry[0][1])
1880
old_entry = state._get_entry(source_index,
1882
# update the source details variable to be the real
1884
source_details = old_entry[1][source_index]
1885
source_minikind = source_details[0]
1887
old_dirname = entry[0][0]
1888
old_basename = entry[0][1]
1889
old_path = path = None
1890
if path_info is None:
1891
# the file is missing on disk, show as removed.
1892
content_change = True
1896
# source and target are both versioned and disk file is present.
1897
target_kind = path_info[2]
1898
if target_kind == 'directory':
1900
old_path = path = pathjoin(old_dirname, old_basename)
1901
new_dirname_to_file_id[path] = file_id
1902
if source_minikind != 'd':
1903
content_change = True
1905
# directories have no fingerprint
1906
content_change = False
1908
elif target_kind == 'file':
1909
if source_minikind != 'f':
1910
content_change = True
1912
# We could check the size, but we already have the
1914
content_change = (link_or_sha1 != source_details[1])
1915
# Target details is updated at update_entry time
1916
if use_filesystem_for_exec:
1917
# We don't need S_ISREG here, because we are sure
1918
# we are dealing with a file.
1919
target_exec = bool(stat.S_IEXEC & path_info[3].st_mode)
1921
target_exec = target_details[3]
1922
elif target_kind == 'symlink':
1923
if source_minikind != 'l':
1924
content_change = True
1926
content_change = (link_or_sha1 != source_details[1])
1928
elif target_kind == 'tree-reference':
1929
if source_minikind != 't':
1930
content_change = True
1932
content_change = False
1935
raise Exception, "unknown kind %s" % path_info[2]
1936
if source_minikind == 'd':
1938
old_path = path = pathjoin(old_dirname, old_basename)
1939
old_dirname_to_file_id[old_path] = file_id
1940
# parent id is the entry for the path in the target tree
1941
if old_dirname == last_source_parent[0]:
1942
source_parent_id = last_source_parent[1]
1945
source_parent_id = old_dirname_to_file_id[old_dirname]
1947
source_parent_entry = state._get_entry(source_index,
1948
path_utf8=old_dirname)
1949
source_parent_id = source_parent_entry[0][2]
1950
if source_parent_id == entry[0][2]:
1951
# This is the root, so the parent is None
1952
source_parent_id = None
1954
last_source_parent[0] = old_dirname
1955
last_source_parent[1] = source_parent_id
1956
new_dirname = entry[0][0]
1957
if new_dirname == last_target_parent[0]:
1958
target_parent_id = last_target_parent[1]
1961
target_parent_id = new_dirname_to_file_id[new_dirname]
1963
# TODO: We don't always need to do the lookup, because the
1964
# parent entry will be the same as the source entry.
1965
target_parent_entry = state._get_entry(target_index,
1966
path_utf8=new_dirname)
1967
assert target_parent_entry != (None, None), (
1968
"Could not find target parent in wt: %s\nparent of: %s"
1969
% (new_dirname, entry))
1970
target_parent_id = target_parent_entry[0][2]
1971
if target_parent_id == entry[0][2]:
1972
# This is the root, so the parent is None
1973
target_parent_id = None
1975
last_target_parent[0] = new_dirname
1976
last_target_parent[1] = target_parent_id
1978
source_exec = source_details[3]
1979
if (include_unchanged
1981
or source_parent_id != target_parent_id
1982
or old_basename != entry[0][1]
1983
or source_exec != target_exec
1985
if old_path is None:
1986
old_path = path = pathjoin(old_dirname, old_basename)
1987
old_path_u = utf8_decode(old_path)[0]
1990
old_path_u = utf8_decode(old_path)[0]
1991
if old_path == path:
1994
path_u = utf8_decode(path)[0]
1995
source_kind = _minikind_to_kind[source_minikind]
1996
return (entry[0][2],
1997
(old_path_u, path_u),
2000
(source_parent_id, target_parent_id),
2001
(utf8_decode(old_basename)[0], utf8_decode(entry[0][1])[0]),
2002
(source_kind, target_kind),
2003
(source_exec, target_exec))
2005
return uninteresting
2006
elif source_minikind in 'a' and target_minikind in 'fdlt':
2007
# looks like a new file
2008
if path_info is not None:
2009
path = pathjoin(entry[0][0], entry[0][1])
2010
# parent id is the entry for the path in the target tree
2011
# TODO: these are the same for an entire directory: cache em.
2012
parent_id = state._get_entry(target_index,
2013
path_utf8=entry[0][0])[0][2]
2014
if parent_id == entry[0][2]:
2016
if use_filesystem_for_exec:
2017
# We need S_ISREG here, because we aren't sure if this
2020
stat.S_ISREG(path_info[3].st_mode)
2021
and stat.S_IEXEC & path_info[3].st_mode)
2023
target_exec = target_details[3]
2024
return (entry[0][2],
2025
(None, utf8_decode(path)[0]),
2029
(None, utf8_decode(entry[0][1])[0]),
2030
(None, path_info[2]),
2031
(None, target_exec))
2033
# but its not on disk: we deliberately treat this as just
2034
# never-present. (Why ?! - RBC 20070224)
2036
elif source_minikind in 'fdlt' and target_minikind in 'a':
2037
# unversioned, possibly, or possibly not deleted: we dont care.
2038
# if its still on disk, *and* theres no other entry at this
2039
# path [we dont know this in this routine at the moment -
2040
# perhaps we should change this - then it would be an unknown.
2041
old_path = pathjoin(entry[0][0], entry[0][1])
2042
# parent id is the entry for the path in the target tree
2043
parent_id = state._get_entry(source_index, path_utf8=entry[0][0])[0][2]
2044
if parent_id == entry[0][2]:
2046
return (entry[0][2],
2047
(utf8_decode(old_path)[0], None),
2051
(utf8_decode(entry[0][1])[0], None),
2052
(_minikind_to_kind[source_minikind], None),
2053
(source_details[3], None))
2054
elif source_minikind in 'fdlt' and target_minikind in 'r':
2055
# a rename; could be a true rename, or a rename inherited from
2056
# a renamed parent. TODO: handle this efficiently. Its not
2057
# common case to rename dirs though, so a correct but slow
2058
# implementation will do.
2059
if not osutils.is_inside_any(searched_specific_files, target_details[1]):
2060
search_specific_files.add(target_details[1])
2061
elif source_minikind in 'ra' and target_minikind in 'ra':
2062
# neither of the selected trees contain this file,
2063
# so skip over it. This is not currently directly tested, but
2064
# is indirectly via test_too_much.TestCommands.test_conflicts.
2067
raise AssertionError("don't know how to compare "
2068
"source_minikind=%r, target_minikind=%r"
2069
% (source_minikind, target_minikind))
2070
## import pdb;pdb.set_trace()
2073
while search_specific_files:
2074
# TODO: the pending list should be lexically sorted? the
2075
# interface doesn't require it.
2076
current_root = search_specific_files.pop()
2077
current_root_unicode = current_root.decode('utf8')
2078
searched_specific_files.add(current_root)
2079
# process the entries for this containing directory: the rest will be
2080
# found by their parents recursively.
2081
root_entries = _entries_for_path(current_root)
2082
root_abspath = self.target.abspath(current_root_unicode)
2084
root_stat = os.lstat(root_abspath)
2086
if e.errno == errno.ENOENT:
2087
# the path does not exist: let _process_entry know that.
2088
root_dir_info = None
2090
# some other random error: hand it up.
2093
root_dir_info = ('', current_root,
2094
osutils.file_kind_from_stat_mode(root_stat.st_mode), root_stat,
2096
if root_dir_info[2] == 'directory':
2097
if self.target._directory_is_tree_reference(
2098
current_root.decode('utf8')):
2099
root_dir_info = root_dir_info[:2] + \
2100
('tree-reference',) + root_dir_info[3:]
2102
if not root_entries and not root_dir_info:
2103
# this specified path is not present at all, skip it.
2105
path_handled = False
2106
for entry in root_entries:
2107
result = _process_entry(entry, root_dir_info)
2108
if result is not None:
2110
if result is not uninteresting:
2112
if want_unversioned and not path_handled and root_dir_info:
2113
new_executable = bool(
2114
stat.S_ISREG(root_dir_info[3].st_mode)
2115
and stat.S_IEXEC & root_dir_info[3].st_mode)
2117
(None, current_root_unicode),
2121
(None, splitpath(current_root_unicode)[-1]),
2122
(None, root_dir_info[2]),
2123
(None, new_executable)
2125
initial_key = (current_root, '', '')
2126
block_index, _ = state._find_block_index_from_key(initial_key)
2127
if block_index == 0:
2128
# we have processed the total root already, but because the
2129
# initial key matched it we should skip it here.
2131
if root_dir_info and root_dir_info[2] == 'tree-reference':
2132
current_dir_info = None
2134
dir_iterator = osutils._walkdirs_utf8(root_abspath, prefix=current_root)
2136
current_dir_info = dir_iterator.next()
2138
# on win32, python2.4 has e.errno == ERROR_DIRECTORY, but
2139
# python 2.5 has e.errno == EINVAL,
2140
# and e.winerror == ERROR_DIRECTORY
2141
e_winerror = getattr(e, 'winerror', None)
2142
win_errors = (ERROR_DIRECTORY, ERROR_PATH_NOT_FOUND)
2143
# there may be directories in the inventory even though
2144
# this path is not a file on disk: so mark it as end of
2146
if e.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
2147
current_dir_info = None
2148
elif (sys.platform == 'win32'
2149
and (e.errno in win_errors
2150
or e_winerror in win_errors)):
2151
current_dir_info = None
2155
if current_dir_info[0][0] == '':
2156
# remove .bzr from iteration
2157
bzr_index = bisect_left(current_dir_info[1], ('.bzr',))
2158
assert current_dir_info[1][bzr_index][0] == '.bzr'
2159
del current_dir_info[1][bzr_index]
2160
# walk until both the directory listing and the versioned metadata
2162
if (block_index < len(state._dirblocks) and
2163
osutils.is_inside(current_root, state._dirblocks[block_index][0])):
2164
current_block = state._dirblocks[block_index]
2166
current_block = None
2167
while (current_dir_info is not None or
2168
current_block is not None):
2169
if (current_dir_info and current_block
2170
and current_dir_info[0][0] != current_block[0]):
2171
if current_dir_info[0][0].split('/') < current_block[0].split('/'):
2172
# filesystem data refers to paths not covered by the dirblock.
2173
# this has two possibilities:
2174
# A) it is versioned but empty, so there is no block for it
2175
# B) it is not versioned.
2177
# if (A) then we need to recurse into it to check for
2178
# new unknown files or directories.
2179
# if (B) then we should ignore it, because we don't
2180
# recurse into unknown directories.
2182
while path_index < len(current_dir_info[1]):
2183
current_path_info = current_dir_info[1][path_index]
2184
if want_unversioned:
2185
if current_path_info[2] == 'directory':
2186
if self.target._directory_is_tree_reference(
2187
current_path_info[0].decode('utf8')):
2188
current_path_info = current_path_info[:2] + \
2189
('tree-reference',) + current_path_info[3:]
2190
new_executable = bool(
2191
stat.S_ISREG(current_path_info[3].st_mode)
2192
and stat.S_IEXEC & current_path_info[3].st_mode)
2194
(None, utf8_decode(current_path_info[0])[0]),
2198
(None, utf8_decode(current_path_info[1])[0]),
2199
(None, current_path_info[2]),
2200
(None, new_executable))
2201
# dont descend into this unversioned path if it is
2203
if current_path_info[2] in ('directory',
2205
del current_dir_info[1][path_index]
2209
# This dir info has been handled, go to the next
2211
current_dir_info = dir_iterator.next()
2212
except StopIteration:
2213
current_dir_info = None
2215
# We have a dirblock entry for this location, but there
2216
# is no filesystem path for this. This is most likely
2217
# because a directory was removed from the disk.
2218
# We don't have to report the missing directory,
2219
# because that should have already been handled, but we
2220
# need to handle all of the files that are contained
2222
for current_entry in current_block[1]:
2223
# entry referring to file not present on disk.
2224
# advance the entry only, after processing.
2225
result = _process_entry(current_entry, None)
2226
if result is not None:
2227
if result is not uninteresting:
2230
if (block_index < len(state._dirblocks) and
2231
osutils.is_inside(current_root,
2232
state._dirblocks[block_index][0])):
2233
current_block = state._dirblocks[block_index]
2235
current_block = None
2238
if current_block and entry_index < len(current_block[1]):
2239
current_entry = current_block[1][entry_index]
2241
current_entry = None
2242
advance_entry = True
2244
if current_dir_info and path_index < len(current_dir_info[1]):
2245
current_path_info = current_dir_info[1][path_index]
2246
if current_path_info[2] == 'directory':
2247
if self.target._directory_is_tree_reference(
2248
current_path_info[0].decode('utf8')):
2249
current_path_info = current_path_info[:2] + \
2250
('tree-reference',) + current_path_info[3:]
2252
current_path_info = None
2254
path_handled = False
2255
while (current_entry is not None or
2256
current_path_info is not None):
2257
if current_entry is None:
2258
# the check for path_handled when the path is adnvaced
2259
# will yield this path if needed.
2261
elif current_path_info is None:
2262
# no path is fine: the per entry code will handle it.
2263
result = _process_entry(current_entry, current_path_info)
2264
if result is not None:
2265
if result is not uninteresting:
2267
elif (current_entry[0][1] != current_path_info[1]
2268
or current_entry[1][target_index][0] in 'ar'):
2269
# The current path on disk doesn't match the dirblock
2270
# record. Either the dirblock is marked as absent, or
2271
# the file on disk is not present at all in the
2272
# dirblock. Either way, report about the dirblock
2273
# entry, and let other code handle the filesystem one.
2275
# Compare the basename for these files to determine
2277
if current_path_info[1] < current_entry[0][1]:
2278
# extra file on disk: pass for now, but only
2279
# increment the path, not the entry
2280
advance_entry = False
2282
# entry referring to file not present on disk.
2283
# advance the entry only, after processing.
2284
result = _process_entry(current_entry, None)
2285
if result is not None:
2286
if result is not uninteresting:
2288
advance_path = False
2290
result = _process_entry(current_entry, current_path_info)
2291
if result is not None:
2293
if result is not uninteresting:
2295
if advance_entry and current_entry is not None:
2297
if entry_index < len(current_block[1]):
2298
current_entry = current_block[1][entry_index]
2300
current_entry = None
2302
advance_entry = True # reset the advance flaga
2303
if advance_path and current_path_info is not None:
2304
if not path_handled:
2305
# unversioned in all regards
2306
if want_unversioned:
2307
new_executable = bool(
2308
stat.S_ISREG(current_path_info[3].st_mode)
2309
and stat.S_IEXEC & current_path_info[3].st_mode)
2311
(None, utf8_decode(current_path_info[0])[0]),
2315
(None, utf8_decode(current_path_info[1])[0]),
2316
(None, current_path_info[2]),
2317
(None, new_executable))
2318
# dont descend into this unversioned path if it is
2320
if current_path_info[2] in ('directory'):
2321
del current_dir_info[1][path_index]
2323
# dont descend the disk iterator into any tree
2325
if current_path_info[2] == 'tree-reference':
2326
del current_dir_info[1][path_index]
2329
if path_index < len(current_dir_info[1]):
2330
current_path_info = current_dir_info[1][path_index]
2331
if current_path_info[2] == 'directory':
2332
if self.target._directory_is_tree_reference(
2333
current_path_info[0].decode('utf8')):
2334
current_path_info = current_path_info[:2] + \
2335
('tree-reference',) + current_path_info[3:]
2337
current_path_info = None
2338
path_handled = False
2340
advance_path = True # reset the advance flagg.
2341
if current_block is not None:
2343
if (block_index < len(state._dirblocks) and
2344
osutils.is_inside(current_root, state._dirblocks[block_index][0])):
2345
current_block = state._dirblocks[block_index]
2347
current_block = None
2348
if current_dir_info is not None:
2350
current_dir_info = dir_iterator.next()
2351
except StopIteration:
2352
current_dir_info = None
2049
iter_changes = self.target._iter_changes(include_unchanged,
2050
use_filesystem_for_exec, search_specific_files, state,
2051
source_index, target_index, want_unversioned, self.target)
2052
return iter_changes.iter_changes()
2356
2055
def is_compatible(source, target):
2357
2056
# the target must be a dirstate working tree
2358
if not isinstance(target, WorkingTree4):
2057
if not isinstance(target, DirStateWorkingTree):
2360
# the source must be a revtreee or dirstate rev tree.
2059
# the source must be a revtree or dirstate rev tree.
2361
2060
if not isinstance(source,
2362
2061
(revisiontree.RevisionTree, DirStateRevisionTree)):
2364
2063
# the source revid must be in the target dirstate
2365
if not (source._revision_id == NULL_REVISION or
2064
if not (source._revision_id == _mod_revision.NULL_REVISION or
2366
2065
source._revision_id in target.get_parent_ids()):
2367
# TODO: what about ghosts? it may well need to
2066
# TODO: what about ghosts? it may well need to
2368
2067
# check for them explicitly.