34
32
revision as _mod_revision,
38
35
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
39
ReusingTransform, CantMoveRoot,
36
ReusingTransform, NotVersionedError, CantMoveRoot,
40
37
ExistingLimbo, ImmortalLimbo, NoFinalPath,
41
38
UnableCreateSymlink)
42
39
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
81
78
class TreeTransformBase(object):
82
79
"""The base class for TreeTransform and its kin."""
84
def __init__(self, tree, pb=None,
81
def __init__(self, tree, pb=DummyProgress(),
85
82
case_sensitive=True):
88
85
:param tree: The tree that will be transformed, but not necessarily
87
:param pb: A ProgressTask indicating how much progress is being made
91
88
:param case_sensitive: If True, the target of the transform is
92
89
case sensitive, not just case preserving.
929
926
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):
928
def commit(self, branch, message, merge_parents=None, strict=False):
934
929
"""Commit the result of this TreeTransform to a branch.
936
931
:param branch: The branch to commit to.
937
932
: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.)
933
:param merge_parents: Additional parents specified by pending merges.
952
934
:return: The revision_id of the revision committed.
954
936
self._check_malformed()
971
953
if self._tree.get_revision_id() != last_rev_id:
972
954
raise ValueError('TreeTransform not based on branch basis: %s' %
973
955
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)
956
builder = branch.get_commit_builder(parent_ids)
981
957
preview = self.get_preview_tree()
982
958
list(builder.record_iter_changes(preview, last_rev_id,
983
959
self.iter_changes()))
1085
1061
class DiskTreeTransform(TreeTransformBase):
1086
1062
"""Tree transform storing its contents on disk."""
1088
def __init__(self, tree, limbodir, pb=None,
1064
def __init__(self, tree, limbodir, pb=DummyProgress(),
1089
1065
case_sensitive=True):
1090
1066
"""Constructor.
1091
1067
:param tree: The tree that will be transformed, but not necessarily
1092
1068
the output tree.
1093
1069
:param limbodir: A directory where new files can be stored until
1094
1070
they are installed in their proper places
1071
:param pb: A ProgressBar indicating how much progress is being made
1096
1072
:param case_sensitive: If True, the target of the transform is
1097
1073
case sensitive, not just case preserving.
1108
1084
self._limbo_children_names = {}
1109
1085
# List of transform ids that need to be renamed from limbo into place
1110
1086
self._needs_rename = set()
1111
self._creation_mtime = None
1113
1088
def finalize(self):
1114
1089
"""Release the working tree lock, if held, clean up limbo dir.
1182
1157
if trans_id not in self._new_contents:
1184
1159
new_path = self._limbo_name(trans_id)
1185
osutils.rename(old_path, new_path)
1186
for descendant in self._limbo_descendants(trans_id):
1187
desc_path = self._limbo_files[descendant]
1188
desc_path = new_path + desc_path[len(old_path):]
1189
self._limbo_files[descendant] = desc_path
1191
def _limbo_descendants(self, trans_id):
1192
"""Return the set of trans_ids whose limbo paths descend from this."""
1193
descendants = set(self._limbo_children.get(trans_id, []))
1194
for descendant in list(descendants):
1195
descendants.update(self._limbo_descendants(descendant))
1160
os.rename(old_path, new_path)
1198
1162
def create_file(self, contents, trans_id, mode_id=None):
1199
1163
"""Schedule creation of a new file.
1234
1197
def _read_symlink_target(self, trans_id):
1235
1198
return os.readlink(self._limbo_name(trans_id))
1237
def _set_mtime(self, path):
1238
"""All files that are created get the same mtime.
1240
This time is set by the first object to be created.
1242
if self._creation_mtime is None:
1243
self._creation_mtime = time.time()
1244
os.utime(path, (self._creation_mtime, self._creation_mtime))
1246
1200
def create_hardlink(self, path, trans_id):
1247
1201
"""Schedule creation of a hard link"""
1248
1202
name = self._limbo_name(trans_id)
1362
1316
FileMover does not delete files until it is sure that a rollback will not
1365
def __init__(self, tree, pb=None):
1319
def __init__(self, tree, pb=DummyProgress()):
1366
1320
"""Note: a tree_write lock is taken on the tree.
1368
1322
Use TreeTransform.finalize() to release the lock (can be omitted if
1714
1668
unversioned files in the input tree.
1717
def __init__(self, tree, pb=None, case_sensitive=True):
1671
def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
1718
1672
tree.lock_read()
1719
1673
limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1720
1674
DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
1820
1774
executable = self.is_executable(file_id, path)
1821
1775
return kind, executable, None
1823
def is_locked(self):
1826
1777
def lock_read(self):
1827
1778
# Perhaps in theory, this should lock the TreeTransform?
1830
1781
def unlock(self):
2014
1965
def get_file_mtime(self, file_id, path=None):
2015
1966
"""See Tree.get_file_mtime"""
2016
1967
if not self._content_change(file_id):
2017
return self._transform._tree.get_file_mtime(file_id)
1968
return self._transform._tree.get_file_mtime(file_id, path)
2018
1969
return self._stat_limbo_file(file_id).st_mtime
2020
1971
def _file_size(self, entry, stat_value):
2082
2033
executable = None
2083
2034
if kind == 'symlink':
2084
2035
link_or_sha1 = os.readlink(limbo_name).decode(osutils._fs_enc)
2085
executable = tt._new_executability.get(trans_id, executable)
2036
if supports_executable():
2037
executable = tt._new_executability.get(trans_id, executable)
2086
2038
return kind, size, executable, link_or_sha1
2088
2040
def iter_changes(self, from_tree, include_unchanged=False,
2401
2353
new_desired_files = desired_files
2403
2355
iter = accelerator_tree.iter_changes(tree, include_unchanged=True)
2404
unchanged = [(f, p[1]) for (f, p, c, v, d, n, k, e)
2405
in iter if not (c or e[0] != e[1])]
2406
if accelerator_tree.supports_content_filtering():
2407
unchanged = [(f, p) for (f, p) in unchanged
2408
if not accelerator_tree.iter_search_rules([p]).next()]
2409
unchanged = dict(unchanged)
2356
unchanged = dict((f, p[1]) for (f, p, c, v, d, n, k, e)
2357
in iter if not (c or e[0] != e[1]))
2410
2358
new_desired_files = []
2412
2360
for file_id, (trans_id, tree_path) in desired_files:
2617
2565
def revert(working_tree, target_tree, filenames, backups=False,
2618
pb=None, change_reporter=None):
2566
pb=DummyProgress(), change_reporter=None):
2619
2567
"""Revert a working tree's contents to those of a target tree."""
2620
2568
target_tree.lock_read()
2621
pb = ui.ui_factory.nested_progress_bar()
2622
2569
tt = TreeTransform(working_tree, pb)
2624
2571
pp = ProgressPhase("Revert phase", 3, pb)
2643
2590
def _prepare_revert_transform(working_tree, target_tree, tt, filenames,
2644
2591
backups, pp, basis_tree=None,
2645
2592
merge_modified=None):
2646
2594
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2648
2596
if merge_modified is None:
2652
2600
merge_modified, basis_tree)
2654
2602
child_pb.finished()
2655
2604
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2657
2606
raw_conflicts = resolve_conflicts(tt, child_pb,
2779
2728
return merge_modified
2782
def resolve_conflicts(tt, pb=None, pass_func=None):
2731
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
2783
2732
"""Make many conflict-resolution attempts, but die if they fail"""
2784
2733
if pass_func is None:
2785
2734
pass_func = conflict_pass
2786
2735
new_conflicts = set()
2787
pb = ui.ui_factory.nested_progress_bar()
2789
2737
for n in range(10):
2790
2738
pb.update('Resolution pass', n+1, 10)
2794
2742
new_conflicts.update(pass_func(tt, conflicts))
2795
2743
raise MalformedTransform(conflicts=conflicts)
2800
2748
def conflict_pass(tt, conflicts, path_tree=None):
2923
2871
self.pending_deletions = []
2925
2873
def rename(self, from_, to):
2926
"""Rename a file from one path to another."""
2874
"""Rename a file from one path to another. Functions like os.rename"""
2928
osutils.rename(from_, to)
2876
os.rename(from_, to)
2929
2877
except OSError, e:
2930
2878
if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
2931
2879
raise errors.FileExists(to, str(e))
2945
2893
def rollback(self):
2946
2894
"""Reverse all renames that have been performed"""
2947
2895
for from_, to in reversed(self.past_renames):
2948
osutils.rename(to, from_)
2896
os.rename(to, from_)
2949
2897
# after rollback, don't reuse _FileMover
2950
2898
past_renames = None
2951
2899
pending_deletions = None