14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from __future__ import absolute_import
19
21
from stat import S_ISREG, S_IEXEC
21
from bzrlib.lazy_import import lazy_import
22
lazy_import(globals(), """
31
lazy_import.lazy_import(globals(), """
23
32
from bzrlib import (
32
42
revision as _mod_revision,
46
from bzrlib.i18n import gettext
35
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
36
ReusingTransform, NotVersionedError, CantMoveRoot,
48
from bzrlib.errors import (DuplicateKey, MalformedTransform,
49
ReusingTransform, CantMoveRoot,
37
50
ExistingLimbo, ImmortalLimbo, NoFinalPath,
38
51
UnableCreateSymlink)
39
52
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
40
from bzrlib.inventory import InventoryEntry
53
from bzrlib.mutabletree import MutableTree
41
54
from bzrlib.osutils import (
51
from bzrlib.progress import DummyProgress, ProgressPhase
62
from bzrlib.progress import ProgressPhase
52
63
from bzrlib.symbol_versioning import (
56
from bzrlib.trace import mutter, warning
57
from bzrlib import tree
59
import bzrlib.urlutils as urlutils
62
70
ROOT_PARENT = "root-parent"
65
72
def unique_add(map, key, value):
67
74
raise DuplicateKey(key=key)
71
79
class _TransformResults(object):
72
80
def __init__(self, modified_paths, rename_count):
73
81
object.__init__(self)
202
220
self.version_file(old_root_file_id, old_root)
203
221
self.unversion_file(self._new_root)
223
def fixup_new_roots(self):
224
"""Reinterpret requests to change the root directory
226
Instead of creating a root directory, or moving an existing directory,
227
all the attributes and children of the new root are applied to the
228
existing root directory.
230
This means that the old root trans-id becomes obsolete, so it is
231
recommended only to invoke this after the root trans-id has become
235
new_roots = [k for k, v in self._new_parent.iteritems() if v ==
237
if len(new_roots) < 1:
239
if len(new_roots) != 1:
240
raise ValueError('A tree cannot have two roots!')
241
if self._new_root is None:
242
self._new_root = new_roots[0]
244
old_new_root = new_roots[0]
245
# unversion the new root's directory.
246
if self.final_kind(self._new_root) is None:
247
file_id = self.final_file_id(old_new_root)
249
file_id = self.final_file_id(self._new_root)
250
if old_new_root in self._new_id:
251
self.cancel_versioning(old_new_root)
253
self.unversion_file(old_new_root)
254
# if, at this stage, root still has an old file_id, zap it so we can
255
# stick a new one in.
256
if (self.tree_file_id(self._new_root) is not None and
257
self._new_root not in self._removed_id):
258
self.unversion_file(self._new_root)
259
if file_id is not None:
260
self.version_file(file_id, self._new_root)
262
# Now move children of new root into old root directory.
263
# Ensure all children are registered with the transaction, but don't
264
# use directly-- some tree children have new parents
265
list(self.iter_tree_children(old_new_root))
266
# Move all children of new root into old root directory.
267
for child in self.by_parent().get(old_new_root, []):
268
self.adjust_path(self.final_name(child), self._new_root, child)
270
# Ensure old_new_root has no directory.
271
if old_new_root in self._new_contents:
272
self.cancel_creation(old_new_root)
274
self.delete_contents(old_new_root)
276
# prevent deletion of root directory.
277
if self._new_root in self._removed_contents:
278
self.cancel_deletion(self._new_root)
280
# destroy path info for old_new_root.
281
del self._new_parent[old_new_root]
282
del self._new_name[old_new_root]
205
284
def trans_id_tree_file_id(self, inventory_id):
206
285
"""Determine the transaction id of a working tree file.
317
397
return sorted(FinalPaths(self).get_paths(new_ids))
319
399
def _inventory_altered(self):
320
"""Get the trans_ids and paths of files needing new inv entries."""
322
for id_set in [self._new_name, self._new_parent, self._new_id,
400
"""Determine which trans_ids need new Inventory entries.
402
An new entry is needed when anything that would be reflected by an
403
inventory entry changes, including file name, file_id, parent file_id,
404
file kind, and the execute bit.
406
Some care is taken to return entries with real changes, not cases
407
where the value is deleted and then restored to its original value,
408
but some actually unchanged values may be returned.
410
:returns: A list of (path, trans_id) for all items requiring an
411
inventory change. Ordered by path.
414
# Find entries whose file_ids are new (or changed).
415
new_file_id = set(t for t in self._new_id
416
if self._new_id[t] != self.tree_file_id(t))
417
for id_set in [self._new_name, self._new_parent, new_file_id,
323
418
self._new_executability]:
324
new_ids.update(id_set)
419
changed_ids.update(id_set)
420
# removing implies a kind change
325
421
changed_kind = set(self._removed_contents)
326
423
changed_kind.intersection_update(self._new_contents)
327
changed_kind.difference_update(new_ids)
328
changed_kind = (t for t in changed_kind if self.tree_kind(t) !=
330
new_ids.update(changed_kind)
331
return sorted(FinalPaths(self).get_paths(new_ids))
424
# Ignore entries that are already known to have changed.
425
changed_kind.difference_update(changed_ids)
426
# to keep only the truly changed ones
427
changed_kind = (t for t in changed_kind
428
if self.tree_kind(t) != self.final_kind(t))
429
# all kind changes will alter the inventory
430
changed_ids.update(changed_kind)
431
# To find entries with changed parent_ids, find parents which existed,
432
# but changed file_id.
433
changed_file_id = set(t for t in new_file_id if t in self._removed_id)
434
# Now add all their children to the set.
435
for parent_trans_id in new_file_id:
436
changed_ids.update(self.iter_tree_children(parent_trans_id))
437
return sorted(FinalPaths(self).get_paths(changed_ids))
333
439
def final_kind(self, trans_id):
334
440
"""Determine the final file kind, after any changes applied.
336
Raises NoSuchFile if the file does not exist/has no contents.
337
(It is conceivable that a path would be created without the
338
corresponding contents insertion command)
442
:return: None if the file does not exist/has no contents. (It is
443
conceivable that a path would be created without the corresponding
444
contents insertion command)
340
446
if trans_id in self._new_contents:
341
447
return self._new_contents[trans_id]
342
448
elif trans_id in self._removed_contents:
343
raise NoSuchFile(None)
345
451
return self.tree_kind(trans_id)
463
574
# ensure that all children are registered with the transaction
464
575
list(self.iter_tree_children(parent_id))
577
@deprecated_method(deprecated_in((2, 3, 0)))
466
578
def has_named_child(self, by_parent, parent_id, name):
468
children = by_parent[parent_id]
471
for child in children:
579
return self._has_named_child(
580
name, parent_id, known_children=by_parent.get(parent_id, []))
582
def _has_named_child(self, name, parent_id, known_children):
583
"""Does a parent already have a name child.
585
:param name: The searched for name.
587
:param parent_id: The parent for which the check is made.
589
:param known_children: The already known children. This should have
590
been recently obtained from `self.by_parent.get(parent_id)`
591
(or will be if None is passed).
593
if known_children is None:
594
known_children = self.by_parent().get(parent_id, [])
595
for child in known_children:
472
596
if self.final_name(child) == name:
475
path = self._tree_id_paths[parent_id]
598
parent_path = self._tree_id_paths.get(parent_id, None)
599
if parent_path is None:
600
# No parent... no children
478
childpath = joinpath(path, name)
479
child_id = self._tree_path_ids.get(childpath)
602
child_path = joinpath(parent_path, name)
603
child_id = self._tree_path_ids.get(child_path, None)
480
604
if child_id is None:
481
return lexists(self._tree.abspath(childpath))
605
# Not known by the tree transform yet, check the filesystem
606
return osutils.lexists(self._tree.abspath(child_path))
483
if self.final_parent(child_id) != parent_id:
485
if child_id in self._removed_contents:
486
# XXX What about dangling file-ids?
608
raise AssertionError('child_id is missing: %s, %s, %s'
609
% (name, parent_id, child_id))
611
def _available_backup_name(self, name, target_id):
612
"""Find an available backup name.
614
:param name: The basename of the file.
616
:param target_id: The directory trans_id where the backup should
619
known_children = self.by_parent().get(target_id, [])
620
return osutils.available_backup_name(
622
lambda base: self._has_named_child(
623
base, target_id, known_children))
491
625
def _parent_loops(self):
492
626
"""No entry should be its own ancestor"""
613
742
def _parent_type_conflicts(self, by_parent):
614
"""parents must have directory 'contents'."""
743
"""Children must have a directory parent"""
616
745
for parent_id, children in by_parent.iteritems():
617
if parent_id is ROOT_PARENT:
619
if not self._any_contents(children):
621
for child in children:
623
self.final_kind(child)
627
kind = self.final_kind(parent_id)
746
if parent_id == ROOT_PARENT:
749
for child_id in children:
750
if self.final_kind(child_id) is not None:
755
# There is at least a child, so we need an existing directory to
757
kind = self.final_kind(parent_id)
759
# The directory will be deleted
631
760
conflicts.append(('missing parent', parent_id))
632
761
elif kind != "directory":
762
# Meh, we need a *directory* to put something in it
633
763
conflicts.append(('non-directory parent', parent_id))
636
def _any_contents(self, trans_ids):
637
"""Return true if any of the trans_ids, will have contents."""
638
for trans_id in trans_ids:
640
kind = self.final_kind(trans_id)
646
766
def _set_executability(self, path, trans_id):
647
767
"""Set the executability of versioned files """
648
if supports_executable():
768
if self._tree._supports_executable():
649
769
new_executability = self._new_executability[trans_id]
650
770
abspath = self._tree.abspath(path)
651
771
current_mode = os.stat(abspath).st_mode
712
832
self.create_symlink(target, trans_id)
835
def new_orphan(self, trans_id, parent_id):
836
"""Schedule an item to be orphaned.
838
When a directory is about to be removed, its children, if they are not
839
versioned are moved out of the way: they don't have a parent anymore.
841
:param trans_id: The trans_id of the existing item.
842
:param parent_id: The parent trans_id of the item.
844
raise NotImplementedError(self.new_orphan)
846
def _get_potential_orphans(self, dir_id):
847
"""Find the potential orphans in a directory.
849
A directory can't be safely deleted if there are versioned files in it.
850
If all the contained files are unversioned then they can be orphaned.
852
The 'None' return value means that the directory contains at least one
853
versioned file and should not be deleted.
855
:param dir_id: The directory trans id.
857
:return: A list of the orphan trans ids or None if at least one
858
versioned file is present.
861
# Find the potential orphans, stop if one item should be kept
862
for child_tid in self.by_parent()[dir_id]:
863
if child_tid in self._removed_contents:
864
# The child is removed as part of the transform. Since it was
865
# versioned before, it's not an orphan
867
elif self.final_file_id(child_tid) is None:
868
# The child is not versioned
869
orphans.append(child_tid)
871
# We have a versioned file here, searching for orphans is
715
877
def _affected_ids(self):
716
878
"""Return the set of transform ids affected by the transform"""
717
879
trans_ids = set(self._removed_id)
854
1013
def get_preview_tree(self):
855
1014
"""Return a tree representing the result of the transform.
857
This tree only supports the subset of Tree functionality required
858
by show_diff_trees. It must only be compared to tt._tree.
1016
The tree is a snapshot, and altering the TreeTransform will invalidate
860
1019
return _PreviewTree(self)
1021
def commit(self, branch, message, merge_parents=None, strict=False,
1022
timestamp=None, timezone=None, committer=None, authors=None,
1023
revprops=None, revision_id=None):
1024
"""Commit the result of this TreeTransform to a branch.
1026
:param branch: The branch to commit to.
1027
:param message: The message to attach to the commit.
1028
:param merge_parents: Additional parent revision-ids specified by
1030
:param strict: If True, abort the commit if there are unversioned
1032
:param timestamp: if not None, seconds-since-epoch for the time and
1033
date. (May be a float.)
1034
:param timezone: Optional timezone for timestamp, as an offset in
1036
:param committer: Optional committer in email-id format.
1037
(e.g. "J Random Hacker <jrandom@example.com>")
1038
:param authors: Optional list of authors in email-id format.
1039
:param revprops: Optional dictionary of revision properties.
1040
:param revision_id: Optional revision id. (Specifying a revision-id
1041
may reduce performance for some non-native formats.)
1042
:return: The revision_id of the revision committed.
1044
self._check_malformed()
1046
unversioned = set(self._new_contents).difference(set(self._new_id))
1047
for trans_id in unversioned:
1048
if self.final_file_id(trans_id) is None:
1049
raise errors.StrictCommitFailed()
1051
revno, last_rev_id = branch.last_revision_info()
1052
if last_rev_id == _mod_revision.NULL_REVISION:
1053
if merge_parents is not None:
1054
raise ValueError('Cannot supply merge parents for first'
1058
parent_ids = [last_rev_id]
1059
if merge_parents is not None:
1060
parent_ids.extend(merge_parents)
1061
if self._tree.get_revision_id() != last_rev_id:
1062
raise ValueError('TreeTransform not based on branch basis: %s' %
1063
self._tree.get_revision_id())
1064
revprops = commit.Commit.update_revprops(revprops, branch, authors)
1065
builder = branch.get_commit_builder(parent_ids,
1066
timestamp=timestamp,
1068
committer=committer,
1070
revision_id=revision_id)
1071
preview = self.get_preview_tree()
1072
list(builder.record_iter_changes(preview, last_rev_id,
1073
self.iter_changes()))
1074
builder.finish_inventory()
1075
revision_id = builder.commit(message)
1076
branch.set_last_revision_info(revno + 1, revision_id)
862
1079
def _text_parent(self, trans_id):
863
1080
file_id = self.tree_file_id(trans_id)
991
1210
if self._tree is None:
994
entries = [(self._limbo_name(t), t, k) for t, k in
995
self._new_contents.iteritems()]
996
entries.sort(reverse=True)
997
for path, trans_id, kind in entries:
998
if kind == "directory":
1213
limbo_paths = self._limbo_files.values() + list(
1214
self._possibly_stale_limbo_files)
1215
limbo_paths = sorted(limbo_paths, reverse=True)
1216
for path in limbo_paths:
1220
if e.errno != errno.ENOENT:
1222
# XXX: warn? perhaps we just got interrupted at an
1223
# inconvenient moment, but perhaps files are disappearing
1003
os.rmdir(self._limbodir)
1226
delete_any(self._limbodir)
1004
1227
except OSError:
1005
1228
# We don't especially care *why* the dir is immortal.
1006
1229
raise ImmortalLimbo(self._limbodir)
1008
1231
if self._deletiondir is not None:
1009
os.rmdir(self._deletiondir)
1232
delete_any(self._deletiondir)
1010
1233
except OSError:
1011
1234
raise errors.ImmortalPendingDeletion(self._deletiondir)
1013
1236
TreeTransformBase.finalize(self)
1238
def _limbo_supports_executable(self):
1239
"""Check if the limbo path supports the executable bit."""
1240
# FIXME: Check actual file system capabilities of limbodir
1241
return osutils.supports_executable()
1015
1243
def _limbo_name(self, trans_id):
1016
1244
"""Generate the limbo name of a file"""
1017
1245
limbo_name = self._limbo_files.get(trans_id)
1018
if limbo_name is not None:
1020
parent = self._new_parent.get(trans_id)
1021
# if the parent directory is already in limbo (e.g. when building a
1022
# tree), choose a limbo name inside the parent, to reduce further
1024
use_direct_path = False
1025
if self._new_contents.get(parent) == 'directory':
1026
filename = self._new_name.get(trans_id)
1027
if filename is not None:
1028
if parent not in self._limbo_children:
1029
self._limbo_children[parent] = set()
1030
self._limbo_children_names[parent] = {}
1031
use_direct_path = True
1032
# the direct path can only be used if no other file has
1033
# already taken this pathname, i.e. if the name is unused, or
1034
# if it is already associated with this trans_id.
1035
elif self._case_sensitive_target:
1036
if (self._limbo_children_names[parent].get(filename)
1037
in (trans_id, None)):
1038
use_direct_path = True
1040
for l_filename, l_trans_id in\
1041
self._limbo_children_names[parent].iteritems():
1042
if l_trans_id == trans_id:
1044
if l_filename.lower() == filename.lower():
1047
use_direct_path = True
1050
limbo_name = pathjoin(self._limbo_files[parent], filename)
1051
self._limbo_children[parent].add(trans_id)
1052
self._limbo_children_names[parent][filename] = trans_id
1054
limbo_name = pathjoin(self._limbodir, trans_id)
1055
self._needs_rename.add(trans_id)
1056
self._limbo_files[trans_id] = limbo_name
1246
if limbo_name is None:
1247
limbo_name = self._generate_limbo_path(trans_id)
1248
self._limbo_files[trans_id] = limbo_name
1057
1249
return limbo_name
1251
def _generate_limbo_path(self, trans_id):
1252
"""Generate a limbo path using the trans_id as the relative path.
1254
This is suitable as a fallback, and when the transform should not be
1255
sensitive to the path encoding of the limbo directory.
1257
self._needs_rename.add(trans_id)
1258
return pathjoin(self._limbodir, trans_id)
1059
1260
def adjust_path(self, name, parent, trans_id):
1060
1261
previous_parent = self._new_parent.get(trans_id)
1061
1262
previous_name = self._new_name.get(trans_id)
1078
1281
entries from _limbo_files, because they are now stale.
1080
1283
for trans_id in trans_ids:
1081
old_path = self._limbo_files.pop(trans_id)
1284
old_path = self._limbo_files[trans_id]
1285
self._possibly_stale_limbo_files.add(old_path)
1286
del self._limbo_files[trans_id]
1082
1287
if trans_id not in self._new_contents:
1084
1289
new_path = self._limbo_name(trans_id)
1085
1290
os.rename(old_path, new_path)
1087
def create_file(self, contents, trans_id, mode_id=None):
1291
self._possibly_stale_limbo_files.remove(old_path)
1292
for descendant in self._limbo_descendants(trans_id):
1293
desc_path = self._limbo_files[descendant]
1294
desc_path = new_path + desc_path[len(old_path):]
1295
self._limbo_files[descendant] = desc_path
1297
def _limbo_descendants(self, trans_id):
1298
"""Return the set of trans_ids whose limbo paths descend from this."""
1299
descendants = set(self._limbo_children.get(trans_id, []))
1300
for descendant in list(descendants):
1301
descendants.update(self._limbo_descendants(descendant))
1304
def create_file(self, contents, trans_id, mode_id=None, sha1=None):
1088
1305
"""Schedule creation of a new file.
1092
Contents is an iterator of strings, all of which will be written
1093
to the target destination.
1095
New file takes the permissions of any existing file with that id,
1096
unless mode_id is specified.
1309
:param contents: an iterator of strings, all of which will be written
1310
to the target destination.
1311
:param trans_id: TreeTransform handle
1312
:param mode_id: If not None, force the mode of the target file to match
1313
the mode of the object referenced by mode_id.
1314
Otherwise, we will try to preserve mode bits of an existing file.
1315
:param sha1: If the sha1 of this content is already known, pass it in.
1316
We can use it to prevent future sha1 computations.
1098
1318
name = self._limbo_name(trans_id)
1099
1319
f = open(name, 'wb')
1102
unique_add(self._new_contents, trans_id, 'file')
1104
# Clean up the file, it never got registered so
1105
# TreeTransform.finalize() won't clean it up.
1321
unique_add(self._new_contents, trans_id, 'file')
1110
1322
f.writelines(contents)
1325
self._set_mtime(name)
1113
1326
self._set_mode(trans_id, mode_id, S_ISREG)
1327
# It is unfortunate we have to use lstat instead of fstat, but we just
1328
# used utime and chmod on the file, so we need the accurate final
1330
if sha1 is not None:
1331
self._observed_sha1s[trans_id] = (sha1, osutils.lstat(name))
1115
1333
def _read_file_chunks(self, trans_id):
1116
1334
cur_file = open(self._limbo_name(trans_id), 'rb')
1175
1404
del self._limbo_children_names[trans_id]
1176
1405
delete_any(self._limbo_name(trans_id))
1407
def new_orphan(self, trans_id, parent_id):
1408
# FIXME: There is no tree config, so we use the branch one (it's weird
1409
# to define it this way as orphaning can only occur in a working tree,
1410
# but that's all we have (for now). It will find the option in
1411
# locations.conf or bazaar.conf though) -- vila 20100916
1412
conf = self._tree.branch.get_config()
1413
conf_var_name = 'bzr.transform.orphan_policy'
1414
orphan_policy = conf.get_user_option(conf_var_name)
1415
default_policy = orphaning_registry.default_key
1416
if orphan_policy is None:
1417
orphan_policy = default_policy
1418
if orphan_policy not in orphaning_registry:
1419
trace.warning('%s (from %s) is not a known policy, defaulting '
1420
'to %s' % (orphan_policy, conf_var_name, default_policy))
1421
orphan_policy = default_policy
1422
handle_orphan = orphaning_registry.get(orphan_policy)
1423
handle_orphan(self, trans_id, parent_id)
1426
class OrphaningError(errors.BzrError):
1428
# Only bugs could lead to such exception being seen by the user
1429
internal_error = True
1430
_fmt = "Error while orphaning %s in %s directory"
1432
def __init__(self, orphan, parent):
1433
errors.BzrError.__init__(self)
1434
self.orphan = orphan
1435
self.parent = parent
1438
class OrphaningForbidden(OrphaningError):
1440
_fmt = "Policy: %s doesn't allow creating orphans."
1442
def __init__(self, policy):
1443
errors.BzrError.__init__(self)
1444
self.policy = policy
1447
def move_orphan(tt, orphan_id, parent_id):
1448
"""See TreeTransformBase.new_orphan.
1450
This creates a new orphan in the `bzr-orphans` dir at the root of the
1453
:param tt: The TreeTransform orphaning `trans_id`.
1455
:param orphan_id: The trans id that should be orphaned.
1457
:param parent_id: The orphan parent trans id.
1459
# Add the orphan dir if it doesn't exist
1460
orphan_dir_basename = 'bzr-orphans'
1461
od_id = tt.trans_id_tree_path(orphan_dir_basename)
1462
if tt.final_kind(od_id) is None:
1463
tt.create_directory(od_id)
1464
parent_path = tt._tree_id_paths[parent_id]
1465
# Find a name that doesn't exist yet in the orphan dir
1466
actual_name = tt.final_name(orphan_id)
1467
new_name = tt._available_backup_name(actual_name, od_id)
1468
tt.adjust_path(new_name, od_id, orphan_id)
1469
trace.warning('%s has been orphaned in %s'
1470
% (joinpath(parent_path, actual_name), orphan_dir_basename))
1473
def refuse_orphan(tt, orphan_id, parent_id):
1474
"""See TreeTransformBase.new_orphan.
1476
This refuses to create orphan, letting the caller handle the conflict.
1478
raise OrphaningForbidden('never')
1481
orphaning_registry = registry.Registry()
1482
orphaning_registry.register(
1483
'conflict', refuse_orphan,
1484
'Leave orphans in place and create a conflict on the directory.')
1485
orphaning_registry.register(
1486
'move', move_orphan,
1487
'Move orphans into the bzr-orphans directory.')
1488
orphaning_registry._set_default_key('conflict')
1179
1491
class TreeTransform(DiskTreeTransform):
1180
1492
"""Represent a tree transformation.
1358
1663
yield self.trans_id_tree_path(childpath)
1665
def _generate_limbo_path(self, trans_id):
1666
"""Generate a limbo path using the final path if possible.
1668
This optimizes the performance of applying the tree transform by
1669
avoiding renames. These renames can be avoided only when the parent
1670
directory is already scheduled for creation.
1672
If the final path cannot be used, falls back to using the trans_id as
1675
parent = self._new_parent.get(trans_id)
1676
# if the parent directory is already in limbo (e.g. when building a
1677
# tree), choose a limbo name inside the parent, to reduce further
1679
use_direct_path = False
1680
if self._new_contents.get(parent) == 'directory':
1681
filename = self._new_name.get(trans_id)
1682
if filename is not None:
1683
if parent not in self._limbo_children:
1684
self._limbo_children[parent] = set()
1685
self._limbo_children_names[parent] = {}
1686
use_direct_path = True
1687
# the direct path can only be used if no other file has
1688
# already taken this pathname, i.e. if the name is unused, or
1689
# if it is already associated with this trans_id.
1690
elif self._case_sensitive_target:
1691
if (self._limbo_children_names[parent].get(filename)
1692
in (trans_id, None)):
1693
use_direct_path = True
1695
for l_filename, l_trans_id in\
1696
self._limbo_children_names[parent].iteritems():
1697
if l_trans_id == trans_id:
1699
if l_filename.lower() == filename.lower():
1702
use_direct_path = True
1704
if not use_direct_path:
1705
return DiskTreeTransform._generate_limbo_path(self, trans_id)
1707
limbo_name = pathjoin(self._limbo_files[parent], filename)
1708
self._limbo_children[parent].add(trans_id)
1709
self._limbo_children_names[parent][filename] = trans_id
1361
1713
def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1362
1714
"""Apply all changes to the inventory and filesystem.
1479
1833
tree_paths = list(self._tree_path_ids.iteritems())
1480
1834
tree_paths.sort(reverse=True)
1481
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1835
child_pb = ui.ui_factory.nested_progress_bar()
1483
for num, data in enumerate(tree_paths):
1484
path, trans_id = data
1485
child_pb.update('removing file', num, len(tree_paths))
1837
for num, (path, trans_id) in enumerate(tree_paths):
1838
# do not attempt to move root into a subdirectory of itself.
1841
child_pb.update(gettext('removing file'), num, len(tree_paths))
1486
1842
full_path = self._tree.abspath(path)
1487
1843
if trans_id in self._removed_contents:
1488
mover.pre_delete(full_path, os.path.join(self._deletiondir,
1490
elif trans_id in self._new_name or trans_id in \
1844
delete_path = os.path.join(self._deletiondir, trans_id)
1845
mover.pre_delete(full_path, delete_path)
1846
elif (trans_id in self._new_name
1847
or trans_id in self._new_parent):
1493
1849
mover.rename(full_path, self._limbo_name(trans_id))
1850
except errors.TransformRenameFailed, e:
1495
1851
if e.errno != errno.ENOENT:
1513
1869
modified_paths = []
1514
1870
new_path_file_ids = dict((t, self.final_file_id(t)) for p, t in
1516
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1872
child_pb = ui.ui_factory.nested_progress_bar()
1518
1874
for num, (path, trans_id) in enumerate(new_paths):
1519
1875
if (num % 10) == 0:
1520
child_pb.update('adding file', num, len(new_paths))
1876
child_pb.update(gettext('adding file'), num, len(new_paths))
1521
1877
full_path = self._tree.abspath(path)
1522
1878
if trans_id in self._needs_rename:
1524
1880
mover.rename(self._limbo_name(trans_id), full_path)
1881
except errors.TransformRenameFailed, e:
1526
1882
# We may be renaming a dangling inventory id
1527
1883
if e.errno != errno.ENOENT:
1530
1886
self.rename_count += 1
1887
# TODO: if trans_id in self._observed_sha1s, we should
1888
# re-stat the final target, since ctime will be
1889
# updated by the change.
1531
1890
if (trans_id in self._new_contents or
1532
1891
self.path_changed(trans_id)):
1533
1892
if trans_id in self._new_contents:
1534
1893
modified_paths.append(full_path)
1535
1894
if trans_id in self._new_executability:
1536
1895
self._set_executability(path, trans_id)
1896
if trans_id in self._observed_sha1s:
1897
o_sha1, o_st_val = self._observed_sha1s[trans_id]
1898
st = osutils.lstat(full_path)
1899
self._observed_sha1s[trans_id] = (o_sha1, st)
1538
1901
child_pb.finished()
1902
for path, trans_id in new_paths:
1903
# new_paths includes stuff like workingtree conflicts. Only the
1904
# stuff in new_contents actually comes from limbo.
1905
if trans_id in self._limbo_files:
1906
del self._limbo_files[trans_id]
1539
1907
self._new_contents.clear()
1540
1908
return modified_paths
1910
def _apply_observed_sha1s(self):
1911
"""After we have finished renaming everything, update observed sha1s
1913
This has to be done after self._tree.apply_inventory_delta, otherwise
1914
it doesn't know anything about the files we are updating. Also, we want
1915
to do this as late as possible, so that most entries end up cached.
1917
# TODO: this doesn't update the stat information for directories. So
1918
# the first 'bzr status' will still need to rewrite
1919
# .bzr/checkout/dirstate. However, we at least don't need to
1920
# re-read all of the files.
1921
# TODO: If the operation took a while, we could do a time.sleep(3) here
1922
# to allow the clock to tick over and ensure we won't have any
1923
# problems. (we could observe start time, and finish time, and if
1924
# it is less than eg 10% overhead, add a sleep call.)
1925
paths = FinalPaths(self)
1926
for trans_id, observed in self._observed_sha1s.iteritems():
1927
path = paths.get_path(trans_id)
1928
# We could get the file_id, but dirstate prefers to use the path
1929
# anyway, and it is 'cheaper' to determine.
1930
# file_id = self._new_id[trans_id]
1931
self._tree._observed_sha1(None, path, observed)
1543
1934
class TransformPreview(DiskTreeTransform):
1544
1935
"""A TreeTransform for generating preview trees.
2135
2568
pp.next_phase()
2136
2569
file_trans_id[wt.get_root_id()] = \
2137
2570
tt.trans_id_tree_file_id(wt.get_root_id())
2138
pb = bzrlib.ui.ui_factory.nested_progress_bar()
2571
pb = ui.ui_factory.nested_progress_bar()
2140
2573
deferred_contents = []
2142
total = len(tree.inventory)
2575
total = len(tree.all_file_ids())
2143
2576
if delta_from_tree:
2144
2577
precomputed_delta = []
2146
2579
precomputed_delta = None
2580
# Check if tree inventory has content. If so, we populate
2581
# existing_files with the directory content. If there are no
2582
# entries we skip populating existing_files as its not used.
2583
# This improves performance and unncessary work on large
2584
# directory trees. (#501307)
2586
existing_files = set()
2587
for dir, files in wt.walkdirs():
2588
existing_files.update(f[0] for f in files)
2147
2589
for num, (tree_path, entry) in \
2148
enumerate(tree.inventory.iter_entries_by_dir()):
2149
pb.update("Building tree", num - len(deferred_contents), total)
2590
enumerate(tree.iter_entries_by_dir()):
2591
pb.update(gettext("Building tree"), num - len(deferred_contents), total)
2150
2592
if entry.parent_id is None:
2152
2594
reparent = False
2222
2664
new_desired_files = desired_files
2224
2666
iter = accelerator_tree.iter_changes(tree, include_unchanged=True)
2225
unchanged = dict((f, p[1]) for (f, p, c, v, d, n, k, e)
2226
in iter if not (c or e[0] != e[1]))
2667
unchanged = [(f, p[1]) for (f, p, c, v, d, n, k, e)
2668
in iter if not (c or e[0] != e[1])]
2669
if accelerator_tree.supports_content_filtering():
2670
unchanged = [(f, p) for (f, p) in unchanged
2671
if not accelerator_tree.iter_search_rules([p]).next()]
2672
unchanged = dict(unchanged)
2227
2673
new_desired_files = []
2229
for file_id, (trans_id, tree_path) in desired_files:
2675
for file_id, (trans_id, tree_path, text_sha1) in desired_files:
2230
2676
accelerator_path = unchanged.get(file_id)
2231
2677
if accelerator_path is None:
2232
new_desired_files.append((file_id, (trans_id, tree_path)))
2678
new_desired_files.append((file_id,
2679
(trans_id, tree_path, text_sha1)))
2234
pb.update('Adding file contents', count + offset, total)
2681
pb.update(gettext('Adding file contents'), count + offset, total)
2236
2683
tt.create_hardlink(accelerator_tree.abspath(accelerator_path),
2253
2700
offset += count
2254
for count, ((trans_id, tree_path), contents) in enumerate(
2701
for count, ((trans_id, tree_path, text_sha1), contents) in enumerate(
2255
2702
tree.iter_files_bytes(new_desired_files)):
2256
2703
if wt.supports_content_filtering():
2257
2704
filters = wt._content_filter_stack(tree_path)
2258
2705
contents = filtered_output_bytes(contents, filters,
2259
2706
ContentFilterContext(tree_path, tree))
2260
tt.create_file(contents, trans_id)
2261
pb.update('Adding file contents', count + offset, total)
2707
tt.create_file(contents, trans_id, sha1=text_sha1)
2708
pb.update(gettext('Adding file contents'), count + offset, total)
2264
2711
def _reparent_children(tt, old_parent, new_parent):
2265
2712
for child in tt.iter_tree_children(old_parent):
2266
2713
tt.adjust_path(tt.final_name(child), new_parent, child)
2268
2716
def _reparent_transform_children(tt, old_parent, new_parent):
2269
2717
by_parent = tt.by_parent()
2270
2718
for child in by_parent[old_parent]:
2271
2719
tt.adjust_path(tt.final_name(child), new_parent, child)
2272
2720
return by_parent[old_parent]
2274
2723
def _content_match(tree, entry, file_id, kind, target_path):
2275
2724
if entry.kind != kind:
2277
2726
if entry.kind == "directory":
2279
2728
if entry.kind == "file":
2280
if tree.get_file(file_id).read() == file(target_path, 'rb').read():
2729
f = file(target_path, 'rb')
2731
if tree.get_file_text(file_id) == f.read():
2282
2735
elif entry.kind == "symlink":
2283
2736
if tree.get_symlink_target(file_id) == os.readlink(target_path):
2336
2789
raise errors.BadFileKindError(name, kind)
2339
@deprecated_function(deprecated_in((1, 9, 0)))
2340
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
2341
"""Create new file contents according to an inventory entry.
2343
DEPRECATED. Use create_from_tree instead.
2792
def create_from_tree(tt, trans_id, tree, file_id, bytes=None,
2793
filter_tree_path=None):
2794
"""Create new file contents according to tree contents.
2796
:param filter_tree_path: the tree path to use to lookup
2797
content filters to apply to the bytes output in the working tree.
2798
This only applies if the working tree supports content filtering.
2345
if entry.kind == "file":
2347
lines = tree.get_file(entry.file_id).readlines()
2348
tt.create_file(lines, trans_id, mode_id=mode_id)
2349
elif entry.kind == "symlink":
2350
tt.create_symlink(tree.get_symlink_target(entry.file_id), trans_id)
2351
elif entry.kind == "directory":
2352
tt.create_directory(trans_id)
2355
def create_from_tree(tt, trans_id, tree, file_id, bytes=None):
2356
"""Create new file contents according to tree contents."""
2357
2800
kind = tree.kind(file_id)
2358
2801
if kind == 'directory':
2359
2802
tt.create_directory(trans_id)
2393
2843
return new_name
2396
def _entry_changes(file_id, entry, working_tree):
2397
"""Determine in which ways the inventory entry has changed.
2399
Returns booleans: has_contents, content_mod, meta_mod
2400
has_contents means there are currently contents, but they differ
2401
contents_mod means contents need to be modified
2402
meta_mod means the metadata needs to be modified
2404
cur_entry = working_tree.inventory[file_id]
2406
working_kind = working_tree.kind(file_id)
2409
has_contents = False
2412
if has_contents is True:
2413
if entry.kind != working_kind:
2414
contents_mod, meta_mod = True, False
2416
cur_entry._read_tree_state(working_tree.id2path(file_id),
2418
contents_mod, meta_mod = entry.detect_changes(cur_entry)
2419
cur_entry._forget_tree_state()
2420
return has_contents, contents_mod, meta_mod
2423
2846
def revert(working_tree, target_tree, filenames, backups=False,
2424
pb=DummyProgress(), change_reporter=None):
2847
pb=None, change_reporter=None):
2425
2848
"""Revert a working tree's contents to those of a target tree."""
2426
2849
target_tree.lock_read()
2850
pb = ui.ui_factory.nested_progress_bar()
2427
2851
tt = TreeTransform(working_tree, pb)
2429
2853
pp = ProgressPhase("Revert phase", 3, pb)
2498
2930
if basis_tree is None:
2499
2931
basis_tree = working_tree.basis_tree()
2500
2932
basis_tree.lock_read()
2501
if file_id in basis_tree:
2933
if basis_tree.has_id(file_id):
2502
2934
if wt_sha1 != basis_tree.get_file_sha1(file_id):
2503
2935
keep_content = True
2504
elif kind[1] is None and not versioned[1]:
2936
elif target_kind is None and not target_versioned:
2505
2937
keep_content = True
2506
if kind[0] is not None:
2938
if wt_kind is not None:
2507
2939
if not keep_content:
2508
2940
tt.delete_contents(trans_id)
2509
elif kind[1] is not None:
2510
parent_trans_id = tt.trans_id_file_id(parent[0])
2511
by_parent = tt.by_parent()
2512
backup_name = _get_backup_name(name[0], by_parent,
2513
parent_trans_id, tt)
2941
elif target_kind is not None:
2942
parent_trans_id = tt.trans_id_file_id(wt_parent)
2943
backup_name = tt._available_backup_name(
2944
wt_name, parent_trans_id)
2514
2945
tt.adjust_path(backup_name, parent_trans_id, trans_id)
2515
new_trans_id = tt.create_path(name[0], parent_trans_id)
2516
if versioned == (True, True):
2946
new_trans_id = tt.create_path(wt_name, parent_trans_id)
2947
if wt_versioned and target_versioned:
2517
2948
tt.unversion_file(trans_id)
2518
2949
tt.version_file(file_id, new_trans_id)
2519
2950
# New contents should have the same unix perms as old
2521
2952
mode_id = trans_id
2522
2953
trans_id = new_trans_id
2523
if kind[1] in ('directory', 'tree-reference'):
2954
if target_kind in ('directory', 'tree-reference'):
2524
2955
tt.create_directory(trans_id)
2525
if kind[1] == 'tree-reference':
2956
if target_kind == 'tree-reference':
2526
2957
revision = target_tree.get_reference_revision(file_id,
2528
2959
tt.set_tree_reference(revision, trans_id)
2529
elif kind[1] == 'symlink':
2960
elif target_kind == 'symlink':
2530
2961
tt.create_symlink(target_tree.get_symlink_target(file_id),
2532
elif kind[1] == 'file':
2963
elif target_kind == 'file':
2533
2964
deferred_files.append((file_id, (trans_id, mode_id)))
2534
2965
if basis_tree is None:
2535
2966
basis_tree = working_tree.basis_tree()
2536
2967
basis_tree.lock_read()
2537
2968
new_sha1 = target_tree.get_file_sha1(file_id)
2538
if (file_id in basis_tree and new_sha1 ==
2539
basis_tree.get_file_sha1(file_id)):
2969
if (basis_tree.has_id(file_id) and
2970
new_sha1 == basis_tree.get_file_sha1(file_id)):
2540
2971
if file_id in merge_modified:
2541
2972
del merge_modified[file_id]
2543
2974
merge_modified[file_id] = new_sha1
2545
2976
# preserve the execute bit when backing up
2546
if keep_content and executable[0] == executable[1]:
2547
tt.set_executability(executable[1], trans_id)
2548
elif kind[1] is not None:
2549
raise AssertionError(kind[1])
2550
if versioned == (False, True):
2977
if keep_content and wt_executable == target_executable:
2978
tt.set_executability(target_executable, trans_id)
2979
elif target_kind is not None:
2980
raise AssertionError(target_kind)
2981
if not wt_versioned and target_versioned:
2551
2982
tt.version_file(file_id, trans_id)
2552
if versioned == (True, False):
2983
if wt_versioned and not target_versioned:
2553
2984
tt.unversion_file(trans_id)
2554
if (name[1] is not None and
2555
(name[0] != name[1] or parent[0] != parent[1])):
2556
if name[1] == '' and parent[1] is None:
2985
if (target_name is not None and
2986
(wt_name != target_name or wt_parent != target_parent)):
2987
if target_name == '' and target_parent is None:
2557
2988
parent_trans = ROOT_PARENT
2559
parent_trans = tt.trans_id_file_id(parent[1])
2560
tt.adjust_path(name[1], parent_trans, trans_id)
2561
if executable[0] != executable[1] and kind[1] == "file":
2562
tt.set_executability(executable[1], trans_id)
2563
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2565
tt.create_file(bytes, trans_id, mode_id)
2990
parent_trans = tt.trans_id_file_id(target_parent)
2991
if wt_parent is None and wt_versioned:
2992
tt.adjust_root_path(target_name, parent_trans)
2994
tt.adjust_path(target_name, parent_trans, trans_id)
2995
if wt_executable != target_executable and target_kind == "file":
2996
tt.set_executability(target_executable, trans_id)
2997
if working_tree.supports_content_filtering():
2998
for index, ((trans_id, mode_id), bytes) in enumerate(
2999
target_tree.iter_files_bytes(deferred_files)):
3000
file_id = deferred_files[index][0]
3001
# We're reverting a tree to the target tree so using the
3002
# target tree to find the file path seems the best choice
3003
# here IMO - Ian C 27/Oct/2009
3004
filter_tree_path = target_tree.id2path(file_id)
3005
filters = working_tree._content_filter_stack(filter_tree_path)
3006
bytes = filtered_output_bytes(bytes, filters,
3007
ContentFilterContext(filter_tree_path, working_tree))
3008
tt.create_file(bytes, trans_id, mode_id)
3010
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
3012
tt.create_file(bytes, trans_id, mode_id)
3013
tt.fixup_new_roots()
2567
3015
if basis_tree is not None:
2568
3016
basis_tree.unlock()
2569
3017
return merge_modified
2572
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
3020
def resolve_conflicts(tt, pb=None, pass_func=None):
2573
3021
"""Make many conflict-resolution attempts, but die if they fail"""
2574
3022
if pass_func is None:
2575
3023
pass_func = conflict_pass
2576
3024
new_conflicts = set()
3025
pb = ui.ui_factory.nested_progress_bar()
2578
3027
for n in range(10):
2579
pb.update('Resolution pass', n+1, 10)
3028
pb.update(gettext('Resolution pass'), n+1, 10)
2580
3029
conflicts = tt.find_conflicts()
2581
3030
if len(conflicts) == 0:
2582
3031
return new_conflicts
2583
3032
new_conflicts.update(pass_func(tt, conflicts))
2584
3033
raise MalformedTransform(conflicts=conflicts)
2589
3038
def conflict_pass(tt, conflicts, path_tree=None):