/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
31
31
    multiparent,
32
32
    osutils,
33
33
    revision as _mod_revision,
 
34
    ui,
34
35
    )
35
36
""")
36
37
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
37
 
                           ReusingTransform, NotVersionedError, CantMoveRoot,
 
38
                           ReusingTransform, CantMoveRoot,
38
39
                           ExistingLimbo, ImmortalLimbo, NoFinalPath,
39
40
                           UnableCreateSymlink)
40
41
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
49
50
    splitpath,
50
51
    supports_executable,
51
52
)
52
 
from bzrlib.progress import DummyProgress, ProgressPhase
 
53
from bzrlib.progress import ProgressPhase
53
54
from bzrlib.symbol_versioning import (
54
55
        deprecated_function,
55
56
        deprecated_in,
79
80
class TreeTransformBase(object):
80
81
    """The base class for TreeTransform and its kin."""
81
82
 
82
 
    def __init__(self, tree, pb=DummyProgress(),
 
83
    def __init__(self, tree, pb=None,
83
84
                 case_sensitive=True):
84
85
        """Constructor.
85
86
 
86
87
        :param tree: The tree that will be transformed, but not necessarily
87
88
            the output tree.
88
 
        :param pb: A ProgressTask indicating how much progress is being made
 
89
        :param pb: ignored
89
90
        :param case_sensitive: If True, the target of the transform is
90
91
            case sensitive, not just case preserving.
91
92
        """
162
163
 
163
164
    def adjust_path(self, name, parent, trans_id):
164
165
        """Change the path that is assigned to a transaction id."""
 
166
        if parent is None:
 
167
            raise ValueError("Parent trans-id may not be None")
165
168
        if trans_id == self._new_root:
166
169
            raise CantMoveRoot
167
170
        self._new_name[trans_id] = name
168
171
        self._new_parent[trans_id] = parent
169
 
        if parent == ROOT_PARENT:
170
 
            if self._new_root is not None:
171
 
                raise ValueError("Cannot have multiple roots.")
172
 
            self._new_root = trans_id
173
172
 
174
173
    def adjust_root_path(self, name, parent):
175
174
        """Emulate moving the root by moving all children, instead.
203
202
        self.version_file(old_root_file_id, old_root)
204
203
        self.unversion_file(self._new_root)
205
204
 
 
205
    def fixup_new_roots(self):
 
206
        """Reinterpret requests to change the root directory
 
207
 
 
208
        Instead of creating a root directory, or moving an existing directory,
 
209
        all the attributes and children of the new root are applied to the
 
210
        existing root directory.
 
211
 
 
212
        This means that the old root trans-id becomes obsolete, so it is
 
213
        recommended only to invoke this after the root trans-id has become
 
214
        irrelevant.
 
215
        """
 
216
        new_roots = [k for k, v in self._new_parent.iteritems() if v is
 
217
                     ROOT_PARENT]
 
218
        if len(new_roots) < 1:
 
219
            return
 
220
        if len(new_roots) != 1:
 
221
            raise ValueError('A tree cannot have two roots!')
 
222
        if self._new_root is None:
 
223
            self._new_root = new_roots[0]
 
224
            return
 
225
        old_new_root = new_roots[0]
 
226
        # TODO: What to do if a old_new_root is present, but self._new_root is
 
227
        #       not listed as being removed? This code explicitly unversions
 
228
        #       the old root and versions it with the new file_id. Though that
 
229
        #       seems like an incomplete delta
 
230
 
 
231
        # unversion the new root's directory.
 
232
        file_id = self.final_file_id(old_new_root)
 
233
        if old_new_root in self._new_id:
 
234
            self.cancel_versioning(old_new_root)
 
235
        else:
 
236
            self.unversion_file(old_new_root)
 
237
        # if, at this stage, root still has an old file_id, zap it so we can
 
238
        # stick a new one in.
 
239
        if (self.tree_file_id(self._new_root) is not None and
 
240
            self._new_root not in self._removed_id):
 
241
            self.unversion_file(self._new_root)
 
242
        self.version_file(file_id, self._new_root)
 
243
 
 
244
        # Now move children of new root into old root directory.
 
245
        # Ensure all children are registered with the transaction, but don't
 
246
        # use directly-- some tree children have new parents
 
247
        list(self.iter_tree_children(old_new_root))
 
248
        # Move all children of new root into old root directory.
 
249
        for child in self.by_parent().get(old_new_root, []):
 
250
            self.adjust_path(self.final_name(child), self._new_root, child)
 
251
 
 
252
        # Ensure old_new_root has no directory.
 
253
        if old_new_root in self._new_contents:
 
254
            self.cancel_creation(old_new_root)
 
255
        else:
 
256
            self.delete_contents(old_new_root)
 
257
 
 
258
        # prevent deletion of root directory.
 
259
        if self._new_root in self._removed_contents:
 
260
            self.cancel_deletion(self._new_root)
 
261
 
 
262
        # destroy path info for old_new_root.
 
263
        del self._new_parent[old_new_root]
 
264
        del self._new_name[old_new_root]
 
265
 
206
266
    def trans_id_tree_file_id(self, inventory_id):
207
267
        """Determine the transaction id of a working tree file.
208
268
 
254
314
 
255
315
    def delete_contents(self, trans_id):
256
316
        """Schedule the contents of a path entry for deletion"""
 
317
        # Ensure that the object exists in the WorkingTree, this will raise an
 
318
        # exception if there is a problem
257
319
        self.tree_kind(trans_id)
258
320
        self._removed_contents.add(trans_id)
259
321
 
1001
1063
class DiskTreeTransform(TreeTransformBase):
1002
1064
    """Tree transform storing its contents on disk."""
1003
1065
 
1004
 
    def __init__(self, tree, limbodir, pb=DummyProgress(),
 
1066
    def __init__(self, tree, limbodir, pb=None,
1005
1067
                 case_sensitive=True):
1006
1068
        """Constructor.
1007
1069
        :param tree: The tree that will be transformed, but not necessarily
1008
1070
            the output tree.
1009
1071
        :param limbodir: A directory where new files can be stored until
1010
1072
            they are installed in their proper places
1011
 
        :param pb: A ProgressBar indicating how much progress is being made
 
1073
        :param pb: ignored
1012
1074
        :param case_sensitive: If True, the target of the transform is
1013
1075
            case sensitive, not just case preserving.
1014
1076
        """
1077
1139
        if (trans_id in self._limbo_files and
1078
1140
            trans_id not in self._needs_rename):
1079
1141
            self._rename_in_limbo([trans_id])
1080
 
            self._limbo_children[previous_parent].remove(trans_id)
1081
 
            del self._limbo_children_names[previous_parent][previous_name]
 
1142
            if previous_parent != parent:
 
1143
                self._limbo_children[previous_parent].remove(trans_id)
 
1144
            if previous_parent != parent or previous_name != name:
 
1145
                del self._limbo_children_names[previous_parent][previous_name]
1082
1146
 
1083
1147
    def _rename_in_limbo(self, trans_ids):
1084
1148
        """Fix limbo names so that the right final path is produced.
1096
1160
            if trans_id not in self._new_contents:
1097
1161
                continue
1098
1162
            new_path = self._limbo_name(trans_id)
1099
 
            os.rename(old_path, new_path)
 
1163
            osutils.rename(old_path, new_path)
1100
1164
            for descendant in self._limbo_descendants(trans_id):
1101
1165
                desc_path = self._limbo_files[descendant]
1102
1166
                desc_path = new_path + desc_path[len(old_path):]
1276
1340
    FileMover does not delete files until it is sure that a rollback will not
1277
1341
    happen.
1278
1342
    """
1279
 
    def __init__(self, tree, pb=DummyProgress()):
 
1343
    def __init__(self, tree, pb=None):
1280
1344
        """Note: a tree_write lock is taken on the tree.
1281
1345
 
1282
1346
        Use TreeTransform.finalize() to release the lock (can be omitted if
1565
1629
                child_pb.update('removing file', num, len(tree_paths))
1566
1630
                full_path = self._tree.abspath(path)
1567
1631
                if trans_id in self._removed_contents:
1568
 
                    mover.pre_delete(full_path, os.path.join(self._deletiondir,
1569
 
                                     trans_id))
1570
 
                elif trans_id in self._new_name or trans_id in \
1571
 
                    self._new_parent:
 
1632
                    delete_path = os.path.join(self._deletiondir, trans_id)
 
1633
                    mover.pre_delete(full_path, delete_path)
 
1634
                elif (trans_id in self._new_name
 
1635
                      or trans_id in self._new_parent):
1572
1636
                    try:
1573
1637
                        mover.rename(full_path, self._limbo_name(trans_id))
1574
1638
                    except OSError, e:
1628
1692
    unversioned files in the input tree.
1629
1693
    """
1630
1694
 
1631
 
    def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
 
1695
    def __init__(self, tree, pb=None, case_sensitive=True):
1632
1696
        tree.lock_read()
1633
1697
        limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1634
1698
        DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
2526
2590
 
2527
2591
 
2528
2592
def revert(working_tree, target_tree, filenames, backups=False,
2529
 
           pb=DummyProgress(), change_reporter=None):
 
2593
           pb=None, change_reporter=None):
2530
2594
    """Revert a working tree's contents to those of a target tree."""
2531
2595
    target_tree.lock_read()
 
2596
    pb = ui.ui_factory.nested_progress_bar()
2532
2597
    tt = TreeTransform(working_tree, pb)
2533
2598
    try:
2534
2599
        pp = ProgressPhase("Revert phase", 3, pb)
2553
2618
def _prepare_revert_transform(working_tree, target_tree, tt, filenames,
2554
2619
                              backups, pp, basis_tree=None,
2555
2620
                              merge_modified=None):
2556
 
    pp.next_phase()
2557
2621
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2558
2622
    try:
2559
2623
        if merge_modified is None:
2563
2627
                                      merge_modified, basis_tree)
2564
2628
    finally:
2565
2629
        child_pb.finished()
2566
 
    pp.next_phase()
2567
2630
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2568
2631
    try:
2569
2632
        raw_conflicts = resolve_conflicts(tt, child_pb,
2662
2725
                    parent_trans = ROOT_PARENT
2663
2726
                else:
2664
2727
                    parent_trans = tt.trans_id_file_id(parent[1])
2665
 
                tt.adjust_path(name[1], parent_trans, trans_id)
 
2728
                if parent[0] is None and versioned[0]:
 
2729
                    tt.adjust_root_path(name[1], parent_trans)
 
2730
                else:
 
2731
                    tt.adjust_path(name[1], parent_trans, trans_id)
2666
2732
            if executable[0] != executable[1] and kind[1] == "file":
2667
2733
                tt.set_executability(executable[1], trans_id)
2668
2734
        if working_tree.supports_content_filtering():
2681
2747
            for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2682
2748
                deferred_files):
2683
2749
                tt.create_file(bytes, trans_id, mode_id)
 
2750
        tt.fixup_new_roots()
2684
2751
    finally:
2685
2752
        if basis_tree is not None:
2686
2753
            basis_tree.unlock()
2687
2754
    return merge_modified
2688
2755
 
2689
2756
 
2690
 
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
 
2757
def resolve_conflicts(tt, pb=None, pass_func=None):
2691
2758
    """Make many conflict-resolution attempts, but die if they fail"""
2692
2759
    if pass_func is None:
2693
2760
        pass_func = conflict_pass
2694
2761
    new_conflicts = set()
 
2762
    pb = ui.ui_factory.nested_progress_bar()
2695
2763
    try:
2696
2764
        for n in range(10):
2697
2765
            pb.update('Resolution pass', n+1, 10)
2701
2769
            new_conflicts.update(pass_func(tt, conflicts))
2702
2770
        raise MalformedTransform(conflicts=conflicts)
2703
2771
    finally:
2704
 
        pb.clear()
 
2772
        pb.finished()
2705
2773
 
2706
2774
 
2707
2775
def conflict_pass(tt, conflicts, path_tree=None):
2756
2824
                        # special-case the other tree root (move its
2757
2825
                        # children to current root)
2758
2826
                        if entry.parent_id is None:
2759
 
                            create=False
 
2827
                            create = False
2760
2828
                            moved = _reparent_transform_children(
2761
2829
                                tt, trans_id, tt.root)
2762
2830
                            for child in moved:
2830
2898
        self.pending_deletions = []
2831
2899
 
2832
2900
    def rename(self, from_, to):
2833
 
        """Rename a file from one path to another.  Functions like os.rename"""
 
2901
        """Rename a file from one path to another."""
2834
2902
        try:
2835
 
            os.rename(from_, to)
 
2903
            osutils.rename(from_, to)
2836
2904
        except OSError, e:
2837
2905
            if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
2838
2906
                raise errors.FileExists(to, str(e))
2852
2920
    def rollback(self):
2853
2921
        """Reverse all renames that have been performed"""
2854
2922
        for from_, to in reversed(self.past_renames):
2855
 
            os.rename(to, from_)
 
2923
            osutils.rename(to, from_)
2856
2924
        # after rollback, don't reuse _FileMover
2857
2925
        past_renames = None
2858
2926
        pending_deletions = None