80
79
class TreeTransformBase(object):
81
80
"""The base class for TreeTransform and its kin."""
83
def __init__(self, tree, pb=None,
82
def __init__(self, tree, pb=DummyProgress(),
84
83
case_sensitive=True):
87
86
:param tree: The tree that will be transformed, but not necessarily
88
:param pb: A ProgressTask indicating how much progress is being made
90
89
:param case_sensitive: If True, the target of the transform is
91
90
case sensitive, not just case preserving.
164
163
def adjust_path(self, name, parent, trans_id):
165
164
"""Change the path that is assigned to a transaction id."""
167
raise ValueError("Parent trans-id may not be None")
168
165
if trans_id == self._new_root:
169
166
raise CantMoveRoot
170
167
self._new_name[trans_id] = name
171
168
self._new_parent[trans_id] = parent
169
if parent == ROOT_PARENT:
170
if self._new_root is not None:
171
raise ValueError("Cannot have multiple roots.")
172
self._new_root = trans_id
173
174
def adjust_root_path(self, name, parent):
174
175
"""Emulate moving the root by moving all children, instead.
202
203
self.version_file(old_root_file_id, old_root)
203
204
self.unversion_file(self._new_root)
205
def fixup_new_roots(self):
206
"""Reinterpret requests to change the root directory
208
Instead of creating a root directory, or moving an existing directory,
209
all the attributes and children of the new root are applied to the
210
existing root directory.
212
This means that the old root trans-id becomes obsolete, so it is
213
recommended only to invoke this after the root trans-id has become
216
new_roots = [k for k, v in self._new_parent.iteritems() if v is
218
if len(new_roots) < 1:
220
if len(new_roots) != 1:
221
raise ValueError('A tree cannot have two roots!')
222
if self._new_root is None:
223
self._new_root = new_roots[0]
225
old_new_root = new_roots[0]
226
# TODO: What to do if a old_new_root is present, but self._new_root is
227
# not listed as being removed? This code explicitly unversions
228
# the old root and versions it with the new file_id. Though that
229
# seems like an incomplete delta
231
# unversion the new root's directory.
232
file_id = self.final_file_id(old_new_root)
233
if old_new_root in self._new_id:
234
self.cancel_versioning(old_new_root)
236
self.unversion_file(old_new_root)
237
# if, at this stage, root still has an old file_id, zap it so we can
238
# stick a new one in.
239
if (self.tree_file_id(self._new_root) is not None and
240
self._new_root not in self._removed_id):
241
self.unversion_file(self._new_root)
242
self.version_file(file_id, self._new_root)
244
# Now move children of new root into old root directory.
245
# Ensure all children are registered with the transaction, but don't
246
# use directly-- some tree children have new parents
247
list(self.iter_tree_children(old_new_root))
248
# Move all children of new root into old root directory.
249
for child in self.by_parent().get(old_new_root, []):
250
self.adjust_path(self.final_name(child), self._new_root, child)
252
# Ensure old_new_root has no directory.
253
if old_new_root in self._new_contents:
254
self.cancel_creation(old_new_root)
256
self.delete_contents(old_new_root)
258
# prevent deletion of root directory.
259
if self._new_root in self._removed_contents:
260
self.cancel_deletion(self._new_root)
262
# destroy path info for old_new_root.
263
del self._new_parent[old_new_root]
264
del self._new_name[old_new_root]
266
206
def trans_id_tree_file_id(self, inventory_id):
267
207
"""Determine the transaction id of a working tree file.
1063
1001
class DiskTreeTransform(TreeTransformBase):
1064
1002
"""Tree transform storing its contents on disk."""
1066
def __init__(self, tree, limbodir, pb=None,
1004
def __init__(self, tree, limbodir, pb=DummyProgress(),
1067
1005
case_sensitive=True):
1068
1006
"""Constructor.
1069
1007
:param tree: The tree that will be transformed, but not necessarily
1070
1008
the output tree.
1071
1009
:param limbodir: A directory where new files can be stored until
1072
1010
they are installed in their proper places
1011
:param pb: A ProgressBar indicating how much progress is being made
1074
1012
:param case_sensitive: If True, the target of the transform is
1075
1013
case sensitive, not just case preserving.
1139
1077
if (trans_id in self._limbo_files and
1140
1078
trans_id not in self._needs_rename):
1141
1079
self._rename_in_limbo([trans_id])
1142
if previous_parent != parent:
1143
self._limbo_children[previous_parent].remove(trans_id)
1144
if previous_parent != parent or previous_name != name:
1145
del self._limbo_children_names[previous_parent][previous_name]
1080
self._limbo_children[previous_parent].remove(trans_id)
1081
del self._limbo_children_names[previous_parent][previous_name]
1147
1083
def _rename_in_limbo(self, trans_ids):
1148
1084
"""Fix limbo names so that the right final path is produced.
1629
1565
child_pb.update('removing file', num, len(tree_paths))
1630
1566
full_path = self._tree.abspath(path)
1631
1567
if trans_id in self._removed_contents:
1632
delete_path = os.path.join(self._deletiondir, trans_id)
1633
mover.pre_delete(full_path, delete_path)
1634
elif (trans_id in self._new_name
1635
or trans_id in self._new_parent):
1568
mover.pre_delete(full_path, os.path.join(self._deletiondir,
1570
elif trans_id in self._new_name or trans_id in \
1637
1573
mover.rename(full_path, self._limbo_name(trans_id))
1638
1574
except OSError, e:
1692
1628
unversioned files in the input tree.
1695
def __init__(self, tree, pb=None, case_sensitive=True):
1631
def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
1696
1632
tree.lock_read()
1697
1633
limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1698
1634
DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
2592
2528
def revert(working_tree, target_tree, filenames, backups=False,
2593
pb=None, change_reporter=None):
2529
pb=DummyProgress(), change_reporter=None):
2594
2530
"""Revert a working tree's contents to those of a target tree."""
2595
2531
target_tree.lock_read()
2596
pb = ui.ui_factory.nested_progress_bar()
2597
2532
tt = TreeTransform(working_tree, pb)
2599
2534
pp = ProgressPhase("Revert phase", 3, pb)
2725
2662
parent_trans = ROOT_PARENT
2727
2664
parent_trans = tt.trans_id_file_id(parent[1])
2728
if parent[0] is None and versioned[0]:
2729
tt.adjust_root_path(name[1], parent_trans)
2731
tt.adjust_path(name[1], parent_trans, trans_id)
2665
tt.adjust_path(name[1], parent_trans, trans_id)
2732
2666
if executable[0] != executable[1] and kind[1] == "file":
2733
2667
tt.set_executability(executable[1], trans_id)
2734
2668
if working_tree.supports_content_filtering():
2747
2681
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2748
2682
deferred_files):
2749
2683
tt.create_file(bytes, trans_id, mode_id)
2750
tt.fixup_new_roots()
2752
2685
if basis_tree is not None:
2753
2686
basis_tree.unlock()
2754
2687
return merge_modified
2757
def resolve_conflicts(tt, pb=None, pass_func=None):
2690
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
2758
2691
"""Make many conflict-resolution attempts, but die if they fail"""
2759
2692
if pass_func is None:
2760
2693
pass_func = conflict_pass
2761
2694
new_conflicts = set()
2762
pb = ui.ui_factory.nested_progress_bar()
2764
2696
for n in range(10):
2765
2697
pb.update('Resolution pass', n+1, 10)