859
859
def get_preview_tree(self):
860
860
"""Return a tree representing the result of the transform.
862
This tree only supports the subset of Tree functionality required
863
by show_diff_trees. It must only be compared to tt._tree.
862
The tree is a snapshot, and altering the TreeTransform will invalidate
865
865
return _PreviewTree(self)
1054
1054
def _limbo_name(self, trans_id):
1055
1055
"""Generate the limbo name of a file"""
1056
1056
limbo_name = self._limbo_files.get(trans_id)
1057
if limbo_name is not None:
1059
parent = self._new_parent.get(trans_id)
1060
# if the parent directory is already in limbo (e.g. when building a
1061
# tree), choose a limbo name inside the parent, to reduce further
1063
use_direct_path = False
1064
if self._new_contents.get(parent) == 'directory':
1065
filename = self._new_name.get(trans_id)
1066
if filename is not None:
1067
if parent not in self._limbo_children:
1068
self._limbo_children[parent] = set()
1069
self._limbo_children_names[parent] = {}
1070
use_direct_path = True
1071
# the direct path can only be used if no other file has
1072
# already taken this pathname, i.e. if the name is unused, or
1073
# if it is already associated with this trans_id.
1074
elif self._case_sensitive_target:
1075
if (self._limbo_children_names[parent].get(filename)
1076
in (trans_id, None)):
1077
use_direct_path = True
1079
for l_filename, l_trans_id in\
1080
self._limbo_children_names[parent].iteritems():
1081
if l_trans_id == trans_id:
1083
if l_filename.lower() == filename.lower():
1086
use_direct_path = True
1089
limbo_name = pathjoin(self._limbo_files[parent], filename)
1090
self._limbo_children[parent].add(trans_id)
1091
self._limbo_children_names[parent][filename] = trans_id
1093
limbo_name = pathjoin(self._limbodir, trans_id)
1094
self._needs_rename.add(trans_id)
1095
self._limbo_files[trans_id] = limbo_name
1057
if limbo_name is None:
1058
limbo_name = self._generate_limbo_path(trans_id)
1059
self._limbo_files[trans_id] = limbo_name
1096
1060
return limbo_name
1062
def _generate_limbo_path(self, trans_id):
1063
"""Generate a limbo path using the trans_id as the relative path.
1065
This is suitable as a fallback, and when the transform should not be
1066
sensitive to the path encoding of the limbo directory.
1068
self._needs_rename.add(trans_id)
1069
return pathjoin(self._limbodir, trans_id)
1098
1071
def adjust_path(self, name, parent, trans_id):
1099
1072
previous_parent = self._new_parent.get(trans_id)
1100
1073
previous_name = self._new_name.get(trans_id)
1123
1096
new_path = self._limbo_name(trans_id)
1124
1097
os.rename(old_path, new_path)
1098
for descendant in self._limbo_descendants(trans_id):
1099
desc_path = self._limbo_files[descendant]
1100
desc_path = new_path + desc_path[len(old_path):]
1101
self._limbo_files[descendant] = desc_path
1103
def _limbo_descendants(self, trans_id):
1104
"""Return the set of trans_ids whose limbo paths descend from this."""
1105
descendants = set(self._limbo_children.get(trans_id, []))
1106
for descendant in list(descendants):
1107
descendants.update(self._limbo_descendants(descendant))
1126
1110
def create_file(self, contents, trans_id, mode_id=None):
1127
1111
"""Schedule creation of a new file.
1397
1381
yield self.trans_id_tree_path(childpath)
1383
def _generate_limbo_path(self, trans_id):
1384
"""Generate a limbo path using the final path if possible.
1386
This optimizes the performance of applying the tree transform by
1387
avoiding renames. These renames can be avoided only when the parent
1388
directory is already scheduled for creation.
1390
If the final path cannot be used, falls back to using the trans_id as
1393
parent = self._new_parent.get(trans_id)
1394
# if the parent directory is already in limbo (e.g. when building a
1395
# tree), choose a limbo name inside the parent, to reduce further
1397
use_direct_path = False
1398
if self._new_contents.get(parent) == 'directory':
1399
filename = self._new_name.get(trans_id)
1400
if filename is not None:
1401
if parent not in self._limbo_children:
1402
self._limbo_children[parent] = set()
1403
self._limbo_children_names[parent] = {}
1404
use_direct_path = True
1405
# the direct path can only be used if no other file has
1406
# already taken this pathname, i.e. if the name is unused, or
1407
# if it is already associated with this trans_id.
1408
elif self._case_sensitive_target:
1409
if (self._limbo_children_names[parent].get(filename)
1410
in (trans_id, None)):
1411
use_direct_path = True
1413
for l_filename, l_trans_id in\
1414
self._limbo_children_names[parent].iteritems():
1415
if l_trans_id == trans_id:
1417
if l_filename.lower() == filename.lower():
1420
use_direct_path = True
1422
if not use_direct_path:
1423
return DiskTreeTransform._generate_limbo_path(self, trans_id)
1425
limbo_name = pathjoin(self._limbo_files[parent], filename)
1426
self._limbo_children[parent].add(trans_id)
1427
self._limbo_children_names[parent][filename] = trans_id
1399
1431
def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1400
1432
"""Apply all changes to the inventory and filesystem.
1635
1667
self._all_children_cache = {}
1636
1668
self._path2trans_id_cache = {}
1637
1669
self._final_name_cache = {}
1639
def _changes(self, file_id):
1640
for changes in self._transform.iter_changes():
1641
if changes[0] == file_id:
1670
self._iter_changes_cache = dict((c[0], c) for c in
1671
self._transform.iter_changes())
1644
1673
def _content_change(self, file_id):
1645
1674
"""Return True if the content of this file changed"""
1646
changes = self._changes(file_id)
1675
changes = self._iter_changes_cache.get(file_id)
1647
1676
# changes[2] is true if the file content changed. See
1648
1677
# InterTree.iter_changes.
1649
1678
return (changes is not None and changes[2])
1884
1913
def get_file_mtime(self, file_id, path=None):
1885
1914
"""See Tree.get_file_mtime"""
1886
1915
if not self._content_change(file_id):
1887
return self._transform._tree.get_file_mtime(file_id, path)
1916
return self._transform._tree.get_file_mtime(file_id)
1888
1917
return self._stat_limbo_file(file_id).st_mtime
1890
1919
def _file_size(self, entry, stat_value):
1952
1981
executable = None
1953
1982
if kind == 'symlink':
1954
1983
link_or_sha1 = os.readlink(limbo_name).decode(osutils._fs_enc)
1955
if supports_executable():
1956
executable = tt._new_executability.get(trans_id, executable)
1984
executable = tt._new_executability.get(trans_id, executable)
1957
1985
return kind, size, executable, link_or_sha1
1959
1987
def iter_changes(self, from_tree, include_unchanged=False,
2621
2649
tt.adjust_path(name[1], parent_trans, trans_id)
2622
2650
if executable[0] != executable[1] and kind[1] == "file":
2623
2651
tt.set_executability(executable[1], trans_id)
2624
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2626
tt.create_file(bytes, trans_id, mode_id)
2652
if working_tree.supports_content_filtering():
2653
for index, ((trans_id, mode_id), bytes) in enumerate(
2654
target_tree.iter_files_bytes(deferred_files)):
2655
file_id = deferred_files[index][0]
2656
# We're reverting a tree to the target tree so using the
2657
# target tree to find the file path seems the best choice
2658
# here IMO - Ian C 27/Oct/2009
2659
filter_tree_path = target_tree.id2path(file_id)
2660
filters = working_tree._content_filter_stack(filter_tree_path)
2661
bytes = filtered_output_bytes(bytes, filters,
2662
ContentFilterContext(filter_tree_path, working_tree))
2663
tt.create_file(bytes, trans_id, mode_id)
2665
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2667
tt.create_file(bytes, trans_id, mode_id)
2628
2669
if basis_tree is not None:
2629
2670
basis_tree.unlock()