~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-01-12 07:32:41 UTC
  • mfrom: (4948.2.3 504842-subprocess-error)
  • Revision ID: pqm@pqm.ubuntu.com-20100112073241-u9oi9nbq4og7senr
(mbp) better message when failing to start message editor

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008, 2009 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,
35
34
    )
36
35
""")
37
36
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
50
49
    splitpath,
51
50
    supports_executable,
52
51
)
53
 
from bzrlib.progress import ProgressPhase
 
52
from bzrlib.progress import DummyProgress, ProgressPhase
54
53
from bzrlib.symbol_versioning import (
55
54
        deprecated_function,
56
55
        deprecated_in,
80
79
class TreeTransformBase(object):
81
80
    """The base class for TreeTransform and its kin."""
82
81
 
83
 
    def __init__(self, tree, pb=None,
 
82
    def __init__(self, tree, pb=DummyProgress(),
84
83
                 case_sensitive=True):
85
84
        """Constructor.
86
85
 
87
86
        :param tree: The tree that will be transformed, but not necessarily
88
87
            the output tree.
89
 
        :param pb: ignored
 
88
        :param pb: A ProgressTask indicating how much progress is being made
90
89
        :param case_sensitive: If True, the target of the transform is
91
90
            case sensitive, not just case preserving.
92
91
        """
163
162
 
164
163
    def adjust_path(self, name, parent, trans_id):
165
164
        """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")
168
165
        if trans_id == self._new_root:
169
166
            raise CantMoveRoot
170
167
        self._new_name[trans_id] = name
171
168
        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
172
173
 
173
174
    def adjust_root_path(self, name, parent):
174
175
        """Emulate moving the root by moving all children, instead.
202
203
        self.version_file(old_root_file_id, old_root)
203
204
        self.unversion_file(self._new_root)
204
205
 
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
 
 
266
206
    def trans_id_tree_file_id(self, inventory_id):
267
207
        """Determine the transaction id of a working tree file.
268
208
 
314
254
 
315
255
    def delete_contents(self, trans_id):
316
256
        """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
319
257
        self.tree_kind(trans_id)
320
258
        self._removed_contents.add(trans_id)
321
259
 
1063
1001
class DiskTreeTransform(TreeTransformBase):
1064
1002
    """Tree transform storing its contents on disk."""
1065
1003
 
1066
 
    def __init__(self, tree, limbodir, pb=None,
 
1004
    def __init__(self, tree, limbodir, pb=DummyProgress(),
1067
1005
                 case_sensitive=True):
1068
1006
        """Constructor.
1069
1007
        :param tree: The tree that will be transformed, but not necessarily
1070
1008
            the output tree.
1071
1009
        :param limbodir: A directory where new files can be stored until
1072
1010
            they are installed in their proper places
1073
 
        :param pb: ignored
 
1011
        :param pb: A ProgressBar indicating how much progress is being made
1074
1012
        :param case_sensitive: If True, the target of the transform is
1075
1013
            case sensitive, not just case preserving.
1076
1014
        """
1139
1077
        if (trans_id in self._limbo_files and
1140
1078
            trans_id not in self._needs_rename):
1141
1079
            self._rename_in_limbo([trans_id])
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]
 
1080
            self._limbo_children[previous_parent].remove(trans_id)
 
1081
            del self._limbo_children_names[previous_parent][previous_name]
1146
1082
 
1147
1083
    def _rename_in_limbo(self, trans_ids):
1148
1084
        """Fix limbo names so that the right final path is produced.
1340
1276
    FileMover does not delete files until it is sure that a rollback will not
1341
1277
    happen.
1342
1278
    """
1343
 
    def __init__(self, tree, pb=None):
 
1279
    def __init__(self, tree, pb=DummyProgress()):
1344
1280
        """Note: a tree_write lock is taken on the tree.
1345
1281
 
1346
1282
        Use TreeTransform.finalize() to release the lock (can be omitted if
1629
1565
                child_pb.update('removing file', num, len(tree_paths))
1630
1566
                full_path = self._tree.abspath(path)
1631
1567
                if trans_id in self._removed_contents:
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):
 
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:
1636
1572
                    try:
1637
1573
                        mover.rename(full_path, self._limbo_name(trans_id))
1638
1574
                    except OSError, e:
1692
1628
    unversioned files in the input tree.
1693
1629
    """
1694
1630
 
1695
 
    def __init__(self, tree, pb=None, case_sensitive=True):
 
1631
    def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
1696
1632
        tree.lock_read()
1697
1633
        limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1698
1634
        DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
2590
2526
 
2591
2527
 
2592
2528
def revert(working_tree, target_tree, filenames, backups=False,
2593
 
           pb=None, change_reporter=None):
 
2529
           pb=DummyProgress(), change_reporter=None):
2594
2530
    """Revert a working tree's contents to those of a target tree."""
2595
2531
    target_tree.lock_read()
2596
 
    pb = ui.ui_factory.nested_progress_bar()
2597
2532
    tt = TreeTransform(working_tree, pb)
2598
2533
    try:
2599
2534
        pp = ProgressPhase("Revert phase", 3, pb)
2618
2553
def _prepare_revert_transform(working_tree, target_tree, tt, filenames,
2619
2554
                              backups, pp, basis_tree=None,
2620
2555
                              merge_modified=None):
 
2556
    pp.next_phase()
2621
2557
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2622
2558
    try:
2623
2559
        if merge_modified is None:
2627
2563
                                      merge_modified, basis_tree)
2628
2564
    finally:
2629
2565
        child_pb.finished()
 
2566
    pp.next_phase()
2630
2567
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2631
2568
    try:
2632
2569
        raw_conflicts = resolve_conflicts(tt, child_pb,
2725
2662
                    parent_trans = ROOT_PARENT
2726
2663
                else:
2727
2664
                    parent_trans = tt.trans_id_file_id(parent[1])
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)
 
2665
                tt.adjust_path(name[1], parent_trans, trans_id)
2732
2666
            if executable[0] != executable[1] and kind[1] == "file":
2733
2667
                tt.set_executability(executable[1], trans_id)
2734
2668
        if working_tree.supports_content_filtering():
2747
2681
            for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2748
2682
                deferred_files):
2749
2683
                tt.create_file(bytes, trans_id, mode_id)
2750
 
        tt.fixup_new_roots()
2751
2684
    finally:
2752
2685
        if basis_tree is not None:
2753
2686
            basis_tree.unlock()
2754
2687
    return merge_modified
2755
2688
 
2756
2689
 
2757
 
def resolve_conflicts(tt, pb=None, pass_func=None):
 
2690
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
2758
2691
    """Make many conflict-resolution attempts, but die if they fail"""
2759
2692
    if pass_func is None:
2760
2693
        pass_func = conflict_pass
2761
2694
    new_conflicts = set()
2762
 
    pb = ui.ui_factory.nested_progress_bar()
2763
2695
    try:
2764
2696
        for n in range(10):
2765
2697
            pb.update('Resolution pass', n+1, 10)
2769
2701
            new_conflicts.update(pass_func(tt, conflicts))
2770
2702
        raise MalformedTransform(conflicts=conflicts)
2771
2703
    finally:
2772
 
        pb.finished()
 
2704
        pb.clear()
2773
2705
 
2774
2706
 
2775
2707
def conflict_pass(tt, conflicts, path_tree=None):