199
163
def adjust_path(self, name, parent, trans_id):
200
164
"""Change the path that is assigned to a transaction id."""
166
raise ValueError("Parent trans-id may not be None")
201
167
if trans_id == self._new_root:
202
168
raise CantMoveRoot
203
previous_parent = self._new_parent.get(trans_id)
204
previous_name = self._new_name.get(trans_id)
205
169
self._new_name[trans_id] = name
206
170
self._new_parent[trans_id] = parent
207
if parent == ROOT_PARENT:
208
if self._new_root is not None:
209
raise ValueError("Cannot have multiple roots.")
210
self._new_root = trans_id
211
if (trans_id in self._limbo_files and
212
trans_id not in self._needs_rename):
213
self._rename_in_limbo([trans_id])
214
self._limbo_children[previous_parent].remove(trans_id)
215
del self._limbo_children_names[previous_parent][previous_name]
217
def _rename_in_limbo(self, trans_ids):
218
"""Fix limbo names so that the right final path is produced.
220
This means we outsmarted ourselves-- we tried to avoid renaming
221
these files later by creating them with their final names in their
222
final parents. But now the previous name or parent is no longer
223
suitable, so we have to rename them.
225
Even for trans_ids that have no new contents, we must remove their
226
entries from _limbo_files, because they are now stale.
228
for trans_id in trans_ids:
229
old_path = self._limbo_files.pop(trans_id)
230
if trans_id not in self._new_contents:
232
new_path = self._limbo_name(trans_id)
233
os.rename(old_path, new_path)
235
172
def adjust_root_path(self, name, parent):
236
173
"""Emulate moving the root by moving all children, instead.
264
201
self.version_file(old_root_file_id, old_root)
265
202
self.unversion_file(self._new_root)
204
def fixup_new_roots(self):
205
"""Reinterpret requests to change the root directory
207
Instead of creating a root directory, or moving an existing directory,
208
all the attributes and children of the new root are applied to the
209
existing root directory.
211
This means that the old root trans-id becomes obsolete, so it is
212
recommended only to invoke this after the root trans-id has become
215
new_roots = [k for k, v in self._new_parent.iteritems() if v is
217
if len(new_roots) < 1:
219
if len(new_roots) != 1:
220
raise ValueError('A tree cannot have two roots!')
221
if self._new_root is None:
222
self._new_root = new_roots[0]
224
old_new_root = new_roots[0]
225
# TODO: What to do if a old_new_root is present, but self._new_root is
226
# not listed as being removed? This code explicitly unversions
227
# the old root and versions it with the new file_id. Though that
228
# seems like an incomplete delta
230
# unversion the new root's directory.
231
file_id = self.final_file_id(old_new_root)
232
if old_new_root in self._new_id:
233
self.cancel_versioning(old_new_root)
235
self.unversion_file(old_new_root)
236
# if, at this stage, root still has an old file_id, zap it so we can
237
# stick a new one in.
238
if (self.tree_file_id(self._new_root) is not None and
239
self._new_root not in self._removed_id):
240
self.unversion_file(self._new_root)
241
self.version_file(file_id, self._new_root)
243
# Now move children of new root into old root directory.
244
# Ensure all children are registered with the transaction, but don't
245
# use directly-- some tree children have new parents
246
list(self.iter_tree_children(old_new_root))
247
# Move all children of new root into old root directory.
248
for child in self.by_parent().get(old_new_root, []):
249
self.adjust_path(self.final_name(child), self._new_root, child)
251
# Ensure old_new_root has no directory.
252
if old_new_root in self._new_contents:
253
self.cancel_creation(old_new_root)
255
self.delete_contents(old_new_root)
257
# prevent deletion of root directory.
258
if self._new_root in self._removed_contents:
259
self.cancel_deletion(self._new_root)
261
# destroy path info for old_new_root.
262
del self._new_parent[old_new_root]
263
del self._new_name[old_new_root]
267
265
def trans_id_tree_file_id(self, inventory_id):
268
266
"""Determine the transaction id of a working tree file.
332
311
return ROOT_PARENT
333
312
return self.trans_id_tree_path(os.path.dirname(path))
335
def create_file(self, contents, trans_id, mode_id=None):
336
"""Schedule creation of a new file.
340
Contents is an iterator of strings, all of which will be written
341
to the target destination.
343
New file takes the permissions of any existing file with that id,
344
unless mode_id is specified.
346
name = self._limbo_name(trans_id)
350
unique_add(self._new_contents, trans_id, 'file')
352
# Clean up the file, it never got registered so
353
# TreeTransform.finalize() won't clean it up.
358
f.writelines(contents)
361
self._set_mode(trans_id, mode_id, S_ISREG)
363
def _set_mode(self, trans_id, mode_id, typefunc):
364
"""Set the mode of new file contents.
365
The mode_id is the existing file to get the mode from (often the same
366
as trans_id). The operation is only performed if there's a mode match
367
according to typefunc.
372
old_path = self._tree_id_paths[mode_id]
376
mode = os.stat(self._tree.abspath(old_path)).st_mode
378
if e.errno in (errno.ENOENT, errno.ENOTDIR):
379
# Either old_path doesn't exist, or the parent of the
380
# target is not a directory (but will be one eventually)
381
# Either way, we know it doesn't exist *right now*
382
# See also bug #248448
387
os.chmod(self._limbo_name(trans_id), mode)
389
def create_hardlink(self, path, trans_id):
390
"""Schedule creation of a hard link"""
391
name = self._limbo_name(trans_id)
395
if e.errno != errno.EPERM:
397
raise errors.HardLinkNotSupported(path)
399
unique_add(self._new_contents, trans_id, 'file')
401
# Clean up the file, it never got registered so
402
# TreeTransform.finalize() won't clean it up.
406
def create_directory(self, trans_id):
407
"""Schedule creation of a new directory.
409
See also new_directory.
411
os.mkdir(self._limbo_name(trans_id))
412
unique_add(self._new_contents, trans_id, 'directory')
414
def create_symlink(self, target, trans_id):
415
"""Schedule creation of a new symbolic link.
417
target is a bytestring.
418
See also new_symlink.
421
os.symlink(target, self._limbo_name(trans_id))
422
unique_add(self._new_contents, trans_id, 'symlink')
425
path = FinalPaths(self).get_path(trans_id)
428
raise UnableCreateSymlink(path=path)
430
def cancel_creation(self, trans_id):
431
"""Cancel the creation of new file contents."""
432
del self._new_contents[trans_id]
433
children = self._limbo_children.get(trans_id)
434
# if this is a limbo directory with children, move them before removing
436
if children is not None:
437
self._rename_in_limbo(children)
438
del self._limbo_children[trans_id]
439
del self._limbo_children_names[trans_id]
440
delete_any(self._limbo_name(trans_id))
442
314
def delete_contents(self, trans_id):
443
315
"""Schedule the contents of a path entry for deletion"""
316
# Ensure that the object exists in the WorkingTree, this will raise an
317
# exception if there is a problem
444
318
self.tree_kind(trans_id)
445
319
self._removed_contents.add(trans_id)
870
def _limbo_name(self, trans_id):
871
"""Generate the limbo name of a file"""
872
limbo_name = self._limbo_files.get(trans_id)
873
if limbo_name is not None:
875
parent = self._new_parent.get(trans_id)
876
# if the parent directory is already in limbo (e.g. when building a
877
# tree), choose a limbo name inside the parent, to reduce further
879
use_direct_path = False
880
if self._new_contents.get(parent) == 'directory':
881
filename = self._new_name.get(trans_id)
882
if filename is not None:
883
if parent not in self._limbo_children:
884
self._limbo_children[parent] = set()
885
self._limbo_children_names[parent] = {}
886
use_direct_path = True
887
# the direct path can only be used if no other file has
888
# already taken this pathname, i.e. if the name is unused, or
889
# if it is already associated with this trans_id.
890
elif self._case_sensitive_target:
891
if (self._limbo_children_names[parent].get(filename)
892
in (trans_id, None)):
893
use_direct_path = True
895
for l_filename, l_trans_id in\
896
self._limbo_children_names[parent].iteritems():
897
if l_trans_id == trans_id:
899
if l_filename.lower() == filename.lower():
902
use_direct_path = True
905
limbo_name = pathjoin(self._limbo_files[parent], filename)
906
self._limbo_children[parent].add(trans_id)
907
self._limbo_children_names[parent][filename] = trans_id
909
limbo_name = pathjoin(self._limbodir, trans_id)
910
self._needs_rename.add(trans_id)
911
self._limbo_files[trans_id] = limbo_name
914
713
def _set_executability(self, path, trans_id):
915
714
"""Set the executability of versioned files """
916
715
if supports_executable():
1122
921
def get_preview_tree(self):
1123
922
"""Return a tree representing the result of the transform.
1125
This tree only supports the subset of Tree functionality required
1126
by show_diff_trees. It must only be compared to tt._tree.
924
The tree is a snapshot, and altering the TreeTransform will invalidate
1128
927
return _PreviewTree(self)
929
def commit(self, branch, message, merge_parents=None, strict=False):
930
"""Commit the result of this TreeTransform to a branch.
932
:param branch: The branch to commit to.
933
:param message: The message to attach to the commit.
934
:param merge_parents: Additional parents specified by pending merges.
935
:return: The revision_id of the revision committed.
937
self._check_malformed()
939
unversioned = set(self._new_contents).difference(set(self._new_id))
940
for trans_id in unversioned:
941
if self.final_file_id(trans_id) is None:
942
raise errors.StrictCommitFailed()
944
revno, last_rev_id = branch.last_revision_info()
945
if last_rev_id == _mod_revision.NULL_REVISION:
946
if merge_parents is not None:
947
raise ValueError('Cannot supply merge parents for first'
951
parent_ids = [last_rev_id]
952
if merge_parents is not None:
953
parent_ids.extend(merge_parents)
954
if self._tree.get_revision_id() != last_rev_id:
955
raise ValueError('TreeTransform not based on branch basis: %s' %
956
self._tree.get_revision_id())
957
builder = branch.get_commit_builder(parent_ids)
958
preview = self.get_preview_tree()
959
list(builder.record_iter_changes(preview, last_rev_id,
960
self.iter_changes()))
961
builder.finish_inventory()
962
revision_id = builder.commit(message)
963
branch.set_last_revision_info(revno + 1, revision_id)
1130
966
def _text_parent(self, trans_id):
1131
967
file_id = self.tree_file_id(trans_id)
1227
1059
self.create_symlink(content.decode('utf-8'), trans_id)
1230
class TreeTransform(TreeTransformBase):
1062
class DiskTreeTransform(TreeTransformBase):
1063
"""Tree transform storing its contents on disk."""
1065
def __init__(self, tree, limbodir, pb=DummyProgress(),
1066
case_sensitive=True):
1068
:param tree: The tree that will be transformed, but not necessarily
1070
:param limbodir: A directory where new files can be stored until
1071
they are installed in their proper places
1072
:param pb: A ProgressBar indicating how much progress is being made
1073
:param case_sensitive: If True, the target of the transform is
1074
case sensitive, not just case preserving.
1076
TreeTransformBase.__init__(self, tree, pb, case_sensitive)
1077
self._limbodir = limbodir
1078
self._deletiondir = None
1079
# A mapping of transform ids to their limbo filename
1080
self._limbo_files = {}
1081
# A mapping of transform ids to a set of the transform ids of children
1082
# that their limbo directory has
1083
self._limbo_children = {}
1084
# Map transform ids to maps of child filename to child transform id
1085
self._limbo_children_names = {}
1086
# List of transform ids that need to be renamed from limbo into place
1087
self._needs_rename = set()
1088
self._creation_mtime = None
1091
"""Release the working tree lock, if held, clean up limbo dir.
1093
This is required if apply has not been invoked, but can be invoked
1096
if self._tree is None:
1099
entries = [(self._limbo_name(t), t, k) for t, k in
1100
self._new_contents.iteritems()]
1101
entries.sort(reverse=True)
1102
for path, trans_id, kind in entries:
1105
delete_any(self._limbodir)
1107
# We don't especially care *why* the dir is immortal.
1108
raise ImmortalLimbo(self._limbodir)
1110
if self._deletiondir is not None:
1111
delete_any(self._deletiondir)
1113
raise errors.ImmortalPendingDeletion(self._deletiondir)
1115
TreeTransformBase.finalize(self)
1117
def _limbo_name(self, trans_id):
1118
"""Generate the limbo name of a file"""
1119
limbo_name = self._limbo_files.get(trans_id)
1120
if limbo_name is None:
1121
limbo_name = self._generate_limbo_path(trans_id)
1122
self._limbo_files[trans_id] = limbo_name
1125
def _generate_limbo_path(self, trans_id):
1126
"""Generate a limbo path using the trans_id as the relative path.
1128
This is suitable as a fallback, and when the transform should not be
1129
sensitive to the path encoding of the limbo directory.
1131
self._needs_rename.add(trans_id)
1132
return pathjoin(self._limbodir, trans_id)
1134
def adjust_path(self, name, parent, trans_id):
1135
previous_parent = self._new_parent.get(trans_id)
1136
previous_name = self._new_name.get(trans_id)
1137
TreeTransformBase.adjust_path(self, name, parent, trans_id)
1138
if (trans_id in self._limbo_files and
1139
trans_id not in self._needs_rename):
1140
self._rename_in_limbo([trans_id])
1141
if previous_parent != parent:
1142
self._limbo_children[previous_parent].remove(trans_id)
1143
if previous_parent != parent or previous_name != name:
1144
del self._limbo_children_names[previous_parent][previous_name]
1146
def _rename_in_limbo(self, trans_ids):
1147
"""Fix limbo names so that the right final path is produced.
1149
This means we outsmarted ourselves-- we tried to avoid renaming
1150
these files later by creating them with their final names in their
1151
final parents. But now the previous name or parent is no longer
1152
suitable, so we have to rename them.
1154
Even for trans_ids that have no new contents, we must remove their
1155
entries from _limbo_files, because they are now stale.
1157
for trans_id in trans_ids:
1158
old_path = self._limbo_files.pop(trans_id)
1159
if trans_id not in self._new_contents:
1161
new_path = self._limbo_name(trans_id)
1162
os.rename(old_path, new_path)
1163
for descendant in self._limbo_descendants(trans_id):
1164
desc_path = self._limbo_files[descendant]
1165
desc_path = new_path + desc_path[len(old_path):]
1166
self._limbo_files[descendant] = desc_path
1168
def _limbo_descendants(self, trans_id):
1169
"""Return the set of trans_ids whose limbo paths descend from this."""
1170
descendants = set(self._limbo_children.get(trans_id, []))
1171
for descendant in list(descendants):
1172
descendants.update(self._limbo_descendants(descendant))
1175
def create_file(self, contents, trans_id, mode_id=None):
1176
"""Schedule creation of a new file.
1180
Contents is an iterator of strings, all of which will be written
1181
to the target destination.
1183
New file takes the permissions of any existing file with that id,
1184
unless mode_id is specified.
1186
name = self._limbo_name(trans_id)
1187
f = open(name, 'wb')
1190
unique_add(self._new_contents, trans_id, 'file')
1192
# Clean up the file, it never got registered so
1193
# TreeTransform.finalize() won't clean it up.
1198
f.writelines(contents)
1201
self._set_mtime(name)
1202
self._set_mode(trans_id, mode_id, S_ISREG)
1204
def _read_file_chunks(self, trans_id):
1205
cur_file = open(self._limbo_name(trans_id), 'rb')
1207
return cur_file.readlines()
1211
def _read_symlink_target(self, trans_id):
1212
return os.readlink(self._limbo_name(trans_id))
1214
def _set_mtime(self, path):
1215
"""All files that are created get the same mtime.
1217
This time is set by the first object to be created.
1219
if self._creation_mtime is None:
1220
self._creation_mtime = time.time()
1221
os.utime(path, (self._creation_mtime, self._creation_mtime))
1223
def create_hardlink(self, path, trans_id):
1224
"""Schedule creation of a hard link"""
1225
name = self._limbo_name(trans_id)
1229
if e.errno != errno.EPERM:
1231
raise errors.HardLinkNotSupported(path)
1233
unique_add(self._new_contents, trans_id, 'file')
1235
# Clean up the file, it never got registered so
1236
# TreeTransform.finalize() won't clean it up.
1240
def create_directory(self, trans_id):
1241
"""Schedule creation of a new directory.
1243
See also new_directory.
1245
os.mkdir(self._limbo_name(trans_id))
1246
unique_add(self._new_contents, trans_id, 'directory')
1248
def create_symlink(self, target, trans_id):
1249
"""Schedule creation of a new symbolic link.
1251
target is a bytestring.
1252
See also new_symlink.
1255
os.symlink(target, self._limbo_name(trans_id))
1256
unique_add(self._new_contents, trans_id, 'symlink')
1259
path = FinalPaths(self).get_path(trans_id)
1262
raise UnableCreateSymlink(path=path)
1264
def cancel_creation(self, trans_id):
1265
"""Cancel the creation of new file contents."""
1266
del self._new_contents[trans_id]
1267
children = self._limbo_children.get(trans_id)
1268
# if this is a limbo directory with children, move them before removing
1270
if children is not None:
1271
self._rename_in_limbo(children)
1272
del self._limbo_children[trans_id]
1273
del self._limbo_children_names[trans_id]
1274
delete_any(self._limbo_name(trans_id))
1277
class TreeTransform(DiskTreeTransform):
1231
1278
"""Represent a tree transformation.
1233
1280
This object is designed to support incremental generation of the transform,
1322
TreeTransformBase.__init__(self, tree, limbodir, pb,
1369
# Cache of realpath results, to speed up canonical_path
1370
self._realpaths = {}
1371
# Cache of relpath results, to speed up canonical_path
1373
DiskTreeTransform.__init__(self, tree, limbodir, pb,
1323
1374
tree.case_sensitive)
1324
1375
self._deletiondir = deletiondir
1377
def canonical_path(self, path):
1378
"""Get the canonical tree-relative path"""
1379
# don't follow final symlinks
1380
abs = self._tree.abspath(path)
1381
if abs in self._relpaths:
1382
return self._relpaths[abs]
1383
dirname, basename = os.path.split(abs)
1384
if dirname not in self._realpaths:
1385
self._realpaths[dirname] = os.path.realpath(dirname)
1386
dirname = self._realpaths[dirname]
1387
abs = pathjoin(dirname, basename)
1388
if dirname in self._relpaths:
1389
relpath = pathjoin(self._relpaths[dirname], basename)
1390
relpath = relpath.rstrip('/\\')
1392
relpath = self._tree.relpath(abs)
1393
self._relpaths[abs] = relpath
1396
def tree_kind(self, trans_id):
1397
"""Determine the file kind in the working tree.
1399
Raises NoSuchFile if the file does not exist
1401
path = self._tree_id_paths.get(trans_id)
1403
raise NoSuchFile(None)
1405
return file_kind(self._tree.abspath(path))
1407
if e.errno != errno.ENOENT:
1410
raise NoSuchFile(path)
1412
def _set_mode(self, trans_id, mode_id, typefunc):
1413
"""Set the mode of new file contents.
1414
The mode_id is the existing file to get the mode from (often the same
1415
as trans_id). The operation is only performed if there's a mode match
1416
according to typefunc.
1421
old_path = self._tree_id_paths[mode_id]
1425
mode = os.stat(self._tree.abspath(old_path)).st_mode
1427
if e.errno in (errno.ENOENT, errno.ENOTDIR):
1428
# Either old_path doesn't exist, or the parent of the
1429
# target is not a directory (but will be one eventually)
1430
# Either way, we know it doesn't exist *right now*
1431
# See also bug #248448
1436
os.chmod(self._limbo_name(trans_id), mode)
1438
def iter_tree_children(self, parent_id):
1439
"""Iterate through the entry's tree children, if any"""
1441
path = self._tree_id_paths[parent_id]
1445
children = os.listdir(self._tree.abspath(path))
1447
if not (osutils._is_error_enotdir(e)
1448
or e.errno in (errno.ENOENT, errno.ESRCH)):
1452
for child in children:
1453
childpath = joinpath(path, child)
1454
if self._tree.is_control_filename(childpath):
1456
yield self.trans_id_tree_path(childpath)
1458
def _generate_limbo_path(self, trans_id):
1459
"""Generate a limbo path using the final path if possible.
1461
This optimizes the performance of applying the tree transform by
1462
avoiding renames. These renames can be avoided only when the parent
1463
directory is already scheduled for creation.
1465
If the final path cannot be used, falls back to using the trans_id as
1468
parent = self._new_parent.get(trans_id)
1469
# if the parent directory is already in limbo (e.g. when building a
1470
# tree), choose a limbo name inside the parent, to reduce further
1472
use_direct_path = False
1473
if self._new_contents.get(parent) == 'directory':
1474
filename = self._new_name.get(trans_id)
1475
if filename is not None:
1476
if parent not in self._limbo_children:
1477
self._limbo_children[parent] = set()
1478
self._limbo_children_names[parent] = {}
1479
use_direct_path = True
1480
# the direct path can only be used if no other file has
1481
# already taken this pathname, i.e. if the name is unused, or
1482
# if it is already associated with this trans_id.
1483
elif self._case_sensitive_target:
1484
if (self._limbo_children_names[parent].get(filename)
1485
in (trans_id, None)):
1486
use_direct_path = True
1488
for l_filename, l_trans_id in\
1489
self._limbo_children_names[parent].iteritems():
1490
if l_trans_id == trans_id:
1492
if l_filename.lower() == filename.lower():
1495
use_direct_path = True
1497
if not use_direct_path:
1498
return DiskTreeTransform._generate_limbo_path(self, trans_id)
1500
limbo_name = pathjoin(self._limbo_files[parent], filename)
1501
self._limbo_children[parent].add(trans_id)
1502
self._limbo_children_names[parent][filename] = trans_id
1326
1506
def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1327
1507
"""Apply all changes to the inventory and filesystem.
1754
1936
ordered_ids = self._list_files_by_dir()
1755
1937
for entry, trans_id in self._make_inv_entries(ordered_ids,
1757
yield unicode(self._final_paths.get_path(trans_id)), entry
1759
def list_files(self, include_root=False):
1760
"""See Tree.list_files."""
1938
specific_file_ids, yield_parents=yield_parents):
1939
yield unicode(self._final_paths.get_path(trans_id)), entry
1941
def _iter_entries_for_dir(self, dir_path):
1942
"""Return path, entry for items in a directory without recursing down."""
1943
dir_file_id = self.path2id(dir_path)
1945
for file_id in self.iter_children(dir_file_id):
1946
trans_id = self._transform.trans_id_file_id(file_id)
1947
ordered_ids.append((trans_id, file_id))
1948
for entry, trans_id in self._make_inv_entries(ordered_ids):
1949
yield unicode(self._final_paths.get_path(trans_id)), entry
1951
def list_files(self, include_root=False, from_dir=None, recursive=True):
1952
"""See WorkingTree.list_files."""
1761
1953
# XXX This should behave like WorkingTree.list_files, but is really
1762
1954
# more like RevisionTree.list_files.
1763
for path, entry in self.iter_entries_by_dir():
1764
if entry.name == '' and not include_root:
1766
yield path, 'V', entry.kind, entry.file_id, entry
1958
prefix = from_dir + '/'
1959
entries = self.iter_entries_by_dir()
1960
for path, entry in entries:
1961
if entry.name == '' and not include_root:
1964
if not path.startswith(prefix):
1966
path = path[len(prefix):]
1967
yield path, 'V', entry.kind, entry.file_id, entry
1969
if from_dir is None and include_root is True:
1970
root_entry = inventory.make_entry('directory', '',
1971
ROOT_PARENT, self.get_root_id())
1972
yield '', 'V', 'directory', root_entry.file_id, root_entry
1973
entries = self._iter_entries_for_dir(from_dir or '')
1974
for path, entry in entries:
1975
yield path, 'V', entry.kind, entry.file_id, entry
1768
1977
def kind(self, file_id):
1769
1978
trans_id = self._transform.trans_id_file_id(file_id)
2491
2725
parent_trans = ROOT_PARENT
2493
2727
parent_trans = tt.trans_id_file_id(parent[1])
2494
tt.adjust_path(name[1], parent_trans, trans_id)
2728
if parent[0] is None and versioned[0]:
2729
tt.adjust_root_path(name[1], parent_trans)
2731
tt.adjust_path(name[1], parent_trans, trans_id)
2495
2732
if executable[0] != executable[1] and kind[1] == "file":
2496
2733
tt.set_executability(executable[1], trans_id)
2497
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2499
tt.create_file(bytes, trans_id, mode_id)
2734
if working_tree.supports_content_filtering():
2735
for index, ((trans_id, mode_id), bytes) in enumerate(
2736
target_tree.iter_files_bytes(deferred_files)):
2737
file_id = deferred_files[index][0]
2738
# We're reverting a tree to the target tree so using the
2739
# target tree to find the file path seems the best choice
2740
# here IMO - Ian C 27/Oct/2009
2741
filter_tree_path = target_tree.id2path(file_id)
2742
filters = working_tree._content_filter_stack(filter_tree_path)
2743
bytes = filtered_output_bytes(bytes, filters,
2744
ContentFilterContext(filter_tree_path, working_tree))
2745
tt.create_file(bytes, trans_id, mode_id)
2747
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2749
tt.create_file(bytes, trans_id, mode_id)
2750
tt.fixup_new_roots()
2501
2752
if basis_tree is not None:
2502
2753
basis_tree.unlock()