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
468
# ensure that all children are registered with the transaction
573
469
list(self.iter_tree_children(parent_id))
575
@deprecated_method(deprecated_in((2, 3, 0)))
576
471
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:
473
children = by_parent[parent_id]
476
for child in children:
594
477
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
480
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)
483
childpath = joinpath(path, name)
484
child_id = self._tree_path_ids.get(childpath)
602
485
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))
486
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))
488
if self.final_parent(child_id) != parent_id:
490
if child_id in self._removed_contents:
491
# XXX What about dangling file-ids?
623
496
def _parent_loops(self):
624
497
"""No entry should be its own ancestor"""
740
618
def _parent_type_conflicts(self, by_parent):
741
"""Children must have a directory parent"""
619
"""parents must have directory 'contents'."""
743
621
for parent_id, children in by_parent.iteritems():
744
622
if parent_id is ROOT_PARENT:
747
for child_id in children:
748
if self.final_kind(child_id) is not None:
624
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)
626
for child in children:
628
self.final_kind(child)
632
kind = self.final_kind(parent_id)
757
# The directory will be deleted
758
636
conflicts.append(('missing parent', parent_id))
759
637
elif kind != "directory":
760
# Meh, we need a *directory* to put something in it
761
638
conflicts.append(('non-directory parent', parent_id))
641
def _any_contents(self, trans_ids):
642
"""Return true if any of the trans_ids, will have contents."""
643
for trans_id in trans_ids:
645
kind = self.final_kind(trans_id)
764
651
def _set_executability(self, path, trans_id):
765
652
"""Set the executability of versioned files """
766
653
if supports_executable():
830
717
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
720
def _affected_ids(self):
876
721
"""Return the set of transform ids affected by the transform"""
877
722
trans_ids = set(self._removed_id)
1017
865
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):
867
def commit(self, branch, message, merge_parents=None, strict=False):
1022
868
"""Commit the result of this TreeTransform to a branch.
1024
870
:param branch: The branch to commit to.
1025
871
: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.)
872
:param merge_parents: Additional parents specified by pending merges.
1040
873
:return: The revision_id of the revision committed.
1042
875
self._check_malformed()
1236
1054
def _limbo_name(self, trans_id):
1237
1055
"""Generate the limbo name of a file"""
1238
1056
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
1057
if limbo_name is not None:
1059
parent = self._new_parent.get(trans_id)
1060
# if the parent directory is already in limbo (e.g. when building a
1061
# tree), choose a limbo name inside the parent, to reduce further
1063
use_direct_path = False
1064
if self._new_contents.get(parent) == 'directory':
1065
filename = self._new_name.get(trans_id)
1066
if filename is not None:
1067
if parent not in self._limbo_children:
1068
self._limbo_children[parent] = set()
1069
self._limbo_children_names[parent] = {}
1070
use_direct_path = True
1071
# the direct path can only be used if no other file has
1072
# already taken this pathname, i.e. if the name is unused, or
1073
# if it is already associated with this trans_id.
1074
elif self._case_sensitive_target:
1075
if (self._limbo_children_names[parent].get(filename)
1076
in (trans_id, None)):
1077
use_direct_path = True
1079
for l_filename, l_trans_id in\
1080
self._limbo_children_names[parent].iteritems():
1081
if l_trans_id == trans_id:
1083
if l_filename.lower() == filename.lower():
1086
use_direct_path = True
1089
limbo_name = pathjoin(self._limbo_files[parent], filename)
1090
self._limbo_children[parent].add(trans_id)
1091
self._limbo_children_names[parent][filename] = trans_id
1093
limbo_name = pathjoin(self._limbodir, trans_id)
1094
self._needs_rename.add(trans_id)
1095
self._limbo_files[trans_id] = limbo_name
1242
1096
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
1098
def adjust_path(self, name, parent, trans_id):
1254
1099
previous_parent = self._new_parent.get(trans_id)
1255
1100
previous_name = self._new_name.get(trans_id)
1274
1117
entries from _limbo_files, because they are now stale.
1276
1119
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]
1120
old_path = self._limbo_files.pop(trans_id)
1280
1121
if trans_id not in self._new_contents:
1282
1123
new_path = self._limbo_name(trans_id)
1283
1124
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):
1126
def create_file(self, contents, trans_id, mode_id=None):
1298
1127
"""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.
1131
Contents is an iterator of strings, all of which will be written
1132
to the target destination.
1134
New file takes the permissions of any existing file with that id,
1135
unless mode_id is specified.
1311
1137
name = self._limbo_name(trans_id)
1312
1138
f = open(name, 'wb')
1314
unique_add(self._new_contents, trans_id, 'file')
1141
unique_add(self._new_contents, trans_id, 'file')
1143
# Clean up the file, it never got registered so
1144
# TreeTransform.finalize() won't clean it up.
1315
1149
f.writelines(contents)
1318
self._set_mtime(name)
1319
1152
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
1154
def _read_file_chunks(self, trans_id):
1327
1155
cur_file = open(self._limbo_name(trans_id), 'rb')
1397
1214
del self._limbo_children_names[trans_id]
1398
1215
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
1218
class TreeTransform(DiskTreeTransform):
1485
1219
"""Represent a tree transformation.
1660
1397
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
1399
def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1711
1400
"""Apply all changes to the inventory and filesystem.
1828
1515
tree_paths = list(self._tree_path_ids.iteritems())
1829
1516
tree_paths.sort(reverse=True)
1830
child_pb = ui.ui_factory.nested_progress_bar()
1517
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.
1519
for num, data in enumerate(tree_paths):
1520
path, trans_id = data
1836
1521
child_pb.update('removing file', num, len(tree_paths))
1837
1522
full_path = self._tree.abspath(path)
1838
1523
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):
1524
mover.pre_delete(full_path, os.path.join(self._deletiondir,
1526
elif trans_id in self._new_name or trans_id in \
1844
1529
mover.rename(full_path, self._limbo_name(trans_id))
1845
except errors.TransformRenameFailed, e:
1846
1531
if e.errno != errno.ENOENT:
1873
1558
if trans_id in self._needs_rename:
1875
1560
mover.rename(self._limbo_name(trans_id), full_path)
1876
except errors.TransformRenameFailed, e:
1877
1562
# We may be renaming a dangling inventory id
1878
1563
if e.errno != errno.ENOENT:
1881
1566
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
1567
if (trans_id in self._new_contents or
1886
1568
self.path_changed(trans_id)):
1887
1569
if trans_id in self._new_contents:
1888
1570
modified_paths.append(full_path)
1889
1571
if trans_id in self._new_executability:
1890
1572
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
1574
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
1575
self._new_contents.clear()
1903
1576
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
1579
class TransformPreview(DiskTreeTransform):
1930
1580
"""A TreeTransform for generating preview trees.
2014
1659
yield self._get_repository().revision_tree(revision_id)
2016
1661
def _get_file_revision(self, file_id, vf, tree_revision):
2017
parent_keys = [(file_id, t.get_file_revision(file_id)) for t in
1662
parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
2018
1663
self._iter_parent_trees()]
2019
1664
vf.add_lines((file_id, tree_revision), parent_keys,
2020
self.get_file_lines(file_id))
1665
self.get_file(file_id).readlines())
2021
1666
repo = self._get_repository()
2022
1667
base_vf = repo.texts
2023
1668
if base_vf not in vf.fallback_versionedfiles:
2024
1669
vf.fallback_versionedfiles.append(base_vf)
2025
1670
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)
1672
def _stat_limbo_file(self, file_id):
1673
trans_id = self._transform.trans_id_file_id(file_id)
2030
1674
name = self._transform._limbo_name(trans_id)
2031
1675
return os.lstat(name)
2659
2269
new_desired_files = desired_files
2661
2271
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)
2272
unchanged = dict((f, p[1]) for (f, p, c, v, d, n, k, e)
2273
in iter if not (c or e[0] != e[1]))
2668
2274
new_desired_files = []
2670
for file_id, (trans_id, tree_path, text_sha1) in desired_files:
2276
for file_id, (trans_id, tree_path) in desired_files:
2671
2277
accelerator_path = unchanged.get(file_id)
2672
2278
if accelerator_path is None:
2673
new_desired_files.append((file_id,
2674
(trans_id, tree_path, text_sha1)))
2279
new_desired_files.append((file_id, (trans_id, tree_path)))
2676
2281
pb.update('Adding file contents', count + offset, total)
2784
2383
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.
2386
@deprecated_function(deprecated_in((1, 9, 0)))
2387
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
2388
"""Create new file contents according to an inventory entry.
2390
DEPRECATED. Use create_from_tree instead.
2392
if entry.kind == "file":
2394
lines = tree.get_file(entry.file_id).readlines()
2395
tt.create_file(lines, trans_id, mode_id=mode_id)
2396
elif entry.kind == "symlink":
2397
tt.create_symlink(tree.get_symlink_target(entry.file_id), trans_id)
2398
elif entry.kind == "directory":
2399
tt.create_directory(trans_id)
2402
def create_from_tree(tt, trans_id, tree, file_id, bytes=None):
2403
"""Create new file contents according to tree contents."""
2795
2404
kind = tree.kind(file_id)
2796
2405
if kind == 'directory':
2797
2406
tt.create_directory(trans_id)
2931
2530
deferred_files = []
2932
2531
for id_num, (file_id, path, changed_content, versioned, parent, name,
2933
2532
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:
2533
if skip_root and file_id[0] is not None and parent[0] is None:
2942
2535
trans_id = tt.trans_id_file_id(file_id)
2944
2537
if changed_content:
2945
2538
keep_content = False
2946
if wt_kind == 'file' and (backups or target_kind is None):
2539
if kind[0] == 'file' and (backups or kind[1] is None):
2947
2540
wt_sha1 = working_tree.get_file_sha1(file_id)
2948
2541
if merge_modified.get(file_id) != wt_sha1:
2949
2542
# acquire the basis tree lazily to prevent the
2952
2545
if basis_tree is None:
2953
2546
basis_tree = working_tree.basis_tree()
2954
2547
basis_tree.lock_read()
2955
if basis_tree.has_id(file_id):
2548
if file_id in basis_tree:
2956
2549
if wt_sha1 != basis_tree.get_file_sha1(file_id):
2957
2550
keep_content = True
2958
elif target_kind is None and not target_versioned:
2551
elif kind[1] is None and not versioned[1]:
2959
2552
keep_content = True
2960
if wt_kind is not None:
2553
if kind[0] is not None:
2961
2554
if not keep_content:
2962
2555
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)
2556
elif kind[1] is not None:
2557
parent_trans_id = tt.trans_id_file_id(parent[0])
2558
by_parent = tt.by_parent()
2559
backup_name = _get_backup_name(name[0], by_parent,
2560
parent_trans_id, tt)
2967
2561
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:
2562
new_trans_id = tt.create_path(name[0], parent_trans_id)
2563
if versioned == (True, True):
2970
2564
tt.unversion_file(trans_id)
2971
2565
tt.version_file(file_id, new_trans_id)
2972
2566
# New contents should have the same unix perms as old
2974
2568
mode_id = trans_id
2975
2569
trans_id = new_trans_id
2976
if target_kind in ('directory', 'tree-reference'):
2570
if kind[1] in ('directory', 'tree-reference'):
2977
2571
tt.create_directory(trans_id)
2978
if target_kind == 'tree-reference':
2572
if kind[1] == 'tree-reference':
2979
2573
revision = target_tree.get_reference_revision(file_id,
2981
2575
tt.set_tree_reference(revision, trans_id)
2982
elif target_kind == 'symlink':
2576
elif kind[1] == 'symlink':
2983
2577
tt.create_symlink(target_tree.get_symlink_target(file_id),
2985
elif target_kind == 'file':
2579
elif kind[1] == 'file':
2986
2580
deferred_files.append((file_id, (trans_id, mode_id)))
2987
2581
if basis_tree is None:
2988
2582
basis_tree = working_tree.basis_tree()
2989
2583
basis_tree.lock_read()
2990
2584
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)):
2585
if (file_id in basis_tree and new_sha1 ==
2586
basis_tree.get_file_sha1(file_id)):
2993
2587
if file_id in merge_modified:
2994
2588
del merge_modified[file_id]
2996
2590
merge_modified[file_id] = new_sha1
2998
2592
# 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:
2593
if keep_content and executable[0] == executable[1]:
2594
tt.set_executability(executable[1], trans_id)
2595
elif kind[1] is not None:
2596
raise AssertionError(kind[1])
2597
if versioned == (False, True):
3004
2598
tt.version_file(file_id, trans_id)
3005
if wt_versioned and not target_versioned:
2599
if versioned == (True, False):
3006
2600
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:
2601
if (name[1] is not None and
2602
(name[0] != name[1] or parent[0] != parent[1])):
2603
if name[1] == '' and parent[1] is None:
3010
2604
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()
2606
parent_trans = tt.trans_id_file_id(parent[1])
2607
tt.adjust_path(name[1], parent_trans, trans_id)
2608
if executable[0] != executable[1] and kind[1] == "file":
2609
tt.set_executability(executable[1], trans_id)
2610
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2612
tt.create_file(bytes, trans_id, mode_id)
3037
2614
if basis_tree is not None:
3038
2615
basis_tree.unlock()
3039
2616
return merge_modified
3042
def resolve_conflicts(tt, pb=None, pass_func=None):
2619
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
3043
2620
"""Make many conflict-resolution attempts, but die if they fail"""
3044
2621
if pass_func is None:
3045
2622
pass_func = conflict_pass
3046
2623
new_conflicts = set()
3047
pb = ui.ui_factory.nested_progress_bar()
3049
2625
for n in range(10):
3050
2626
pb.update('Resolution pass', n+1, 10)
3182
2739
modified_path = fp.get_path(conflict[2])
3183
2740
modified_id = tt.final_file_id(conflict[2])
3184
2741
if len(conflict) == 3:
3185
yield conflicts.Conflict.factory(
3186
c_type, action=action, path=modified_path, file_id=modified_id)
2742
yield Conflict.factory(c_type, action=action, path=modified_path,
2743
file_id=modified_id)
3189
2746
conflicting_path = fp.get_path(conflict[3])
3190
2747
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)
2748
yield Conflict.factory(c_type, action=action, path=modified_path,
2749
file_id=modified_id,
2750
conflict_path=conflicting_path,
2751
conflict_file_id=conflicting_id)
3198
2754
class _FileMover(object):