19
19
from stat import S_ISREG, S_IEXEC
22
21
from bzrlib.lazy_import import lazy_import
23
22
lazy_import(globals(), """
24
23
from bzrlib import (
34
31
revision as _mod_revision,
33
from bzrlib.util import bencode
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 ProgressBar 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.
165
162
def adjust_path(self, name, parent, trans_id):
166
163
"""Change the path that is assigned to a transaction id."""
168
raise ValueError("Parent trans-id may not be None")
169
164
if trans_id == self._new_root:
170
165
raise CantMoveRoot
171
166
self._new_name[trans_id] = name
172
167
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
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)
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
205
def trans_id_tree_file_id(self, inventory_id):
268
206
"""Determine the transaction id of a working tree file.
388
325
changed_kind = set(self._removed_contents)
389
326
changed_kind.intersection_update(self._new_contents)
390
327
changed_kind.difference_update(new_ids)
391
changed_kind = (t for t in changed_kind
392
if self.tree_kind(t) != self.final_kind(t))
328
changed_kind = (t for t in changed_kind if self.tree_kind(t) !=
393
330
new_ids.update(changed_kind)
394
331
return sorted(FinalPaths(self).get_paths(new_ids))
396
333
def final_kind(self, trans_id):
397
334
"""Determine the final file kind, after any changes applied.
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)
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)
403
340
if trans_id in self._new_contents:
404
341
return self._new_contents[trans_id]
405
342
elif trans_id in self._removed_contents:
343
raise NoSuchFile(None)
408
345
return self.tree_kind(trans_id)
823
776
Return a (name, parent, kind, executable) tuple
825
778
to_name = self.final_name(to_trans_id)
826
to_kind = self.final_kind(to_trans_id)
780
to_kind = self.final_kind(to_trans_id)
827
783
to_parent = self.final_file_id(self.final_parent(to_trans_id))
828
784
if to_trans_id in self._new_executability:
829
785
to_executable = self._new_executability[to_trans_id]
898
854
def get_preview_tree(self):
899
855
"""Return a tree representing the result of the transform.
901
The tree is a snapshot, and altering the TreeTransform will invalidate
857
This tree only supports the subset of Tree functionality required
858
by show_diff_trees. It must only be compared to tt._tree.
904
860
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)
964
862
def _text_parent(self, trans_id):
965
863
file_id = self.tree_file_id(trans_id)
1060
958
class DiskTreeTransform(TreeTransformBase):
1061
959
"""Tree transform storing its contents on disk."""
1063
def __init__(self, tree, limbodir, pb=None,
961
def __init__(self, tree, limbodir, pb=DummyProgress(),
1064
962
case_sensitive=True):
1066
964
:param tree: The tree that will be transformed, but not necessarily
1067
965
the output tree.
1068
966
:param limbodir: A directory where new files can be stored until
1069
967
they are installed in their proper places
968
:param pb: A ProgressBar indicating how much progress is being made
1071
969
:param case_sensitive: If True, the target of the transform is
1072
970
case sensitive, not just case preserving.
1098
995
self._new_contents.iteritems()]
1099
996
entries.sort(reverse=True)
1100
997
for path, trans_id, kind in entries:
998
if kind == "directory":
1103
delete_any(self._limbodir)
1003
os.rmdir(self._limbodir)
1104
1004
except OSError:
1105
1005
# We don't especially care *why* the dir is immortal.
1106
1006
raise ImmortalLimbo(self._limbodir)
1108
1008
if self._deletiondir is not None:
1109
delete_any(self._deletiondir)
1009
os.rmdir(self._deletiondir)
1110
1010
except OSError:
1111
1011
raise errors.ImmortalPendingDeletion(self._deletiondir)
1115
1015
def _limbo_name(self, trans_id):
1116
1016
"""Generate the limbo name of a file"""
1117
1017
limbo_name = self._limbo_files.get(trans_id)
1118
if limbo_name is None:
1119
limbo_name = self._generate_limbo_path(trans_id)
1120
self._limbo_files[trans_id] = limbo_name
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
1121
1057
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)
1132
1059
def adjust_path(self, name, parent, trans_id):
1133
1060
previous_parent = self._new_parent.get(trans_id)
1134
1061
previous_name = self._new_name.get(trans_id)
1136
1063
if (trans_id in self._limbo_files and
1137
1064
trans_id not in self._needs_rename):
1138
1065
self._rename_in_limbo([trans_id])
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]
1066
self._limbo_children[previous_parent].remove(trans_id)
1067
del self._limbo_children_names[previous_parent][previous_name]
1144
1069
def _rename_in_limbo(self, trans_ids):
1145
1070
"""Fix limbo names so that the right final path is produced.
1159
1084
new_path = self._limbo_name(trans_id)
1160
1085
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))
1173
1087
def create_file(self, contents, trans_id, mode_id=None):
1174
1088
"""Schedule creation of a new file.
1209
1122
def _read_symlink_target(self, trans_id):
1210
1123
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))
1221
1125
def create_hardlink(self, path, trans_id):
1222
1126
"""Schedule creation of a hard link"""
1223
1127
name = self._limbo_name(trans_id)
1394
1298
def tree_kind(self, trans_id):
1395
1299
"""Determine the file kind in the working tree.
1397
:returns: The file kind or None if the file does not exist
1301
Raises NoSuchFile if the file does not exist
1399
1303
path = self._tree_id_paths.get(trans_id)
1400
1304
if path is None:
1305
raise NoSuchFile(None)
1403
1307
return file_kind(self._tree.abspath(path))
1404
except errors.NoSuchFile:
1309
if e.errno != errno.ENOENT:
1312
raise NoSuchFile(path)
1407
1314
def _set_mode(self, trans_id, mode_id, typefunc):
1408
1315
"""Set the mode of new file contents.
1451
1358
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
1501
1361
def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1502
1362
"""Apply all changes to the inventory and filesystem.
1622
1485
child_pb.update('removing file', num, len(tree_paths))
1623
1486
full_path = self._tree.abspath(path)
1624
1487
if trans_id in self._removed_contents:
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):
1488
mover.pre_delete(full_path, os.path.join(self._deletiondir,
1490
elif trans_id in self._new_name or trans_id in \
1630
1493
mover.rename(full_path, self._limbo_name(trans_id))
1631
except errors.TransformRenameFailed, e:
1632
1495
if e.errno != errno.ENOENT:
1685
1548
unversioned files in the input tree.
1688
def __init__(self, tree, pb=None, case_sensitive=True):
1551
def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
1689
1552
tree.lock_read()
1690
1553
limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1691
1554
DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
1696
1559
def tree_kind(self, trans_id):
1697
1560
path = self._tree_id_paths.get(trans_id)
1698
1561
if path is None:
1562
raise NoSuchFile(None)
1700
1563
file_id = self._tree.path2id(path)
1702
return self._tree.kind(file_id)
1703
except errors.NoSuchFile:
1564
return self._tree.kind(file_id)
1706
1566
def _set_mode(self, trans_id, mode_id, typefunc):
1707
1567
"""Set the mode of new file contents.
1739
1599
self._all_children_cache = {}
1740
1600
self._path2trans_id_cache = {}
1741
1601
self._final_name_cache = {}
1742
self._iter_changes_cache = dict((c[0], c) for c in
1743
self._transform.iter_changes())
1603
def _changes(self, file_id):
1604
for changes in self._transform.iter_changes():
1605
if changes[0] == file_id:
1745
1608
def _content_change(self, file_id):
1746
1609
"""Return True if the content of this file changed"""
1747
changes = self._iter_changes_cache.get(file_id)
1610
changes = self._changes(file_id)
1748
1611
# changes[2] is true if the file content changed. See
1749
1612
# InterTree.iter_changes.
1750
1613
return (changes is not None and changes[2])
1766
1629
parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
1767
1630
self._iter_parent_trees()]
1768
1631
vf.add_lines((file_id, tree_revision), parent_keys,
1769
self.get_file_lines(file_id))
1632
self.get_file(file_id).readlines())
1770
1633
repo = self._get_repository()
1771
1634
base_vf = repo.texts
1772
1635
if base_vf not in vf.fallback_versionedfiles:
1822
1682
def __iter__(self):
1823
1683
return iter(self.all_file_ids())
1825
def _has_id(self, file_id, fallback_check):
1685
def has_id(self, file_id):
1826
1686
if file_id in self._transform._r_new_id:
1828
1688
elif file_id in set([self._transform.tree_file_id(trans_id) for
1829
1689
trans_id in self._transform._removed_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)
1692
return self._transform._tree.has_id(file_id)
1840
1694
def _path2trans_id(self, path):
1841
1695
# We must not use None here, because that is a valid value to store.
1894
1748
if self._transform.final_file_id(trans_id) is None:
1895
1749
yield self._final_paths._determine_path(trans_id)
1897
def _make_inv_entries(self, ordered_entries, specific_file_ids=None,
1898
yield_parents=False):
1751
def _make_inv_entries(self, ordered_entries, specific_file_ids):
1899
1752
for trans_id, parent_file_id in ordered_entries:
1900
1753
file_id = self._transform.final_file_id(trans_id)
1901
1754
if file_id is None:
1926
1780
ordered_ids.append((trans_id, parent_file_id))
1927
1781
return ordered_ids
1929
def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
1783
def iter_entries_by_dir(self, specific_file_ids=None):
1930
1784
# This may not be a maximally efficient implementation, but it is
1931
1785
# reasonably straightforward. An implementation that grafts the
1932
1786
# TreeTransform changes onto the tree's iter_entries_by_dir results
1935
1789
ordered_ids = self._list_files_by_dir()
1936
1790
for entry, trans_id in self._make_inv_entries(ordered_ids,
1937
specific_file_ids, yield_parents=yield_parents):
1938
yield unicode(self._final_paths.get_path(trans_id)), entry
1940
def _iter_entries_for_dir(self, dir_path):
1941
"""Return path, entry for items in a directory without recursing down."""
1942
dir_file_id = self.path2id(dir_path)
1944
for file_id in self.iter_children(dir_file_id):
1945
trans_id = self._transform.trans_id_file_id(file_id)
1946
ordered_ids.append((trans_id, file_id))
1947
for entry, trans_id in self._make_inv_entries(ordered_ids):
1948
yield unicode(self._final_paths.get_path(trans_id)), entry
1950
def list_files(self, include_root=False, from_dir=None, recursive=True):
1951
"""See WorkingTree.list_files."""
1792
yield unicode(self._final_paths.get_path(trans_id)), entry
1794
def list_files(self, include_root=False):
1795
"""See Tree.list_files."""
1952
1796
# XXX This should behave like WorkingTree.list_files, but is really
1953
1797
# more like RevisionTree.list_files.
1957
prefix = from_dir + '/'
1958
entries = self.iter_entries_by_dir()
1959
for path, entry in entries:
1960
if entry.name == '' and not include_root:
1963
if not path.startswith(prefix):
1965
path = path[len(prefix):]
1966
yield path, 'V', entry.kind, entry.file_id, entry
1968
if from_dir is None and include_root is True:
1969
root_entry = inventory.make_entry('directory', '',
1970
ROOT_PARENT, self.get_root_id())
1971
yield '', 'V', 'directory', root_entry.file_id, root_entry
1972
entries = self._iter_entries_for_dir(from_dir or '')
1973
for path, entry in entries:
1974
yield path, 'V', entry.kind, entry.file_id, entry
1798
for path, entry in self.iter_entries_by_dir():
1799
if entry.name == '' and not include_root:
1801
yield path, 'V', entry.kind, entry.file_id, entry
1976
1803
def kind(self, file_id):
1977
1804
trans_id = self._transform.trans_id_file_id(file_id)
1987
1814
def get_file_mtime(self, file_id, path=None):
1988
1815
"""See Tree.get_file_mtime"""
1989
1816
if not self._content_change(file_id):
1990
return self._transform._tree.get_file_mtime(file_id)
1817
return self._transform._tree.get_file_mtime(file_id, path)
1991
1818
return self._stat_limbo_file(file_id).st_mtime
1993
1820
def _file_size(self, entry, stat_value):
2055
1882
executable = None
2056
1883
if kind == 'symlink':
2057
1884
link_or_sha1 = os.readlink(limbo_name).decode(osutils._fs_enc)
2058
executable = tt._new_executability.get(trans_id, executable)
1885
if supports_executable():
1886
executable = tt._new_executability.get(trans_id, executable)
2059
1887
return kind, size, executable, link_or_sha1
2061
1889
def iter_changes(self, from_tree, include_unchanged=False,
2110
1938
return old_annotation
2111
1939
if not changed_content:
2112
1940
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.
2120
1941
return annotate.reannotate([old_annotation],
2121
1942
self.get_file(file_id).readlines(),
2122
1943
default_revision)
2264
2085
for num, _unused in enumerate(wt.all_file_ids()):
2265
2086
if num > 0: # more than just a root
2266
2087
raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
2088
existing_files = set()
2089
for dir, files in wt.walkdirs():
2090
existing_files.update(f[0] for f in files)
2267
2091
file_trans_id = {}
2268
2092
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2269
2093
pp = ProgressPhase("Build phase", 2, top_pb)
2293
2117
precomputed_delta = []
2295
2119
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)
2305
2120
for num, (tree_path, entry) in \
2306
2121
enumerate(tree.inventory.iter_entries_by_dir()):
2307
2122
pb.update("Building tree", num - len(deferred_contents), total)
2380
2195
new_desired_files = desired_files
2382
2197
iter = accelerator_tree.iter_changes(tree, include_unchanged=True)
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)
2198
unchanged = dict((f, p[1]) for (f, p, c, v, d, n, k, e)
2199
in iter if not (c or e[0] != e[1]))
2389
2200
new_desired_files = []
2391
2202
for file_id, (trans_id, tree_path) in desired_files:
2502
2309
raise errors.BadFileKindError(name, kind)
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.
2312
@deprecated_function(deprecated_in((1, 9, 0)))
2313
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
2314
"""Create new file contents according to an inventory entry.
2316
DEPRECATED. Use create_from_tree instead.
2318
if entry.kind == "file":
2320
lines = tree.get_file(entry.file_id).readlines()
2321
tt.create_file(lines, trans_id, mode_id=mode_id)
2322
elif entry.kind == "symlink":
2323
tt.create_symlink(tree.get_symlink_target(entry.file_id), trans_id)
2324
elif entry.kind == "directory":
2325
tt.create_directory(trans_id)
2328
def create_from_tree(tt, trans_id, tree, file_id, bytes=None):
2329
"""Create new file contents according to tree contents."""
2513
2330
kind = tree.kind(file_id)
2514
2331
if kind == 'directory':
2515
2332
tt.create_directory(trans_id)
2520
2337
bytes = tree_file.readlines()
2522
2339
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))
2528
2340
tt.create_file(bytes, trans_id)
2529
2341
elif kind == "symlink":
2530
2342
tt.create_symlink(tree.get_symlink_target(file_id), trans_id)
2584
2396
def revert(working_tree, target_tree, filenames, backups=False,
2585
pb=None, change_reporter=None):
2397
pb=DummyProgress(), change_reporter=None):
2586
2398
"""Revert a working tree's contents to those of a target tree."""
2587
2399
target_tree.lock_read()
2588
pb = ui.ui_factory.nested_progress_bar()
2589
2400
tt = TreeTransform(working_tree, pb)
2591
2402
pp = ProgressPhase("Revert phase", 3, pb)
2717
2530
parent_trans = ROOT_PARENT
2719
2532
parent_trans = tt.trans_id_file_id(parent[1])
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)
2533
tt.adjust_path(name[1], parent_trans, trans_id)
2724
2534
if executable[0] != executable[1] and kind[1] == "file":
2725
2535
tt.set_executability(executable[1], trans_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()
2536
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2538
tt.create_file(bytes, trans_id, mode_id)
2744
2540
if basis_tree is not None:
2745
2541
basis_tree.unlock()
2746
2542
return merge_modified
2749
def resolve_conflicts(tt, pb=None, pass_func=None):
2545
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
2750
2546
"""Make many conflict-resolution attempts, but die if they fail"""
2751
2547
if pass_func is None:
2752
2548
pass_func = conflict_pass
2753
2549
new_conflicts = set()
2754
pb = ui.ui_factory.nested_progress_bar()
2756
2551
for n in range(10):
2757
2552
pb.update('Resolution pass', n+1, 10)
2890
2685
self.pending_deletions = []
2892
2687
def rename(self, from_, to):
2893
"""Rename a file from one path to another."""
2688
"""Rename a file from one path to another. Functions like os.rename"""
2895
2690
os.rename(from_, to)
2896
2691
except OSError, e:
2897
2692
if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
2898
2693
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)
2902
2695
self.past_renames.append((from_, to))
2904
2697
def pre_delete(self, from_, to):