14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from __future__ import absolute_import
19
21
from stat import S_ISREG, S_IEXEC
22
24
from bzrlib import (
25
config as _mod_config,
47
from bzrlib.i18n import gettext
46
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
49
from bzrlib.errors import (DuplicateKey, MalformedTransform,
47
50
ReusingTransform, CantMoveRoot,
48
ExistingLimbo, ImmortalLimbo, NoFinalPath,
51
ImmortalLimbo, NoFinalPath,
49
52
UnableCreateSymlink)
50
53
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
54
from bzrlib.mutabletree import MutableTree
51
55
from bzrlib.osutils import (
231
new_roots = [k for k, v in self._new_parent.iteritems() if v is
236
new_roots = [k for k, v in self._new_parent.iteritems() if v ==
233
238
if len(new_roots) < 1:
234
if self.final_kind(self.root) is None:
235
self.cancel_deletion(self.root)
236
if self.final_file_id(self.root) is None:
237
self.version_file(self.tree_file_id(self.root),
240
240
if len(new_roots) != 1:
241
241
raise ValueError('A tree cannot have two roots!')
566
566
for trans_id in self._removed_id:
567
567
file_id = self.tree_file_id(trans_id)
568
568
if file_id is not None:
569
# XXX: This seems like something that should go via a different
571
if self._tree.inventory[file_id].kind == 'directory':
569
if self._tree.stored_kind(file_id) == 'directory':
572
570
parents.append(trans_id)
573
571
elif self.tree_kind(trans_id) == 'directory':
574
572
parents.append(trans_id)
577
575
# ensure that all children are registered with the transaction
578
576
list(self.iter_tree_children(parent_id))
580
@deprecated_method(deprecated_in((2, 3, 0)))
581
def has_named_child(self, by_parent, parent_id, name):
582
return self._has_named_child(
583
name, parent_id, known_children=by_parent.get(parent_id, []))
585
578
def _has_named_child(self, name, parent_id, known_children):
586
579
"""Does a parent already have a name child.
647
640
"""If parent directories are versioned, children must be versioned."""
649
642
for parent_id, children in by_parent.iteritems():
650
if parent_id is ROOT_PARENT:
643
if parent_id == ROOT_PARENT:
652
645
if self.final_file_id(parent_id) is not None:
746
739
"""Children must have a directory parent"""
748
741
for parent_id, children in by_parent.iteritems():
749
if parent_id is ROOT_PARENT:
742
if parent_id == ROOT_PARENT:
751
744
no_children = True
752
745
for child_id in children:
769
762
def _set_executability(self, path, trans_id):
770
763
"""Set the executability of versioned files """
771
if supports_executable():
764
if self._tree._supports_executable():
772
765
new_executability = self._new_executability[trans_id]
773
766
abspath = self._tree.abspath(path)
774
767
current_mode = os.stat(abspath).st_mode
1239
1232
TreeTransformBase.finalize(self)
1234
def _limbo_supports_executable(self):
1235
"""Check if the limbo path supports the executable bit."""
1236
# FIXME: Check actual file system capabilities of limbodir
1237
return osutils.supports_executable()
1241
1239
def _limbo_name(self, trans_id):
1242
1240
"""Generate the limbo name of a file"""
1243
1241
limbo_name = self._limbo_files.get(trans_id)
1403
1401
delete_any(self._limbo_name(trans_id))
1405
1403
def new_orphan(self, trans_id, parent_id):
1406
# FIXME: There is no tree config, so we use the branch one (it's weird
1407
# to define it this way as orphaning can only occur in a working tree,
1408
# but that's all we have (for now). It will find the option in
1409
# locations.conf or bazaar.conf though) -- vila 20100916
1410
conf = self._tree.branch.get_config()
1411
conf_var_name = 'bzr.transform.orphan_policy'
1412
orphan_policy = conf.get_user_option(conf_var_name)
1413
default_policy = orphaning_registry.default_key
1414
if orphan_policy is None:
1415
orphan_policy = default_policy
1416
if orphan_policy not in orphaning_registry:
1417
trace.warning('%s (from %s) is not a known policy, defaulting '
1418
'to %s' % (orphan_policy, conf_var_name, default_policy))
1419
orphan_policy = default_policy
1420
handle_orphan = orphaning_registry.get(orphan_policy)
1404
conf = self._tree.get_config_stack()
1405
handle_orphan = conf.get('bzr.transform.orphan_policy')
1421
1406
handle_orphan(self, trans_id, parent_id)
1486
1471
orphaning_registry._set_default_key('conflict')
1474
opt_transform_orphan = _mod_config.RegistryOption(
1475
'bzr.transform.orphan_policy', orphaning_registry,
1476
help='Policy for orphaned files during transform operations.',
1489
1480
class TreeTransform(DiskTreeTransform):
1490
1481
"""Represent a tree transformation.
1722
1713
calculating one.
1723
1714
:param _mover: Supply an alternate FileMover, for testing
1716
for hook in MutableTree.hooks['pre_transform']:
1717
hook(self._tree, self)
1725
1718
if not no_conflicts:
1726
1719
self._check_malformed()
1727
1720
child_pb = ui.ui_factory.nested_progress_bar()
1729
1722
if precomputed_delta is None:
1730
child_pb.update('Apply phase', 0, 2)
1723
child_pb.update(gettext('Apply phase'), 0, 2)
1731
1724
inventory_delta = self._generate_inventory_delta()
1741
child_pb.update('Apply phase', 0 + offset, 2 + offset)
1734
child_pb.update(gettext('Apply phase'), 0 + offset, 2 + offset)
1742
1735
self._apply_removals(mover)
1743
child_pb.update('Apply phase', 1 + offset, 2 + offset)
1736
child_pb.update(gettext('Apply phase'), 1 + offset, 2 + offset)
1744
1737
modified_paths = self._apply_insertions(mover)
1746
1739
mover.rollback()
1765
1760
for num, trans_id in enumerate(self._removed_id):
1766
1761
if (num % 10) == 0:
1767
child_pb.update('removing file', num, total_entries)
1762
child_pb.update(gettext('removing file'), num, total_entries)
1768
1763
if trans_id == self._new_root:
1769
1764
file_id = self._tree.get_root_id()
1782
1777
final_kinds = {}
1783
1778
for num, (path, trans_id) in enumerate(new_paths):
1784
1779
if (num % 10) == 0:
1785
child_pb.update('adding file',
1780
child_pb.update(gettext('adding file'),
1786
1781
num + len(self._removed_id), total_entries)
1787
1782
file_id = new_path_file_ids[trans_id]
1788
1783
if file_id is None:
1832
1827
# do not attempt to move root into a subdirectory of itself.
1835
child_pb.update('removing file', num, len(tree_paths))
1830
child_pb.update(gettext('removing file'), num, len(tree_paths))
1836
1831
full_path = self._tree.abspath(path)
1837
1832
if trans_id in self._removed_contents:
1838
1833
delete_path = os.path.join(self._deletiondir, trans_id)
1868
1863
for num, (path, trans_id) in enumerate(new_paths):
1869
1864
if (num % 10) == 0:
1870
child_pb.update('adding file', num, len(new_paths))
1865
child_pb.update(gettext('adding file'), num, len(new_paths))
1871
1866
full_path = self._tree.abspath(path)
1872
1867
if trans_id in self._needs_rename:
2054
@deprecated_method(deprecated_in((2, 5, 0)))
2059
2055
def inventory(self):
2060
2056
"""This Tree does not use inventory as its backing data."""
2061
2057
raise NotImplementedError(_PreviewTree.inventory)
2060
def root_inventory(self):
2061
"""This Tree does not use inventory as its backing data."""
2062
raise NotImplementedError(_PreviewTree.root_inventory)
2063
2064
def get_root_id(self):
2064
2065
return self._transform.final_file_id(self._transform.root)
2177
2182
ordered_ids.append((trans_id, parent_file_id))
2178
2183
return ordered_ids
2185
def iter_child_entries(self, file_id, path=None):
2186
self.id2path(file_id)
2187
trans_id = self._transform.trans_id_file_id(file_id)
2188
todo = [(child_trans_id, trans_id) for child_trans_id in
2189
self._all_children(trans_id)]
2190
for entry, trans_id in self._make_inv_entries(todo):
2180
2193
def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
2181
2194
# This may not be a maximally efficient implementation, but it is
2182
2195
# reasonably straightforward. An implementation that grafts the
2273
def get_file_verifier(self, file_id, path=None, stat_value=None):
2274
trans_id = self._transform.trans_id_file_id(file_id)
2275
kind = self._transform._new_contents.get(trans_id)
2277
return self._transform._tree.get_file_verifier(file_id)
2279
fileobj = self.get_file(file_id)
2281
return ("SHA1", sha_file(fileobj))
2260
2285
def get_file_sha1(self, file_id, path=None, stat_value=None):
2261
2286
trans_id = self._transform.trans_id_file_id(file_id)
2262
2287
kind = self._transform._new_contents.get(trans_id)
2533
2558
file_trans_id = {}
2534
2559
top_pb = ui.ui_factory.nested_progress_bar()
2535
2560
pp = ProgressPhase("Build phase", 2, top_pb)
2536
if tree.inventory.root is not None:
2561
if tree.get_root_id() is not None:
2537
2562
# This is kind of a hack: we should be altering the root
2538
2563
# as part of the regular tree shape diff logic.
2539
2564
# The conditional test here is to avoid doing an
2569
2594
for dir, files in wt.walkdirs():
2570
2595
existing_files.update(f[0] for f in files)
2571
2596
for num, (tree_path, entry) in \
2572
enumerate(tree.inventory.iter_entries_by_dir()):
2573
pb.update("Building tree", num - len(deferred_contents), total)
2597
enumerate(tree.iter_entries_by_dir()):
2598
pb.update(gettext("Building tree"), num - len(deferred_contents), total)
2574
2599
if entry.parent_id is None:
2576
2601
reparent = False
2660
2685
new_desired_files.append((file_id,
2661
2686
(trans_id, tree_path, text_sha1)))
2663
pb.update('Adding file contents', count + offset, total)
2688
pb.update(gettext('Adding file contents'), count + offset, total)
2665
2690
tt.create_hardlink(accelerator_tree.abspath(accelerator_path),
2687
2712
contents = filtered_output_bytes(contents, filters,
2688
2713
ContentFilterContext(tree_path, tree))
2689
2714
tt.create_file(contents, trans_id, sha1=text_sha1)
2690
pb.update('Adding file contents', count + offset, total)
2715
pb.update(gettext('Adding file contents'), count + offset, total)
2693
2718
def _reparent_children(tt, old_parent, new_parent):
2807
2832
tt.set_executability(entry.executable, trans_id)
2810
@deprecated_function(deprecated_in((2, 3, 0)))
2811
def get_backup_name(entry, by_parent, parent_trans_id, tt):
2812
return _get_backup_name(entry.name, by_parent, parent_trans_id, tt)
2815
@deprecated_function(deprecated_in((2, 3, 0)))
2816
def _get_backup_name(name, by_parent, parent_trans_id, tt):
2817
"""Produce a backup-style name that appears to be available"""
2821
yield "%s.~%d~" % (name, counter)
2823
for new_name in name_gen():
2824
if not tt.has_named_child(by_parent, parent_trans_id, new_name):
2828
def _entry_changes(file_id, entry, working_tree):
2829
"""Determine in which ways the inventory entry has changed.
2831
Returns booleans: has_contents, content_mod, meta_mod
2832
has_contents means there are currently contents, but they differ
2833
contents_mod means contents need to be modified
2834
meta_mod means the metadata needs to be modified
2836
cur_entry = working_tree.inventory[file_id]
2838
working_kind = working_tree.kind(file_id)
2841
has_contents = False
2844
if has_contents is True:
2845
if entry.kind != working_kind:
2846
contents_mod, meta_mod = True, False
2848
cur_entry._read_tree_state(working_tree.id2path(file_id),
2850
contents_mod, meta_mod = entry.detect_changes(cur_entry)
2851
cur_entry._forget_tree_state()
2852
return has_contents, contents_mod, meta_mod
2855
2835
def revert(working_tree, target_tree, filenames, backups=False,
2856
2836
pb=None, change_reporter=None):
2857
2837
"""Revert a working tree's contents to those of a target tree."""
3034
3014
pb = ui.ui_factory.nested_progress_bar()
3036
3016
for n in range(10):
3037
pb.update('Resolution pass', n+1, 10)
3017
pb.update(gettext('Resolution pass'), n+1, 10)
3038
3018
conflicts = tt.find_conflicts()
3039
3019
if len(conflicts) == 0:
3040
3020
return new_conflicts