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
1015
def _limbo_name(self, trans_id):
1237
1016
"""Generate the limbo name of a file"""
1238
1017
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
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
1242
1057
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
1059
def adjust_path(self, name, parent, trans_id):
1254
1060
previous_parent = self._new_parent.get(trans_id)
1255
1061
previous_name = self._new_name.get(trans_id)
1274
1078
entries from _limbo_files, because they are now stale.
1276
1080
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]
1081
old_path = self._limbo_files.pop(trans_id)
1280
1082
if trans_id not in self._new_contents:
1282
1084
new_path = self._limbo_name(trans_id)
1283
1085
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):
1087
def create_file(self, contents, trans_id, mode_id=None):
1298
1088
"""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.
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.
1311
1098
name = self._limbo_name(trans_id)
1312
1099
f = open(name, 'wb')
1314
unique_add(self._new_contents, trans_id, 'file')
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.
1315
1110
f.writelines(contents)
1318
self._set_mtime(name)
1319
1113
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
1115
def _read_file_chunks(self, trans_id):
1327
1116
cur_file = open(self._limbo_name(trans_id), 'rb')
1397
1175
del self._limbo_children_names[trans_id]
1398
1176
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
1179
class TreeTransform(DiskTreeTransform):
1485
1180
"""Represent a tree transformation.
1660
1358
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
1361
def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1711
1362
"""Apply all changes to the inventory and filesystem.
1828
1479
tree_paths = list(self._tree_path_ids.iteritems())
1829
1480
tree_paths.sort(reverse=True)
1830
child_pb = ui.ui_factory.nested_progress_bar()
1481
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.
1483
for num, data in enumerate(tree_paths):
1484
path, trans_id = data
1836
1485
child_pb.update('removing file', num, len(tree_paths))
1837
1486
full_path = self._tree.abspath(path)
1838
1487
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):
1488
mover.pre_delete(full_path, os.path.join(self._deletiondir,
1490
elif trans_id in self._new_name or trans_id in \
1844
1493
mover.rename(full_path, self._limbo_name(trans_id))
1845
except errors.TransformRenameFailed, e:
1846
1495
if e.errno != errno.ENOENT:
1873
1522
if trans_id in self._needs_rename:
1875
1524
mover.rename(self._limbo_name(trans_id), full_path)
1876
except errors.TransformRenameFailed, e:
1877
1526
# We may be renaming a dangling inventory id
1878
1527
if e.errno != errno.ENOENT:
1881
1530
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
1531
if (trans_id in self._new_contents or
1886
1532
self.path_changed(trans_id)):
1887
1533
if trans_id in self._new_contents:
1888
1534
modified_paths.append(full_path)
1889
1535
if trans_id in self._new_executability:
1890
1536
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
1538
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
1539
self._new_contents.clear()
1903
1540
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
1543
class TransformPreview(DiskTreeTransform):
1930
1544
"""A TreeTransform for generating preview trees.
2014
1626
yield self._get_repository().revision_tree(revision_id)
2016
1628
def _get_file_revision(self, file_id, vf, tree_revision):
2017
parent_keys = [(file_id, t.get_file_revision(file_id)) for t in
1629
parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
2018
1630
self._iter_parent_trees()]
2019
1631
vf.add_lines((file_id, tree_revision), parent_keys,
2020
self.get_file_lines(file_id))
1632
self.get_file(file_id).readlines())
2021
1633
repo = self._get_repository()
2022
1634
base_vf = repo.texts
2023
1635
if base_vf not in vf.fallback_versionedfiles:
2024
1636
vf.fallback_versionedfiles.append(base_vf)
2025
1637
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)
1639
def _stat_limbo_file(self, file_id):
1640
trans_id = self._transform.trans_id_file_id(file_id)
2030
1641
name = self._transform._limbo_name(trans_id)
2031
1642
return os.lstat(name)
2187
1789
ordered_ids = self._list_files_by_dir()
2188
1790
for entry, trans_id in self._make_inv_entries(ordered_ids,
2189
specific_file_ids, yield_parents=yield_parents):
2190
yield unicode(self._final_paths.get_path(trans_id)), entry
2192
def _iter_entries_for_dir(self, dir_path):
2193
"""Return path, entry for items in a directory without recursing down."""
2194
dir_file_id = self.path2id(dir_path)
2196
for file_id in self.iter_children(dir_file_id):
2197
trans_id = self._transform.trans_id_file_id(file_id)
2198
ordered_ids.append((trans_id, file_id))
2199
for entry, trans_id in self._make_inv_entries(ordered_ids):
2200
yield unicode(self._final_paths.get_path(trans_id)), entry
2202
def list_files(self, include_root=False, from_dir=None, recursive=True):
2203
"""See WorkingTree.list_files."""
1792
yield unicode(self._final_paths.get_path(trans_id)), entry
1794
def list_files(self, include_root=False):
1795
"""See Tree.list_files."""
2204
1796
# XXX This should behave like WorkingTree.list_files, but is really
2205
1797
# more like RevisionTree.list_files.
2209
prefix = from_dir + '/'
2210
entries = self.iter_entries_by_dir()
2211
for path, entry in entries:
2212
if entry.name == '' and not include_root:
2215
if not path.startswith(prefix):
2217
path = path[len(prefix):]
2218
yield path, 'V', entry.kind, entry.file_id, entry
2220
if from_dir is None and include_root is True:
2221
root_entry = inventory.make_entry('directory', '',
2222
ROOT_PARENT, self.get_root_id())
2223
yield '', 'V', 'directory', root_entry.file_id, root_entry
2224
entries = self._iter_entries_for_dir(from_dir or '')
2225
for path, entry in entries:
2226
yield path, 'V', entry.kind, entry.file_id, entry
1798
for path, entry in self.iter_entries_by_dir():
1799
if entry.name == '' and not include_root:
1801
yield path, 'V', entry.kind, entry.file_id, entry
2228
1803
def kind(self, file_id):
2229
1804
trans_id = self._transform.trans_id_file_id(file_id)
2659
2195
new_desired_files = desired_files
2661
2197
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)
2198
unchanged = dict((f, p[1]) for (f, p, c, v, d, n, k, e)
2199
in iter if not (c or e[0] != e[1]))
2668
2200
new_desired_files = []
2670
for file_id, (trans_id, tree_path, text_sha1) in desired_files:
2202
for file_id, (trans_id, tree_path) in desired_files:
2671
2203
accelerator_path = unchanged.get(file_id)
2672
2204
if accelerator_path is None:
2673
new_desired_files.append((file_id,
2674
(trans_id, tree_path, text_sha1)))
2205
new_desired_files.append((file_id, (trans_id, tree_path)))
2676
2207
pb.update('Adding file contents', count + offset, total)
2784
2309
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.
2312
@deprecated_function(deprecated_in((1, 9, 0)))
2313
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
2314
"""Create new file contents according to an inventory entry.
2316
DEPRECATED. Use create_from_tree instead.
2318
if entry.kind == "file":
2320
lines = tree.get_file(entry.file_id).readlines()
2321
tt.create_file(lines, trans_id, mode_id=mode_id)
2322
elif entry.kind == "symlink":
2323
tt.create_symlink(tree.get_symlink_target(entry.file_id), trans_id)
2324
elif entry.kind == "directory":
2325
tt.create_directory(trans_id)
2328
def create_from_tree(tt, trans_id, tree, file_id, bytes=None):
2329
"""Create new file contents according to tree contents."""
2795
2330
kind = tree.kind(file_id)
2796
2331
if kind == 'directory':
2797
2332
tt.create_directory(trans_id)
2931
2456
deferred_files = []
2932
2457
for id_num, (file_id, path, changed_content, versioned, parent, name,
2933
2458
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:
2459
if skip_root and file_id[0] is not None and parent[0] is None:
2942
2461
trans_id = tt.trans_id_file_id(file_id)
2944
2463
if changed_content:
2945
2464
keep_content = False
2946
if wt_kind == 'file' and (backups or target_kind is None):
2465
if kind[0] == 'file' and (backups or kind[1] is None):
2947
2466
wt_sha1 = working_tree.get_file_sha1(file_id)
2948
2467
if merge_modified.get(file_id) != wt_sha1:
2949
2468
# acquire the basis tree lazily to prevent the
2952
2471
if basis_tree is None:
2953
2472
basis_tree = working_tree.basis_tree()
2954
2473
basis_tree.lock_read()
2955
if basis_tree.has_id(file_id):
2474
if file_id in basis_tree:
2956
2475
if wt_sha1 != basis_tree.get_file_sha1(file_id):
2957
2476
keep_content = True
2958
elif target_kind is None and not target_versioned:
2477
elif kind[1] is None and not versioned[1]:
2959
2478
keep_content = True
2960
if wt_kind is not None:
2479
if kind[0] is not None:
2961
2480
if not keep_content:
2962
2481
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)
2482
elif kind[1] is not None:
2483
parent_trans_id = tt.trans_id_file_id(parent[0])
2484
by_parent = tt.by_parent()
2485
backup_name = _get_backup_name(name[0], by_parent,
2486
parent_trans_id, tt)
2967
2487
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:
2488
new_trans_id = tt.create_path(name[0], parent_trans_id)
2489
if versioned == (True, True):
2970
2490
tt.unversion_file(trans_id)
2971
2491
tt.version_file(file_id, new_trans_id)
2972
2492
# New contents should have the same unix perms as old
2974
2494
mode_id = trans_id
2975
2495
trans_id = new_trans_id
2976
if target_kind in ('directory', 'tree-reference'):
2496
if kind[1] in ('directory', 'tree-reference'):
2977
2497
tt.create_directory(trans_id)
2978
if target_kind == 'tree-reference':
2498
if kind[1] == 'tree-reference':
2979
2499
revision = target_tree.get_reference_revision(file_id,
2981
2501
tt.set_tree_reference(revision, trans_id)
2982
elif target_kind == 'symlink':
2502
elif kind[1] == 'symlink':
2983
2503
tt.create_symlink(target_tree.get_symlink_target(file_id),
2985
elif target_kind == 'file':
2505
elif kind[1] == 'file':
2986
2506
deferred_files.append((file_id, (trans_id, mode_id)))
2987
2507
if basis_tree is None:
2988
2508
basis_tree = working_tree.basis_tree()
2989
2509
basis_tree.lock_read()
2990
2510
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)):
2511
if (file_id in basis_tree and new_sha1 ==
2512
basis_tree.get_file_sha1(file_id)):
2993
2513
if file_id in merge_modified:
2994
2514
del merge_modified[file_id]
2996
2516
merge_modified[file_id] = new_sha1
2998
2518
# 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:
2519
if keep_content and executable[0] == executable[1]:
2520
tt.set_executability(executable[1], trans_id)
2521
elif kind[1] is not None:
2522
raise AssertionError(kind[1])
2523
if versioned == (False, True):
3004
2524
tt.version_file(file_id, trans_id)
3005
if wt_versioned and not target_versioned:
2525
if versioned == (True, False):
3006
2526
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:
2527
if (name[1] is not None and
2528
(name[0] != name[1] or parent[0] != parent[1])):
2529
if name[1] == '' and parent[1] is None:
3010
2530
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()
2532
parent_trans = tt.trans_id_file_id(parent[1])
2533
tt.adjust_path(name[1], parent_trans, trans_id)
2534
if executable[0] != executable[1] and kind[1] == "file":
2535
tt.set_executability(executable[1], trans_id)
2536
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2538
tt.create_file(bytes, trans_id, mode_id)
3037
2540
if basis_tree is not None:
3038
2541
basis_tree.unlock()
3039
2542
return merge_modified
3042
def resolve_conflicts(tt, pb=None, pass_func=None):
2545
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
3043
2546
"""Make many conflict-resolution attempts, but die if they fail"""
3044
2547
if pass_func is None:
3045
2548
pass_func = conflict_pass
3046
2549
new_conflicts = set()
3047
pb = ui.ui_factory.nested_progress_bar()
3049
2551
for n in range(10):
3050
2552
pb.update('Resolution pass', n+1, 10)
3182
2665
modified_path = fp.get_path(conflict[2])
3183
2666
modified_id = tt.final_file_id(conflict[2])
3184
2667
if len(conflict) == 3:
3185
yield conflicts.Conflict.factory(
3186
c_type, action=action, path=modified_path, file_id=modified_id)
2668
yield Conflict.factory(c_type, action=action, path=modified_path,
2669
file_id=modified_id)
3189
2672
conflicting_path = fp.get_path(conflict[3])
3190
2673
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)
2674
yield Conflict.factory(c_type, action=action, path=modified_path,
2675
file_id=modified_id,
2676
conflict_path=conflicting_path,
2677
conflict_file_id=conflicting_id)
3198
2680
class _FileMover(object):