33
34
revision as _mod_revision,
36
38
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
37
ReusingTransform, NotVersionedError, CantMoveRoot,
39
ReusingTransform, CantMoveRoot,
38
40
ExistingLimbo, ImmortalLimbo, NoFinalPath,
39
41
UnableCreateSymlink)
40
42
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
79
81
class TreeTransformBase(object):
80
82
"""The base class for TreeTransform and its kin."""
82
def __init__(self, tree, pb=DummyProgress(),
84
def __init__(self, tree, pb=None,
83
85
case_sensitive=True):
86
88
:param tree: The tree that will be transformed, but not necessarily
88
:param pb: A ProgressTask indicating how much progress is being made
89
91
:param case_sensitive: If True, the target of the transform is
90
92
case sensitive, not just case preserving.
927
929
return _PreviewTree(self)
929
def commit(self, branch, message, merge_parents=None, strict=False):
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):
930
934
"""Commit the result of this TreeTransform to a branch.
932
936
:param branch: The branch to commit to.
933
937
:param message: The message to attach to the commit.
934
:param merge_parents: Additional parents specified by pending merges.
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.)
935
952
:return: The revision_id of the revision committed.
937
954
self._check_malformed()
954
971
if self._tree.get_revision_id() != last_rev_id:
955
972
raise ValueError('TreeTransform not based on branch basis: %s' %
956
973
self._tree.get_revision_id())
957
builder = branch.get_commit_builder(parent_ids)
974
revprops = commit.Commit.update_revprops(revprops, branch, authors)
975
builder = branch.get_commit_builder(parent_ids,
980
revision_id=revision_id)
958
981
preview = self.get_preview_tree()
959
982
list(builder.record_iter_changes(preview, last_rev_id,
960
983
self.iter_changes()))
1062
1085
class DiskTreeTransform(TreeTransformBase):
1063
1086
"""Tree transform storing its contents on disk."""
1065
def __init__(self, tree, limbodir, pb=DummyProgress(),
1088
def __init__(self, tree, limbodir, pb=None,
1066
1089
case_sensitive=True):
1067
1090
"""Constructor.
1068
1091
:param tree: The tree that will be transformed, but not necessarily
1069
1092
the output tree.
1070
1093
:param limbodir: A directory where new files can be stored until
1071
1094
they are installed in their proper places
1072
:param pb: A ProgressBar indicating how much progress is being made
1073
1096
:param case_sensitive: If True, the target of the transform is
1074
1097
case sensitive, not just case preserving.
1159
1182
if trans_id not in self._new_contents:
1161
1184
new_path = self._limbo_name(trans_id)
1162
os.rename(old_path, new_path)
1185
osutils.rename(old_path, new_path)
1163
1186
for descendant in self._limbo_descendants(trans_id):
1164
1187
desc_path = self._limbo_files[descendant]
1165
1188
desc_path = new_path + desc_path[len(old_path):]
1339
1362
FileMover does not delete files until it is sure that a rollback will not
1342
def __init__(self, tree, pb=DummyProgress()):
1365
def __init__(self, tree, pb=None):
1343
1366
"""Note: a tree_write lock is taken on the tree.
1345
1368
Use TreeTransform.finalize() to release the lock (can be omitted if
1634
1657
or trans_id in self._new_parent):
1636
1659
mover.rename(full_path, self._limbo_name(trans_id))
1660
except errors.TransformRenameFailed, e:
1638
1661
if e.errno != errno.ENOENT:
1665
1688
if trans_id in self._needs_rename:
1667
1690
mover.rename(self._limbo_name(trans_id), full_path)
1691
except errors.TransformRenameFailed, e:
1669
1692
# We may be renaming a dangling inventory id
1670
1693
if e.errno != errno.ENOENT:
1691
1714
unversioned files in the input tree.
1694
def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
1717
def __init__(self, tree, pb=None, case_sensitive=True):
1695
1718
tree.lock_read()
1696
1719
limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1697
1720
DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
1769
1792
parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
1770
1793
self._iter_parent_trees()]
1771
1794
vf.add_lines((file_id, tree_revision), parent_keys,
1772
self.get_file(file_id).readlines())
1795
self.get_file_lines(file_id))
1773
1796
repo = self._get_repository()
1774
1797
base_vf = repo.texts
1775
1798
if base_vf not in vf.fallback_versionedfiles:
1797
1820
executable = self.is_executable(file_id, path)
1798
1821
return kind, executable, None
1823
def is_locked(self):
1800
1826
def lock_read(self):
1801
1827
# Perhaps in theory, this should lock the TreeTransform?
1804
1830
def unlock(self):
2265
2291
for num, _unused in enumerate(wt.all_file_ids()):
2266
2292
if num > 0: # more than just a root
2267
2293
raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
2268
existing_files = set()
2269
for dir, files in wt.walkdirs():
2270
existing_files.update(f[0] for f in files)
2271
2294
file_trans_id = {}
2272
2295
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2273
2296
pp = ProgressPhase("Build phase", 2, top_pb)
2297
2320
precomputed_delta = []
2299
2322
precomputed_delta = None
2323
# Check if tree inventory has content. If so, we populate
2324
# existing_files with the directory content. If there are no
2325
# entries we skip populating existing_files as its not used.
2326
# This improves performance and unncessary work on large
2327
# directory trees. (#501307)
2329
existing_files = set()
2330
for dir, files in wt.walkdirs():
2331
existing_files.update(f[0] for f in files)
2300
2332
for num, (tree_path, entry) in \
2301
2333
enumerate(tree.inventory.iter_entries_by_dir()):
2302
2334
pb.update("Building tree", num - len(deferred_contents), total)
2434
2466
if entry.kind == "directory":
2436
2468
if entry.kind == "file":
2437
if tree.get_file(file_id).read() == file(target_path, 'rb').read():
2469
f = file(target_path, 'rb')
2471
if tree.get_file_text(file_id) == f.read():
2439
2475
elif entry.kind == "symlink":
2440
2476
if tree.get_symlink_target(file_id) == os.readlink(target_path):
2591
2627
def revert(working_tree, target_tree, filenames, backups=False,
2592
pb=DummyProgress(), change_reporter=None):
2628
pb=None, change_reporter=None):
2593
2629
"""Revert a working tree's contents to those of a target tree."""
2594
2630
target_tree.lock_read()
2631
pb = ui.ui_factory.nested_progress_bar()
2595
2632
tt = TreeTransform(working_tree, pb)
2597
2634
pp = ProgressPhase("Revert phase", 3, pb)
2616
2653
def _prepare_revert_transform(working_tree, target_tree, tt, filenames,
2617
2654
backups, pp, basis_tree=None,
2618
2655
merge_modified=None):
2620
2656
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2622
2658
if merge_modified is None:
2626
2662
merge_modified, basis_tree)
2628
2664
child_pb.finished()
2630
2665
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2632
2667
raw_conflicts = resolve_conflicts(tt, child_pb,
2754
2789
return merge_modified
2757
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
2792
def resolve_conflicts(tt, pb=None, pass_func=None):
2758
2793
"""Make many conflict-resolution attempts, but die if they fail"""
2759
2794
if pass_func is None:
2760
2795
pass_func = conflict_pass
2761
2796
new_conflicts = set()
2797
pb = ui.ui_factory.nested_progress_bar()
2763
2799
for n in range(10):
2764
2800
pb.update('Resolution pass', n+1, 10)
2768
2804
new_conflicts.update(pass_func(tt, conflicts))
2769
2805
raise MalformedTransform(conflicts=conflicts)
2774
2810
def conflict_pass(tt, conflicts, path_tree=None):
2897
2933
self.pending_deletions = []
2899
2935
def rename(self, from_, to):
2900
"""Rename a file from one path to another. Functions like os.rename"""
2936
"""Rename a file from one path to another."""
2902
os.rename(from_, to)
2938
osutils.rename(from_, to)
2939
except (IOError, OSError), e:
2904
2940
if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
2905
2941
raise errors.FileExists(to, str(e))
2942
# normal OSError doesn't include filenames so it's hard to see where
2943
# the problem is, see https://bugs.launchpad.net/bzr/+bug/491763
2944
raise errors.TransformRenameFailed(from_, to, str(e), e.errno)
2907
2945
self.past_renames.append((from_, to))
2909
2947
def pre_delete(self, from_, to):
2919
2957
def rollback(self):
2920
2958
"""Reverse all renames that have been performed"""
2921
2959
for from_, to in reversed(self.past_renames):
2922
os.rename(to, from_)
2961
osutils.rename(to, from_)
2962
except (OSError, IOError), e:
2963
raise errors.TransformRenameFailed(to, from_, str(e), e.errno)
2923
2964
# after rollback, don't reuse _FileMover
2924
2965
past_renames = None
2925
2966
pending_deletions = None