162
162
def adjust_path(self, name, parent, trans_id):
163
163
"""Change the path that is assigned to a transaction id."""
165
raise ValueError("Parent trans-id may not be None")
164
166
if trans_id == self._new_root:
165
167
raise CantMoveRoot
166
168
self._new_name[trans_id] = name
167
169
self._new_parent[trans_id] = parent
168
if parent == ROOT_PARENT:
169
if self._new_root is not None:
170
raise ValueError("Cannot have multiple roots.")
171
self._new_root = trans_id
173
171
def adjust_root_path(self, name, parent):
174
172
"""Emulate moving the root by moving all children, instead.
202
200
self.version_file(old_root_file_id, old_root)
203
201
self.unversion_file(self._new_root)
203
def fixup_new_roots(self):
204
"""Reinterpret requests to change the root directory
206
Instead of creating a root directory, or moving an existing directory,
207
all the attributes and children of the new root are applied to the
208
existing root directory.
210
This means that the old root trans-id becomes obsolete, so it is
211
recommended only to invoke this after the root trans-id has become
214
new_roots = [k for k, v in self._new_parent.iteritems() if v is
216
if len(new_roots) < 1:
218
if len(new_roots) != 1:
219
raise ValueError('A tree cannot have two roots!')
220
if self._new_root is None:
221
self._new_root = new_roots[0]
223
old_new_root = new_roots[0]
224
# TODO: What to do if a old_new_root is present, but self._new_root is
225
# not listed as being removed? This code explicitly unversions
226
# the old root and versions it with the new file_id. Though that
227
# seems like an incomplete delta
229
# unversion the new root's directory.
230
file_id = self.final_file_id(old_new_root)
231
if old_new_root in self._new_id:
232
self.cancel_versioning(old_new_root)
234
self.unversion_file(old_new_root)
235
# if, at this stage, root still has an old file_id, zap it so we can
236
# stick a new one in.
237
if (self.tree_file_id(self._new_root) is not None and
238
self._new_root not in self._removed_id):
239
self.unversion_file(self._new_root)
240
self.version_file(file_id, self._new_root)
242
# Now move children of new root into old root directory.
243
# Ensure all children are registered with the transaction, but don't
244
# use directly-- some tree children have new parents
245
list(self.iter_tree_children(old_new_root))
246
# Move all children of new root into old root directory.
247
for child in self.by_parent().get(old_new_root, []):
248
self.adjust_path(self.final_name(child), self._new_root, child)
250
# Ensure old_new_root has no directory.
251
if old_new_root in self._new_contents:
252
self.cancel_creation(old_new_root)
254
self.delete_contents(old_new_root)
256
# prevent deletion of root directory.
257
if self._new_root in self._removed_contents:
258
self.cancel_deletion(self._new_root)
260
# destroy path info for old_new_root.
261
del self._new_parent[old_new_root]
262
del self._new_name[old_new_root]
205
264
def trans_id_tree_file_id(self, inventory_id):
206
265
"""Determine the transaction id of a working tree file.
1054
1115
def _limbo_name(self, trans_id):
1055
1116
"""Generate the limbo name of a file"""
1056
1117
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
1118
if limbo_name is None:
1119
limbo_name = self._generate_limbo_path(trans_id)
1120
self._limbo_files[trans_id] = limbo_name
1096
1121
return limbo_name
1123
def _generate_limbo_path(self, trans_id):
1124
"""Generate a limbo path using the trans_id as the relative path.
1126
This is suitable as a fallback, and when the transform should not be
1127
sensitive to the path encoding of the limbo directory.
1129
self._needs_rename.add(trans_id)
1130
return pathjoin(self._limbodir, trans_id)
1098
1132
def adjust_path(self, name, parent, trans_id):
1099
1133
previous_parent = self._new_parent.get(trans_id)
1100
1134
previous_name = self._new_name.get(trans_id)
1102
1136
if (trans_id in self._limbo_files and
1103
1137
trans_id not in self._needs_rename):
1104
1138
self._rename_in_limbo([trans_id])
1105
self._limbo_children[previous_parent].remove(trans_id)
1106
del self._limbo_children_names[previous_parent][previous_name]
1139
if previous_parent != parent:
1140
self._limbo_children[previous_parent].remove(trans_id)
1141
if previous_parent != parent or previous_name != name:
1142
del self._limbo_children_names[previous_parent][previous_name]
1108
1144
def _rename_in_limbo(self, trans_ids):
1109
1145
"""Fix limbo names so that the right final path is produced.
1397
1433
yield self.trans_id_tree_path(childpath)
1435
def _generate_limbo_path(self, trans_id):
1436
"""Generate a limbo path using the final path if possible.
1438
This optimizes the performance of applying the tree transform by
1439
avoiding renames. These renames can be avoided only when the parent
1440
directory is already scheduled for creation.
1442
If the final path cannot be used, falls back to using the trans_id as
1445
parent = self._new_parent.get(trans_id)
1446
# if the parent directory is already in limbo (e.g. when building a
1447
# tree), choose a limbo name inside the parent, to reduce further
1449
use_direct_path = False
1450
if self._new_contents.get(parent) == 'directory':
1451
filename = self._new_name.get(trans_id)
1452
if filename is not None:
1453
if parent not in self._limbo_children:
1454
self._limbo_children[parent] = set()
1455
self._limbo_children_names[parent] = {}
1456
use_direct_path = True
1457
# the direct path can only be used if no other file has
1458
# already taken this pathname, i.e. if the name is unused, or
1459
# if it is already associated with this trans_id.
1460
elif self._case_sensitive_target:
1461
if (self._limbo_children_names[parent].get(filename)
1462
in (trans_id, None)):
1463
use_direct_path = True
1465
for l_filename, l_trans_id in\
1466
self._limbo_children_names[parent].iteritems():
1467
if l_trans_id == trans_id:
1469
if l_filename.lower() == filename.lower():
1472
use_direct_path = True
1474
if not use_direct_path:
1475
return DiskTreeTransform._generate_limbo_path(self, trans_id)
1477
limbo_name = pathjoin(self._limbo_files[parent], filename)
1478
self._limbo_children[parent].add(trans_id)
1479
self._limbo_children_names[parent][filename] = trans_id
1399
1483
def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1400
1484
"""Apply all changes to the inventory and filesystem.
1521
1605
child_pb.update('removing file', num, len(tree_paths))
1522
1606
full_path = self._tree.abspath(path)
1523
1607
if trans_id in self._removed_contents:
1524
mover.pre_delete(full_path, os.path.join(self._deletiondir,
1526
elif trans_id in self._new_name or trans_id in \
1608
delete_path = os.path.join(self._deletiondir, trans_id)
1609
mover.pre_delete(full_path, delete_path)
1610
elif (trans_id in self._new_name
1611
or trans_id in self._new_parent):
1529
1613
mover.rename(full_path, self._limbo_name(trans_id))
1530
1614
except OSError, e:
1635
1719
self._all_children_cache = {}
1636
1720
self._path2trans_id_cache = {}
1637
1721
self._final_name_cache = {}
1639
def _changes(self, file_id):
1640
for changes in self._transform.iter_changes():
1641
if changes[0] == file_id:
1722
self._iter_changes_cache = dict((c[0], c) for c in
1723
self._transform.iter_changes())
1644
1725
def _content_change(self, file_id):
1645
1726
"""Return True if the content of this file changed"""
1646
changes = self._changes(file_id)
1727
changes = self._iter_changes_cache.get(file_id)
1647
1728
# changes[2] is true if the file content changed. See
1648
1729
# InterTree.iter_changes.
1649
1730
return (changes is not None and changes[2])
1790
1871
if self._transform.final_file_id(trans_id) is None:
1791
1872
yield self._final_paths._determine_path(trans_id)
1793
def _make_inv_entries(self, ordered_entries, specific_file_ids=None):
1874
def _make_inv_entries(self, ordered_entries, specific_file_ids=None,
1875
yield_parents=False):
1794
1876
for trans_id, parent_file_id in ordered_entries:
1795
1877
file_id = self._transform.final_file_id(trans_id)
1796
1878
if file_id is None:
1822
1904
ordered_ids.append((trans_id, parent_file_id))
1823
1905
return ordered_ids
1825
def iter_entries_by_dir(self, specific_file_ids=None):
1907
def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
1826
1908
# This may not be a maximally efficient implementation, but it is
1827
1909
# reasonably straightforward. An implementation that grafts the
1828
1910
# TreeTransform changes onto the tree's iter_entries_by_dir results
2401
2483
tt.create_directory(trans_id)
2404
def create_from_tree(tt, trans_id, tree, file_id, bytes=None):
2405
"""Create new file contents according to tree contents."""
2486
def create_from_tree(tt, trans_id, tree, file_id, bytes=None,
2487
filter_tree_path=None):
2488
"""Create new file contents according to tree contents.
2490
:param filter_tree_path: the tree path to use to lookup
2491
content filters to apply to the bytes output in the working tree.
2492
This only applies if the working tree supports content filtering.
2406
2494
kind = tree.kind(file_id)
2407
2495
if kind == 'directory':
2408
2496
tt.create_directory(trans_id)
2413
2501
bytes = tree_file.readlines()
2415
2503
tree_file.close()
2505
if wt.supports_content_filtering() and filter_tree_path is not None:
2506
filters = wt._content_filter_stack(filter_tree_path)
2507
bytes = filtered_output_bytes(bytes, filters,
2508
ContentFilterContext(filter_tree_path, tree))
2416
2509
tt.create_file(bytes, trans_id)
2417
2510
elif kind == "symlink":
2418
2511
tt.create_symlink(tree.get_symlink_target(file_id), trans_id)
2606
2699
parent_trans = ROOT_PARENT
2608
2701
parent_trans = tt.trans_id_file_id(parent[1])
2609
tt.adjust_path(name[1], parent_trans, trans_id)
2702
if parent[0] is None and versioned[0]:
2703
tt.adjust_root_path(name[1], parent_trans)
2705
tt.adjust_path(name[1], parent_trans, trans_id)
2610
2706
if executable[0] != executable[1] and kind[1] == "file":
2611
2707
tt.set_executability(executable[1], trans_id)
2612
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2614
tt.create_file(bytes, trans_id, mode_id)
2708
if working_tree.supports_content_filtering():
2709
for index, ((trans_id, mode_id), bytes) in enumerate(
2710
target_tree.iter_files_bytes(deferred_files)):
2711
file_id = deferred_files[index][0]
2712
# We're reverting a tree to the target tree so using the
2713
# target tree to find the file path seems the best choice
2714
# here IMO - Ian C 27/Oct/2009
2715
filter_tree_path = target_tree.id2path(file_id)
2716
filters = working_tree._content_filter_stack(filter_tree_path)
2717
bytes = filtered_output_bytes(bytes, filters,
2718
ContentFilterContext(filter_tree_path, working_tree))
2719
tt.create_file(bytes, trans_id, mode_id)
2721
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2723
tt.create_file(bytes, trans_id, mode_id)
2724
tt.fixup_new_roots()
2616
2726
if basis_tree is not None:
2617
2727
basis_tree.unlock()