~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Jelmer Vernooij
  • Date: 2012-02-18 16:55:04 UTC
  • mfrom: (6437.23.10 2.5)
  • mto: This revision was merged to the branch mainline in revision 6469.
  • Revision ID: jelmer@samba.org-20120218165504-c9oe5c5ue805y8wp
Merge bzr/2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
16
16
 
 
17
from __future__ import absolute_import
 
18
 
17
19
import os
18
20
import errno
19
21
from stat import S_ISREG, S_IEXEC
20
22
import time
21
23
 
22
24
from bzrlib import (
 
25
    config as _mod_config,
23
26
    errors,
24
27
    lazy_import,
25
28
    registry,
34
37
    commit,
35
38
    conflicts,
36
39
    delta,
37
 
    errors,
38
40
    inventory,
39
41
    multiparent,
40
42
    osutils,
44
46
    )
45
47
from bzrlib.i18n import gettext
46
48
""")
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 (
53
56
    delete_any,
54
57
    file_kind,
56
59
    pathjoin,
57
60
    sha_file,
58
61
    splitpath,
59
 
    supports_executable,
60
62
    )
61
63
from bzrlib.progress import ProgressPhase
62
64
from bzrlib.symbol_versioning import (
155
157
        """
156
158
        if self._tree is None:
157
159
            return
 
160
        for hook in MutableTree.hooks['post_transform']:
 
161
            hook(self._tree, self)
158
162
        self._tree.unlock()
159
163
        self._tree = None
160
164
 
229
233
        irrelevant.
230
234
 
231
235
        """
232
 
        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
237
                     ROOT_PARENT]
234
238
        if len(new_roots) < 1:
235
239
            return
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
566
 
                #      indirection.
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)
627
629
        for trans_id in self._new_parent:
628
630
            seen = set()
629
631
            parent_id = trans_id
630
 
            while parent_id is not ROOT_PARENT:
 
632
            while parent_id != ROOT_PARENT:
631
633
                seen.add(parent_id)
632
634
                try:
633
635
                    parent_id = self.final_parent(parent_id)
643
645
        """If parent directories are versioned, children must be versioned."""
644
646
        conflicts = []
645
647
        for parent_id, children in by_parent.iteritems():
646
 
            if parent_id is ROOT_PARENT:
 
648
            if parent_id == ROOT_PARENT:
647
649
                continue
648
650
            if self.final_file_id(parent_id) is not None:
649
651
                continue
742
744
        """Children must have a directory parent"""
743
745
        conflicts = []
744
746
        for parent_id, children in by_parent.iteritems():
745
 
            if parent_id is ROOT_PARENT:
 
747
            if parent_id == ROOT_PARENT:
746
748
                continue
747
749
            no_children = True
748
750
            for child_id in children:
764
766
 
765
767
    def _set_executability(self, path, trans_id):
766
768
        """Set the executability of versioned files """
767
 
        if supports_executable():
 
769
        if self._tree._supports_executable():
768
770
            new_executability = self._new_executability[trans_id]
769
771
            abspath = self._tree.abspath(path)
770
772
            current_mode = os.stat(abspath).st_mode
779
781
                    to_mode |= 0010 & ~umask
780
782
            else:
781
783
                to_mode = current_mode & ~0111
782
 
            os.chmod(abspath, to_mode)
 
784
            osutils.chmod_if_possible(abspath, to_mode)
783
785
 
784
786
    def _new_entry(self, name, parent_id, file_id):
785
787
        """Helper function to create a new filesystem entry."""
1234
1236
        finally:
1235
1237
            TreeTransformBase.finalize(self)
1236
1238
 
 
1239
    def _limbo_supports_executable(self):
 
1240
        """Check if the limbo path supports the executable bit."""
 
1241
        # FIXME: Check actual file system capabilities of limbodir
 
1242
        return osutils.supports_executable()
 
1243
 
1237
1244
    def _limbo_name(self, trans_id):
1238
1245
        """Generate the limbo name of a file"""
1239
1246
        limbo_name = self._limbo_files.get(trans_id)
1399
1406
        delete_any(self._limbo_name(trans_id))
1400
1407
 
1401
1408
    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)
 
1409
        conf = self._tree.get_config_stack()
 
1410
        handle_orphan = conf.get('bzr.transform.orphan_policy')
1417
1411
        handle_orphan(self, trans_id, parent_id)
1418
1412
 
1419
1413
 
1482
1476
orphaning_registry._set_default_key('conflict')
1483
1477
 
1484
1478
 
 
1479
opt_transform_orphan = _mod_config.RegistryOption(
 
1480
    'bzr.transform.orphan_policy', orphaning_registry,
 
1481
    help='Policy for orphaned files during transform operations.',
 
1482
    invalid='warning')
 
1483
 
 
1484
 
1485
1485
class TreeTransform(DiskTreeTransform):
1486
1486
    """Represent a tree transformation.
1487
1487
 
1558
1558
        try:
1559
1559
            limbodir = urlutils.local_path_from_url(
1560
1560
                tree._transport.abspath('limbo'))
1561
 
            try:
1562
 
                os.mkdir(limbodir)
1563
 
            except OSError, e:
1564
 
                if e.errno == errno.EEXIST:
1565
 
                    raise ExistingLimbo(limbodir)
 
1561
            osutils.ensure_empty_directory_exists(
 
1562
                limbodir,
 
1563
                errors.ExistingLimbo)
1566
1564
            deletiondir = urlutils.local_path_from_url(
1567
1565
                tree._transport.abspath('pending-deletion'))
1568
 
            try:
1569
 
                os.mkdir(deletiondir)
1570
 
            except OSError, e:
1571
 
                if e.errno == errno.EEXIST:
1572
 
                    raise errors.ExistingPendingDeletion(deletiondir)
 
1566
            osutils.ensure_empty_directory_exists(
 
1567
                deletiondir,
 
1568
                errors.ExistingPendingDeletion)
1573
1569
        except:
1574
1570
            tree.unlock()
1575
1571
            raise
1638
1634
            else:
1639
1635
                raise
1640
1636
        if typefunc(mode):
1641
 
            os.chmod(self._limbo_name(trans_id), mode)
 
1637
            osutils.chmod_if_possible(self._limbo_name(trans_id), mode)
1642
1638
 
1643
1639
    def iter_tree_children(self, parent_id):
1644
1640
        """Iterate through the entry's tree children, if any"""
1722
1718
            calculating one.
1723
1719
        :param _mover: Supply an alternate FileMover, for testing
1724
1720
        """
 
1721
        for hook in MutableTree.hooks['pre_transform']:
 
1722
            hook(self._tree, self)
1725
1723
        if not no_conflicts:
1726
1724
            self._check_malformed()
1727
1725
        child_pb = ui.ui_factory.nested_progress_bar()
2058
2056
        pass
2059
2057
 
2060
2058
    @property
 
2059
    @deprecated_method(deprecated_in((2, 5, 0)))
2061
2060
    def inventory(self):
2062
2061
        """This Tree does not use inventory as its backing data."""
2063
2062
        raise NotImplementedError(_PreviewTree.inventory)
2064
2063
 
 
2064
    @property
 
2065
    def root_inventory(self):
 
2066
        """This Tree does not use inventory as its backing data."""
 
2067
        raise NotImplementedError(_PreviewTree.root_inventory)
 
2068
 
2065
2069
    def get_root_id(self):
2066
2070
        return self._transform.final_file_id(self._transform.root)
2067
2071
 
2326
2330
            if kind == 'file':
2327
2331
                statval = os.lstat(limbo_name)
2328
2332
                size = statval.st_size
2329
 
                if not supports_executable():
 
2333
                if not tt._limbo_supports_executable():
2330
2334
                    executable = False
2331
2335
                else:
2332
2336
                    executable = statval.st_mode & S_IEXEC
2547
2551
    file_trans_id = {}
2548
2552
    top_pb = ui.ui_factory.nested_progress_bar()
2549
2553
    pp = ProgressPhase("Build phase", 2, top_pb)
2550
 
    if tree.inventory.root is not None:
 
2554
    if tree.get_root_id() is not None:
2551
2555
        # This is kind of a hack: we should be altering the root
2552
2556
        # as part of the regular tree shape diff logic.
2553
2557
        # The conditional test here is to avoid doing an
2568
2572
        try:
2569
2573
            deferred_contents = []
2570
2574
            num = 0
2571
 
            total = len(tree.inventory)
 
2575
            total = len(tree.all_file_ids())
2572
2576
            if delta_from_tree:
2573
2577
                precomputed_delta = []
2574
2578
            else:
2583
2587
                for dir, files in wt.walkdirs():
2584
2588
                    existing_files.update(f[0] for f in files)
2585
2589
            for num, (tree_path, entry) in \
2586
 
                enumerate(tree.inventory.iter_entries_by_dir()):
 
2590
                enumerate(tree.iter_entries_by_dir()):
2587
2591
                pb.update(gettext("Building tree"), num - len(deferred_contents), total)
2588
2592
                if entry.parent_id is None:
2589
2593
                    continue
2839
2843
            return new_name
2840
2844
 
2841
2845
 
2842
 
def _entry_changes(file_id, entry, working_tree):
2843
 
    """Determine in which ways the inventory entry has changed.
2844
 
 
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
2849
 
    """
2850
 
    cur_entry = working_tree.inventory[file_id]
2851
 
    try:
2852
 
        working_kind = working_tree.kind(file_id)
2853
 
        has_contents = True
2854
 
    except NoSuchFile:
2855
 
        has_contents = False
2856
 
        contents_mod = True
2857
 
        meta_mod = False
2858
 
    if has_contents is True:
2859
 
        if entry.kind != working_kind:
2860
 
            contents_mod, meta_mod = True, False
2861
 
        else:
2862
 
            cur_entry._read_tree_state(working_tree.id2path(file_id),
2863
 
                                       working_tree)
2864
 
            contents_mod, meta_mod = entry.detect_changes(cur_entry)
2865
 
            cur_entry._forget_tree_state()
2866
 
    return has_contents, contents_mod, meta_mod
2867
 
 
2868
 
 
2869
2846
def revert(working_tree, target_tree, filenames, backups=False,
2870
2847
           pb=None, change_reporter=None):
2871
2848
    """Revert a working tree's contents to those of a target tree."""
3078
3055
                existing_file, new_file = conflict[2], conflict[1]
3079
3056
            else:
3080
3057
                existing_file, new_file = conflict[1], conflict[2]
3081
 
            new_name = tt.final_name(existing_file)+'.moved'
 
3058
            new_name = tt.final_name(existing_file) + '.moved'
3082
3059
            tt.adjust_path(new_name, final_parent, existing_file)
3083
3060
            new_conflicts.add((c_type, 'Moved existing file to',
3084
3061
                               existing_file, new_file))