/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

Merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
108
108
            except OSError, e:
109
109
                if e.errno == errno.EEXIST:
110
110
                    raise ExistingLimbo(self._limbodir)
 
111
            self._deletiondir = urlutils.local_path_from_url(
 
112
                control_files.controlfilename('pending-deletion'))
 
113
            try:
 
114
                os.mkdir(self._deletiondir)
 
115
            except OSError, e:
 
116
                if e.errno == errno.EEXIST:
 
117
                    raise errors.ExistingPendingDeletion(self._deletiondir)
 
118
 
111
119
        except: 
112
120
            self._tree.unlock()
113
121
            raise
170
178
            except OSError:
171
179
                # We don't especially care *why* the dir is immortal.
172
180
                raise ImmortalLimbo(self._limbodir)
 
181
            try:
 
182
                os.rmdir(self._deletiondir)
 
183
            except OSError:
 
184
                raise errors.ImmortalPendingDeletion(self._deletiondir)
173
185
        finally:
174
186
            self._tree.unlock()
175
187
            self._tree = None
789
801
            return True
790
802
        return False
791
803
            
792
 
    def apply(self, no_conflicts=False):
 
804
    def apply(self, no_conflicts=False, _mover=None):
793
805
        """Apply all changes to the inventory and filesystem.
794
806
        
795
807
        If filesystem or inventory conflicts are present, MalformedTransform
799
811
 
800
812
        :param no_conflicts: if True, the caller guarantees there are no
801
813
            conflicts, so no check is made.
 
814
        :param _mover: Supply an alternate FileMover, for testing
802
815
        """
803
816
        if not no_conflicts:
804
817
            conflicts = self.find_conflicts()
808
821
        inventory_delta = []
809
822
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
810
823
        try:
811
 
            child_pb.update('Apply phase', 0, 2)
812
 
            self._apply_removals(inv, inventory_delta)
813
 
            child_pb.update('Apply phase', 1, 2)
814
 
            modified_paths = self._apply_insertions(inv, inventory_delta)
 
824
            if _mover is None:
 
825
                mover = _FileMover()
 
826
            else:
 
827
                mover = _mover
 
828
            try:
 
829
                child_pb.update('Apply phase', 0, 2)
 
830
                self._apply_removals(inv, inventory_delta, mover)
 
831
                child_pb.update('Apply phase', 1, 2)
 
832
                modified_paths = self._apply_insertions(inv, inventory_delta,
 
833
                                                        mover)
 
834
            except:
 
835
                mover.rollback()
 
836
                raise
 
837
            else:
 
838
                mover.apply_deletions()
815
839
        finally:
816
840
            child_pb.finished()
817
841
        self._tree.apply_inventory_delta(inventory_delta)
852
876
        self._limbo_files[trans_id] = limbo_name
853
877
        return limbo_name
854
878
 
855
 
    def _apply_removals(self, inv, inventory_delta):
 
879
    def _apply_removals(self, inv, inventory_delta, mover):
856
880
        """Perform tree operations that remove directory/inventory names.
857
881
        
858
882
        That is, delete files that are to be deleted, and put any files that
868
892
                child_pb.update('removing file', num, len(tree_paths))
869
893
                full_path = self._tree.abspath(path)
870
894
                if trans_id in self._removed_contents:
871
 
                    delete_any(full_path)
 
895
                    mover.pre_delete(full_path, os.path.join(self._deletiondir,
 
896
                                     trans_id))
872
897
                elif trans_id in self._new_name or trans_id in \
873
898
                    self._new_parent:
874
899
                    try:
875
 
                        os.rename(full_path, self._limbo_name(trans_id))
 
900
                        mover.rename(full_path, self._limbo_name(trans_id))
876
901
                    except OSError, e:
877
902
                        if e.errno != errno.ENOENT:
878
903
                            raise
888
913
        finally:
889
914
            child_pb.finished()
890
915
 
891
 
    def _apply_insertions(self, inv, inventory_delta):
 
916
    def _apply_insertions(self, inv, inventory_delta, mover):
892
917
        """Perform tree operations that insert directory/inventory names.
893
918
        
894
919
        That is, create any files that need to be created, and restore from
911
936
                    full_path = self._tree.abspath(path)
912
937
                    if trans_id in self._needs_rename:
913
938
                        try:
914
 
                            os.rename(self._limbo_name(trans_id), full_path)
 
939
                            mover.rename(self._limbo_name(trans_id), full_path)
915
940
                        except OSError, e:
916
941
                            # We may be renaming a dangling inventory id
917
942
                            if e.errno != errno.ENOENT:
1763
1788
                                   file_id=modified_id, 
1764
1789
                                   conflict_path=conflicting_path,
1765
1790
                                   conflict_file_id=conflicting_id)
 
1791
 
 
1792
 
 
1793
class _FileMover(object):
 
1794
    """Moves and deletes files for TreeTransform, tracking operations"""
 
1795
 
 
1796
    def __init__(self):
 
1797
        self.past_renames = []
 
1798
        self.pending_deletions = []
 
1799
 
 
1800
    def rename(self, from_, to):
 
1801
        """Rename a file from one path to another.  Functions like os.rename"""
 
1802
        os.rename(from_, to)
 
1803
        self.past_renames.append((from_, to))
 
1804
 
 
1805
    def pre_delete(self, from_, to):
 
1806
        """Rename a file out of the way and mark it for deletion.
 
1807
 
 
1808
        Unlike os.unlink, this works equally well for files and directories.
 
1809
        :param from_: The current file path
 
1810
        :param to: A temporary path for the file
 
1811
        """
 
1812
        self.rename(from_, to)
 
1813
        self.pending_deletions.append(to)
 
1814
 
 
1815
    def rollback(self):
 
1816
        """Reverse all renames that have been performed"""
 
1817
        for from_, to in reversed(self.past_renames):
 
1818
            os.rename(to, from_)
 
1819
        # after rollback, don't reuse _FileMover
 
1820
        past_renames = None
 
1821
        pending_deletions = None
 
1822
 
 
1823
    def apply_deletions(self):
 
1824
        """Apply all marked deletions"""
 
1825
        for path in self.pending_deletions:
 
1826
            delete_any(path)
 
1827
        # after apply_deletions, don't reuse _FileMover
 
1828
        past_renames = None
 
1829
        pending_deletions = None