34
33
revision as _mod_revision,
38
36
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
39
ReusingTransform, CantMoveRoot,
37
ReusingTransform, NotVersionedError, CantMoveRoot,
40
38
ExistingLimbo, ImmortalLimbo, NoFinalPath,
41
39
UnableCreateSymlink)
42
40
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
81
79
class TreeTransformBase(object):
82
80
"""The base class for TreeTransform and its kin."""
84
def __init__(self, tree, pb=None,
82
def __init__(self, tree, pb=DummyProgress(),
85
83
case_sensitive=True):
88
86
:param tree: The tree that will be transformed, but not necessarily
88
:param pb: A ProgressTask indicating how much progress is being made
91
89
:param case_sensitive: If True, the target of the transform is
92
90
case sensitive, not just case preserving.
165
163
def adjust_path(self, name, parent, trans_id):
166
164
"""Change the path that is assigned to a transaction id."""
168
raise ValueError("Parent trans-id may not be None")
169
165
if trans_id == self._new_root:
170
166
raise CantMoveRoot
171
167
self._new_name[trans_id] = name
172
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
174
174
def adjust_root_path(self, name, parent):
175
175
"""Emulate moving the root by moving all children, instead.
203
203
self.version_file(old_root_file_id, old_root)
204
204
self.unversion_file(self._new_root)
206
def fixup_new_roots(self):
207
"""Reinterpret requests to change the root directory
209
Instead of creating a root directory, or moving an existing directory,
210
all the attributes and children of the new root are applied to the
211
existing root directory.
213
This means that the old root trans-id becomes obsolete, so it is
214
recommended only to invoke this after the root trans-id has become
217
new_roots = [k for k, v in self._new_parent.iteritems() if v is
219
if len(new_roots) < 1:
221
if len(new_roots) != 1:
222
raise ValueError('A tree cannot have two roots!')
223
if self._new_root is None:
224
self._new_root = new_roots[0]
226
old_new_root = new_roots[0]
227
# TODO: What to do if a old_new_root is present, but self._new_root is
228
# not listed as being removed? This code explicitly unversions
229
# the old root and versions it with the new file_id. Though that
230
# seems like an incomplete delta
232
# unversion the new root's directory.
233
file_id = self.final_file_id(old_new_root)
234
if old_new_root in self._new_id:
235
self.cancel_versioning(old_new_root)
237
self.unversion_file(old_new_root)
238
# if, at this stage, root still has an old file_id, zap it so we can
239
# stick a new one in.
240
if (self.tree_file_id(self._new_root) is not None and
241
self._new_root not in self._removed_id):
242
self.unversion_file(self._new_root)
243
self.version_file(file_id, self._new_root)
245
# Now move children of new root into old root directory.
246
# Ensure all children are registered with the transaction, but don't
247
# use directly-- some tree children have new parents
248
list(self.iter_tree_children(old_new_root))
249
# Move all children of new root into old root directory.
250
for child in self.by_parent().get(old_new_root, []):
251
self.adjust_path(self.final_name(child), self._new_root, child)
253
# Ensure old_new_root has no directory.
254
if old_new_root in self._new_contents:
255
self.cancel_creation(old_new_root)
257
self.delete_contents(old_new_root)
259
# prevent deletion of root directory.
260
if self._new_root in self._removed_contents:
261
self.cancel_deletion(self._new_root)
263
# destroy path info for old_new_root.
264
del self._new_parent[old_new_root]
265
del self._new_name[old_new_root]
267
206
def trans_id_tree_file_id(self, inventory_id):
268
207
"""Determine the transaction id of a working tree file.
929
866
return _PreviewTree(self)
931
def commit(self, branch, message, merge_parents=None, strict=False,
932
timestamp=None, timezone=None, committer=None, authors=None,
933
revprops=None, revision_id=None):
868
def commit(self, branch, message, merge_parents=None, strict=False):
934
869
"""Commit the result of this TreeTransform to a branch.
936
871
:param branch: The branch to commit to.
937
872
:param message: The message to attach to the commit.
938
:param merge_parents: Additional parent revision-ids specified by
940
:param strict: If True, abort the commit if there are unversioned
942
:param timestamp: if not None, seconds-since-epoch for the time and
943
date. (May be a float.)
944
:param timezone: Optional timezone for timestamp, as an offset in
946
:param committer: Optional committer in email-id format.
947
(e.g. "J Random Hacker <jrandom@example.com>")
948
:param authors: Optional list of authors in email-id format.
949
:param revprops: Optional dictionary of revision properties.
950
:param revision_id: Optional revision id. (Specifying a revision-id
951
may reduce performance for some non-native formats.)
873
:param merge_parents: Additional parents specified by pending merges.
952
874
:return: The revision_id of the revision committed.
954
876
self._check_malformed()
971
893
if self._tree.get_revision_id() != last_rev_id:
972
894
raise ValueError('TreeTransform not based on branch basis: %s' %
973
895
self._tree.get_revision_id())
974
revprops = commit.Commit.update_revprops(revprops, branch, authors)
975
builder = branch.get_commit_builder(parent_ids,
980
revision_id=revision_id)
896
builder = branch.get_commit_builder(parent_ids)
981
897
preview = self.get_preview_tree()
982
898
list(builder.record_iter_changes(preview, last_rev_id,
983
899
self.iter_changes()))
1085
1001
class DiskTreeTransform(TreeTransformBase):
1086
1002
"""Tree transform storing its contents on disk."""
1088
def __init__(self, tree, limbodir, pb=None,
1004
def __init__(self, tree, limbodir, pb=DummyProgress(),
1089
1005
case_sensitive=True):
1090
1006
"""Constructor.
1091
1007
:param tree: The tree that will be transformed, but not necessarily
1092
1008
the output tree.
1093
1009
:param limbodir: A directory where new files can be stored until
1094
1010
they are installed in their proper places
1011
:param pb: A ProgressBar indicating how much progress is being made
1096
1012
:param case_sensitive: If True, the target of the transform is
1097
1013
case sensitive, not just case preserving.
1161
1077
if (trans_id in self._limbo_files and
1162
1078
trans_id not in self._needs_rename):
1163
1079
self._rename_in_limbo([trans_id])
1164
if previous_parent != parent:
1165
self._limbo_children[previous_parent].remove(trans_id)
1166
if previous_parent != parent or previous_name != name:
1167
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]
1169
1083
def _rename_in_limbo(self, trans_ids):
1170
1084
"""Fix limbo names so that the right final path is produced.
1182
1096
if trans_id not in self._new_contents:
1184
1098
new_path = self._limbo_name(trans_id)
1185
osutils.rename(old_path, new_path)
1099
os.rename(old_path, new_path)
1186
1100
for descendant in self._limbo_descendants(trans_id):
1187
1101
desc_path = self._limbo_files[descendant]
1188
1102
desc_path = new_path + desc_path[len(old_path):]
1651
1565
child_pb.update('removing file', num, len(tree_paths))
1652
1566
full_path = self._tree.abspath(path)
1653
1567
if trans_id in self._removed_contents:
1654
delete_path = os.path.join(self._deletiondir, trans_id)
1655
mover.pre_delete(full_path, delete_path)
1656
elif (trans_id in self._new_name
1657
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 \
1659
1573
mover.rename(full_path, self._limbo_name(trans_id))
1660
1574
except OSError, e:
1714
1628
unversioned files in the input tree.
1717
def __init__(self, tree, pb=None, case_sensitive=True):
1631
def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
1718
1632
tree.lock_read()
1719
1633
limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1720
1634
DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
2617
2528
def revert(working_tree, target_tree, filenames, backups=False,
2618
pb=None, change_reporter=None):
2529
pb=DummyProgress(), change_reporter=None):
2619
2530
"""Revert a working tree's contents to those of a target tree."""
2620
2531
target_tree.lock_read()
2621
pb = ui.ui_factory.nested_progress_bar()
2622
2532
tt = TreeTransform(working_tree, pb)
2624
2534
pp = ProgressPhase("Revert phase", 3, pb)
2750
2662
parent_trans = ROOT_PARENT
2752
2664
parent_trans = tt.trans_id_file_id(parent[1])
2753
if parent[0] is None and versioned[0]:
2754
tt.adjust_root_path(name[1], parent_trans)
2756
tt.adjust_path(name[1], parent_trans, trans_id)
2665
tt.adjust_path(name[1], parent_trans, trans_id)
2757
2666
if executable[0] != executable[1] and kind[1] == "file":
2758
2667
tt.set_executability(executable[1], trans_id)
2759
2668
if working_tree.supports_content_filtering():
2772
2681
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2773
2682
deferred_files):
2774
2683
tt.create_file(bytes, trans_id, mode_id)
2775
tt.fixup_new_roots()
2777
2685
if basis_tree is not None:
2778
2686
basis_tree.unlock()
2779
2687
return merge_modified
2782
def resolve_conflicts(tt, pb=None, pass_func=None):
2690
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
2783
2691
"""Make many conflict-resolution attempts, but die if they fail"""
2784
2692
if pass_func is None:
2785
2693
pass_func = conflict_pass
2786
2694
new_conflicts = set()
2787
pb = ui.ui_factory.nested_progress_bar()
2789
2696
for n in range(10):
2790
2697
pb.update('Resolution pass', n+1, 10)
2923
2830
self.pending_deletions = []
2925
2832
def rename(self, from_, to):
2926
"""Rename a file from one path to another."""
2833
"""Rename a file from one path to another. Functions like os.rename"""
2928
osutils.rename(from_, to)
2835
os.rename(from_, to)
2929
2836
except OSError, e:
2930
2837
if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
2931
2838
raise errors.FileExists(to, str(e))