~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Jelmer Vernooij
  • Date: 2012-02-20 12:19:29 UTC
  • mfrom: (6437.23.11 2.5)
  • mto: (6581.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 6582.
  • Revision ID: jelmer@samba.org-20120220121929-7ni2psvjoatm1yp4
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
30
32
from bzrlib import (
31
33
    annotate,
32
34
    bencode,
33
 
    bzrdir,
 
35
    controldir,
34
36
    commit,
35
37
    conflicts,
36
38
    delta,
37
 
    errors,
38
39
    inventory,
39
40
    multiparent,
40
41
    osutils,
42
43
    ui,
43
44
    urlutils,
44
45
    )
 
46
from bzrlib.i18n import gettext
45
47
""")
46
 
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
 
48
from bzrlib.errors import (DuplicateKey, MalformedTransform,
47
49
                           ReusingTransform, CantMoveRoot,
48
50
                           ExistingLimbo, ImmortalLimbo, NoFinalPath,
49
51
                           UnableCreateSymlink)
50
52
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
 
53
from bzrlib.mutabletree import MutableTree
51
54
from bzrlib.osutils import (
52
55
    delete_any,
53
56
    file_kind,
55
58
    pathjoin,
56
59
    sha_file,
57
60
    splitpath,
58
 
    supports_executable,
59
61
    )
60
62
from bzrlib.progress import ProgressPhase
61
63
from bzrlib.symbol_versioning import (
154
156
        """
155
157
        if self._tree is None:
156
158
            return
 
159
        for hook in MutableTree.hooks['post_transform']:
 
160
            hook(self._tree, self)
157
161
        self._tree.unlock()
158
162
        self._tree = None
159
163
 
228
232
        irrelevant.
229
233
 
230
234
        """
231
 
        new_roots = [k for k, v in self._new_parent.iteritems() if v is
 
235
        new_roots = [k for k, v in self._new_parent.iteritems() if v ==
232
236
                     ROOT_PARENT]
233
237
        if len(new_roots) < 1:
234
238
            return
561
565
        for trans_id in self._removed_id:
562
566
            file_id = self.tree_file_id(trans_id)
563
567
            if file_id is not None:
564
 
                # XXX: This seems like something that should go via a different
565
 
                #      indirection.
566
 
                if self._tree.inventory[file_id].kind == 'directory':
 
568
                if self._tree.stored_kind(file_id) == 'directory':
567
569
                    parents.append(trans_id)
568
570
            elif self.tree_kind(trans_id) == 'directory':
569
571
                parents.append(trans_id)
626
628
        for trans_id in self._new_parent:
627
629
            seen = set()
628
630
            parent_id = trans_id
629
 
            while parent_id is not ROOT_PARENT:
 
631
            while parent_id != ROOT_PARENT:
630
632
                seen.add(parent_id)
631
633
                try:
632
634
                    parent_id = self.final_parent(parent_id)
642
644
        """If parent directories are versioned, children must be versioned."""
643
645
        conflicts = []
644
646
        for parent_id, children in by_parent.iteritems():
645
 
            if parent_id is ROOT_PARENT:
 
647
            if parent_id == ROOT_PARENT:
646
648
                continue
647
649
            if self.final_file_id(parent_id) is not None:
648
650
                continue
741
743
        """Children must have a directory parent"""
742
744
        conflicts = []
743
745
        for parent_id, children in by_parent.iteritems():
744
 
            if parent_id is ROOT_PARENT:
 
746
            if parent_id == ROOT_PARENT:
745
747
                continue
746
748
            no_children = True
747
749
            for child_id in children:
763
765
 
764
766
    def _set_executability(self, path, trans_id):
765
767
        """Set the executability of versioned files """
766
 
        if supports_executable():
 
768
        if self._tree._supports_executable():
767
769
            new_executability = self._new_executability[trans_id]
768
770
            abspath = self._tree.abspath(path)
769
771
            current_mode = os.stat(abspath).st_mode
778
780
                    to_mode |= 0010 & ~umask
779
781
            else:
780
782
                to_mode = current_mode & ~0111
781
 
            os.chmod(abspath, to_mode)
 
783
            osutils.chmod_if_possible(abspath, to_mode)
782
784
 
783
785
    def _new_entry(self, name, parent_id, file_id):
784
786
        """Helper function to create a new filesystem entry."""
1233
1235
        finally:
1234
1236
            TreeTransformBase.finalize(self)
1235
1237
 
 
1238
    def _limbo_supports_executable(self):
 
1239
        """Check if the limbo path supports the executable bit."""
 
1240
        # FIXME: Check actual file system capabilities of limbodir
 
1241
        return osutils.supports_executable()
 
1242
 
1236
1243
    def _limbo_name(self, trans_id):
1237
1244
        """Generate the limbo name of a file"""
1238
1245
        limbo_name = self._limbo_files.get(trans_id)
1557
1564
        try:
1558
1565
            limbodir = urlutils.local_path_from_url(
1559
1566
                tree._transport.abspath('limbo'))
1560
 
            try:
1561
 
                os.mkdir(limbodir)
1562
 
            except OSError, e:
1563
 
                if e.errno == errno.EEXIST:
1564
 
                    raise ExistingLimbo(limbodir)
 
1567
            osutils.ensure_empty_directory_exists(
 
1568
                limbodir,
 
1569
                errors.ExistingLimbo)
1565
1570
            deletiondir = urlutils.local_path_from_url(
1566
1571
                tree._transport.abspath('pending-deletion'))
1567
 
            try:
1568
 
                os.mkdir(deletiondir)
1569
 
            except OSError, e:
1570
 
                if e.errno == errno.EEXIST:
1571
 
                    raise errors.ExistingPendingDeletion(deletiondir)
 
1572
            osutils.ensure_empty_directory_exists(
 
1573
                deletiondir,
 
1574
                errors.ExistingPendingDeletion)
1572
1575
        except:
1573
1576
            tree.unlock()
1574
1577
            raise
1637
1640
            else:
1638
1641
                raise
1639
1642
        if typefunc(mode):
1640
 
            os.chmod(self._limbo_name(trans_id), mode)
 
1643
            osutils.chmod_if_possible(self._limbo_name(trans_id), mode)
1641
1644
 
1642
1645
    def iter_tree_children(self, parent_id):
1643
1646
        """Iterate through the entry's tree children, if any"""
1721
1724
            calculating one.
1722
1725
        :param _mover: Supply an alternate FileMover, for testing
1723
1726
        """
 
1727
        for hook in MutableTree.hooks['pre_transform']:
 
1728
            hook(self._tree, self)
1724
1729
        if not no_conflicts:
1725
1730
            self._check_malformed()
1726
1731
        child_pb = ui.ui_factory.nested_progress_bar()
1727
1732
        try:
1728
1733
            if precomputed_delta is None:
1729
 
                child_pb.update('Apply phase', 0, 2)
 
1734
                child_pb.update(gettext('Apply phase'), 0, 2)
1730
1735
                inventory_delta = self._generate_inventory_delta()
1731
1736
                offset = 1
1732
1737
            else:
1737
1742
            else:
1738
1743
                mover = _mover
1739
1744
            try:
1740
 
                child_pb.update('Apply phase', 0 + offset, 2 + offset)
 
1745
                child_pb.update(gettext('Apply phase'), 0 + offset, 2 + offset)
1741
1746
                self._apply_removals(mover)
1742
 
                child_pb.update('Apply phase', 1 + offset, 2 + offset)
 
1747
                child_pb.update(gettext('Apply phase'), 1 + offset, 2 + offset)
1743
1748
                modified_paths = self._apply_insertions(mover)
1744
1749
            except:
1745
1750
                mover.rollback()
1765
1770
        try:
1766
1771
            for num, trans_id in enumerate(self._removed_id):
1767
1772
                if (num % 10) == 0:
1768
 
                    child_pb.update('removing file', num, total_entries)
 
1773
                    child_pb.update(gettext('removing file'), num, total_entries)
1769
1774
                if trans_id == self._new_root:
1770
1775
                    file_id = self._tree.get_root_id()
1771
1776
                else:
1783
1788
            final_kinds = {}
1784
1789
            for num, (path, trans_id) in enumerate(new_paths):
1785
1790
                if (num % 10) == 0:
1786
 
                    child_pb.update('adding file',
 
1791
                    child_pb.update(gettext('adding file'),
1787
1792
                                    num + len(self._removed_id), total_entries)
1788
1793
                file_id = new_path_file_ids[trans_id]
1789
1794
                if file_id is None:
1833
1838
                # do not attempt to move root into a subdirectory of itself.
1834
1839
                if path == '':
1835
1840
                    continue
1836
 
                child_pb.update('removing file', num, len(tree_paths))
 
1841
                child_pb.update(gettext('removing file'), num, len(tree_paths))
1837
1842
                full_path = self._tree.abspath(path)
1838
1843
                if trans_id in self._removed_contents:
1839
1844
                    delete_path = os.path.join(self._deletiondir, trans_id)
1868
1873
        try:
1869
1874
            for num, (path, trans_id) in enumerate(new_paths):
1870
1875
                if (num % 10) == 0:
1871
 
                    child_pb.update('adding file', num, len(new_paths))
 
1876
                    child_pb.update(gettext('adding file'), num, len(new_paths))
1872
1877
                full_path = self._tree.abspath(path)
1873
1878
                if trans_id in self._needs_rename:
1874
1879
                    try:
2325
2330
            if kind == 'file':
2326
2331
                statval = os.lstat(limbo_name)
2327
2332
                size = statval.st_size
2328
 
                if not supports_executable():
 
2333
                if not tt._limbo_supports_executable():
2329
2334
                    executable = False
2330
2335
                else:
2331
2336
                    executable = statval.st_mode & S_IEXEC
2546
2551
    file_trans_id = {}
2547
2552
    top_pb = ui.ui_factory.nested_progress_bar()
2548
2553
    pp = ProgressPhase("Build phase", 2, top_pb)
2549
 
    if tree.inventory.root is not None:
 
2554
    if tree.get_root_id() is not None:
2550
2555
        # This is kind of a hack: we should be altering the root
2551
2556
        # as part of the regular tree shape diff logic.
2552
2557
        # The conditional test here is to avoid doing an
2567
2572
        try:
2568
2573
            deferred_contents = []
2569
2574
            num = 0
2570
 
            total = len(tree.inventory)
 
2575
            total = len(tree.all_file_ids())
2571
2576
            if delta_from_tree:
2572
2577
                precomputed_delta = []
2573
2578
            else:
2582
2587
                for dir, files in wt.walkdirs():
2583
2588
                    existing_files.update(f[0] for f in files)
2584
2589
            for num, (tree_path, entry) in \
2585
 
                enumerate(tree.inventory.iter_entries_by_dir()):
2586
 
                pb.update("Building tree", num - len(deferred_contents), total)
 
2590
                enumerate(tree.iter_entries_by_dir()):
 
2591
                pb.update(gettext("Building tree"), num - len(deferred_contents), total)
2587
2592
                if entry.parent_id is None:
2588
2593
                    continue
2589
2594
                reparent = False
2595
2600
                    kind = file_kind(target_path)
2596
2601
                    if kind == "directory":
2597
2602
                        try:
2598
 
                            bzrdir.BzrDir.open(target_path)
 
2603
                            controldir.ControlDir.open(target_path)
2599
2604
                        except errors.NotBranchError:
2600
2605
                            pass
2601
2606
                        else:
2673
2678
                new_desired_files.append((file_id,
2674
2679
                    (trans_id, tree_path, text_sha1)))
2675
2680
                continue
2676
 
            pb.update('Adding file contents', count + offset, total)
 
2681
            pb.update(gettext('Adding file contents'), count + offset, total)
2677
2682
            if hardlink:
2678
2683
                tt.create_hardlink(accelerator_tree.abspath(accelerator_path),
2679
2684
                                   trans_id)
2700
2705
            contents = filtered_output_bytes(contents, filters,
2701
2706
                ContentFilterContext(tree_path, tree))
2702
2707
        tt.create_file(contents, trans_id, sha1=text_sha1)
2703
 
        pb.update('Adding file contents', count + offset, total)
 
2708
        pb.update(gettext('Adding file contents'), count + offset, total)
2704
2709
 
2705
2710
 
2706
2711
def _reparent_children(tt, old_parent, new_parent):
2838
2843
            return new_name
2839
2844
 
2840
2845
 
2841
 
def _entry_changes(file_id, entry, working_tree):
2842
 
    """Determine in which ways the inventory entry has changed.
2843
 
 
2844
 
    Returns booleans: has_contents, content_mod, meta_mod
2845
 
    has_contents means there are currently contents, but they differ
2846
 
    contents_mod means contents need to be modified
2847
 
    meta_mod means the metadata needs to be modified
2848
 
    """
2849
 
    cur_entry = working_tree.inventory[file_id]
2850
 
    try:
2851
 
        working_kind = working_tree.kind(file_id)
2852
 
        has_contents = True
2853
 
    except NoSuchFile:
2854
 
        has_contents = False
2855
 
        contents_mod = True
2856
 
        meta_mod = False
2857
 
    if has_contents is True:
2858
 
        if entry.kind != working_kind:
2859
 
            contents_mod, meta_mod = True, False
2860
 
        else:
2861
 
            cur_entry._read_tree_state(working_tree.id2path(file_id),
2862
 
                                       working_tree)
2863
 
            contents_mod, meta_mod = entry.detect_changes(cur_entry)
2864
 
            cur_entry._forget_tree_state()
2865
 
    return has_contents, contents_mod, meta_mod
2866
 
 
2867
 
 
2868
2846
def revert(working_tree, target_tree, filenames, backups=False,
2869
2847
           pb=None, change_reporter=None):
2870
2848
    """Revert a working tree's contents to those of a target tree."""
3047
3025
    pb = ui.ui_factory.nested_progress_bar()
3048
3026
    try:
3049
3027
        for n in range(10):
3050
 
            pb.update('Resolution pass', n+1, 10)
 
3028
            pb.update(gettext('Resolution pass'), n+1, 10)
3051
3029
            conflicts = tt.find_conflicts()
3052
3030
            if len(conflicts) == 0:
3053
3031
                return new_conflicts
3077
3055
                existing_file, new_file = conflict[2], conflict[1]
3078
3056
            else:
3079
3057
                existing_file, new_file = conflict[1], conflict[2]
3080
 
            new_name = tt.final_name(existing_file)+'.moved'
 
3058
            new_name = tt.final_name(existing_file) + '.moved'
3081
3059
            tt.adjust_path(new_name, final_parent, existing_file)
3082
3060
            new_conflicts.add((c_type, 'Moved existing file to',
3083
3061
                               existing_file, new_file))