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,
45
47
from bzrlib.i18n import gettext
47
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
49
from bzrlib.errors import (DuplicateKey, MalformedTransform,
48
50
ReusingTransform, CantMoveRoot,
49
ExistingLimbo, ImmortalLimbo, NoFinalPath,
51
ImmortalLimbo, NoFinalPath,
50
52
UnableCreateSymlink)
51
53
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
54
from bzrlib.mutabletree import MutableTree
52
55
from bzrlib.osutils import (
562
566
for trans_id in self._removed_id:
563
567
file_id = self.tree_file_id(trans_id)
564
568
if file_id is not None:
565
# XXX: This seems like something that should go via a different
567
if self._tree.inventory[file_id].kind == 'directory':
569
if self._tree.stored_kind(file_id) == 'directory':
568
570
parents.append(trans_id)
569
571
elif self.tree_kind(trans_id) == 'directory':
570
572
parents.append(trans_id)
573
575
# ensure that all children are registered with the transaction
574
576
list(self.iter_tree_children(parent_id))
576
@deprecated_method(deprecated_in((2, 3, 0)))
577
def has_named_child(self, by_parent, parent_id, name):
578
return self._has_named_child(
579
name, parent_id, known_children=by_parent.get(parent_id, []))
581
578
def _has_named_child(self, name, parent_id, known_children):
582
579
"""Does a parent already have a name child.
643
640
"""If parent directories are versioned, children must be versioned."""
645
642
for parent_id, children in by_parent.iteritems():
646
if parent_id is ROOT_PARENT:
643
if parent_id == ROOT_PARENT:
648
645
if self.final_file_id(parent_id) is not None:
742
739
"""Children must have a directory parent"""
744
741
for parent_id, children in by_parent.iteritems():
745
if parent_id is ROOT_PARENT:
742
if parent_id == ROOT_PARENT:
747
744
no_children = True
748
745
for child_id in children:
765
762
def _set_executability(self, path, trans_id):
766
763
"""Set the executability of versioned files """
767
if supports_executable():
764
if self._tree._supports_executable():
768
765
new_executability = self._new_executability[trans_id]
769
766
abspath = self._tree.abspath(path)
770
767
current_mode = os.stat(abspath).st_mode
779
776
to_mode |= 0010 & ~umask
781
778
to_mode = current_mode & ~0111
782
os.chmod(abspath, to_mode)
779
osutils.chmod_if_possible(abspath, to_mode)
784
781
def _new_entry(self, name, parent_id, file_id):
785
782
"""Helper function to create a new filesystem entry."""
1235
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()
1237
1239
def _limbo_name(self, trans_id):
1238
1240
"""Generate the limbo name of a file"""
1239
1241
limbo_name = self._limbo_files.get(trans_id)
1399
1401
delete_any(self._limbo_name(trans_id))
1401
1403
def new_orphan(self, trans_id, parent_id):
1402
# FIXME: There is no tree config, so we use the branch one (it's weird
1403
# to define it this way as orphaning can only occur in a working tree,
1404
# but that's all we have (for now). It will find the option in
1405
# locations.conf or bazaar.conf though) -- vila 20100916
1406
conf = self._tree.branch.get_config()
1407
conf_var_name = 'bzr.transform.orphan_policy'
1408
orphan_policy = conf.get_user_option(conf_var_name)
1409
default_policy = orphaning_registry.default_key
1410
if orphan_policy is None:
1411
orphan_policy = default_policy
1412
if orphan_policy not in orphaning_registry:
1413
trace.warning('%s (from %s) is not a known policy, defaulting '
1414
'to %s' % (orphan_policy, conf_var_name, default_policy))
1415
orphan_policy = default_policy
1416
handle_orphan = orphaning_registry.get(orphan_policy)
1404
conf = self._tree.get_config_stack()
1405
handle_orphan = conf.get('bzr.transform.orphan_policy')
1417
1406
handle_orphan(self, trans_id, parent_id)
1482
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.',
1485
1480
class TreeTransform(DiskTreeTransform):
1486
1481
"""Represent a tree transformation.
1559
1554
limbodir = urlutils.local_path_from_url(
1560
1555
tree._transport.abspath('limbo'))
1564
if e.errno == errno.EEXIST:
1565
raise ExistingLimbo(limbodir)
1556
osutils.ensure_empty_directory_exists(
1558
errors.ExistingLimbo)
1566
1559
deletiondir = urlutils.local_path_from_url(
1567
1560
tree._transport.abspath('pending-deletion'))
1569
os.mkdir(deletiondir)
1571
if e.errno == errno.EEXIST:
1572
raise errors.ExistingPendingDeletion(deletiondir)
1561
osutils.ensure_empty_directory_exists(
1563
errors.ExistingPendingDeletion)
1640
1631
if typefunc(mode):
1641
os.chmod(self._limbo_name(trans_id), mode)
1632
osutils.chmod_if_possible(self._limbo_name(trans_id), mode)
1643
1634
def iter_tree_children(self, parent_id):
1644
1635
"""Iterate through the entry's tree children, if any"""
2054
@deprecated_method(deprecated_in((2, 5, 0)))
2061
2055
def inventory(self):
2062
2056
"""This Tree does not use inventory as its backing data."""
2063
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)
2065
2064
def get_root_id(self):
2066
2065
return self._transform.final_file_id(self._transform.root)
2179
2182
ordered_ids.append((trans_id, parent_file_id))
2180
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):
2182
2193
def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
2183
2194
# This may not be a maximally efficient implementation, but it is
2184
2195
# reasonably straightforward. An implementation that grafts the
2547
2558
file_trans_id = {}
2548
2559
top_pb = ui.ui_factory.nested_progress_bar()
2549
2560
pp = ProgressPhase("Build phase", 2, top_pb)
2550
if tree.inventory.root is not None:
2561
if tree.get_root_id() is not None:
2551
2562
# This is kind of a hack: we should be altering the root
2552
2563
# as part of the regular tree shape diff logic.
2553
2564
# The conditional test here is to avoid doing an
2583
2594
for dir, files in wt.walkdirs():
2584
2595
existing_files.update(f[0] for f in files)
2585
2596
for num, (tree_path, entry) in \
2586
enumerate(tree.inventory.iter_entries_by_dir()):
2597
enumerate(tree.iter_entries_by_dir()):
2587
2598
pb.update(gettext("Building tree"), num - len(deferred_contents), total)
2588
2599
if entry.parent_id is None:
2821
2832
tt.set_executability(entry.executable, trans_id)
2824
@deprecated_function(deprecated_in((2, 3, 0)))
2825
def get_backup_name(entry, by_parent, parent_trans_id, tt):
2826
return _get_backup_name(entry.name, by_parent, parent_trans_id, tt)
2829
@deprecated_function(deprecated_in((2, 3, 0)))
2830
def _get_backup_name(name, by_parent, parent_trans_id, tt):
2831
"""Produce a backup-style name that appears to be available"""
2835
yield "%s.~%d~" % (name, counter)
2837
for new_name in name_gen():
2838
if not tt.has_named_child(by_parent, parent_trans_id, new_name):
2842
def _entry_changes(file_id, entry, working_tree):
2843
"""Determine in which ways the inventory entry has changed.
2845
Returns booleans: has_contents, content_mod, meta_mod
2846
has_contents means there are currently contents, but they differ
2847
contents_mod means contents need to be modified
2848
meta_mod means the metadata needs to be modified
2850
cur_entry = working_tree.inventory[file_id]
2852
working_kind = working_tree.kind(file_id)
2855
has_contents = False
2858
if has_contents is True:
2859
if entry.kind != working_kind:
2860
contents_mod, meta_mod = True, False
2862
cur_entry._read_tree_state(working_tree.id2path(file_id),
2864
contents_mod, meta_mod = entry.detect_changes(cur_entry)
2865
cur_entry._forget_tree_state()
2866
return has_contents, contents_mod, meta_mod
2869
2835
def revert(working_tree, target_tree, filenames, backups=False,
2870
2836
pb=None, change_reporter=None):
2871
2837
"""Revert a working tree's contents to those of a target tree."""
3078
3044
existing_file, new_file = conflict[2], conflict[1]
3080
3046
existing_file, new_file = conflict[1], conflict[2]
3081
new_name = tt.final_name(existing_file)+'.moved'
3047
new_name = tt.final_name(existing_file) + '.moved'
3082
3048
tt.adjust_path(new_name, final_parent, existing_file)
3083
3049
new_conflicts.add((c_type, 'Moved existing file to',
3084
3050
existing_file, new_file))