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 (
41
32
revision as _mod_revision,
46
35
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
47
ReusingTransform, CantMoveRoot,
36
ReusingTransform, NotVersionedError, CantMoveRoot,
48
37
ExistingLimbo, ImmortalLimbo, NoFinalPath,
49
38
UnableCreateSymlink)
50
39
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
40
from bzrlib.inventory import InventoryEntry
51
41
from bzrlib.osutils import (
58
49
supports_executable,
60
from bzrlib.progress import ProgressPhase
51
from bzrlib.progress import DummyProgress, ProgressPhase
61
52
from bzrlib.symbol_versioning import (
56
from bzrlib.trace import mutter, warning
57
from bzrlib import tree
59
import bzrlib.urlutils as urlutils
68
62
ROOT_PARENT = "root-parent"
70
65
def unique_add(map, key, value):
72
67
raise DuplicateKey(key=key)
77
71
class _TransformResults(object):
78
72
def __init__(self, modified_paths, rename_count):
79
73
object.__init__(self)
216
202
self.version_file(old_root_file_id, old_root)
217
203
self.unversion_file(self._new_root)
219
def fixup_new_roots(self):
220
"""Reinterpret requests to change the root directory
222
Instead of creating a root directory, or moving an existing directory,
223
all the attributes and children of the new root are applied to the
224
existing root directory.
226
This means that the old root trans-id becomes obsolete, so it is
227
recommended only to invoke this after the root trans-id has become
231
new_roots = [k for k, v in self._new_parent.iteritems() if v is
233
if len(new_roots) < 1:
235
if len(new_roots) != 1:
236
raise ValueError('A tree cannot have two roots!')
237
if self._new_root is None:
238
self._new_root = new_roots[0]
240
old_new_root = new_roots[0]
241
# unversion the new root's directory.
242
if self.final_kind(self._new_root) is None:
243
file_id = self.final_file_id(old_new_root)
245
file_id = self.final_file_id(self._new_root)
246
if old_new_root in self._new_id:
247
self.cancel_versioning(old_new_root)
249
self.unversion_file(old_new_root)
250
# if, at this stage, root still has an old file_id, zap it so we can
251
# stick a new one in.
252
if (self.tree_file_id(self._new_root) is not None and
253
self._new_root not in self._removed_id):
254
self.unversion_file(self._new_root)
255
if file_id is not None:
256
self.version_file(file_id, self._new_root)
258
# Now move children of new root into old root directory.
259
# Ensure all children are registered with the transaction, but don't
260
# use directly-- some tree children have new parents
261
list(self.iter_tree_children(old_new_root))
262
# Move all children of new root into old root directory.
263
for child in self.by_parent().get(old_new_root, []):
264
self.adjust_path(self.final_name(child), self._new_root, child)
266
# Ensure old_new_root has no directory.
267
if old_new_root in self._new_contents:
268
self.cancel_creation(old_new_root)
270
self.delete_contents(old_new_root)
272
# prevent deletion of root directory.
273
if self._new_root in self._removed_contents:
274
self.cancel_deletion(self._new_root)
276
# destroy path info for old_new_root.
277
del self._new_parent[old_new_root]
278
del self._new_name[old_new_root]
280
205
def trans_id_tree_file_id(self, inventory_id):
281
206
"""Determine the transaction id of a working tree file.
393
317
return sorted(FinalPaths(self).get_paths(new_ids))
395
319
def _inventory_altered(self):
396
"""Determine which trans_ids need new Inventory entries.
398
An new entry is needed when anything that would be reflected by an
399
inventory entry changes, including file name, file_id, parent file_id,
400
file kind, and the execute bit.
402
Some care is taken to return entries with real changes, not cases
403
where the value is deleted and then restored to its original value,
404
but some actually unchanged values may be returned.
406
:returns: A list of (path, trans_id) for all items requiring an
407
inventory change. Ordered by path.
410
# Find entries whose file_ids are new (or changed).
411
new_file_id = set(t for t in self._new_id
412
if self._new_id[t] != self.tree_file_id(t))
413
for id_set in [self._new_name, self._new_parent, new_file_id,
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,
414
323
self._new_executability]:
415
changed_ids.update(id_set)
416
# removing implies a kind change
324
new_ids.update(id_set)
417
325
changed_kind = set(self._removed_contents)
419
326
changed_kind.intersection_update(self._new_contents)
420
# Ignore entries that are already known to have changed.
421
changed_kind.difference_update(changed_ids)
422
# to keep only the truly changed ones
423
changed_kind = (t for t in changed_kind
424
if self.tree_kind(t) != self.final_kind(t))
425
# all kind changes will alter the inventory
426
changed_ids.update(changed_kind)
427
# To find entries with changed parent_ids, find parents which existed,
428
# but changed file_id.
429
changed_file_id = set(t for t in new_file_id if t in self._removed_id)
430
# Now add all their children to the set.
431
for parent_trans_id in new_file_id:
432
changed_ids.update(self.iter_tree_children(parent_trans_id))
433
return sorted(FinalPaths(self).get_paths(changed_ids))
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))
435
333
def final_kind(self, trans_id):
436
334
"""Determine the final file kind, after any changes applied.
438
:return: None if the file does not exist/has no contents. (It is
439
conceivable that a path would be created without the corresponding
440
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)
442
340
if trans_id in self._new_contents:
443
341
return self._new_contents[trans_id]
444
342
elif trans_id in self._removed_contents:
343
raise NoSuchFile(None)
447
345
return self.tree_kind(trans_id)
572
463
# ensure that all children are registered with the transaction
573
464
list(self.iter_tree_children(parent_id))
575
@deprecated_method(deprecated_in((2, 3, 0)))
576
466
def has_named_child(self, by_parent, parent_id, name):
577
return self._has_named_child(
578
name, parent_id, known_children=by_parent.get(parent_id, []))
580
def _has_named_child(self, name, parent_id, known_children):
581
"""Does a parent already have a name child.
583
:param name: The searched for name.
585
:param parent_id: The parent for which the check is made.
587
:param known_children: The already known children. This should have
588
been recently obtained from `self.by_parent.get(parent_id)`
589
(or will be if None is passed).
591
if known_children is None:
592
known_children = self.by_parent().get(parent_id, [])
593
for child in known_children:
468
children = by_parent[parent_id]
471
for child in children:
594
472
if self.final_name(child) == name:
596
parent_path = self._tree_id_paths.get(parent_id, None)
597
if parent_path is None:
598
# No parent... no children
475
path = self._tree_id_paths[parent_id]
600
child_path = joinpath(parent_path, name)
601
child_id = self._tree_path_ids.get(child_path, None)
478
childpath = joinpath(path, name)
479
child_id = self._tree_path_ids.get(childpath)
602
480
if child_id is None:
603
# Not known by the tree transform yet, check the filesystem
604
return osutils.lexists(self._tree.abspath(child_path))
481
return lexists(self._tree.abspath(childpath))
606
raise AssertionError('child_id is missing: %s, %s, %s'
607
% (name, parent_id, child_id))
609
def _available_backup_name(self, name, target_id):
610
"""Find an available backup name.
612
:param name: The basename of the file.
614
:param target_id: The directory trans_id where the backup should
617
known_children = self.by_parent().get(target_id, [])
618
return osutils.available_backup_name(
620
lambda base: self._has_named_child(
621
base, target_id, known_children))
483
if self.final_parent(child_id) != parent_id:
485
if child_id in self._removed_contents:
486
# XXX What about dangling file-ids?
623
491
def _parent_loops(self):
624
492
"""No entry should be its own ancestor"""
740
613
def _parent_type_conflicts(self, by_parent):
741
"""Children must have a directory parent"""
614
"""parents must have directory 'contents'."""
743
616
for parent_id, children in by_parent.iteritems():
744
617
if parent_id is ROOT_PARENT:
747
for child_id in children:
748
if self.final_kind(child_id) is not None:
619
if not self._any_contents(children):
753
# There is at least a child, so we need an existing directory to
755
kind = self.final_kind(parent_id)
621
for child in children:
623
self.final_kind(child)
627
kind = self.final_kind(parent_id)
757
# The directory will be deleted
758
631
conflicts.append(('missing parent', parent_id))
759
632
elif kind != "directory":
760
# Meh, we need a *directory* to put something in it
761
633
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)
764
646
def _set_executability(self, path, trans_id):
765
647
"""Set the executability of versioned files """
766
648
if supports_executable():
830
712
self.create_symlink(target, trans_id)
833
def new_orphan(self, trans_id, parent_id):
834
"""Schedule an item to be orphaned.
836
When a directory is about to be removed, its children, if they are not
837
versioned are moved out of the way: they don't have a parent anymore.
839
:param trans_id: The trans_id of the existing item.
840
:param parent_id: The parent trans_id of the item.
842
raise NotImplementedError(self.new_orphan)
844
def _get_potential_orphans(self, dir_id):
845
"""Find the potential orphans in a directory.
847
A directory can't be safely deleted if there are versioned files in it.
848
If all the contained files are unversioned then they can be orphaned.
850
The 'None' return value means that the directory contains at least one
851
versioned file and should not be deleted.
853
:param dir_id: The directory trans id.
855
:return: A list of the orphan trans ids or None if at least one
856
versioned file is present.
859
# Find the potential orphans, stop if one item should be kept
860
for child_tid in self.by_parent()[dir_id]:
861
if child_tid in self._removed_contents:
862
# The child is removed as part of the transform. Since it was
863
# versioned before, it's not an orphan
865
elif self.final_file_id(child_tid) is None:
866
# The child is not versioned
867
orphans.append(child_tid)
869
# We have a versioned file here, searching for orphans is
875
715
def _affected_ids(self):
876
716
"""Return the set of transform ids affected by the transform"""
877
717
trans_ids = set(self._removed_id)
1011
854
def get_preview_tree(self):
1012
855
"""Return a tree representing the result of the transform.
1014
The tree is a snapshot, and altering the TreeTransform will invalidate
857
This tree only supports the subset of Tree functionality required
858
by show_diff_trees. It must only be compared to tt._tree.
1017
860
return _PreviewTree(self)
1019
def commit(self, branch, message, merge_parents=None, strict=False,
1020
timestamp=None, timezone=None, committer=None, authors=None,
1021
revprops=None, revision_id=None):
1022
"""Commit the result of this TreeTransform to a branch.
1024
:param branch: The branch to commit to.
1025
:param message: The message to attach to the commit.
1026
:param merge_parents: Additional parent revision-ids specified by
1028
:param strict: If True, abort the commit if there are unversioned
1030
:param timestamp: if not None, seconds-since-epoch for the time and
1031
date. (May be a float.)
1032
:param timezone: Optional timezone for timestamp, as an offset in
1034
:param committer: Optional committer in email-id format.
1035
(e.g. "J Random Hacker <jrandom@example.com>")
1036
:param authors: Optional list of authors in email-id format.
1037
:param revprops: Optional dictionary of revision properties.
1038
:param revision_id: Optional revision id. (Specifying a revision-id
1039
may reduce performance for some non-native formats.)
1040
:return: The revision_id of the revision committed.
1042
self._check_malformed()
1044
unversioned = set(self._new_contents).difference(set(self._new_id))
1045
for trans_id in unversioned:
1046
if self.final_file_id(trans_id) is None:
1047
raise errors.StrictCommitFailed()
1049
revno, last_rev_id = branch.last_revision_info()
1050
if last_rev_id == _mod_revision.NULL_REVISION:
1051
if merge_parents is not None:
1052
raise ValueError('Cannot supply merge parents for first'
1056
parent_ids = [last_rev_id]
1057
if merge_parents is not None:
1058
parent_ids.extend(merge_parents)
1059
if self._tree.get_revision_id() != last_rev_id:
1060
raise ValueError('TreeTransform not based on branch basis: %s' %
1061
self._tree.get_revision_id())
1062
revprops = commit.Commit.update_revprops(revprops, branch, authors)
1063
builder = branch.get_commit_builder(parent_ids,
1064
timestamp=timestamp,
1066
committer=committer,
1068
revision_id=revision_id)
1069
preview = self.get_preview_tree()
1070
list(builder.record_iter_changes(preview, last_rev_id,
1071
self.iter_changes()))
1072
builder.finish_inventory()
1073
revision_id = builder.commit(message)
1074
branch.set_last_revision_info(revno + 1, revision_id)
1077
862
def _text_parent(self, trans_id):
1078
863
file_id = self.tree_file_id(trans_id)
1236
1012
def _limbo_name(self, trans_id):
1237
1013
"""Generate the limbo name of a file"""
1238
1014
limbo_name = self._limbo_files.get(trans_id)
1239
if limbo_name is None:
1240
limbo_name = self._generate_limbo_path(trans_id)
1241
self._limbo_files[trans_id] = limbo_name
1015
if limbo_name is not None:
1017
parent = self._new_parent.get(trans_id)
1018
# if the parent directory is already in limbo (e.g. when building a
1019
# tree), choose a limbo name inside the parent, to reduce further
1021
use_direct_path = False
1022
if self._new_contents.get(parent) == 'directory':
1023
filename = self._new_name.get(trans_id)
1024
if filename is not None:
1025
if parent not in self._limbo_children:
1026
self._limbo_children[parent] = set()
1027
self._limbo_children_names[parent] = {}
1028
use_direct_path = True
1029
# the direct path can only be used if no other file has
1030
# already taken this pathname, i.e. if the name is unused, or
1031
# if it is already associated with this trans_id.
1032
elif self._case_sensitive_target:
1033
if (self._limbo_children_names[parent].get(filename)
1034
in (trans_id, None)):
1035
use_direct_path = True
1037
for l_filename, l_trans_id in\
1038
self._limbo_children_names[parent].iteritems():
1039
if l_trans_id == trans_id:
1041
if l_filename.lower() == filename.lower():
1044
use_direct_path = True
1047
limbo_name = pathjoin(self._limbo_files[parent], filename)
1048
self._limbo_children[parent].add(trans_id)
1049
self._limbo_children_names[parent][filename] = trans_id
1051
limbo_name = pathjoin(self._limbodir, trans_id)
1052
self._needs_rename.add(trans_id)
1053
self._limbo_files[trans_id] = limbo_name
1242
1054
return limbo_name
1244
def _generate_limbo_path(self, trans_id):
1245
"""Generate a limbo path using the trans_id as the relative path.
1247
This is suitable as a fallback, and when the transform should not be
1248
sensitive to the path encoding of the limbo directory.
1250
self._needs_rename.add(trans_id)
1251
return pathjoin(self._limbodir, trans_id)
1253
1056
def adjust_path(self, name, parent, trans_id):
1254
1057
previous_parent = self._new_parent.get(trans_id)
1255
1058
previous_name = self._new_name.get(trans_id)
1274
1075
entries from _limbo_files, because they are now stale.
1276
1077
for trans_id in trans_ids:
1277
old_path = self._limbo_files[trans_id]
1278
self._possibly_stale_limbo_files.add(old_path)
1279
del self._limbo_files[trans_id]
1078
old_path = self._limbo_files.pop(trans_id)
1280
1079
if trans_id not in self._new_contents:
1282
1081
new_path = self._limbo_name(trans_id)
1283
1082
os.rename(old_path, new_path)
1284
self._possibly_stale_limbo_files.remove(old_path)
1285
for descendant in self._limbo_descendants(trans_id):
1286
desc_path = self._limbo_files[descendant]
1287
desc_path = new_path + desc_path[len(old_path):]
1288
self._limbo_files[descendant] = desc_path
1290
def _limbo_descendants(self, trans_id):
1291
"""Return the set of trans_ids whose limbo paths descend from this."""
1292
descendants = set(self._limbo_children.get(trans_id, []))
1293
for descendant in list(descendants):
1294
descendants.update(self._limbo_descendants(descendant))
1297
def create_file(self, contents, trans_id, mode_id=None, sha1=None):
1084
def create_file(self, contents, trans_id, mode_id=None):
1298
1085
"""Schedule creation of a new file.
1302
:param contents: an iterator of strings, all of which will be written
1303
to the target destination.
1304
:param trans_id: TreeTransform handle
1305
:param mode_id: If not None, force the mode of the target file to match
1306
the mode of the object referenced by mode_id.
1307
Otherwise, we will try to preserve mode bits of an existing file.
1308
:param sha1: If the sha1 of this content is already known, pass it in.
1309
We can use it to prevent future sha1 computations.
1089
Contents is an iterator of strings, all of which will be written
1090
to the target destination.
1092
New file takes the permissions of any existing file with that id,
1093
unless mode_id is specified.
1311
1095
name = self._limbo_name(trans_id)
1312
1096
f = open(name, 'wb')
1314
unique_add(self._new_contents, trans_id, 'file')
1099
unique_add(self._new_contents, trans_id, 'file')
1101
# Clean up the file, it never got registered so
1102
# TreeTransform.finalize() won't clean it up.
1315
1107
f.writelines(contents)
1318
self._set_mtime(name)
1319
1110
self._set_mode(trans_id, mode_id, S_ISREG)
1320
# It is unfortunate we have to use lstat instead of fstat, but we just
1321
# used utime and chmod on the file, so we need the accurate final
1323
if sha1 is not None:
1324
self._observed_sha1s[trans_id] = (sha1, osutils.lstat(name))
1326
1112
def _read_file_chunks(self, trans_id):
1327
1113
cur_file = open(self._limbo_name(trans_id), 'rb')
1397
1172
del self._limbo_children_names[trans_id]
1398
1173
delete_any(self._limbo_name(trans_id))
1400
def new_orphan(self, trans_id, parent_id):
1401
# FIXME: There is no tree config, so we use the branch one (it's weird
1402
# to define it this way as orphaning can only occur in a working tree,
1403
# but that's all we have (for now). It will find the option in
1404
# locations.conf or bazaar.conf though) -- vila 20100916
1405
conf = self._tree.branch.get_config()
1406
conf_var_name = 'bzr.transform.orphan_policy'
1407
orphan_policy = conf.get_user_option(conf_var_name)
1408
default_policy = orphaning_registry.default_key
1409
if orphan_policy is None:
1410
orphan_policy = default_policy
1411
if orphan_policy not in orphaning_registry:
1412
trace.warning('%s (from %s) is not a known policy, defaulting '
1413
'to %s' % (orphan_policy, conf_var_name, default_policy))
1414
orphan_policy = default_policy
1415
handle_orphan = orphaning_registry.get(orphan_policy)
1416
handle_orphan(self, trans_id, parent_id)
1419
class OrphaningError(errors.BzrError):
1421
# Only bugs could lead to such exception being seen by the user
1422
internal_error = True
1423
_fmt = "Error while orphaning %s in %s directory"
1425
def __init__(self, orphan, parent):
1426
errors.BzrError.__init__(self)
1427
self.orphan = orphan
1428
self.parent = parent
1431
class OrphaningForbidden(OrphaningError):
1433
_fmt = "Policy: %s doesn't allow creating orphans."
1435
def __init__(self, policy):
1436
errors.BzrError.__init__(self)
1437
self.policy = policy
1440
def move_orphan(tt, orphan_id, parent_id):
1441
"""See TreeTransformBase.new_orphan.
1443
This creates a new orphan in the `bzr-orphans` dir at the root of the
1446
:param tt: The TreeTransform orphaning `trans_id`.
1448
:param orphan_id: The trans id that should be orphaned.
1450
:param parent_id: The orphan parent trans id.
1452
# Add the orphan dir if it doesn't exist
1453
orphan_dir_basename = 'bzr-orphans'
1454
od_id = tt.trans_id_tree_path(orphan_dir_basename)
1455
if tt.final_kind(od_id) is None:
1456
tt.create_directory(od_id)
1457
parent_path = tt._tree_id_paths[parent_id]
1458
# Find a name that doesn't exist yet in the orphan dir
1459
actual_name = tt.final_name(orphan_id)
1460
new_name = tt._available_backup_name(actual_name, od_id)
1461
tt.adjust_path(new_name, od_id, orphan_id)
1462
trace.warning('%s has been orphaned in %s'
1463
% (joinpath(parent_path, actual_name), orphan_dir_basename))
1466
def refuse_orphan(tt, orphan_id, parent_id):
1467
"""See TreeTransformBase.new_orphan.
1469
This refuses to create orphan, letting the caller handle the conflict.
1471
raise OrphaningForbidden('never')
1474
orphaning_registry = registry.Registry()
1475
orphaning_registry.register(
1476
'conflict', refuse_orphan,
1477
'Leave orphans in place and create a conflict on the directory.')
1478
orphaning_registry.register(
1479
'move', move_orphan,
1480
'Move orphans into the bzr-orphans directory.')
1481
orphaning_registry._set_default_key('conflict')
1484
1176
class TreeTransform(DiskTreeTransform):
1485
1177
"""Represent a tree transformation.
1660
1355
yield self.trans_id_tree_path(childpath)
1662
def _generate_limbo_path(self, trans_id):
1663
"""Generate a limbo path using the final path if possible.
1665
This optimizes the performance of applying the tree transform by
1666
avoiding renames. These renames can be avoided only when the parent
1667
directory is already scheduled for creation.
1669
If the final path cannot be used, falls back to using the trans_id as
1672
parent = self._new_parent.get(trans_id)
1673
# if the parent directory is already in limbo (e.g. when building a
1674
# tree), choose a limbo name inside the parent, to reduce further
1676
use_direct_path = False
1677
if self._new_contents.get(parent) == 'directory':
1678
filename = self._new_name.get(trans_id)
1679
if filename is not None:
1680
if parent not in self._limbo_children:
1681
self._limbo_children[parent] = set()
1682
self._limbo_children_names[parent] = {}
1683
use_direct_path = True
1684
# the direct path can only be used if no other file has
1685
# already taken this pathname, i.e. if the name is unused, or
1686
# if it is already associated with this trans_id.
1687
elif self._case_sensitive_target:
1688
if (self._limbo_children_names[parent].get(filename)
1689
in (trans_id, None)):
1690
use_direct_path = True
1692
for l_filename, l_trans_id in\
1693
self._limbo_children_names[parent].iteritems():
1694
if l_trans_id == trans_id:
1696
if l_filename.lower() == filename.lower():
1699
use_direct_path = True
1701
if not use_direct_path:
1702
return DiskTreeTransform._generate_limbo_path(self, trans_id)
1704
limbo_name = pathjoin(self._limbo_files[parent], filename)
1705
self._limbo_children[parent].add(trans_id)
1706
self._limbo_children_names[parent][filename] = trans_id
1710
1358
def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1711
1359
"""Apply all changes to the inventory and filesystem.
1828
1476
tree_paths = list(self._tree_path_ids.iteritems())
1829
1477
tree_paths.sort(reverse=True)
1830
child_pb = ui.ui_factory.nested_progress_bar()
1478
child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1832
for num, (path, trans_id) in enumerate(tree_paths):
1833
# do not attempt to move root into a subdirectory of itself.
1480
for num, data in enumerate(tree_paths):
1481
path, trans_id = data
1836
1482
child_pb.update('removing file', num, len(tree_paths))
1837
1483
full_path = self._tree.abspath(path)
1838
1484
if trans_id in self._removed_contents:
1839
delete_path = os.path.join(self._deletiondir, trans_id)
1840
mover.pre_delete(full_path, delete_path)
1841
elif (trans_id in self._new_name
1842
or trans_id in self._new_parent):
1485
mover.pre_delete(full_path, os.path.join(self._deletiondir,
1487
elif trans_id in self._new_name or trans_id in \
1844
1490
mover.rename(full_path, self._limbo_name(trans_id))
1845
except errors.TransformRenameFailed, e:
1846
1492
if e.errno != errno.ENOENT:
1873
1519
if trans_id in self._needs_rename:
1875
1521
mover.rename(self._limbo_name(trans_id), full_path)
1876
except errors.TransformRenameFailed, e:
1877
1523
# We may be renaming a dangling inventory id
1878
1524
if e.errno != errno.ENOENT:
1881
1527
self.rename_count += 1
1882
# TODO: if trans_id in self._observed_sha1s, we should
1883
# re-stat the final target, since ctime will be
1884
# updated by the change.
1885
1528
if (trans_id in self._new_contents or
1886
1529
self.path_changed(trans_id)):
1887
1530
if trans_id in self._new_contents:
1888
1531
modified_paths.append(full_path)
1889
1532
if trans_id in self._new_executability:
1890
1533
self._set_executability(path, trans_id)
1891
if trans_id in self._observed_sha1s:
1892
o_sha1, o_st_val = self._observed_sha1s[trans_id]
1893
st = osutils.lstat(full_path)
1894
self._observed_sha1s[trans_id] = (o_sha1, st)
1896
1535
child_pb.finished()
1897
for path, trans_id in new_paths:
1898
# new_paths includes stuff like workingtree conflicts. Only the
1899
# stuff in new_contents actually comes from limbo.
1900
if trans_id in self._limbo_files:
1901
del self._limbo_files[trans_id]
1902
1536
self._new_contents.clear()
1903
1537
return modified_paths
1905
def _apply_observed_sha1s(self):
1906
"""After we have finished renaming everything, update observed sha1s
1908
This has to be done after self._tree.apply_inventory_delta, otherwise
1909
it doesn't know anything about the files we are updating. Also, we want
1910
to do this as late as possible, so that most entries end up cached.
1912
# TODO: this doesn't update the stat information for directories. So
1913
# the first 'bzr status' will still need to rewrite
1914
# .bzr/checkout/dirstate. However, we at least don't need to
1915
# re-read all of the files.
1916
# TODO: If the operation took a while, we could do a time.sleep(3) here
1917
# to allow the clock to tick over and ensure we won't have any
1918
# problems. (we could observe start time, and finish time, and if
1919
# it is less than eg 10% overhead, add a sleep call.)
1920
paths = FinalPaths(self)
1921
for trans_id, observed in self._observed_sha1s.iteritems():
1922
path = paths.get_path(trans_id)
1923
# We could get the file_id, but dirstate prefers to use the path
1924
# anyway, and it is 'cheaper' to determine.
1925
# file_id = self._new_id[trans_id]
1926
self._tree._observed_sha1(None, path, observed)
1929
1540
class TransformPreview(DiskTreeTransform):
1930
1541
"""A TreeTransform for generating preview trees.
2014
1623
yield self._get_repository().revision_tree(revision_id)
2016
1625
def _get_file_revision(self, file_id, vf, tree_revision):
2017
parent_keys = [(file_id, t.get_file_revision(file_id)) for t in
1626
parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
2018
1627
self._iter_parent_trees()]
2019
1628
vf.add_lines((file_id, tree_revision), parent_keys,
2020
self.get_file_lines(file_id))
1629
self.get_file(file_id).readlines())
2021
1630
repo = self._get_repository()
2022
1631
base_vf = repo.texts
2023
1632
if base_vf not in vf.fallback_versionedfiles:
2024
1633
vf.fallback_versionedfiles.append(base_vf)
2025
1634
return tree_revision
2027
def _stat_limbo_file(self, file_id=None, trans_id=None):
2028
if trans_id is None:
2029
trans_id = self._transform.trans_id_file_id(file_id)
1636
def _stat_limbo_file(self, file_id):
1637
trans_id = self._transform.trans_id_file_id(file_id)
2030
1638
name = self._transform._limbo_name(trans_id)
2031
1639
return os.lstat(name)
2659
2219
new_desired_files = desired_files
2661
2221
iter = accelerator_tree.iter_changes(tree, include_unchanged=True)
2662
unchanged = [(f, p[1]) for (f, p, c, v, d, n, k, e)
2663
in iter if not (c or e[0] != e[1])]
2664
if accelerator_tree.supports_content_filtering():
2665
unchanged = [(f, p) for (f, p) in unchanged
2666
if not accelerator_tree.iter_search_rules([p]).next()]
2667
unchanged = dict(unchanged)
2222
unchanged = dict((f, p[1]) for (f, p, c, v, d, n, k, e)
2223
in iter if not (c or e[0] != e[1]))
2668
2224
new_desired_files = []
2670
for file_id, (trans_id, tree_path, text_sha1) in desired_files:
2226
for file_id, (trans_id, tree_path) in desired_files:
2671
2227
accelerator_path = unchanged.get(file_id)
2672
2228
if accelerator_path is None:
2673
new_desired_files.append((file_id,
2674
(trans_id, tree_path, text_sha1)))
2229
new_desired_files.append((file_id, (trans_id, tree_path)))
2676
2231
pb.update('Adding file contents', count + offset, total)
2784
2333
raise errors.BadFileKindError(name, kind)
2787
def create_from_tree(tt, trans_id, tree, file_id, bytes=None,
2788
filter_tree_path=None):
2789
"""Create new file contents according to tree contents.
2791
:param filter_tree_path: the tree path to use to lookup
2792
content filters to apply to the bytes output in the working tree.
2793
This only applies if the working tree supports content filtering.
2336
@deprecated_function(deprecated_in((1, 9, 0)))
2337
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
2338
"""Create new file contents according to an inventory entry.
2340
DEPRECATED. Use create_from_tree instead.
2342
if entry.kind == "file":
2344
lines = tree.get_file(entry.file_id).readlines()
2345
tt.create_file(lines, trans_id, mode_id=mode_id)
2346
elif entry.kind == "symlink":
2347
tt.create_symlink(tree.get_symlink_target(entry.file_id), trans_id)
2348
elif entry.kind == "directory":
2349
tt.create_directory(trans_id)
2352
def create_from_tree(tt, trans_id, tree, file_id, bytes=None):
2353
"""Create new file contents according to tree contents."""
2795
2354
kind = tree.kind(file_id)
2796
2355
if kind == 'directory':
2797
2356
tt.create_directory(trans_id)
2931
2480
deferred_files = []
2932
2481
for id_num, (file_id, path, changed_content, versioned, parent, name,
2933
2482
kind, executable) in enumerate(change_list):
2934
target_path, wt_path = path
2935
target_versioned, wt_versioned = versioned
2936
target_parent, wt_parent = parent
2937
target_name, wt_name = name
2938
target_kind, wt_kind = kind
2939
target_executable, wt_executable = executable
2940
if skip_root and wt_parent is None:
2483
if skip_root and file_id[0] is not None and parent[0] is None:
2942
2485
trans_id = tt.trans_id_file_id(file_id)
2944
2487
if changed_content:
2945
2488
keep_content = False
2946
if wt_kind == 'file' and (backups or target_kind is None):
2489
if kind[0] == 'file' and (backups or kind[1] is None):
2947
2490
wt_sha1 = working_tree.get_file_sha1(file_id)
2948
2491
if merge_modified.get(file_id) != wt_sha1:
2949
2492
# acquire the basis tree lazily to prevent the
2952
2495
if basis_tree is None:
2953
2496
basis_tree = working_tree.basis_tree()
2954
2497
basis_tree.lock_read()
2955
if basis_tree.has_id(file_id):
2498
if file_id in basis_tree:
2956
2499
if wt_sha1 != basis_tree.get_file_sha1(file_id):
2957
2500
keep_content = True
2958
elif target_kind is None and not target_versioned:
2501
elif kind[1] is None and not versioned[1]:
2959
2502
keep_content = True
2960
if wt_kind is not None:
2503
if kind[0] is not None:
2961
2504
if not keep_content:
2962
2505
tt.delete_contents(trans_id)
2963
elif target_kind is not None:
2964
parent_trans_id = tt.trans_id_file_id(wt_parent)
2965
backup_name = tt._available_backup_name(
2966
wt_name, parent_trans_id)
2506
elif kind[1] is not None:
2507
parent_trans_id = tt.trans_id_file_id(parent[0])
2508
by_parent = tt.by_parent()
2509
backup_name = _get_backup_name(name[0], by_parent,
2510
parent_trans_id, tt)
2967
2511
tt.adjust_path(backup_name, parent_trans_id, trans_id)
2968
new_trans_id = tt.create_path(wt_name, parent_trans_id)
2969
if wt_versioned and target_versioned:
2512
new_trans_id = tt.create_path(name[0], parent_trans_id)
2513
if versioned == (True, True):
2970
2514
tt.unversion_file(trans_id)
2971
2515
tt.version_file(file_id, new_trans_id)
2972
2516
# New contents should have the same unix perms as old
2974
2518
mode_id = trans_id
2975
2519
trans_id = new_trans_id
2976
if target_kind in ('directory', 'tree-reference'):
2520
if kind[1] in ('directory', 'tree-reference'):
2977
2521
tt.create_directory(trans_id)
2978
if target_kind == 'tree-reference':
2522
if kind[1] == 'tree-reference':
2979
2523
revision = target_tree.get_reference_revision(file_id,
2981
2525
tt.set_tree_reference(revision, trans_id)
2982
elif target_kind == 'symlink':
2526
elif kind[1] == 'symlink':
2983
2527
tt.create_symlink(target_tree.get_symlink_target(file_id),
2985
elif target_kind == 'file':
2529
elif kind[1] == 'file':
2986
2530
deferred_files.append((file_id, (trans_id, mode_id)))
2987
2531
if basis_tree is None:
2988
2532
basis_tree = working_tree.basis_tree()
2989
2533
basis_tree.lock_read()
2990
2534
new_sha1 = target_tree.get_file_sha1(file_id)
2991
if (basis_tree.has_id(file_id) and
2992
new_sha1 == basis_tree.get_file_sha1(file_id)):
2535
if (file_id in basis_tree and new_sha1 ==
2536
basis_tree.get_file_sha1(file_id)):
2993
2537
if file_id in merge_modified:
2994
2538
del merge_modified[file_id]
2996
2540
merge_modified[file_id] = new_sha1
2998
2542
# preserve the execute bit when backing up
2999
if keep_content and wt_executable == target_executable:
3000
tt.set_executability(target_executable, trans_id)
3001
elif target_kind is not None:
3002
raise AssertionError(target_kind)
3003
if not wt_versioned and target_versioned:
2543
if keep_content and executable[0] == executable[1]:
2544
tt.set_executability(executable[1], trans_id)
2545
elif kind[1] is not None:
2546
raise AssertionError(kind[1])
2547
if versioned == (False, True):
3004
2548
tt.version_file(file_id, trans_id)
3005
if wt_versioned and not target_versioned:
2549
if versioned == (True, False):
3006
2550
tt.unversion_file(trans_id)
3007
if (target_name is not None and
3008
(wt_name != target_name or wt_parent != target_parent)):
3009
if target_name == '' and target_parent is None:
2551
if (name[1] is not None and
2552
(name[0] != name[1] or parent[0] != parent[1])):
2553
if name[1] == '' and parent[1] is None:
3010
2554
parent_trans = ROOT_PARENT
3012
parent_trans = tt.trans_id_file_id(target_parent)
3013
if wt_parent is None and wt_versioned:
3014
tt.adjust_root_path(target_name, parent_trans)
3016
tt.adjust_path(target_name, parent_trans, trans_id)
3017
if wt_executable != target_executable and target_kind == "file":
3018
tt.set_executability(target_executable, trans_id)
3019
if working_tree.supports_content_filtering():
3020
for index, ((trans_id, mode_id), bytes) in enumerate(
3021
target_tree.iter_files_bytes(deferred_files)):
3022
file_id = deferred_files[index][0]
3023
# We're reverting a tree to the target tree so using the
3024
# target tree to find the file path seems the best choice
3025
# here IMO - Ian C 27/Oct/2009
3026
filter_tree_path = target_tree.id2path(file_id)
3027
filters = working_tree._content_filter_stack(filter_tree_path)
3028
bytes = filtered_output_bytes(bytes, filters,
3029
ContentFilterContext(filter_tree_path, working_tree))
3030
tt.create_file(bytes, trans_id, mode_id)
3032
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
3034
tt.create_file(bytes, trans_id, mode_id)
3035
tt.fixup_new_roots()
2556
parent_trans = tt.trans_id_file_id(parent[1])
2557
tt.adjust_path(name[1], parent_trans, trans_id)
2558
if executable[0] != executable[1] and kind[1] == "file":
2559
tt.set_executability(executable[1], trans_id)
2560
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2562
tt.create_file(bytes, trans_id, mode_id)
3037
2564
if basis_tree is not None:
3038
2565
basis_tree.unlock()
3039
2566
return merge_modified
3042
def resolve_conflicts(tt, pb=None, pass_func=None):
2569
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
3043
2570
"""Make many conflict-resolution attempts, but die if they fail"""
3044
2571
if pass_func is None:
3045
2572
pass_func = conflict_pass
3046
2573
new_conflicts = set()
3047
pb = ui.ui_factory.nested_progress_bar()
3049
2575
for n in range(10):
3050
2576
pb.update('Resolution pass', n+1, 10)
3182
2689
modified_path = fp.get_path(conflict[2])
3183
2690
modified_id = tt.final_file_id(conflict[2])
3184
2691
if len(conflict) == 3:
3185
yield conflicts.Conflict.factory(
3186
c_type, action=action, path=modified_path, file_id=modified_id)
2692
yield Conflict.factory(c_type, action=action, path=modified_path,
2693
file_id=modified_id)
3189
2696
conflicting_path = fp.get_path(conflict[3])
3190
2697
conflicting_id = tt.final_file_id(conflict[3])
3191
yield conflicts.Conflict.factory(
3192
c_type, action=action, path=modified_path,
3193
file_id=modified_id,
3194
conflict_path=conflicting_path,
3195
conflict_file_id=conflicting_id)
2698
yield Conflict.factory(c_type, action=action, path=modified_path,
2699
file_id=modified_id,
2700
conflict_path=conflicting_path,
2701
conflict_file_id=conflicting_id)
3198
2704
class _FileMover(object):