33
33
revision as _mod_revision,
36
37
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
37
ReusingTransform, NotVersionedError, CantMoveRoot,
38
ReusingTransform, CantMoveRoot,
38
39
ExistingLimbo, ImmortalLimbo, NoFinalPath,
39
40
UnableCreateSymlink)
40
41
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
79
80
class TreeTransformBase(object):
80
81
"""The base class for TreeTransform and its kin."""
82
def __init__(self, tree, pb=DummyProgress(),
83
def __init__(self, tree, pb=None,
83
84
case_sensitive=True):
86
87
:param tree: The tree that will be transformed, but not necessarily
88
:param pb: A ProgressTask indicating how much progress is being made
89
90
:param case_sensitive: If True, the target of the transform is
90
91
case sensitive, not just case preserving.
163
164
def adjust_path(self, name, parent, trans_id):
164
165
"""Change the path that is assigned to a transaction id."""
167
raise ValueError("Parent trans-id may not be None")
165
168
if trans_id == self._new_root:
166
169
raise CantMoveRoot
167
170
self._new_name[trans_id] = name
168
171
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
174
173
def adjust_root_path(self, name, parent):
175
174
"""Emulate moving the root by moving all children, instead.
203
202
self.version_file(old_root_file_id, old_root)
204
203
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]
206
266
def trans_id_tree_file_id(self, inventory_id):
207
267
"""Determine the transaction id of a working tree file.
1001
1063
class DiskTreeTransform(TreeTransformBase):
1002
1064
"""Tree transform storing its contents on disk."""
1004
def __init__(self, tree, limbodir, pb=DummyProgress(),
1066
def __init__(self, tree, limbodir, pb=None,
1005
1067
case_sensitive=True):
1006
1068
"""Constructor.
1007
1069
:param tree: The tree that will be transformed, but not necessarily
1008
1070
the output tree.
1009
1071
:param limbodir: A directory where new files can be stored until
1010
1072
they are installed in their proper places
1011
:param pb: A ProgressBar indicating how much progress is being made
1012
1074
:param case_sensitive: If True, the target of the transform is
1013
1075
case sensitive, not just case preserving.
1077
1139
if (trans_id in self._limbo_files and
1078
1140
trans_id not in self._needs_rename):
1079
1141
self._rename_in_limbo([trans_id])
1080
self._limbo_children[previous_parent].remove(trans_id)
1081
del self._limbo_children_names[previous_parent][previous_name]
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]
1083
1147
def _rename_in_limbo(self, trans_ids):
1084
1148
"""Fix limbo names so that the right final path is produced.
1096
1160
if trans_id not in self._new_contents:
1098
1162
new_path = self._limbo_name(trans_id)
1099
os.rename(old_path, new_path)
1163
osutils.rename(old_path, new_path)
1100
1164
for descendant in self._limbo_descendants(trans_id):
1101
1165
desc_path = self._limbo_files[descendant]
1102
1166
desc_path = new_path + desc_path[len(old_path):]
1565
1629
child_pb.update('removing file', num, len(tree_paths))
1566
1630
full_path = self._tree.abspath(path)
1567
1631
if trans_id in self._removed_contents:
1568
mover.pre_delete(full_path, os.path.join(self._deletiondir,
1570
elif trans_id in self._new_name or trans_id in \
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):
1573
1637
mover.rename(full_path, self._limbo_name(trans_id))
1574
1638
except OSError, e:
1628
1692
unversioned files in the input tree.
1631
def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
1695
def __init__(self, tree, pb=None, case_sensitive=True):
1632
1696
tree.lock_read()
1633
1697
limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1634
1698
DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
2528
2592
def revert(working_tree, target_tree, filenames, backups=False,
2529
pb=DummyProgress(), change_reporter=None):
2593
pb=None, change_reporter=None):
2530
2594
"""Revert a working tree's contents to those of a target tree."""
2531
2595
target_tree.lock_read()
2596
pb = ui.ui_factory.nested_progress_bar()
2532
2597
tt = TreeTransform(working_tree, pb)
2534
2599
pp = ProgressPhase("Revert phase", 3, pb)
2662
2725
parent_trans = ROOT_PARENT
2664
2727
parent_trans = tt.trans_id_file_id(parent[1])
2665
tt.adjust_path(name[1], parent_trans, trans_id)
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)
2666
2732
if executable[0] != executable[1] and kind[1] == "file":
2667
2733
tt.set_executability(executable[1], trans_id)
2668
2734
if working_tree.supports_content_filtering():
2681
2747
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2682
2748
deferred_files):
2683
2749
tt.create_file(bytes, trans_id, mode_id)
2750
tt.fixup_new_roots()
2685
2752
if basis_tree is not None:
2686
2753
basis_tree.unlock()
2687
2754
return merge_modified
2690
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
2757
def resolve_conflicts(tt, pb=None, pass_func=None):
2691
2758
"""Make many conflict-resolution attempts, but die if they fail"""
2692
2759
if pass_func is None:
2693
2760
pass_func = conflict_pass
2694
2761
new_conflicts = set()
2762
pb = ui.ui_factory.nested_progress_bar()
2696
2764
for n in range(10):
2697
2765
pb.update('Resolution pass', n+1, 10)
2830
2898
self.pending_deletions = []
2832
2900
def rename(self, from_, to):
2833
"""Rename a file from one path to another. Functions like os.rename"""
2901
"""Rename a file from one path to another."""
2835
os.rename(from_, to)
2903
osutils.rename(from_, to)
2836
2904
except OSError, e:
2837
2905
if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
2838
2906
raise errors.FileExists(to, str(e))