~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

NEWS section template into a separate file

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
import os
18
18
import errno
19
19
from stat import S_ISREG, S_IEXEC
20
 
import time
21
20
 
22
21
from bzrlib.lazy_import import lazy_import
23
22
lazy_import(globals(), """
31
30
    multiparent,
32
31
    osutils,
33
32
    revision as _mod_revision,
34
 
    ui,
35
33
    )
36
34
""")
37
35
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
38
 
                           ReusingTransform, CantMoveRoot,
 
36
                           ReusingTransform, NotVersionedError, CantMoveRoot,
39
37
                           ExistingLimbo, ImmortalLimbo, NoFinalPath,
40
38
                           UnableCreateSymlink)
41
39
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
50
48
    splitpath,
51
49
    supports_executable,
52
50
)
53
 
from bzrlib.progress import ProgressPhase
 
51
from bzrlib.progress import DummyProgress, ProgressPhase
54
52
from bzrlib.symbol_versioning import (
55
53
        deprecated_function,
56
54
        deprecated_in,
80
78
class TreeTransformBase(object):
81
79
    """The base class for TreeTransform and its kin."""
82
80
 
83
 
    def __init__(self, tree, pb=None,
 
81
    def __init__(self, tree, pb=DummyProgress(),
84
82
                 case_sensitive=True):
85
83
        """Constructor.
86
84
 
87
85
        :param tree: The tree that will be transformed, but not necessarily
88
86
            the output tree.
89
 
        :param pb: ignored
 
87
        :param pb: A ProgressTask indicating how much progress is being made
90
88
        :param case_sensitive: If True, the target of the transform is
91
89
            case sensitive, not just case preserving.
92
90
        """
1063
1061
class DiskTreeTransform(TreeTransformBase):
1064
1062
    """Tree transform storing its contents on disk."""
1065
1063
 
1066
 
    def __init__(self, tree, limbodir, pb=None,
 
1064
    def __init__(self, tree, limbodir, pb=DummyProgress(),
1067
1065
                 case_sensitive=True):
1068
1066
        """Constructor.
1069
1067
        :param tree: The tree that will be transformed, but not necessarily
1070
1068
            the output tree.
1071
1069
        :param limbodir: A directory where new files can be stored until
1072
1070
            they are installed in their proper places
1073
 
        :param pb: ignored
 
1071
        :param pb: A ProgressBar indicating how much progress is being made
1074
1072
        :param case_sensitive: If True, the target of the transform is
1075
1073
            case sensitive, not just case preserving.
1076
1074
        """
1086
1084
        self._limbo_children_names = {}
1087
1085
        # List of transform ids that need to be renamed from limbo into place
1088
1086
        self._needs_rename = set()
1089
 
        self._creation_mtime = None
1090
1087
 
1091
1088
    def finalize(self):
1092
1089
        """Release the working tree lock, if held, clean up limbo dir.
1160
1157
            if trans_id not in self._new_contents:
1161
1158
                continue
1162
1159
            new_path = self._limbo_name(trans_id)
1163
 
            osutils.rename(old_path, new_path)
1164
 
            for descendant in self._limbo_descendants(trans_id):
1165
 
                desc_path = self._limbo_files[descendant]
1166
 
                desc_path = new_path + desc_path[len(old_path):]
1167
 
                self._limbo_files[descendant] = desc_path
1168
 
 
1169
 
    def _limbo_descendants(self, trans_id):
1170
 
        """Return the set of trans_ids whose limbo paths descend from this."""
1171
 
        descendants = set(self._limbo_children.get(trans_id, []))
1172
 
        for descendant in list(descendants):
1173
 
            descendants.update(self._limbo_descendants(descendant))
1174
 
        return descendants
 
1160
            os.rename(old_path, new_path)
1175
1161
 
1176
1162
    def create_file(self, contents, trans_id, mode_id=None):
1177
1163
        """Schedule creation of a new file.
1199
1185
            f.writelines(contents)
1200
1186
        finally:
1201
1187
            f.close()
1202
 
        self._set_mtime(name)
1203
1188
        self._set_mode(trans_id, mode_id, S_ISREG)
1204
1189
 
1205
1190
    def _read_file_chunks(self, trans_id):
1212
1197
    def _read_symlink_target(self, trans_id):
1213
1198
        return os.readlink(self._limbo_name(trans_id))
1214
1199
 
1215
 
    def _set_mtime(self, path):
1216
 
        """All files that are created get the same mtime.
1217
 
 
1218
 
        This time is set by the first object to be created.
1219
 
        """
1220
 
        if self._creation_mtime is None:
1221
 
            self._creation_mtime = time.time()
1222
 
        os.utime(path, (self._creation_mtime, self._creation_mtime))
1223
 
 
1224
1200
    def create_hardlink(self, path, trans_id):
1225
1201
        """Schedule creation of a hard link"""
1226
1202
        name = self._limbo_name(trans_id)
1340
1316
    FileMover does not delete files until it is sure that a rollback will not
1341
1317
    happen.
1342
1318
    """
1343
 
    def __init__(self, tree, pb=None):
 
1319
    def __init__(self, tree, pb=DummyProgress()):
1344
1320
        """Note: a tree_write lock is taken on the tree.
1345
1321
 
1346
1322
        Use TreeTransform.finalize() to release the lock (can be omitted if
1692
1668
    unversioned files in the input tree.
1693
1669
    """
1694
1670
 
1695
 
    def __init__(self, tree, pb=None, case_sensitive=True):
 
1671
    def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
1696
1672
        tree.lock_read()
1697
1673
        limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1698
1674
        DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
1989
1965
    def get_file_mtime(self, file_id, path=None):
1990
1966
        """See Tree.get_file_mtime"""
1991
1967
        if not self._content_change(file_id):
1992
 
            return self._transform._tree.get_file_mtime(file_id)
 
1968
            return self._transform._tree.get_file_mtime(file_id, path)
1993
1969
        return self._stat_limbo_file(file_id).st_mtime
1994
1970
 
1995
1971
    def _file_size(self, entry, stat_value):
2049
2025
                statval = os.lstat(limbo_name)
2050
2026
                size = statval.st_size
2051
2027
                if not supports_executable():
2052
 
                    executable = False
 
2028
                    executable = None
2053
2029
                else:
2054
2030
                    executable = statval.st_mode & S_IEXEC
2055
2031
            else:
2057
2033
                executable = None
2058
2034
            if kind == 'symlink':
2059
2035
                link_or_sha1 = os.readlink(limbo_name).decode(osutils._fs_enc)
2060
 
        executable = tt._new_executability.get(trans_id, executable)
 
2036
        if supports_executable():
 
2037
            executable = tt._new_executability.get(trans_id, executable)
2061
2038
        return kind, size, executable, link_or_sha1
2062
2039
 
2063
2040
    def iter_changes(self, from_tree, include_unchanged=False,
2376
2353
        new_desired_files = desired_files
2377
2354
    else:
2378
2355
        iter = accelerator_tree.iter_changes(tree, include_unchanged=True)
2379
 
        unchanged = [(f, p[1]) for (f, p, c, v, d, n, k, e)
2380
 
                     in iter if not (c or e[0] != e[1])]
2381
 
        if accelerator_tree.supports_content_filtering():
2382
 
            unchanged = [(f, p) for (f, p) in unchanged
2383
 
                         if not accelerator_tree.iter_search_rules([p]).next()]
2384
 
        unchanged = dict(unchanged)
 
2356
        unchanged = dict((f, p[1]) for (f, p, c, v, d, n, k, e)
 
2357
                         in iter if not (c or e[0] != e[1]))
2385
2358
        new_desired_files = []
2386
2359
        count = 0
2387
2360
        for file_id, (trans_id, tree_path) in desired_files:
2590
2563
 
2591
2564
 
2592
2565
def revert(working_tree, target_tree, filenames, backups=False,
2593
 
           pb=None, change_reporter=None):
 
2566
           pb=DummyProgress(), change_reporter=None):
2594
2567
    """Revert a working tree's contents to those of a target tree."""
2595
2568
    target_tree.lock_read()
2596
 
    pb = ui.ui_factory.nested_progress_bar()
2597
2569
    tt = TreeTransform(working_tree, pb)
2598
2570
    try:
2599
2571
        pp = ProgressPhase("Revert phase", 3, pb)
2618
2590
def _prepare_revert_transform(working_tree, target_tree, tt, filenames,
2619
2591
                              backups, pp, basis_tree=None,
2620
2592
                              merge_modified=None):
 
2593
    pp.next_phase()
2621
2594
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2622
2595
    try:
2623
2596
        if merge_modified is None:
2627
2600
                                      merge_modified, basis_tree)
2628
2601
    finally:
2629
2602
        child_pb.finished()
 
2603
    pp.next_phase()
2630
2604
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2631
2605
    try:
2632
2606
        raw_conflicts = resolve_conflicts(tt, child_pb,
2754
2728
    return merge_modified
2755
2729
 
2756
2730
 
2757
 
def resolve_conflicts(tt, pb=None, pass_func=None):
 
2731
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
2758
2732
    """Make many conflict-resolution attempts, but die if they fail"""
2759
2733
    if pass_func is None:
2760
2734
        pass_func = conflict_pass
2761
2735
    new_conflicts = set()
2762
 
    pb = ui.ui_factory.nested_progress_bar()
2763
2736
    try:
2764
2737
        for n in range(10):
2765
2738
            pb.update('Resolution pass', n+1, 10)
2769
2742
            new_conflicts.update(pass_func(tt, conflicts))
2770
2743
        raise MalformedTransform(conflicts=conflicts)
2771
2744
    finally:
2772
 
        pb.finished()
 
2745
        pb.clear()
2773
2746
 
2774
2747
 
2775
2748
def conflict_pass(tt, conflicts, path_tree=None):
2824
2797
                        # special-case the other tree root (move its
2825
2798
                        # children to current root)
2826
2799
                        if entry.parent_id is None:
2827
 
                            create = False
 
2800
                            create=False
2828
2801
                            moved = _reparent_transform_children(
2829
2802
                                tt, trans_id, tt.root)
2830
2803
                            for child in moved:
2898
2871
        self.pending_deletions = []
2899
2872
 
2900
2873
    def rename(self, from_, to):
2901
 
        """Rename a file from one path to another."""
 
2874
        """Rename a file from one path to another.  Functions like os.rename"""
2902
2875
        try:
2903
 
            osutils.rename(from_, to)
 
2876
            os.rename(from_, to)
2904
2877
        except OSError, e:
2905
2878
            if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
2906
2879
                raise errors.FileExists(to, str(e))
2920
2893
    def rollback(self):
2921
2894
        """Reverse all renames that have been performed"""
2922
2895
        for from_, to in reversed(self.past_renames):
2923
 
            osutils.rename(to, from_)
 
2896
            os.rename(to, from_)
2924
2897
        # after rollback, don't reuse _FileMover
2925
2898
        past_renames = None
2926
2899
        pending_deletions = None