19
19
from stat import S_ISREG, S_IEXEC
29
lazy_import.lazy_import(globals(), """
21
from bzrlib.lazy_import import lazy_import
22
lazy_import(globals(), """
30
23
from bzrlib import (
40
32
revision as _mod_revision,
45
35
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
46
ReusingTransform, CantMoveRoot,
36
ReusingTransform, NotVersionedError, CantMoveRoot,
47
37
ExistingLimbo, ImmortalLimbo, NoFinalPath,
48
38
UnableCreateSymlink)
49
39
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
40
from bzrlib.inventory import InventoryEntry
50
41
from bzrlib.osutils import (
57
49
supports_executable,
59
from bzrlib.progress import ProgressPhase
51
from bzrlib.progress import DummyProgress, ProgressPhase
60
52
from bzrlib.symbol_versioning import (
56
from bzrlib.trace import mutter, warning
57
from bzrlib import tree
59
import bzrlib.urlutils as urlutils
67
62
ROOT_PARENT = "root-parent"
69
65
def unique_add(map, key, value):
71
67
raise DuplicateKey(key=key)
76
71
class _TransformResults(object):
77
72
def __init__(self, modified_paths, rename_count):
78
73
object.__init__(self)
83
78
class TreeTransformBase(object):
84
79
"""The base class for TreeTransform and its kin."""
86
def __init__(self, tree, pb=None,
81
def __init__(self, tree, pb=DummyProgress(),
87
82
case_sensitive=True):
90
85
:param tree: The tree that will be transformed, but not necessarily
87
:param pb: A ProgressTask indicating how much progress is being made
93
88
:param case_sensitive: If True, the target of the transform is
94
89
case sensitive, not just case preserving.
169
162
def adjust_path(self, name, parent, trans_id):
170
163
"""Change the path that is assigned to a transaction id."""
172
raise ValueError("Parent trans-id may not be None")
173
164
if trans_id == self._new_root:
174
165
raise CantMoveRoot
175
166
self._new_name[trans_id] = name
176
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
178
173
def adjust_root_path(self, name, parent):
179
174
"""Emulate moving the root by moving all children, instead.
207
202
self.version_file(old_root_file_id, old_root)
208
203
self.unversion_file(self._new_root)
210
def fixup_new_roots(self):
211
"""Reinterpret requests to change the root directory
213
Instead of creating a root directory, or moving an existing directory,
214
all the attributes and children of the new root are applied to the
215
existing root directory.
217
This means that the old root trans-id becomes obsolete, so it is
218
recommended only to invoke this after the root trans-id has become
221
new_roots = [k for k, v in self._new_parent.iteritems() if v is
223
if len(new_roots) < 1:
225
if len(new_roots) != 1:
226
raise ValueError('A tree cannot have two roots!')
227
if self._new_root is None:
228
self._new_root = new_roots[0]
230
old_new_root = new_roots[0]
231
# TODO: What to do if a old_new_root is present, but self._new_root is
232
# not listed as being removed? This code explicitly unversions
233
# the old root and versions it with the new file_id. Though that
234
# seems like an incomplete delta
236
# unversion the new root's directory.
237
file_id = self.final_file_id(old_new_root)
238
if old_new_root in self._new_id:
239
self.cancel_versioning(old_new_root)
241
self.unversion_file(old_new_root)
242
# if, at this stage, root still has an old file_id, zap it so we can
243
# stick a new one in.
244
if (self.tree_file_id(self._new_root) is not None and
245
self._new_root not in self._removed_id):
246
self.unversion_file(self._new_root)
247
self.version_file(file_id, self._new_root)
249
# Now move children of new root into old root directory.
250
# Ensure all children are registered with the transaction, but don't
251
# use directly-- some tree children have new parents
252
list(self.iter_tree_children(old_new_root))
253
# Move all children of new root into old root directory.
254
for child in self.by_parent().get(old_new_root, []):
255
self.adjust_path(self.final_name(child), self._new_root, child)
257
# Ensure old_new_root has no directory.
258
if old_new_root in self._new_contents:
259
self.cancel_creation(old_new_root)
261
self.delete_contents(old_new_root)
263
# prevent deletion of root directory.
264
if self._new_root in self._removed_contents:
265
self.cancel_deletion(self._new_root)
267
# destroy path info for old_new_root.
268
del self._new_parent[old_new_root]
269
del self._new_name[old_new_root]
271
205
def trans_id_tree_file_id(self, inventory_id):
272
206
"""Determine the transaction id of a working tree file.
392
325
changed_kind = set(self._removed_contents)
393
326
changed_kind.intersection_update(self._new_contents)
394
327
changed_kind.difference_update(new_ids)
395
changed_kind = (t for t in changed_kind
396
if self.tree_kind(t) != self.final_kind(t))
328
changed_kind = (t for t in changed_kind if self.tree_kind(t) !=
397
330
new_ids.update(changed_kind)
398
331
return sorted(FinalPaths(self).get_paths(new_ids))
400
333
def final_kind(self, trans_id):
401
334
"""Determine the final file kind, after any changes applied.
403
:return: None if the file does not exist/has no contents. (It is
404
conceivable that a path would be created without the corresponding
405
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)
407
340
if trans_id in self._new_contents:
408
341
return self._new_contents[trans_id]
409
342
elif trans_id in self._removed_contents:
343
raise NoSuchFile(None)
412
345
return self.tree_kind(trans_id)
537
468
# ensure that all children are registered with the transaction
538
469
list(self.iter_tree_children(parent_id))
540
@deprecated_method(deprecated_in((2, 3, 0)))
541
471
def has_named_child(self, by_parent, parent_id, name):
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:
473
children = by_parent[parent_id]
476
for child in children:
559
477
if self.final_name(child) == name:
561
parent_path = self._tree_id_paths.get(parent_id, None)
562
if parent_path is None:
563
# No parent... no children
480
path = self._tree_id_paths[parent_id]
565
child_path = joinpath(parent_path, name)
566
child_id = self._tree_path_ids.get(child_path, None)
483
childpath = joinpath(path, name)
484
child_id = self._tree_path_ids.get(childpath)
567
485
if child_id is None:
568
# Not known by the tree transform yet, check the filesystem
569
return osutils.lexists(self._tree.abspath(child_path))
486
return lexists(self._tree.abspath(childpath))
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))
488
if self.final_parent(child_id) != parent_id:
490
if child_id in self._removed_contents:
491
# XXX What about dangling file-ids?
588
496
def _parent_loops(self):
589
497
"""No entry should be its own ancestor"""
666
581
if (self._new_name, self._new_parent) == ({}, {}):
668
583
for children in by_parent.itervalues():
670
for child_tid in children:
671
name = self.final_name(child_tid)
673
# Keep children only if they still exist in the end
674
if not self._case_sensitive_target:
676
name_ids.append((name, child_tid))
584
name_ids = [(self.final_name(t), t) for t in children]
585
if not self._case_sensitive_target:
586
name_ids = [(n.lower(), t) for n, t in name_ids]
679
589
last_trans_id = None
680
590
for name, trans_id in name_ids:
681
kind = self.final_kind(trans_id)
592
kind = self.final_kind(trans_id)
682
595
file_id = self.final_file_id(trans_id)
683
596
if kind is None and file_id is None:
705
618
def _parent_type_conflicts(self, by_parent):
706
"""Children must have a directory parent"""
619
"""parents must have directory 'contents'."""
708
621
for parent_id, children in by_parent.iteritems():
709
622
if parent_id is ROOT_PARENT:
712
for child_id in children:
713
if self.final_kind(child_id) is not None:
624
if not self._any_contents(children):
718
# There is at least a child, so we need an existing directory to
720
kind = self.final_kind(parent_id)
626
for child in children:
628
self.final_kind(child)
632
kind = self.final_kind(parent_id)
722
# The directory will be deleted
723
636
conflicts.append(('missing parent', parent_id))
724
637
elif kind != "directory":
725
# Meh, we need a *directory* to put something in it
726
638
conflicts.append(('non-directory parent', parent_id))
641
def _any_contents(self, trans_ids):
642
"""Return true if any of the trans_ids, will have contents."""
643
for trans_id in trans_ids:
645
kind = self.final_kind(trans_id)
729
651
def _set_executability(self, path, trans_id):
730
652
"""Set the executability of versioned files """
731
653
if supports_executable():
795
717
self.create_symlink(target, trans_id)
798
def new_orphan(self, trans_id, parent_id):
799
"""Schedule an item to be orphaned.
801
When a directory is about to be removed, its children, if they are not
802
versioned are moved out of the way: they don't have a parent anymore.
804
:param trans_id: The trans_id of the existing item.
805
:param parent_id: The parent trans_id of the item.
807
raise NotImplementedError(self.new_orphan)
809
def _get_potential_orphans(self, dir_id):
810
"""Find the potential orphans in a directory.
812
A directory can't be safely deleted if there are versioned files in it.
813
If all the contained files are unversioned then they can be orphaned.
815
The 'None' return value means that the directory contains at least one
816
versioned file and should not be deleted.
818
:param dir_id: The directory trans id.
820
:return: A list of the orphan trans ids or None if at least one
821
versioned file is present.
824
# Find the potential orphans, stop if one item should be kept
825
for child_tid in self.by_parent()[dir_id]:
826
if child_tid in self._removed_contents:
827
# The child is removed as part of the transform. Since it was
828
# versioned before, it's not an orphan
830
elif self.final_file_id(child_tid) is None:
831
# The child is not versioned
832
orphans.append(child_tid)
834
# We have a versioned file here, searching for orphans is
840
720
def _affected_ids(self):
841
721
"""Return the set of transform ids affected by the transform"""
842
722
trans_ids = set(self._removed_id)
976
859
def get_preview_tree(self):
977
860
"""Return a tree representing the result of the transform.
979
The tree is a snapshot, and altering the TreeTransform will invalidate
862
This tree only supports the subset of Tree functionality required
863
by show_diff_trees. It must only be compared to tt._tree.
982
865
return _PreviewTree(self)
984
def commit(self, branch, message, merge_parents=None, strict=False,
985
timestamp=None, timezone=None, committer=None, authors=None,
986
revprops=None, revision_id=None):
867
def commit(self, branch, message, merge_parents=None, strict=False):
987
868
"""Commit the result of this TreeTransform to a branch.
989
870
:param branch: The branch to commit to.
990
871
:param message: The message to attach to the commit.
991
:param merge_parents: Additional parent revision-ids specified by
993
:param strict: If True, abort the commit if there are unversioned
995
:param timestamp: if not None, seconds-since-epoch for the time and
996
date. (May be a float.)
997
:param timezone: Optional timezone for timestamp, as an offset in
999
:param committer: Optional committer in email-id format.
1000
(e.g. "J Random Hacker <jrandom@example.com>")
1001
:param authors: Optional list of authors in email-id format.
1002
:param revprops: Optional dictionary of revision properties.
1003
:param revision_id: Optional revision id. (Specifying a revision-id
1004
may reduce performance for some non-native formats.)
872
:param merge_parents: Additional parents specified by pending merges.
1005
873
:return: The revision_id of the revision committed.
1007
875
self._check_malformed()
1138
1000
class DiskTreeTransform(TreeTransformBase):
1139
1001
"""Tree transform storing its contents on disk."""
1141
def __init__(self, tree, limbodir, pb=None,
1003
def __init__(self, tree, limbodir, pb=DummyProgress(),
1142
1004
case_sensitive=True):
1143
1005
"""Constructor.
1144
1006
:param tree: The tree that will be transformed, but not necessarily
1145
1007
the output tree.
1146
1008
:param limbodir: A directory where new files can be stored until
1147
1009
they are installed in their proper places
1010
:param pb: A ProgressBar indicating how much progress is being made
1149
1011
:param case_sensitive: If True, the target of the transform is
1150
1012
case sensitive, not just case preserving.
1193
1054
def _limbo_name(self, trans_id):
1194
1055
"""Generate the limbo name of a file"""
1195
1056
limbo_name = self._limbo_files.get(trans_id)
1196
if limbo_name is None:
1197
limbo_name = self._generate_limbo_path(trans_id)
1198
self._limbo_files[trans_id] = limbo_name
1057
if limbo_name is not None:
1059
parent = self._new_parent.get(trans_id)
1060
# if the parent directory is already in limbo (e.g. when building a
1061
# tree), choose a limbo name inside the parent, to reduce further
1063
use_direct_path = False
1064
if self._new_contents.get(parent) == 'directory':
1065
filename = self._new_name.get(trans_id)
1066
if filename is not None:
1067
if parent not in self._limbo_children:
1068
self._limbo_children[parent] = set()
1069
self._limbo_children_names[parent] = {}
1070
use_direct_path = True
1071
# the direct path can only be used if no other file has
1072
# already taken this pathname, i.e. if the name is unused, or
1073
# if it is already associated with this trans_id.
1074
elif self._case_sensitive_target:
1075
if (self._limbo_children_names[parent].get(filename)
1076
in (trans_id, None)):
1077
use_direct_path = True
1079
for l_filename, l_trans_id in\
1080
self._limbo_children_names[parent].iteritems():
1081
if l_trans_id == trans_id:
1083
if l_filename.lower() == filename.lower():
1086
use_direct_path = True
1089
limbo_name = pathjoin(self._limbo_files[parent], filename)
1090
self._limbo_children[parent].add(trans_id)
1091
self._limbo_children_names[parent][filename] = trans_id
1093
limbo_name = pathjoin(self._limbodir, trans_id)
1094
self._needs_rename.add(trans_id)
1095
self._limbo_files[trans_id] = limbo_name
1199
1096
return limbo_name
1201
def _generate_limbo_path(self, trans_id):
1202
"""Generate a limbo path using the trans_id as the relative path.
1204
This is suitable as a fallback, and when the transform should not be
1205
sensitive to the path encoding of the limbo directory.
1207
self._needs_rename.add(trans_id)
1208
return pathjoin(self._limbodir, trans_id)
1210
1098
def adjust_path(self, name, parent, trans_id):
1211
1099
previous_parent = self._new_parent.get(trans_id)
1212
1100
previous_name = self._new_name.get(trans_id)
1214
1102
if (trans_id in self._limbo_files and
1215
1103
trans_id not in self._needs_rename):
1216
1104
self._rename_in_limbo([trans_id])
1217
if previous_parent != parent:
1218
self._limbo_children[previous_parent].remove(trans_id)
1219
if previous_parent != parent or previous_name != name:
1220
del self._limbo_children_names[previous_parent][previous_name]
1105
self._limbo_children[previous_parent].remove(trans_id)
1106
del self._limbo_children_names[previous_parent][previous_name]
1222
1108
def _rename_in_limbo(self, trans_ids):
1223
1109
"""Fix limbo names so that the right final path is produced.
1237
1123
new_path = self._limbo_name(trans_id)
1238
1124
os.rename(old_path, new_path)
1239
for descendant in self._limbo_descendants(trans_id):
1240
desc_path = self._limbo_files[descendant]
1241
desc_path = new_path + desc_path[len(old_path):]
1242
self._limbo_files[descendant] = desc_path
1244
def _limbo_descendants(self, trans_id):
1245
"""Return the set of trans_ids whose limbo paths descend from this."""
1246
descendants = set(self._limbo_children.get(trans_id, []))
1247
for descendant in list(descendants):
1248
descendants.update(self._limbo_descendants(descendant))
1251
def create_file(self, contents, trans_id, mode_id=None, sha1=None):
1126
def create_file(self, contents, trans_id, mode_id=None):
1252
1127
"""Schedule creation of a new file.
1256
:param contents: an iterator of strings, all of which will be written
1257
to the target destination.
1258
:param trans_id: TreeTransform handle
1259
:param mode_id: If not None, force the mode of the target file to match
1260
the mode of the object referenced by mode_id.
1261
Otherwise, we will try to preserve mode bits of an existing file.
1262
:param sha1: If the sha1 of this content is already known, pass it in.
1263
We can use it to prevent future sha1 computations.
1131
Contents is an iterator of strings, all of which will be written
1132
to the target destination.
1134
New file takes the permissions of any existing file with that id,
1135
unless mode_id is specified.
1265
1137
name = self._limbo_name(trans_id)
1266
1138
f = open(name, 'wb')
1294
1161
def _read_symlink_target(self, trans_id):
1295
1162
return os.readlink(self._limbo_name(trans_id))
1297
def _set_mtime(self, path):
1298
"""All files that are created get the same mtime.
1300
This time is set by the first object to be created.
1302
if self._creation_mtime is None:
1303
self._creation_mtime = time.time()
1304
os.utime(path, (self._creation_mtime, self._creation_mtime))
1306
1164
def create_hardlink(self, path, trans_id):
1307
1165
"""Schedule creation of a hard link"""
1308
1166
name = self._limbo_name(trans_id)
1358
1214
del self._limbo_children_names[trans_id]
1359
1215
delete_any(self._limbo_name(trans_id))
1361
def new_orphan(self, trans_id, parent_id):
1362
# FIXME: There is no tree config, so we use the branch one (it's weird
1363
# to define it this way as orphaning can only occur in a working tree,
1364
# but that's all we have (for now). It will find the option in
1365
# locations.conf or bazaar.conf though) -- vila 20100916
1366
conf = self._tree.branch.get_config()
1367
conf_var_name = 'bzr.transform.orphan_policy'
1368
orphan_policy = conf.get_user_option(conf_var_name)
1369
default_policy = orphaning_registry.default_key
1370
if orphan_policy is None:
1371
orphan_policy = default_policy
1372
if orphan_policy not in orphaning_registry:
1373
trace.warning('%s (from %s) is not a known policy, defaulting '
1374
'to %s' % (orphan_policy, conf_var_name, default_policy))
1375
orphan_policy = default_policy
1376
handle_orphan = orphaning_registry.get(orphan_policy)
1377
handle_orphan(self, trans_id, parent_id)
1380
class OrphaningError(errors.BzrError):
1382
# Only bugs could lead to such exception being seen by the user
1383
internal_error = True
1384
_fmt = "Error while orphaning %s in %s directory"
1386
def __init__(self, orphan, parent):
1387
errors.BzrError.__init__(self)
1388
self.orphan = orphan
1389
self.parent = parent
1392
class OrphaningForbidden(OrphaningError):
1394
_fmt = "Policy: %s doesn't allow creating orphans."
1396
def __init__(self, policy):
1397
errors.BzrError.__init__(self)
1398
self.policy = policy
1401
def move_orphan(tt, orphan_id, parent_id):
1402
"""See TreeTransformBase.new_orphan.
1404
This creates a new orphan in the `bzr-orphans` dir at the root of the
1407
:param tt: The TreeTransform orphaning `trans_id`.
1409
:param orphan_id: The trans id that should be orphaned.
1411
:param parent_id: The orphan parent trans id.
1413
# Add the orphan dir if it doesn't exist
1414
orphan_dir_basename = 'bzr-orphans'
1415
od_id = tt.trans_id_tree_path(orphan_dir_basename)
1416
if tt.final_kind(od_id) is None:
1417
tt.create_directory(od_id)
1418
parent_path = tt._tree_id_paths[parent_id]
1419
# Find a name that doesn't exist yet in the orphan dir
1420
actual_name = tt.final_name(orphan_id)
1421
new_name = tt._available_backup_name(actual_name, od_id)
1422
tt.adjust_path(new_name, od_id, orphan_id)
1423
trace.warning('%s has been orphaned in %s'
1424
% (joinpath(parent_path, actual_name), orphan_dir_basename))
1427
def refuse_orphan(tt, orphan_id, parent_id):
1428
"""See TreeTransformBase.new_orphan.
1430
This refuses to create orphan, letting the caller handle the conflict.
1432
raise OrphaningForbidden('never')
1435
orphaning_registry = registry.Registry()
1436
orphaning_registry.register(
1437
'conflict', refuse_orphan,
1438
'Leave orphans in place and create a conflict on the directory.')
1439
orphaning_registry.register(
1440
'move', move_orphan,
1441
'Move orphans into the bzr-orphans directory.')
1442
orphaning_registry._set_default_key('conflict')
1445
1218
class TreeTransform(DiskTreeTransform):
1446
1219
"""Represent a tree transformation.
1564
1337
def tree_kind(self, trans_id):
1565
1338
"""Determine the file kind in the working tree.
1567
:returns: The file kind or None if the file does not exist
1340
Raises NoSuchFile if the file does not exist
1569
1342
path = self._tree_id_paths.get(trans_id)
1570
1343
if path is None:
1344
raise NoSuchFile(None)
1573
1346
return file_kind(self._tree.abspath(path))
1574
except errors.NoSuchFile:
1348
if e.errno != errno.ENOENT:
1351
raise NoSuchFile(path)
1577
1353
def _set_mode(self, trans_id, mode_id, typefunc):
1578
1354
"""Set the mode of new file contents.
1621
1397
yield self.trans_id_tree_path(childpath)
1623
def _generate_limbo_path(self, trans_id):
1624
"""Generate a limbo path using the final path if possible.
1626
This optimizes the performance of applying the tree transform by
1627
avoiding renames. These renames can be avoided only when the parent
1628
directory is already scheduled for creation.
1630
If the final path cannot be used, falls back to using the trans_id as
1633
parent = self._new_parent.get(trans_id)
1634
# if the parent directory is already in limbo (e.g. when building a
1635
# tree), choose a limbo name inside the parent, to reduce further
1637
use_direct_path = False
1638
if self._new_contents.get(parent) == 'directory':
1639
filename = self._new_name.get(trans_id)
1640
if filename is not None:
1641
if parent not in self._limbo_children:
1642
self._limbo_children[parent] = set()
1643
self._limbo_children_names[parent] = {}
1644
use_direct_path = True
1645
# the direct path can only be used if no other file has
1646
# already taken this pathname, i.e. if the name is unused, or
1647
# if it is already associated with this trans_id.
1648
elif self._case_sensitive_target:
1649
if (self._limbo_children_names[parent].get(filename)
1650
in (trans_id, None)):
1651
use_direct_path = True
1653
for l_filename, l_trans_id in\
1654
self._limbo_children_names[parent].iteritems():
1655
if l_trans_id == trans_id:
1657
if l_filename.lower() == filename.lower():
1660
use_direct_path = True
1662
if not use_direct_path:
1663
return DiskTreeTransform._generate_limbo_path(self, trans_id)
1665
limbo_name = pathjoin(self._limbo_files[parent], filename)
1666
self._limbo_children[parent].add(trans_id)
1667
self._limbo_children_names[parent][filename] = trans_id
1671
1399
def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1672
1400
"""Apply all changes to the inventory and filesystem.
1787
1515
tree_paths = list(self._tree_path_ids.iteritems())
1788
1516
tree_paths.sort(reverse=True)
1789
child_pb = ui.ui_factory.nested_progress_bar()
1517
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1791
1519
for num, data in enumerate(tree_paths):
1792
1520
path, trans_id = data
1793
1521
child_pb.update('removing file', num, len(tree_paths))
1794
1522
full_path = self._tree.abspath(path)
1795
1523
if trans_id in self._removed_contents:
1796
delete_path = os.path.join(self._deletiondir, trans_id)
1797
mover.pre_delete(full_path, delete_path)
1798
elif (trans_id in self._new_name
1799
or trans_id in self._new_parent):
1524
mover.pre_delete(full_path, os.path.join(self._deletiondir,
1526
elif trans_id in self._new_name or trans_id in \
1801
1529
mover.rename(full_path, self._limbo_name(trans_id))
1802
except errors.TransformRenameFailed, e:
1803
1531
if e.errno != errno.ENOENT:
1830
1558
if trans_id in self._needs_rename:
1832
1560
mover.rename(self._limbo_name(trans_id), full_path)
1833
except errors.TransformRenameFailed, e:
1834
1562
# We may be renaming a dangling inventory id
1835
1563
if e.errno != errno.ENOENT:
1838
1566
self.rename_count += 1
1839
# TODO: if trans_id in self._observed_sha1s, we should
1840
# re-stat the final target, since ctime will be
1841
# updated by the change.
1842
1567
if (trans_id in self._new_contents or
1843
1568
self.path_changed(trans_id)):
1844
1569
if trans_id in self._new_contents:
1845
1570
modified_paths.append(full_path)
1846
1571
if trans_id in self._new_executability:
1847
1572
self._set_executability(path, trans_id)
1848
if trans_id in self._observed_sha1s:
1849
o_sha1, o_st_val = self._observed_sha1s[trans_id]
1850
st = osutils.lstat(full_path)
1851
self._observed_sha1s[trans_id] = (o_sha1, st)
1853
1574
child_pb.finished()
1854
1575
self._new_contents.clear()
1855
1576
return modified_paths
1857
def _apply_observed_sha1s(self):
1858
"""After we have finished renaming everything, update observed sha1s
1860
This has to be done after self._tree.apply_inventory_delta, otherwise
1861
it doesn't know anything about the files we are updating. Also, we want
1862
to do this as late as possible, so that most entries end up cached.
1864
# TODO: this doesn't update the stat information for directories. So
1865
# the first 'bzr status' will still need to rewrite
1866
# .bzr/checkout/dirstate. However, we at least don't need to
1867
# re-read all of the files.
1868
# TODO: If the operation took a while, we could do a time.sleep(3) here
1869
# to allow the clock to tick over and ensure we won't have any
1870
# problems. (we could observe start time, and finish time, and if
1871
# it is less than eg 10% overhead, add a sleep call.)
1872
paths = FinalPaths(self)
1873
for trans_id, observed in self._observed_sha1s.iteritems():
1874
path = paths.get_path(trans_id)
1875
# We could get the file_id, but dirstate prefers to use the path
1876
# anyway, and it is 'cheaper' to determine.
1877
# file_id = self._new_id[trans_id]
1878
self._tree._observed_sha1(None, path, observed)
1881
1579
class TransformPreview(DiskTreeTransform):
1882
1580
"""A TreeTransform for generating preview trees.
1943
1635
self._all_children_cache = {}
1944
1636
self._path2trans_id_cache = {}
1945
1637
self._final_name_cache = {}
1946
self._iter_changes_cache = dict((c[0], c) for c in
1947
self._transform.iter_changes())
1639
def _changes(self, file_id):
1640
for changes in self._transform.iter_changes():
1641
if changes[0] == file_id:
1949
1644
def _content_change(self, file_id):
1950
1645
"""Return True if the content of this file changed"""
1951
changes = self._iter_changes_cache.get(file_id)
1646
changes = self._changes(file_id)
1952
1647
# changes[2] is true if the file content changed. See
1953
1648
# InterTree.iter_changes.
1954
1649
return (changes is not None and changes[2])
1967
1662
yield self._get_repository().revision_tree(revision_id)
1969
1664
def _get_file_revision(self, file_id, vf, tree_revision):
1970
parent_keys = [(file_id, t.get_file_revision(file_id)) for t in
1665
parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
1971
1666
self._iter_parent_trees()]
1972
1667
vf.add_lines((file_id, tree_revision), parent_keys,
1973
self.get_file_lines(file_id))
1668
self.get_file(file_id).readlines())
1974
1669
repo = self._get_repository()
1975
1670
base_vf = repo.texts
1976
1671
if base_vf not in vf.fallback_versionedfiles:
2468
2162
for num, _unused in enumerate(wt.all_file_ids()):
2469
2163
if num > 0: # more than just a root
2470
2164
raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
2165
existing_files = set()
2166
for dir, files in wt.walkdirs():
2167
existing_files.update(f[0] for f in files)
2471
2168
file_trans_id = {}
2472
top_pb = ui.ui_factory.nested_progress_bar()
2169
top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2473
2170
pp = ProgressPhase("Build phase", 2, top_pb)
2474
2171
if tree.inventory.root is not None:
2475
2172
# This is kind of a hack: we should be altering the root
2497
2194
precomputed_delta = []
2499
2196
precomputed_delta = None
2500
# Check if tree inventory has content. If so, we populate
2501
# existing_files with the directory content. If there are no
2502
# entries we skip populating existing_files as its not used.
2503
# This improves performance and unncessary work on large
2504
# directory trees. (#501307)
2506
existing_files = set()
2507
for dir, files in wt.walkdirs():
2508
existing_files.update(f[0] for f in files)
2509
2197
for num, (tree_path, entry) in \
2510
2198
enumerate(tree.inventory.iter_entries_by_dir()):
2511
2199
pb.update("Building tree", num - len(deferred_contents), total)
2584
2272
new_desired_files = desired_files
2586
2274
iter = accelerator_tree.iter_changes(tree, include_unchanged=True)
2587
unchanged = [(f, p[1]) for (f, p, c, v, d, n, k, e)
2588
in iter if not (c or e[0] != e[1])]
2589
if accelerator_tree.supports_content_filtering():
2590
unchanged = [(f, p) for (f, p) in unchanged
2591
if not accelerator_tree.iter_search_rules([p]).next()]
2592
unchanged = dict(unchanged)
2275
unchanged = dict((f, p[1]) for (f, p, c, v, d, n, k, e)
2276
in iter if not (c or e[0] != e[1]))
2593
2277
new_desired_files = []
2595
for file_id, (trans_id, tree_path, text_sha1) in desired_files:
2279
for file_id, (trans_id, tree_path) in desired_files:
2596
2280
accelerator_path = unchanged.get(file_id)
2597
2281
if accelerator_path is None:
2598
new_desired_files.append((file_id,
2599
(trans_id, tree_path, text_sha1)))
2282
new_desired_files.append((file_id, (trans_id, tree_path)))
2601
2284
pb.update('Adding file contents', count + offset, total)
2620
2303
offset += count
2621
for count, ((trans_id, tree_path, text_sha1), contents) in enumerate(
2304
for count, ((trans_id, tree_path), contents) in enumerate(
2622
2305
tree.iter_files_bytes(new_desired_files)):
2623
2306
if wt.supports_content_filtering():
2624
2307
filters = wt._content_filter_stack(tree_path)
2625
2308
contents = filtered_output_bytes(contents, filters,
2626
2309
ContentFilterContext(tree_path, tree))
2627
tt.create_file(contents, trans_id, sha1=text_sha1)
2310
tt.create_file(contents, trans_id)
2628
2311
pb.update('Adding file contents', count + offset, total)
2632
2315
for child in tt.iter_tree_children(old_parent):
2633
2316
tt.adjust_path(tt.final_name(child), new_parent, child)
2636
2318
def _reparent_transform_children(tt, old_parent, new_parent):
2637
2319
by_parent = tt.by_parent()
2638
2320
for child in by_parent[old_parent]:
2639
2321
tt.adjust_path(tt.final_name(child), new_parent, child)
2640
2322
return by_parent[old_parent]
2643
2324
def _content_match(tree, entry, file_id, kind, target_path):
2644
2325
if entry.kind != kind:
2646
2327
if entry.kind == "directory":
2648
2329
if entry.kind == "file":
2649
f = file(target_path, 'rb')
2651
if tree.get_file_text(file_id) == f.read():
2330
if tree.get_file(file_id).read() == file(target_path, 'rb').read():
2655
2332
elif entry.kind == "symlink":
2656
2333
if tree.get_symlink_target(file_id) == os.readlink(target_path):
2709
2386
raise errors.BadFileKindError(name, kind)
2712
def create_from_tree(tt, trans_id, tree, file_id, bytes=None,
2713
filter_tree_path=None):
2714
"""Create new file contents according to tree contents.
2716
:param filter_tree_path: the tree path to use to lookup
2717
content filters to apply to the bytes output in the working tree.
2718
This only applies if the working tree supports content filtering.
2389
@deprecated_function(deprecated_in((1, 9, 0)))
2390
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
2391
"""Create new file contents according to an inventory entry.
2393
DEPRECATED. Use create_from_tree instead.
2395
if entry.kind == "file":
2397
lines = tree.get_file(entry.file_id).readlines()
2398
tt.create_file(lines, trans_id, mode_id=mode_id)
2399
elif entry.kind == "symlink":
2400
tt.create_symlink(tree.get_symlink_target(entry.file_id), trans_id)
2401
elif entry.kind == "directory":
2402
tt.create_directory(trans_id)
2405
def create_from_tree(tt, trans_id, tree, file_id, bytes=None):
2406
"""Create new file contents according to tree contents."""
2720
2407
kind = tree.kind(file_id)
2721
2408
if kind == 'directory':
2722
2409
tt.create_directory(trans_id)
2745
2427
tt.set_executability(entry.executable, trans_id)
2748
@deprecated_function(deprecated_in((2, 3, 0)))
2749
2430
def get_backup_name(entry, by_parent, parent_trans_id, tt):
2750
2431
return _get_backup_name(entry.name, by_parent, parent_trans_id, tt)
2753
@deprecated_function(deprecated_in((2, 3, 0)))
2754
2434
def _get_backup_name(name, by_parent, parent_trans_id, tt):
2755
2435
"""Produce a backup-style name that appears to be available"""
2756
2436
def name_gen():
2793
2473
def revert(working_tree, target_tree, filenames, backups=False,
2794
pb=None, change_reporter=None):
2474
pb=DummyProgress(), change_reporter=None):
2795
2475
"""Revert a working tree's contents to those of a target tree."""
2796
2476
target_tree.lock_read()
2797
pb = ui.ui_factory.nested_progress_bar()
2798
2477
tt = TreeTransform(working_tree, pb)
2800
2479
pp = ProgressPhase("Revert phase", 3, pb)
2877
2558
tt.delete_contents(trans_id)
2878
2559
elif kind[1] is not None:
2879
2560
parent_trans_id = tt.trans_id_file_id(parent[0])
2880
backup_name = tt._available_backup_name(
2881
name[0], parent_trans_id)
2561
by_parent = tt.by_parent()
2562
backup_name = _get_backup_name(name[0], by_parent,
2563
parent_trans_id, tt)
2882
2564
tt.adjust_path(backup_name, parent_trans_id, trans_id)
2883
2565
new_trans_id = tt.create_path(name[0], parent_trans_id)
2884
2566
if versioned == (True, True):
2925
2607
parent_trans = ROOT_PARENT
2927
2609
parent_trans = tt.trans_id_file_id(parent[1])
2928
if parent[0] is None and versioned[0]:
2929
tt.adjust_root_path(name[1], parent_trans)
2931
tt.adjust_path(name[1], parent_trans, trans_id)
2610
tt.adjust_path(name[1], parent_trans, trans_id)
2932
2611
if executable[0] != executable[1] and kind[1] == "file":
2933
2612
tt.set_executability(executable[1], trans_id)
2934
if working_tree.supports_content_filtering():
2935
for index, ((trans_id, mode_id), bytes) in enumerate(
2936
target_tree.iter_files_bytes(deferred_files)):
2937
file_id = deferred_files[index][0]
2938
# We're reverting a tree to the target tree so using the
2939
# target tree to find the file path seems the best choice
2940
# here IMO - Ian C 27/Oct/2009
2941
filter_tree_path = target_tree.id2path(file_id)
2942
filters = working_tree._content_filter_stack(filter_tree_path)
2943
bytes = filtered_output_bytes(bytes, filters,
2944
ContentFilterContext(filter_tree_path, working_tree))
2945
tt.create_file(bytes, trans_id, mode_id)
2947
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2949
tt.create_file(bytes, trans_id, mode_id)
2950
tt.fixup_new_roots()
2613
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2615
tt.create_file(bytes, trans_id, mode_id)
2952
2617
if basis_tree is not None:
2953
2618
basis_tree.unlock()
2954
2619
return merge_modified
2957
def resolve_conflicts(tt, pb=None, pass_func=None):
2622
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
2958
2623
"""Make many conflict-resolution attempts, but die if they fail"""
2959
2624
if pass_func is None:
2960
2625
pass_func = conflict_pass
2961
2626
new_conflicts = set()
2962
pb = ui.ui_factory.nested_progress_bar()
2964
2628
for n in range(10):
2965
2629
pb.update('Resolution pass', n+1, 10)
3008
2672
elif c_type == 'missing parent':
3009
2673
trans_id = conflict[1]
3010
if trans_id in tt._removed_contents:
3011
cancel_deletion = True
3012
orphans = tt._get_potential_orphans(trans_id)
3014
cancel_deletion = False
3015
# All children are orphans
3018
tt.new_orphan(o, trans_id)
3019
except OrphaningError:
3020
# Something bad happened so we cancel the directory
3021
# deletion which will leave it in place with a
3022
# conflict. The user can deal with it from there.
3023
# Note that this also catch the case where we don't
3024
# want to create orphans and leave the directory in
3026
cancel_deletion = True
3029
# Cancel the directory deletion
3030
tt.cancel_deletion(trans_id)
3031
new_conflicts.add(('deleting parent', 'Not deleting',
2675
tt.cancel_deletion(trans_id)
2676
new_conflicts.add(('deleting parent', 'Not deleting',
3036
2681
tt.final_name(trans_id)
3119
2762
self.pending_deletions = []
3121
2764
def rename(self, from_, to):
3122
"""Rename a file from one path to another."""
2765
"""Rename a file from one path to another. Functions like os.rename"""
3124
2767
os.rename(from_, to)
3125
2768
except OSError, e:
3126
2769
if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
3127
2770
raise errors.FileExists(to, str(e))
3128
# normal OSError doesn't include filenames so it's hard to see where
3129
# the problem is, see https://bugs.launchpad.net/bzr/+bug/491763
3130
raise errors.TransformRenameFailed(from_, to, str(e), e.errno)
3131
2772
self.past_renames.append((from_, to))
3133
2774
def pre_delete(self, from_, to):