32
34
revision as _mod_revision,
35
38
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
36
ReusingTransform, NotVersionedError, CantMoveRoot,
39
ReusingTransform, CantMoveRoot,
37
40
ExistingLimbo, ImmortalLimbo, NoFinalPath,
38
41
UnableCreateSymlink)
39
42
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
78
81
class TreeTransformBase(object):
79
82
"""The base class for TreeTransform and its kin."""
81
def __init__(self, tree, pb=DummyProgress(),
84
def __init__(self, tree, pb=None,
82
85
case_sensitive=True):
85
88
:param tree: The tree that will be transformed, but not necessarily
87
:param pb: A ProgressBar indicating how much progress is being made
88
91
:param case_sensitive: If True, the target of the transform is
89
92
case sensitive, not just case preserving.
162
165
def adjust_path(self, name, parent, trans_id):
163
166
"""Change the path that is assigned to a transaction id."""
168
raise ValueError("Parent trans-id may not be None")
164
169
if trans_id == self._new_root:
165
170
raise CantMoveRoot
166
171
self._new_name[trans_id] = name
167
172
self._new_parent[trans_id] = parent
168
if parent == ROOT_PARENT:
169
if self._new_root is not None:
170
raise ValueError("Cannot have multiple roots.")
171
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)
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]
205
267
def trans_id_tree_file_id(self, inventory_id):
206
268
"""Determine the transaction id of a working tree file.
325
388
changed_kind = set(self._removed_contents)
326
389
changed_kind.intersection_update(self._new_contents)
327
390
changed_kind.difference_update(new_ids)
328
changed_kind = (t for t in changed_kind if self.tree_kind(t) !=
391
changed_kind = (t for t in changed_kind
392
if self.tree_kind(t) != self.final_kind(t))
330
393
new_ids.update(changed_kind)
331
394
return sorted(FinalPaths(self).get_paths(new_ids))
333
396
def final_kind(self, trans_id):
334
397
"""Determine the final file kind, after any changes applied.
336
Raises NoSuchFile if the file does not exist/has no contents.
337
(It is conceivable that a path would be created without the
338
corresponding contents insertion command)
399
:return: None if the file does not exist/has no contents. (It is
400
conceivable that a path would be created without the corresponding
401
contents insertion command)
340
403
if trans_id in self._new_contents:
341
404
return self._new_contents[trans_id]
342
405
elif trans_id in self._removed_contents:
343
raise NoSuchFile(None)
345
408
return self.tree_kind(trans_id)
776
823
Return a (name, parent, kind, executable) tuple
778
825
to_name = self.final_name(to_trans_id)
780
to_kind = self.final_kind(to_trans_id)
826
to_kind = self.final_kind(to_trans_id)
783
827
to_parent = self.final_file_id(self.final_parent(to_trans_id))
784
828
if to_trans_id in self._new_executability:
785
829
to_executable = self._new_executability[to_trans_id]
854
898
def get_preview_tree(self):
855
899
"""Return a tree representing the result of the transform.
857
This tree only supports the subset of Tree functionality required
858
by show_diff_trees. It must only be compared to tt._tree.
901
The tree is a snapshot, and altering the TreeTransform will invalidate
860
904
return _PreviewTree(self)
906
def commit(self, branch, message, merge_parents=None, strict=False,
907
timestamp=None, timezone=None, committer=None, authors=None,
908
revprops=None, revision_id=None):
909
"""Commit the result of this TreeTransform to a branch.
911
:param branch: The branch to commit to.
912
:param message: The message to attach to the commit.
913
:param merge_parents: Additional parent revision-ids specified by
915
:param strict: If True, abort the commit if there are unversioned
917
:param timestamp: if not None, seconds-since-epoch for the time and
918
date. (May be a float.)
919
:param timezone: Optional timezone for timestamp, as an offset in
921
:param committer: Optional committer in email-id format.
922
(e.g. "J Random Hacker <jrandom@example.com>")
923
:param authors: Optional list of authors in email-id format.
924
:param revprops: Optional dictionary of revision properties.
925
:param revision_id: Optional revision id. (Specifying a revision-id
926
may reduce performance for some non-native formats.)
927
:return: The revision_id of the revision committed.
929
self._check_malformed()
931
unversioned = set(self._new_contents).difference(set(self._new_id))
932
for trans_id in unversioned:
933
if self.final_file_id(trans_id) is None:
934
raise errors.StrictCommitFailed()
936
revno, last_rev_id = branch.last_revision_info()
937
if last_rev_id == _mod_revision.NULL_REVISION:
938
if merge_parents is not None:
939
raise ValueError('Cannot supply merge parents for first'
943
parent_ids = [last_rev_id]
944
if merge_parents is not None:
945
parent_ids.extend(merge_parents)
946
if self._tree.get_revision_id() != last_rev_id:
947
raise ValueError('TreeTransform not based on branch basis: %s' %
948
self._tree.get_revision_id())
949
revprops = commit.Commit.update_revprops(revprops, branch, authors)
950
builder = branch.get_commit_builder(parent_ids,
955
revision_id=revision_id)
956
preview = self.get_preview_tree()
957
list(builder.record_iter_changes(preview, last_rev_id,
958
self.iter_changes()))
959
builder.finish_inventory()
960
revision_id = builder.commit(message)
961
branch.set_last_revision_info(revno + 1, revision_id)
862
964
def _text_parent(self, trans_id):
863
965
file_id = self.tree_file_id(trans_id)
958
1060
class DiskTreeTransform(TreeTransformBase):
959
1061
"""Tree transform storing its contents on disk."""
961
def __init__(self, tree, limbodir, pb=DummyProgress(),
1063
def __init__(self, tree, limbodir, pb=None,
962
1064
case_sensitive=True):
964
1066
:param tree: The tree that will be transformed, but not necessarily
965
1067
the output tree.
966
1068
:param limbodir: A directory where new files can be stored until
967
1069
they are installed in their proper places
968
:param pb: A ProgressBar indicating how much progress is being made
969
1071
:param case_sensitive: If True, the target of the transform is
970
1072
case sensitive, not just case preserving.
995
1098
self._new_contents.iteritems()]
996
1099
entries.sort(reverse=True)
997
1100
for path, trans_id, kind in entries:
998
if kind == "directory":
1003
os.rmdir(self._limbodir)
1103
delete_any(self._limbodir)
1004
1104
except OSError:
1005
1105
# We don't especially care *why* the dir is immortal.
1006
1106
raise ImmortalLimbo(self._limbodir)
1008
1108
if self._deletiondir is not None:
1009
os.rmdir(self._deletiondir)
1109
delete_any(self._deletiondir)
1010
1110
except OSError:
1011
1111
raise errors.ImmortalPendingDeletion(self._deletiondir)
1015
1115
def _limbo_name(self, trans_id):
1016
1116
"""Generate the limbo name of a file"""
1017
1117
limbo_name = self._limbo_files.get(trans_id)
1018
if limbo_name is not None:
1020
parent = self._new_parent.get(trans_id)
1021
# if the parent directory is already in limbo (e.g. when building a
1022
# tree), choose a limbo name inside the parent, to reduce further
1024
use_direct_path = False
1025
if self._new_contents.get(parent) == 'directory':
1026
filename = self._new_name.get(trans_id)
1027
if filename is not None:
1028
if parent not in self._limbo_children:
1029
self._limbo_children[parent] = set()
1030
self._limbo_children_names[parent] = {}
1031
use_direct_path = True
1032
# the direct path can only be used if no other file has
1033
# already taken this pathname, i.e. if the name is unused, or
1034
# if it is already associated with this trans_id.
1035
elif self._case_sensitive_target:
1036
if (self._limbo_children_names[parent].get(filename)
1037
in (trans_id, None)):
1038
use_direct_path = True
1040
for l_filename, l_trans_id in\
1041
self._limbo_children_names[parent].iteritems():
1042
if l_trans_id == trans_id:
1044
if l_filename.lower() == filename.lower():
1047
use_direct_path = True
1050
limbo_name = pathjoin(self._limbo_files[parent], filename)
1051
self._limbo_children[parent].add(trans_id)
1052
self._limbo_children_names[parent][filename] = trans_id
1054
limbo_name = pathjoin(self._limbodir, trans_id)
1055
self._needs_rename.add(trans_id)
1056
self._limbo_files[trans_id] = limbo_name
1118
if limbo_name is None:
1119
limbo_name = self._generate_limbo_path(trans_id)
1120
self._limbo_files[trans_id] = limbo_name
1057
1121
return limbo_name
1123
def _generate_limbo_path(self, trans_id):
1124
"""Generate a limbo path using the trans_id as the relative path.
1126
This is suitable as a fallback, and when the transform should not be
1127
sensitive to the path encoding of the limbo directory.
1129
self._needs_rename.add(trans_id)
1130
return pathjoin(self._limbodir, trans_id)
1059
1132
def adjust_path(self, name, parent, trans_id):
1060
1133
previous_parent = self._new_parent.get(trans_id)
1061
1134
previous_name = self._new_name.get(trans_id)
1063
1136
if (trans_id in self._limbo_files and
1064
1137
trans_id not in self._needs_rename):
1065
1138
self._rename_in_limbo([trans_id])
1066
self._limbo_children[previous_parent].remove(trans_id)
1067
del self._limbo_children_names[previous_parent][previous_name]
1139
if previous_parent != parent:
1140
self._limbo_children[previous_parent].remove(trans_id)
1141
if previous_parent != parent or previous_name != name:
1142
del self._limbo_children_names[previous_parent][previous_name]
1069
1144
def _rename_in_limbo(self, trans_ids):
1070
1145
"""Fix limbo names so that the right final path is produced.
1084
1159
new_path = self._limbo_name(trans_id)
1085
1160
os.rename(old_path, new_path)
1161
for descendant in self._limbo_descendants(trans_id):
1162
desc_path = self._limbo_files[descendant]
1163
desc_path = new_path + desc_path[len(old_path):]
1164
self._limbo_files[descendant] = desc_path
1166
def _limbo_descendants(self, trans_id):
1167
"""Return the set of trans_ids whose limbo paths descend from this."""
1168
descendants = set(self._limbo_children.get(trans_id, []))
1169
for descendant in list(descendants):
1170
descendants.update(self._limbo_descendants(descendant))
1087
1173
def create_file(self, contents, trans_id, mode_id=None):
1088
1174
"""Schedule creation of a new file.
1122
1209
def _read_symlink_target(self, trans_id):
1123
1210
return os.readlink(self._limbo_name(trans_id))
1212
def _set_mtime(self, path):
1213
"""All files that are created get the same mtime.
1215
This time is set by the first object to be created.
1217
if self._creation_mtime is None:
1218
self._creation_mtime = time.time()
1219
os.utime(path, (self._creation_mtime, self._creation_mtime))
1125
1221
def create_hardlink(self, path, trans_id):
1126
1222
"""Schedule creation of a hard link"""
1127
1223
name = self._limbo_name(trans_id)
1298
1394
def tree_kind(self, trans_id):
1299
1395
"""Determine the file kind in the working tree.
1301
Raises NoSuchFile if the file does not exist
1397
:returns: The file kind or None if the file does not exist
1303
1399
path = self._tree_id_paths.get(trans_id)
1304
1400
if path is None:
1305
raise NoSuchFile(None)
1307
1403
return file_kind(self._tree.abspath(path))
1309
if e.errno != errno.ENOENT:
1312
raise NoSuchFile(path)
1404
except errors.NoSuchFile:
1314
1407
def _set_mode(self, trans_id, mode_id, typefunc):
1315
1408
"""Set the mode of new file contents.
1358
1451
yield self.trans_id_tree_path(childpath)
1453
def _generate_limbo_path(self, trans_id):
1454
"""Generate a limbo path using the final path if possible.
1456
This optimizes the performance of applying the tree transform by
1457
avoiding renames. These renames can be avoided only when the parent
1458
directory is already scheduled for creation.
1460
If the final path cannot be used, falls back to using the trans_id as
1463
parent = self._new_parent.get(trans_id)
1464
# if the parent directory is already in limbo (e.g. when building a
1465
# tree), choose a limbo name inside the parent, to reduce further
1467
use_direct_path = False
1468
if self._new_contents.get(parent) == 'directory':
1469
filename = self._new_name.get(trans_id)
1470
if filename is not None:
1471
if parent not in self._limbo_children:
1472
self._limbo_children[parent] = set()
1473
self._limbo_children_names[parent] = {}
1474
use_direct_path = True
1475
# the direct path can only be used if no other file has
1476
# already taken this pathname, i.e. if the name is unused, or
1477
# if it is already associated with this trans_id.
1478
elif self._case_sensitive_target:
1479
if (self._limbo_children_names[parent].get(filename)
1480
in (trans_id, None)):
1481
use_direct_path = True
1483
for l_filename, l_trans_id in\
1484
self._limbo_children_names[parent].iteritems():
1485
if l_trans_id == trans_id:
1487
if l_filename.lower() == filename.lower():
1490
use_direct_path = True
1492
if not use_direct_path:
1493
return DiskTreeTransform._generate_limbo_path(self, trans_id)
1495
limbo_name = pathjoin(self._limbo_files[parent], filename)
1496
self._limbo_children[parent].add(trans_id)
1497
self._limbo_children_names[parent][filename] = trans_id
1361
1501
def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1362
1502
"""Apply all changes to the inventory and filesystem.
1485
1622
child_pb.update('removing file', num, len(tree_paths))
1486
1623
full_path = self._tree.abspath(path)
1487
1624
if trans_id in self._removed_contents:
1488
mover.pre_delete(full_path, os.path.join(self._deletiondir,
1490
elif trans_id in self._new_name or trans_id in \
1625
delete_path = os.path.join(self._deletiondir, trans_id)
1626
mover.pre_delete(full_path, delete_path)
1627
elif (trans_id in self._new_name
1628
or trans_id in self._new_parent):
1493
1630
mover.rename(full_path, self._limbo_name(trans_id))
1631
except errors.TransformRenameFailed, e:
1495
1632
if e.errno != errno.ENOENT:
1548
1685
unversioned files in the input tree.
1551
def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
1688
def __init__(self, tree, pb=None, case_sensitive=True):
1552
1689
tree.lock_read()
1553
1690
limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1554
1691
DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
1559
1696
def tree_kind(self, trans_id):
1560
1697
path = self._tree_id_paths.get(trans_id)
1561
1698
if path is None:
1562
raise NoSuchFile(None)
1563
1700
file_id = self._tree.path2id(path)
1564
return self._tree.kind(file_id)
1702
return self._tree.kind(file_id)
1703
except errors.NoSuchFile:
1566
1706
def _set_mode(self, trans_id, mode_id, typefunc):
1567
1707
"""Set the mode of new file contents.
1599
1739
self._all_children_cache = {}
1600
1740
self._path2trans_id_cache = {}
1601
1741
self._final_name_cache = {}
1603
def _changes(self, file_id):
1604
for changes in self._transform.iter_changes():
1605
if changes[0] == file_id:
1742
self._iter_changes_cache = dict((c[0], c) for c in
1743
self._transform.iter_changes())
1608
1745
def _content_change(self, file_id):
1609
1746
"""Return True if the content of this file changed"""
1610
changes = self._changes(file_id)
1747
changes = self._iter_changes_cache.get(file_id)
1611
1748
# changes[2] is true if the file content changed. See
1612
1749
# InterTree.iter_changes.
1613
1750
return (changes is not None and changes[2])
1629
1766
parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
1630
1767
self._iter_parent_trees()]
1631
1768
vf.add_lines((file_id, tree_revision), parent_keys,
1632
self.get_file(file_id).readlines())
1769
self.get_file_lines(file_id))
1633
1770
repo = self._get_repository()
1634
1771
base_vf = repo.texts
1635
1772
if base_vf not in vf.fallback_versionedfiles:
1682
1822
def __iter__(self):
1683
1823
return iter(self.all_file_ids())
1685
def has_id(self, file_id):
1825
def _has_id(self, file_id, fallback_check):
1686
1826
if file_id in self._transform._r_new_id:
1688
1828
elif file_id in set([self._transform.tree_file_id(trans_id) for
1689
1829
trans_id in self._transform._removed_id]):
1692
return self._transform._tree.has_id(file_id)
1832
return fallback_check(file_id)
1834
def has_id(self, file_id):
1835
return self._has_id(file_id, self._transform._tree.has_id)
1837
def has_or_had_id(self, file_id):
1838
return self._has_id(file_id, self._transform._tree.has_or_had_id)
1694
1840
def _path2trans_id(self, path):
1695
1841
# We must not use None here, because that is a valid value to store.
1748
1894
if self._transform.final_file_id(trans_id) is None:
1749
1895
yield self._final_paths._determine_path(trans_id)
1751
def _make_inv_entries(self, ordered_entries, specific_file_ids=None):
1897
def _make_inv_entries(self, ordered_entries, specific_file_ids=None,
1898
yield_parents=False):
1752
1899
for trans_id, parent_file_id in ordered_entries:
1753
1900
file_id = self._transform.final_file_id(trans_id)
1754
1901
if file_id is None:
1780
1926
ordered_ids.append((trans_id, parent_file_id))
1781
1927
return ordered_ids
1783
def iter_entries_by_dir(self, specific_file_ids=None):
1929
def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
1784
1930
# This may not be a maximally efficient implementation, but it is
1785
1931
# reasonably straightforward. An implementation that grafts the
1786
1932
# TreeTransform changes onto the tree's iter_entries_by_dir results
1841
1987
def get_file_mtime(self, file_id, path=None):
1842
1988
"""See Tree.get_file_mtime"""
1843
1989
if not self._content_change(file_id):
1844
return self._transform._tree.get_file_mtime(file_id, path)
1990
return self._transform._tree.get_file_mtime(file_id)
1845
1991
return self._stat_limbo_file(file_id).st_mtime
1847
1993
def _file_size(self, entry, stat_value):
1909
2055
executable = None
1910
2056
if kind == 'symlink':
1911
2057
link_or_sha1 = os.readlink(limbo_name).decode(osutils._fs_enc)
1912
if supports_executable():
1913
executable = tt._new_executability.get(trans_id, executable)
2058
executable = tt._new_executability.get(trans_id, executable)
1914
2059
return kind, size, executable, link_or_sha1
1916
2061
def iter_changes(self, from_tree, include_unchanged=False,
1965
2110
return old_annotation
1966
2111
if not changed_content:
1967
2112
return old_annotation
2113
# TODO: This is doing something similar to what WT.annotate_iter is
2114
# doing, however it fails slightly because it doesn't know what
2115
# the *other* revision_id is, so it doesn't know how to give the
2116
# other as the origin for some lines, they all get
2117
# 'default_revision'
2118
# It would be nice to be able to use the new Annotator based
2119
# approach, as well.
1968
2120
return annotate.reannotate([old_annotation],
1969
2121
self.get_file(file_id).readlines(),
1970
2122
default_revision)
2112
2264
for num, _unused in enumerate(wt.all_file_ids()):
2113
2265
if num > 0: # more than just a root
2114
2266
raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
2115
existing_files = set()
2116
for dir, files in wt.walkdirs():
2117
existing_files.update(f[0] for f in files)
2118
2267
file_trans_id = {}
2119
2268
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2120
2269
pp = ProgressPhase("Build phase", 2, top_pb)
2144
2293
precomputed_delta = []
2146
2295
precomputed_delta = None
2296
# Check if tree inventory has content. If so, we populate
2297
# existing_files with the directory content. If there are no
2298
# entries we skip populating existing_files as its not used.
2299
# This improves performance and unncessary work on large
2300
# directory trees. (#501307)
2302
existing_files = set()
2303
for dir, files in wt.walkdirs():
2304
existing_files.update(f[0] for f in files)
2147
2305
for num, (tree_path, entry) in \
2148
2306
enumerate(tree.inventory.iter_entries_by_dir()):
2149
2307
pb.update("Building tree", num - len(deferred_contents), total)
2222
2380
new_desired_files = desired_files
2224
2382
iter = accelerator_tree.iter_changes(tree, include_unchanged=True)
2225
unchanged = dict((f, p[1]) for (f, p, c, v, d, n, k, e)
2226
in iter if not (c or e[0] != e[1]))
2383
unchanged = [(f, p[1]) for (f, p, c, v, d, n, k, e)
2384
in iter if not (c or e[0] != e[1])]
2385
if accelerator_tree.supports_content_filtering():
2386
unchanged = [(f, p) for (f, p) in unchanged
2387
if not accelerator_tree.iter_search_rules([p]).next()]
2388
unchanged = dict(unchanged)
2227
2389
new_desired_files = []
2229
2391
for file_id, (trans_id, tree_path) in desired_files:
2336
2502
raise errors.BadFileKindError(name, kind)
2339
@deprecated_function(deprecated_in((1, 9, 0)))
2340
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
2341
"""Create new file contents according to an inventory entry.
2343
DEPRECATED. Use create_from_tree instead.
2505
def create_from_tree(tt, trans_id, tree, file_id, bytes=None,
2506
filter_tree_path=None):
2507
"""Create new file contents according to tree contents.
2509
:param filter_tree_path: the tree path to use to lookup
2510
content filters to apply to the bytes output in the working tree.
2511
This only applies if the working tree supports content filtering.
2345
if entry.kind == "file":
2347
lines = tree.get_file(entry.file_id).readlines()
2348
tt.create_file(lines, trans_id, mode_id=mode_id)
2349
elif entry.kind == "symlink":
2350
tt.create_symlink(tree.get_symlink_target(entry.file_id), trans_id)
2351
elif entry.kind == "directory":
2352
tt.create_directory(trans_id)
2355
def create_from_tree(tt, trans_id, tree, file_id, bytes=None):
2356
"""Create new file contents according to tree contents."""
2357
2513
kind = tree.kind(file_id)
2358
2514
if kind == 'directory':
2359
2515
tt.create_directory(trans_id)
2364
2520
bytes = tree_file.readlines()
2366
2522
tree_file.close()
2524
if wt.supports_content_filtering() and filter_tree_path is not None:
2525
filters = wt._content_filter_stack(filter_tree_path)
2526
bytes = filtered_output_bytes(bytes, filters,
2527
ContentFilterContext(filter_tree_path, tree))
2367
2528
tt.create_file(bytes, trans_id)
2368
2529
elif kind == "symlink":
2369
2530
tt.create_symlink(tree.get_symlink_target(file_id), trans_id)
2423
2584
def revert(working_tree, target_tree, filenames, backups=False,
2424
pb=DummyProgress(), change_reporter=None):
2585
pb=None, change_reporter=None):
2425
2586
"""Revert a working tree's contents to those of a target tree."""
2426
2587
target_tree.lock_read()
2588
pb = ui.ui_factory.nested_progress_bar()
2427
2589
tt = TreeTransform(working_tree, pb)
2429
2591
pp = ProgressPhase("Revert phase", 3, pb)
2557
2717
parent_trans = ROOT_PARENT
2559
2719
parent_trans = tt.trans_id_file_id(parent[1])
2560
tt.adjust_path(name[1], parent_trans, trans_id)
2720
if parent[0] is None and versioned[0]:
2721
tt.adjust_root_path(name[1], parent_trans)
2723
tt.adjust_path(name[1], parent_trans, trans_id)
2561
2724
if executable[0] != executable[1] and kind[1] == "file":
2562
2725
tt.set_executability(executable[1], trans_id)
2563
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2565
tt.create_file(bytes, trans_id, mode_id)
2726
if working_tree.supports_content_filtering():
2727
for index, ((trans_id, mode_id), bytes) in enumerate(
2728
target_tree.iter_files_bytes(deferred_files)):
2729
file_id = deferred_files[index][0]
2730
# We're reverting a tree to the target tree so using the
2731
# target tree to find the file path seems the best choice
2732
# here IMO - Ian C 27/Oct/2009
2733
filter_tree_path = target_tree.id2path(file_id)
2734
filters = working_tree._content_filter_stack(filter_tree_path)
2735
bytes = filtered_output_bytes(bytes, filters,
2736
ContentFilterContext(filter_tree_path, working_tree))
2737
tt.create_file(bytes, trans_id, mode_id)
2739
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2741
tt.create_file(bytes, trans_id, mode_id)
2742
tt.fixup_new_roots()
2567
2744
if basis_tree is not None:
2568
2745
basis_tree.unlock()
2569
2746
return merge_modified
2572
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
2749
def resolve_conflicts(tt, pb=None, pass_func=None):
2573
2750
"""Make many conflict-resolution attempts, but die if they fail"""
2574
2751
if pass_func is None:
2575
2752
pass_func = conflict_pass
2576
2753
new_conflicts = set()
2754
pb = ui.ui_factory.nested_progress_bar()
2578
2756
for n in range(10):
2579
2757
pb.update('Resolution pass', n+1, 10)
2712
2890
self.pending_deletions = []
2714
2892
def rename(self, from_, to):
2715
"""Rename a file from one path to another. Functions like os.rename"""
2893
"""Rename a file from one path to another."""
2717
2895
os.rename(from_, to)
2718
2896
except OSError, e:
2719
2897
if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
2720
2898
raise errors.FileExists(to, str(e))
2899
# normal OSError doesn't include filenames so it's hard to see where
2900
# the problem is, see https://bugs.launchpad.net/bzr/+bug/491763
2901
raise errors.TransformRenameFailed(from_, to, str(e), e.errno)
2722
2902
self.past_renames.append((from_, to))
2724
2904
def pre_delete(self, from_, to):