~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Vincent Ladeuil
  • Date: 2011-09-29 15:50:58 UTC
  • mfrom: (6177 +trunk)
  • mto: This revision was merged to the branch mainline in revision 6178.
  • Revision ID: v.ladeuil+lp@free.fr-20110929155058-zgbecmx1huzktegm
Merge trunk and resolve conflicts

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