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
21
19
from stat import S_ISREG, S_IEXEC
24
22
from bzrlib import (
25
config as _mod_config,
47
45
from bzrlib.i18n import gettext
49
from bzrlib.errors import (DuplicateKey, MalformedTransform,
47
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
50
48
ReusingTransform, CantMoveRoot,
51
ImmortalLimbo, NoFinalPath,
49
ExistingLimbo, ImmortalLimbo, NoFinalPath,
52
50
UnableCreateSymlink)
53
51
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
54
from bzrlib.mutabletree import MutableTree
55
52
from bzrlib.osutils import (
566
562
for trans_id in self._removed_id:
567
563
file_id = self.tree_file_id(trans_id)
568
564
if file_id is not None:
569
if self._tree.stored_kind(file_id) == 'directory':
565
# XXX: This seems like something that should go via a different
567
if self._tree.inventory[file_id].kind == 'directory':
570
568
parents.append(trans_id)
571
569
elif self.tree_kind(trans_id) == 'directory':
572
570
parents.append(trans_id)
575
573
# ensure that all children are registered with the transaction
576
574
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, []))
578
581
def _has_named_child(self, name, parent_id, known_children):
579
582
"""Does a parent already have a name child.
640
643
"""If parent directories are versioned, children must be versioned."""
642
645
for parent_id, children in by_parent.iteritems():
643
if parent_id == ROOT_PARENT:
646
if parent_id is ROOT_PARENT:
645
648
if self.final_file_id(parent_id) is not None:
739
742
"""Children must have a directory parent"""
741
744
for parent_id, children in by_parent.iteritems():
742
if parent_id == ROOT_PARENT:
745
if parent_id is ROOT_PARENT:
744
747
no_children = True
745
748
for child_id in children:
762
765
def _set_executability(self, path, trans_id):
763
766
"""Set the executability of versioned files """
764
if self._tree._supports_executable():
767
if supports_executable():
765
768
new_executability = self._new_executability[trans_id]
766
769
abspath = self._tree.abspath(path)
767
770
current_mode = os.stat(abspath).st_mode
776
779
to_mode |= 0010 & ~umask
778
781
to_mode = current_mode & ~0111
779
osutils.chmod_if_possible(abspath, to_mode)
782
os.chmod(abspath, to_mode)
781
784
def _new_entry(self, name, parent_id, file_id):
782
785
"""Helper function to create a new filesystem entry."""
1232
1235
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()
1239
1237
def _limbo_name(self, trans_id):
1240
1238
"""Generate the limbo name of a file"""
1241
1239
limbo_name = self._limbo_files.get(trans_id)
1401
1399
delete_any(self._limbo_name(trans_id))
1403
1401
def new_orphan(self, trans_id, parent_id):
1404
conf = self._tree.get_config_stack()
1405
handle_orphan = conf.get('bzr.transform.orphan_policy')
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)
1406
1417
handle_orphan(self, trans_id, parent_id)
1471
1482
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.',
1480
1485
class TreeTransform(DiskTreeTransform):
1481
1486
"""Represent a tree transformation.
1554
1559
limbodir = urlutils.local_path_from_url(
1555
1560
tree._transport.abspath('limbo'))
1556
osutils.ensure_empty_directory_exists(
1558
errors.ExistingLimbo)
1564
if e.errno == errno.EEXIST:
1565
raise ExistingLimbo(limbodir)
1559
1566
deletiondir = urlutils.local_path_from_url(
1560
1567
tree._transport.abspath('pending-deletion'))
1561
osutils.ensure_empty_directory_exists(
1563
errors.ExistingPendingDeletion)
1569
os.mkdir(deletiondir)
1571
if e.errno == errno.EEXIST:
1572
raise errors.ExistingPendingDeletion(deletiondir)
1631
1640
if typefunc(mode):
1632
osutils.chmod_if_possible(self._limbo_name(trans_id), mode)
1641
os.chmod(self._limbo_name(trans_id), mode)
1634
1643
def iter_tree_children(self, parent_id):
1635
1644
"""Iterate through the entry's tree children, if any"""
2054
@deprecated_method(deprecated_in((2, 5, 0)))
2055
2061
def inventory(self):
2056
2062
"""This Tree does not use inventory as its backing data."""
2057
2063
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)
2064
2065
def get_root_id(self):
2065
2066
return self._transform.final_file_id(self._transform.root)
2182
2179
ordered_ids.append((trans_id, parent_file_id))
2183
2180
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):
2193
2182
def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
2194
2183
# This may not be a maximally efficient implementation, but it is
2195
2184
# reasonably straightforward. An implementation that grafts the
2558
2547
file_trans_id = {}
2559
2548
top_pb = ui.ui_factory.nested_progress_bar()
2560
2549
pp = ProgressPhase("Build phase", 2, top_pb)
2561
if tree.get_root_id() is not None:
2550
if tree.inventory.root is not None:
2562
2551
# This is kind of a hack: we should be altering the root
2563
2552
# as part of the regular tree shape diff logic.
2564
2553
# The conditional test here is to avoid doing an
2594
2583
for dir, files in wt.walkdirs():
2595
2584
existing_files.update(f[0] for f in files)
2596
2585
for num, (tree_path, entry) in \
2597
enumerate(tree.iter_entries_by_dir()):
2586
enumerate(tree.inventory.iter_entries_by_dir()):
2598
2587
pb.update(gettext("Building tree"), num - len(deferred_contents), total)
2599
2588
if entry.parent_id is None:
2832
2821
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
2835
2869
def revert(working_tree, target_tree, filenames, backups=False,
2836
2870
pb=None, change_reporter=None):
2837
2871
"""Revert a working tree's contents to those of a target tree."""
3044
3078
existing_file, new_file = conflict[2], conflict[1]
3046
3080
existing_file, new_file = conflict[1], conflict[2]
3047
new_name = tt.final_name(existing_file) + '.moved'
3081
new_name = tt.final_name(existing_file)+'.moved'
3048
3082
tt.adjust_path(new_name, final_parent, existing_file)
3049
3083
new_conflicts.add((c_type, 'Moved existing file to',
3050
3084
existing_file, new_file))