264
209
self.version_file(old_root_file_id, old_root)
265
210
self.unversion_file(self._new_root)
212
def fixup_new_roots(self):
213
"""Reinterpret requests to change the root directory
215
Instead of creating a root directory, or moving an existing directory,
216
all the attributes and children of the new root are applied to the
217
existing root directory.
219
This means that the old root trans-id becomes obsolete, so it is
220
recommended only to invoke this after the root trans-id has become
223
new_roots = [k for k, v in self._new_parent.iteritems() if v is
225
if len(new_roots) < 1:
227
if len(new_roots) != 1:
228
raise ValueError('A tree cannot have two roots!')
229
if self._new_root is None:
230
self._new_root = new_roots[0]
232
old_new_root = new_roots[0]
233
# TODO: What to do if a old_new_root is present, but self._new_root is
234
# not listed as being removed? This code explicitly unversions
235
# the old root and versions it with the new file_id. Though that
236
# seems like an incomplete delta
238
# unversion the new root's directory.
239
file_id = self.final_file_id(old_new_root)
240
if old_new_root in self._new_id:
241
self.cancel_versioning(old_new_root)
243
self.unversion_file(old_new_root)
244
# if, at this stage, root still has an old file_id, zap it so we can
245
# stick a new one in.
246
if (self.tree_file_id(self._new_root) is not None and
247
self._new_root not in self._removed_id):
248
self.unversion_file(self._new_root)
249
self.version_file(file_id, self._new_root)
251
# Now move children of new root into old root directory.
252
# Ensure all children are registered with the transaction, but don't
253
# use directly-- some tree children have new parents
254
list(self.iter_tree_children(old_new_root))
255
# Move all children of new root into old root directory.
256
for child in self.by_parent().get(old_new_root, []):
257
self.adjust_path(self.final_name(child), self._new_root, child)
259
# Ensure old_new_root has no directory.
260
if old_new_root in self._new_contents:
261
self.cancel_creation(old_new_root)
263
self.delete_contents(old_new_root)
265
# prevent deletion of root directory.
266
if self._new_root in self._removed_contents:
267
self.cancel_deletion(self._new_root)
269
# destroy path info for old_new_root.
270
del self._new_parent[old_new_root]
271
del self._new_name[old_new_root]
267
273
def trans_id_tree_file_id(self, inventory_id):
268
274
"""Determine the transaction id of a working tree file.
332
319
return ROOT_PARENT
333
320
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
322
def delete_contents(self, trans_id):
443
323
"""Schedule the contents of a path entry for deletion"""
444
self.tree_kind(trans_id)
445
self._removed_contents.add(trans_id)
324
kind = self.tree_kind(trans_id)
326
self._removed_contents.add(trans_id)
447
328
def cancel_deletion(self, trans_id):
448
329
"""Cancel a scheduled deletion"""
667
537
# ensure that all children are registered with the transaction
668
538
list(self.iter_tree_children(parent_id))
670
def iter_tree_children(self, parent_id):
671
"""Iterate through the entry's tree children, if any"""
673
path = self._tree_id_paths[parent_id]
677
children = os.listdir(self._tree.abspath(path))
679
if not (osutils._is_error_enotdir(e)
680
or e.errno in (errno.ENOENT, errno.ESRCH)):
684
for child in children:
685
childpath = joinpath(path, child)
686
if self._tree.is_control_filename(childpath):
688
yield self.trans_id_tree_path(childpath)
540
@deprecated_method(deprecated_in((2, 3, 0)))
690
541
def has_named_child(self, by_parent, parent_id, name):
692
children = by_parent[parent_id]
695
for child in children:
542
return self._has_named_child(
543
name, parent_id, known_children=by_parent.get(parent_id, []))
545
def _has_named_child(self, name, parent_id, known_children):
546
"""Does a parent already have a name child.
548
:param name: The searched for name.
550
:param parent_id: The parent for which the check is made.
552
:param known_children: The already known children. This should have
553
been recently obtained from `self.by_parent.get(parent_id)`
554
(or will be if None is passed).
556
if known_children is None:
557
known_children = self.by_parent().get(parent_id, [])
558
for child in known_children:
696
559
if self.final_name(child) == name:
699
path = self._tree_id_paths[parent_id]
561
parent_path = self._tree_id_paths.get(parent_id, None)
562
if parent_path is None:
563
# No parent... no children
702
childpath = joinpath(path, name)
703
child_id = self._tree_path_ids.get(childpath)
565
child_path = joinpath(parent_path, name)
566
child_id = self._tree_path_ids.get(child_path, None)
704
567
if child_id is None:
705
return lexists(self._tree.abspath(childpath))
568
# Not known by the tree transform yet, check the filesystem
569
return osutils.lexists(self._tree.abspath(child_path))
707
if self.final_parent(child_id) != parent_id:
709
if child_id in self._removed_contents:
710
# XXX What about dangling file-ids?
571
raise AssertionError('child_id is missing: %s, %s, %s'
572
% (name, parent_id, child_id))
574
def _available_backup_name(self, name, target_id):
575
"""Find an available backup name.
577
:param name: The basename of the file.
579
:param target_id: The directory trans_id where the backup should
582
known_children = self.by_parent().get(target_id, [])
583
return osutils.available_backup_name(
585
lambda base: self._has_named_child(
586
base, target_id, known_children))
715
588
def _parent_loops(self):
716
589
"""No entry should be its own ancestor"""
1122
969
def get_preview_tree(self):
1123
970
"""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.
972
The tree is a snapshot, and altering the TreeTransform will invalidate
1128
975
return _PreviewTree(self)
977
def commit(self, branch, message, merge_parents=None, strict=False,
978
timestamp=None, timezone=None, committer=None, authors=None,
979
revprops=None, revision_id=None):
980
"""Commit the result of this TreeTransform to a branch.
982
:param branch: The branch to commit to.
983
:param message: The message to attach to the commit.
984
:param merge_parents: Additional parent revision-ids specified by
986
:param strict: If True, abort the commit if there are unversioned
988
:param timestamp: if not None, seconds-since-epoch for the time and
989
date. (May be a float.)
990
:param timezone: Optional timezone for timestamp, as an offset in
992
:param committer: Optional committer in email-id format.
993
(e.g. "J Random Hacker <jrandom@example.com>")
994
:param authors: Optional list of authors in email-id format.
995
:param revprops: Optional dictionary of revision properties.
996
:param revision_id: Optional revision id. (Specifying a revision-id
997
may reduce performance for some non-native formats.)
998
:return: The revision_id of the revision committed.
1000
self._check_malformed()
1002
unversioned = set(self._new_contents).difference(set(self._new_id))
1003
for trans_id in unversioned:
1004
if self.final_file_id(trans_id) is None:
1005
raise errors.StrictCommitFailed()
1007
revno, last_rev_id = branch.last_revision_info()
1008
if last_rev_id == _mod_revision.NULL_REVISION:
1009
if merge_parents is not None:
1010
raise ValueError('Cannot supply merge parents for first'
1014
parent_ids = [last_rev_id]
1015
if merge_parents is not None:
1016
parent_ids.extend(merge_parents)
1017
if self._tree.get_revision_id() != last_rev_id:
1018
raise ValueError('TreeTransform not based on branch basis: %s' %
1019
self._tree.get_revision_id())
1020
revprops = commit.Commit.update_revprops(revprops, branch, authors)
1021
builder = branch.get_commit_builder(parent_ids,
1022
timestamp=timestamp,
1024
committer=committer,
1026
revision_id=revision_id)
1027
preview = self.get_preview_tree()
1028
list(builder.record_iter_changes(preview, last_rev_id,
1029
self.iter_changes()))
1030
builder.finish_inventory()
1031
revision_id = builder.commit(message)
1032
branch.set_last_revision_info(revno + 1, revision_id)
1130
1035
def _text_parent(self, trans_id):
1131
1036
file_id = self.tree_file_id(trans_id)
1227
1128
self.create_symlink(content.decode('utf-8'), trans_id)
1230
class TreeTransform(TreeTransformBase):
1131
class DiskTreeTransform(TreeTransformBase):
1132
"""Tree transform storing its contents on disk."""
1134
def __init__(self, tree, limbodir, pb=None,
1135
case_sensitive=True):
1137
:param tree: The tree that will be transformed, but not necessarily
1139
:param limbodir: A directory where new files can be stored until
1140
they are installed in their proper places
1142
:param case_sensitive: If True, the target of the transform is
1143
case sensitive, not just case preserving.
1145
TreeTransformBase.__init__(self, tree, pb, case_sensitive)
1146
self._limbodir = limbodir
1147
self._deletiondir = None
1148
# A mapping of transform ids to their limbo filename
1149
self._limbo_files = {}
1150
# A mapping of transform ids to a set of the transform ids of children
1151
# that their limbo directory has
1152
self._limbo_children = {}
1153
# Map transform ids to maps of child filename to child transform id
1154
self._limbo_children_names = {}
1155
# List of transform ids that need to be renamed from limbo into place
1156
self._needs_rename = set()
1157
self._creation_mtime = None
1160
"""Release the working tree lock, if held, clean up limbo dir.
1162
This is required if apply has not been invoked, but can be invoked
1165
if self._tree is None:
1168
entries = [(self._limbo_name(t), t, k) for t, k in
1169
self._new_contents.iteritems()]
1170
entries.sort(reverse=True)
1171
for path, trans_id, kind in entries:
1174
delete_any(self._limbodir)
1176
# We don't especially care *why* the dir is immortal.
1177
raise ImmortalLimbo(self._limbodir)
1179
if self._deletiondir is not None:
1180
delete_any(self._deletiondir)
1182
raise errors.ImmortalPendingDeletion(self._deletiondir)
1184
TreeTransformBase.finalize(self)
1186
def _limbo_name(self, trans_id):
1187
"""Generate the limbo name of a file"""
1188
limbo_name = self._limbo_files.get(trans_id)
1189
if limbo_name is None:
1190
limbo_name = self._generate_limbo_path(trans_id)
1191
self._limbo_files[trans_id] = limbo_name
1194
def _generate_limbo_path(self, trans_id):
1195
"""Generate a limbo path using the trans_id as the relative path.
1197
This is suitable as a fallback, and when the transform should not be
1198
sensitive to the path encoding of the limbo directory.
1200
self._needs_rename.add(trans_id)
1201
return pathjoin(self._limbodir, trans_id)
1203
def adjust_path(self, name, parent, trans_id):
1204
previous_parent = self._new_parent.get(trans_id)
1205
previous_name = self._new_name.get(trans_id)
1206
TreeTransformBase.adjust_path(self, name, parent, trans_id)
1207
if (trans_id in self._limbo_files and
1208
trans_id not in self._needs_rename):
1209
self._rename_in_limbo([trans_id])
1210
if previous_parent != parent:
1211
self._limbo_children[previous_parent].remove(trans_id)
1212
if previous_parent != parent or previous_name != name:
1213
del self._limbo_children_names[previous_parent][previous_name]
1215
def _rename_in_limbo(self, trans_ids):
1216
"""Fix limbo names so that the right final path is produced.
1218
This means we outsmarted ourselves-- we tried to avoid renaming
1219
these files later by creating them with their final names in their
1220
final parents. But now the previous name or parent is no longer
1221
suitable, so we have to rename them.
1223
Even for trans_ids that have no new contents, we must remove their
1224
entries from _limbo_files, because they are now stale.
1226
for trans_id in trans_ids:
1227
old_path = self._limbo_files.pop(trans_id)
1228
if trans_id not in self._new_contents:
1230
new_path = self._limbo_name(trans_id)
1231
os.rename(old_path, new_path)
1232
for descendant in self._limbo_descendants(trans_id):
1233
desc_path = self._limbo_files[descendant]
1234
desc_path = new_path + desc_path[len(old_path):]
1235
self._limbo_files[descendant] = desc_path
1237
def _limbo_descendants(self, trans_id):
1238
"""Return the set of trans_ids whose limbo paths descend from this."""
1239
descendants = set(self._limbo_children.get(trans_id, []))
1240
for descendant in list(descendants):
1241
descendants.update(self._limbo_descendants(descendant))
1244
def create_file(self, contents, trans_id, mode_id=None):
1245
"""Schedule creation of a new file.
1249
Contents is an iterator of strings, all of which will be written
1250
to the target destination.
1252
New file takes the permissions of any existing file with that id,
1253
unless mode_id is specified.
1255
name = self._limbo_name(trans_id)
1256
f = open(name, 'wb')
1259
unique_add(self._new_contents, trans_id, 'file')
1261
# Clean up the file, it never got registered so
1262
# TreeTransform.finalize() won't clean it up.
1267
f.writelines(contents)
1270
self._set_mtime(name)
1271
self._set_mode(trans_id, mode_id, S_ISREG)
1273
def _read_file_chunks(self, trans_id):
1274
cur_file = open(self._limbo_name(trans_id), 'rb')
1276
return cur_file.readlines()
1280
def _read_symlink_target(self, trans_id):
1281
return os.readlink(self._limbo_name(trans_id))
1283
def _set_mtime(self, path):
1284
"""All files that are created get the same mtime.
1286
This time is set by the first object to be created.
1288
if self._creation_mtime is None:
1289
self._creation_mtime = time.time()
1290
os.utime(path, (self._creation_mtime, self._creation_mtime))
1292
def create_hardlink(self, path, trans_id):
1293
"""Schedule creation of a hard link"""
1294
name = self._limbo_name(trans_id)
1298
if e.errno != errno.EPERM:
1300
raise errors.HardLinkNotSupported(path)
1302
unique_add(self._new_contents, trans_id, 'file')
1304
# Clean up the file, it never got registered so
1305
# TreeTransform.finalize() won't clean it up.
1309
def create_directory(self, trans_id):
1310
"""Schedule creation of a new directory.
1312
See also new_directory.
1314
os.mkdir(self._limbo_name(trans_id))
1315
unique_add(self._new_contents, trans_id, 'directory')
1317
def create_symlink(self, target, trans_id):
1318
"""Schedule creation of a new symbolic link.
1320
target is a bytestring.
1321
See also new_symlink.
1324
os.symlink(target, self._limbo_name(trans_id))
1325
unique_add(self._new_contents, trans_id, 'symlink')
1328
path = FinalPaths(self).get_path(trans_id)
1331
raise UnableCreateSymlink(path=path)
1333
def cancel_creation(self, trans_id):
1334
"""Cancel the creation of new file contents."""
1335
del self._new_contents[trans_id]
1336
children = self._limbo_children.get(trans_id)
1337
# if this is a limbo directory with children, move them before removing
1339
if children is not None:
1340
self._rename_in_limbo(children)
1341
del self._limbo_children[trans_id]
1342
del self._limbo_children_names[trans_id]
1343
delete_any(self._limbo_name(trans_id))
1345
def new_orphan(self, trans_id, parent_id):
1346
# FIXME: There is no tree config, so we use the branch one (it's weird
1347
# to define it this way as orphaning can only occur in a working tree,
1348
# but that's all we have (for now). It will find the option in
1349
# locations.conf or bazaar.conf though) -- vila 20100916
1350
conf = self._tree.branch.get_config()
1351
conf_var_name = 'bzr.transform.orphan_policy'
1352
orphan_policy = conf.get_user_option(conf_var_name)
1353
default_policy = orphaning_registry.default_key
1354
if orphan_policy is None:
1355
orphan_policy = default_policy
1356
if orphan_policy not in orphaning_registry:
1357
trace.warning('%s (from %s) is not a known policy, defaulting to %s'
1358
% (orphan_policy, conf_var_name, default_policy))
1359
orphan_policy = default_policy
1360
handle_orphan = orphaning_registry.get(orphan_policy)
1361
handle_orphan(self, trans_id, parent_id)
1364
class OrphaningError(errors.BzrError):
1366
# Only bugs could lead to such exception being seen by the user
1367
internal_error = True
1368
_fmt = "Error while orphaning %s in %s directory"
1370
def __init__(self, orphan, parent):
1371
errors.BzrError.__init__(self)
1372
self.orphan = orphan
1373
self.parent = parent
1376
class OrphaningForbidden(OrphaningError):
1378
_fmt = "Policy: %s doesn't allow creating orphans."
1380
def __init__(self, policy):
1381
errors.BzrError.__init__(self)
1382
self.policy = policy
1385
def move_orphan(tt, orphan_id, parent_id):
1386
"""See TreeTransformBase.new_orphan.
1388
This creates a new orphan in the `bzr-orphans` dir at the root of the
1391
:param tt: The TreeTransform orphaning `trans_id`.
1393
:param orphan_id: The trans id that should be orphaned.
1395
:param parent_id: The orphan parent trans id.
1397
# Add the orphan dir if it doesn't exist
1398
orphan_dir_basename = 'bzr-orphans'
1399
od_id = tt.trans_id_tree_path(orphan_dir_basename)
1400
if tt.final_kind(od_id) is None:
1401
tt.create_directory(od_id)
1402
parent_path = tt._tree_id_paths[parent_id]
1403
# Find a name that doesn't exist yet in the orphan dir
1404
actual_name = tt.final_name(orphan_id)
1405
new_name = tt._available_backup_name(actual_name, od_id)
1406
tt.adjust_path(new_name, od_id, orphan_id)
1407
trace.warning('%s has been orphaned in %s'
1408
% (joinpath(parent_path, actual_name), orphan_dir_basename))
1411
def refuse_orphan(tt, orphan_id, parent_id):
1412
"""See TreeTransformBase.new_orphan.
1414
This refuses to create orphan, letting the caller handle the conflict.
1416
raise OrphaningForbidden('never')
1419
orphaning_registry = registry.Registry()
1420
orphaning_registry.register(
1421
'conflict', refuse_orphan,
1422
'Leave orphans in place and create a conflict on the directory.')
1423
orphaning_registry.register(
1424
'move', move_orphan,
1425
'Move orphans into the bzr-orphans directory.')
1426
orphaning_registry._set_default_key('conflict')
1429
class TreeTransform(DiskTreeTransform):
1231
1430
"""Represent a tree transformation.
1233
1432
This object is designed to support incremental generation of the transform,
1322
TreeTransformBase.__init__(self, tree, limbodir, pb,
1521
# Cache of realpath results, to speed up canonical_path
1522
self._realpaths = {}
1523
# Cache of relpath results, to speed up canonical_path
1525
DiskTreeTransform.__init__(self, tree, limbodir, pb,
1323
1526
tree.case_sensitive)
1324
1527
self._deletiondir = deletiondir
1529
def canonical_path(self, path):
1530
"""Get the canonical tree-relative path"""
1531
# don't follow final symlinks
1532
abs = self._tree.abspath(path)
1533
if abs in self._relpaths:
1534
return self._relpaths[abs]
1535
dirname, basename = os.path.split(abs)
1536
if dirname not in self._realpaths:
1537
self._realpaths[dirname] = os.path.realpath(dirname)
1538
dirname = self._realpaths[dirname]
1539
abs = pathjoin(dirname, basename)
1540
if dirname in self._relpaths:
1541
relpath = pathjoin(self._relpaths[dirname], basename)
1542
relpath = relpath.rstrip('/\\')
1544
relpath = self._tree.relpath(abs)
1545
self._relpaths[abs] = relpath
1548
def tree_kind(self, trans_id):
1549
"""Determine the file kind in the working tree.
1551
:returns: The file kind or None if the file does not exist
1553
path = self._tree_id_paths.get(trans_id)
1557
return file_kind(self._tree.abspath(path))
1558
except errors.NoSuchFile:
1561
def _set_mode(self, trans_id, mode_id, typefunc):
1562
"""Set the mode of new file contents.
1563
The mode_id is the existing file to get the mode from (often the same
1564
as trans_id). The operation is only performed if there's a mode match
1565
according to typefunc.
1570
old_path = self._tree_id_paths[mode_id]
1574
mode = os.stat(self._tree.abspath(old_path)).st_mode
1576
if e.errno in (errno.ENOENT, errno.ENOTDIR):
1577
# Either old_path doesn't exist, or the parent of the
1578
# target is not a directory (but will be one eventually)
1579
# Either way, we know it doesn't exist *right now*
1580
# See also bug #248448
1585
os.chmod(self._limbo_name(trans_id), mode)
1587
def iter_tree_children(self, parent_id):
1588
"""Iterate through the entry's tree children, if any"""
1590
path = self._tree_id_paths[parent_id]
1594
children = os.listdir(self._tree.abspath(path))
1596
if not (osutils._is_error_enotdir(e)
1597
or e.errno in (errno.ENOENT, errno.ESRCH)):
1601
for child in children:
1602
childpath = joinpath(path, child)
1603
if self._tree.is_control_filename(childpath):
1605
yield self.trans_id_tree_path(childpath)
1607
def _generate_limbo_path(self, trans_id):
1608
"""Generate a limbo path using the final path if possible.
1610
This optimizes the performance of applying the tree transform by
1611
avoiding renames. These renames can be avoided only when the parent
1612
directory is already scheduled for creation.
1614
If the final path cannot be used, falls back to using the trans_id as
1617
parent = self._new_parent.get(trans_id)
1618
# if the parent directory is already in limbo (e.g. when building a
1619
# tree), choose a limbo name inside the parent, to reduce further
1621
use_direct_path = False
1622
if self._new_contents.get(parent) == 'directory':
1623
filename = self._new_name.get(trans_id)
1624
if filename is not None:
1625
if parent not in self._limbo_children:
1626
self._limbo_children[parent] = set()
1627
self._limbo_children_names[parent] = {}
1628
use_direct_path = True
1629
# the direct path can only be used if no other file has
1630
# already taken this pathname, i.e. if the name is unused, or
1631
# if it is already associated with this trans_id.
1632
elif self._case_sensitive_target:
1633
if (self._limbo_children_names[parent].get(filename)
1634
in (trans_id, None)):
1635
use_direct_path = True
1637
for l_filename, l_trans_id in\
1638
self._limbo_children_names[parent].iteritems():
1639
if l_trans_id == trans_id:
1641
if l_filename.lower() == filename.lower():
1644
use_direct_path = True
1646
if not use_direct_path:
1647
return DiskTreeTransform._generate_limbo_path(self, trans_id)
1649
limbo_name = pathjoin(self._limbo_files[parent], filename)
1650
self._limbo_children[parent].add(trans_id)
1651
self._limbo_children_names[parent][filename] = trans_id
1326
1655
def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1327
1656
"""Apply all changes to the inventory and filesystem.
1754
2092
ordered_ids = self._list_files_by_dir()
1755
2093
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."""
2094
specific_file_ids, yield_parents=yield_parents):
2095
yield unicode(self._final_paths.get_path(trans_id)), entry
2097
def _iter_entries_for_dir(self, dir_path):
2098
"""Return path, entry for items in a directory without recursing down."""
2099
dir_file_id = self.path2id(dir_path)
2101
for file_id in self.iter_children(dir_file_id):
2102
trans_id = self._transform.trans_id_file_id(file_id)
2103
ordered_ids.append((trans_id, file_id))
2104
for entry, trans_id in self._make_inv_entries(ordered_ids):
2105
yield unicode(self._final_paths.get_path(trans_id)), entry
2107
def list_files(self, include_root=False, from_dir=None, recursive=True):
2108
"""See WorkingTree.list_files."""
1761
2109
# XXX This should behave like WorkingTree.list_files, but is really
1762
2110
# 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
2114
prefix = from_dir + '/'
2115
entries = self.iter_entries_by_dir()
2116
for path, entry in entries:
2117
if entry.name == '' and not include_root:
2120
if not path.startswith(prefix):
2122
path = path[len(prefix):]
2123
yield path, 'V', entry.kind, entry.file_id, entry
2125
if from_dir is None and include_root is True:
2126
root_entry = inventory.make_entry('directory', '',
2127
ROOT_PARENT, self.get_root_id())
2128
yield '', 'V', 'directory', root_entry.file_id, root_entry
2129
entries = self._iter_entries_for_dir(from_dir or '')
2130
for path, entry in entries:
2131
yield path, 'V', entry.kind, entry.file_id, entry
1768
2133
def kind(self, file_id):
1769
2134
trans_id = self._transform.trans_id_file_id(file_id)
2491
2877
parent_trans = ROOT_PARENT
2493
2879
parent_trans = tt.trans_id_file_id(parent[1])
2494
tt.adjust_path(name[1], parent_trans, trans_id)
2880
if parent[0] is None and versioned[0]:
2881
tt.adjust_root_path(name[1], parent_trans)
2883
tt.adjust_path(name[1], parent_trans, trans_id)
2495
2884
if executable[0] != executable[1] and kind[1] == "file":
2496
2885
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)
2886
if working_tree.supports_content_filtering():
2887
for index, ((trans_id, mode_id), bytes) in enumerate(
2888
target_tree.iter_files_bytes(deferred_files)):
2889
file_id = deferred_files[index][0]
2890
# We're reverting a tree to the target tree so using the
2891
# target tree to find the file path seems the best choice
2892
# here IMO - Ian C 27/Oct/2009
2893
filter_tree_path = target_tree.id2path(file_id)
2894
filters = working_tree._content_filter_stack(filter_tree_path)
2895
bytes = filtered_output_bytes(bytes, filters,
2896
ContentFilterContext(filter_tree_path, working_tree))
2897
tt.create_file(bytes, trans_id, mode_id)
2899
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2901
tt.create_file(bytes, trans_id, mode_id)
2902
tt.fixup_new_roots()
2501
2904
if basis_tree is not None:
2502
2905
basis_tree.unlock()
2503
2906
return merge_modified
2506
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
2909
def resolve_conflicts(tt, pb=None, pass_func=None):
2507
2910
"""Make many conflict-resolution attempts, but die if they fail"""
2508
2911
if pass_func is None:
2509
2912
pass_func = conflict_pass
2510
2913
new_conflicts = set()
2914
pb = ui.ui_factory.nested_progress_bar()
2512
2916
for n in range(10):
2513
2917
pb.update('Resolution pass', n+1, 10)