/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: Lukáš Lalinský
  • Date: 2007-12-17 17:28:25 UTC
  • mfrom: (3120 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3123.
  • Revision ID: lalinsky@gmail.com-20071217172825-tr3pqm1mhvs3gwnn
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
""")
30
30
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
31
31
                           ReusingTransform, NotVersionedError, CantMoveRoot,
32
 
                           ExistingLimbo, ImmortalLimbo, NoFinalPath)
 
32
                           ExistingLimbo, ImmortalLimbo, NoFinalPath,
 
33
                           UnableCreateSymlink)
33
34
from bzrlib.inventory import InventoryEntry
34
35
from bzrlib.osutils import (file_kind, supports_executable, pathjoin, lexists,
35
 
                            delete_any)
 
36
                            delete_any, has_symlinks)
36
37
from bzrlib.progress import DummyProgress, ProgressPhase
37
38
from bzrlib.symbol_versioning import (
38
39
        deprecated_function,
89
90
     * create_file or create_directory or create_symlink
90
91
     * version_file
91
92
     * set_executability
 
93
 
 
94
    Transform/Transaction ids
 
95
    -------------------------
 
96
    trans_ids are temporary ids assigned to all files involved in a transform.
 
97
    It's possible, even common, that not all files in the Tree have trans_ids.
 
98
 
 
99
    trans_ids are used because filenames and file_ids are not good enough
 
100
    identifiers; filenames change, and not all files have file_ids.  File-ids
 
101
    are also associated with trans-ids, so that moving a file moves its
 
102
    file-id.
 
103
 
 
104
    trans_ids are only valid for the TreeTransform that generated them.
 
105
 
 
106
    Limbo
 
107
    -----
 
108
    Limbo is a temporary directory use to hold new versions of files.
 
109
    Files are added to limbo by create_file, create_directory, create_symlink,
 
110
    and their convenience variants (new_*).  Files may be removed from limbo
 
111
    using cancel_creation.  Files are renamed from limbo into their final
 
112
    location as part of TreeTransform.apply
 
113
 
 
114
    Limbo must be cleaned up, by either calling TreeTransform.apply or
 
115
    calling TreeTransform.finalize.
 
116
 
 
117
    Files are placed into limbo inside their parent directories, where
 
118
    possible.  This reduces subsequent renames, and makes operations involving
 
119
    lots of files faster.  This optimization is only possible if the parent
 
120
    directory is created *before* creating any of its children, so avoid
 
121
    creating children before parents, where possible.
 
122
 
 
123
    Pending-deletion
 
124
    ----------------
 
125
    This temporary directory is used by _FileMover for storing files that are
 
126
    about to be deleted.  In case of rollback, the files will be restored.
 
127
    FileMover does not delete files until it is sure that a rollback will not
 
128
    happen.  
92
129
    """
93
130
    def __init__(self, tree, pb=DummyProgress()):
94
131
        """Note: a tree_write lock is taken on the tree.
120
157
            self._tree.unlock()
121
158
            raise
122
159
 
 
160
        # counter used to generate trans-ids (which are locally unique)
123
161
        self._id_number = 0
 
162
        # mapping of trans_id -> new basename
124
163
        self._new_name = {}
 
164
        # mapping of trans_id -> new parent trans_id
125
165
        self._new_parent = {}
 
166
        # mapping of trans_id with new contents -> new file_kind
126
167
        self._new_contents = {}
127
168
        # A mapping of transform ids to their limbo filename
128
169
        self._limbo_files = {}
133
174
        self._limbo_children_names = {}
134
175
        # List of transform ids that need to be renamed from limbo into place
135
176
        self._needs_rename = set()
 
177
        # Set of trans_ids whose contents will be removed
136
178
        self._removed_contents = set()
 
179
        # Mapping of trans_id -> new execute-bit value
137
180
        self._new_executability = {}
 
181
        # Mapping of trans_id -> new tree-reference value
138
182
        self._new_reference_revision = {}
 
183
        # Mapping of trans_id -> new file_id
139
184
        self._new_id = {}
 
185
        # Mapping of old file-id -> trans_id
140
186
        self._non_present_ids = {}
 
187
        # Mapping of new file_id -> trans_id
141
188
        self._r_new_id = {}
 
189
        # Set of file_ids that will be removed
142
190
        self._removed_id = set()
 
191
        # Mapping of path in old tree -> trans_id
143
192
        self._tree_path_ids = {}
 
193
        # Mapping trans_id -> path in old tree
144
194
        self._tree_id_paths = {}
145
195
        # Cache of realpath results, to speed up canonical_path
146
196
        self._realpaths = {}
147
197
        # Cache of relpath results, to speed up canonical_path
148
198
        self._relpaths = {}
 
199
        # The trans_id that will be used as the tree root
149
200
        self._new_root = self.trans_id_tree_file_id(tree.get_root_id())
 
201
        # Indictor of whether the transform has been applied
150
202
        self.__done = False
 
203
        # A progress bar
151
204
        self._pb = pb
 
205
        # A counter of how many files have been renamed
152
206
        self.rename_count = 0
153
207
 
154
208
    def __get_root(self):
387
441
        target is a bytestring.
388
442
        See also new_symlink.
389
443
        """
390
 
        os.symlink(target, self._limbo_name(trans_id))
391
 
        unique_add(self._new_contents, trans_id, 'symlink')
 
444
        if has_symlinks():
 
445
            os.symlink(target, self._limbo_name(trans_id))
 
446
            unique_add(self._new_contents, trans_id, 'symlink')
 
447
        else:
 
448
            try:
 
449
                path = FinalPaths(self).get_path(trans_id)
 
450
            except KeyError:
 
451
                path = None
 
452
            raise UnableCreateSymlink(path=path)
392
453
 
393
454
    def cancel_creation(self, trans_id):
394
455
        """Cancel the creation of new file contents."""
495
556
            return None
496
557
        # the file is old; the old id is still valid
497
558
        if self._new_root == trans_id:
498
 
            return self._tree.inventory.root.file_id
 
559
            return self._tree.get_root_id()
499
560
        return self._tree.inventory.path2id(path)
500
561
 
501
562
    def final_file_id(self, trans_id):
737
798
            return conflicts
738
799
        for children in by_parent.itervalues():
739
800
            name_ids = [(self.final_name(t), t) for t in children]
 
801
            if not self._tree.case_sensitive:
 
802
                name_ids = [(n.lower(), t) for n, t in name_ids]
740
803
            name_ids.sort()
741
804
            last_name = None
742
805
            last_trans_id = None
863
926
                # the direct path can only be used if no other file has
864
927
                # already taken this pathname, i.e. if the name is unused, or
865
928
                # if it is already associated with this trans_id.
866
 
                elif (self._limbo_children_names[parent].get(filename)
867
 
                      in (trans_id, None)):
868
 
                    use_direct_path = True
 
929
                elif self._tree.case_sensitive:
 
930
                    if (self._limbo_children_names[parent].get(filename)
 
931
                        in (trans_id, None)):
 
932
                        use_direct_path = True
 
933
                else:
 
934
                    for l_filename, l_trans_id in\
 
935
                        self._limbo_children_names[parent].iteritems():
 
936
                        if l_trans_id == trans_id:
 
937
                            continue
 
938
                        if l_filename.lower() == filename.lower():
 
939
                            break
 
940
                    else:
 
941
                        use_direct_path = True
 
942
 
869
943
        if use_direct_path:
870
944
            limbo_name = pathjoin(self._limbo_files[parent], filename)
871
945
            self._limbo_children[parent].add(trans_id)
905
979
                        self.rename_count += 1
906
980
                if trans_id in self._removed_id:
907
981
                    if trans_id == self._new_root:
908
 
                        file_id = self._tree.inventory.root.file_id
 
982
                        file_id = self._tree.get_root_id()
909
983
                    else:
910
984
                        file_id = self.tree_file_id(trans_id)
911
985
                    assert file_id is not None
923
997
        new_paths = self.new_paths()
924
998
        modified_paths = []
925
999
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1000
        completed_new = []
926
1001
        try:
927
1002
            for num, (path, trans_id) in enumerate(new_paths):
928
1003
                new_entry = None
945
1020
                            self.rename_count += 1
946
1021
                    if trans_id in self._new_contents:
947
1022
                        modified_paths.append(full_path)
948
 
                        del self._new_contents[trans_id]
 
1023
                        completed_new.append(trans_id)
949
1024
 
950
1025
                if trans_id in self._new_id:
951
1026
                    if kind is None:
990
1065
                                            new_entry))
991
1066
        finally:
992
1067
            child_pb.finished()
 
1068
        for trans_id in completed_new:
 
1069
            del self._new_contents[trans_id]
993
1070
        return modified_paths
994
1071
 
995
1072
    def _set_executability(self, path, entry, trans_id):
1237
1314
            self._known_paths[trans_id] = self._determine_path(trans_id)
1238
1315
        return self._known_paths[trans_id]
1239
1316
 
 
1317
 
1240
1318
def topology_sorted_ids(tree):
1241
1319
    """Determine the topological order of the ids in a tree"""
1242
1320
    file_ids = list(tree)
1268
1346
    finally:
1269
1347
        wt.unlock()
1270
1348
 
 
1349
 
1271
1350
def _build_tree(tree, wt):
1272
1351
    """See build_tree."""
1273
1352
    if len(wt.inventory) > 1:  # more than just a root
1283
1362
        # is set within the tree, nor setting the root and thus
1284
1363
        # marking the tree as dirty, because we use two different
1285
1364
        # idioms here: tree interfaces and inventory interfaces.
1286
 
        if wt.path2id('') != tree.inventory.root.file_id:
1287
 
            wt.set_root_id(tree.inventory.root.file_id)
 
1365
        if wt.get_root_id() != tree.get_root_id():
 
1366
            wt.set_root_id(tree.get_root_id())
1288
1367
            wt.flush()
1289
1368
    tt = TreeTransform(wt)
1290
1369
    divert = set()
1438
1517
    else:
1439
1518
        raise errors.BadFileKindError(name, kind)
1440
1519
 
 
1520
 
1441
1521
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
1442
1522
    """Create new file contents according to an inventory entry."""
1443
1523
    if entry.kind == "file":
1449
1529
    elif entry.kind == "directory":
1450
1530
        tt.create_directory(trans_id)
1451
1531
 
 
1532
 
1452
1533
def create_entry_executability(tt, entry, trans_id):
1453
1534
    """Set the executability of a trans_id according to an inventory entry"""
1454
1535
    if entry.kind == "file":
1576
1657
        pp.next_phase()
1577
1658
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1578
1659
        try:
1579
 
            raw_conflicts = resolve_conflicts(tt, child_pb)
 
1660
            raw_conflicts = resolve_conflicts(tt, child_pb,
 
1661
                lambda t, c: conflict_pass(t, c, target_tree))
1580
1662
        finally:
1581
1663
            child_pb.finished()
1582
1664
        conflicts = cook_conflicts(raw_conflicts, tt)
1721
1803
                               conflict[1], conflict[2], ))
1722
1804
        elif c_type == 'duplicate':
1723
1805
            # files that were renamed take precedence
1724
 
            new_name = tt.final_name(conflict[1])+'.moved'
1725
1806
            final_parent = tt.final_parent(conflict[1])
1726
1807
            if tt.path_changed(conflict[1]):
1727
 
                tt.adjust_path(new_name, final_parent, conflict[2])
1728
 
                new_conflicts.add((c_type, 'Moved existing file to', 
1729
 
                                   conflict[2], conflict[1]))
 
1808
                existing_file, new_file = conflict[2], conflict[1]
1730
1809
            else:
1731
 
                tt.adjust_path(new_name, final_parent, conflict[1])
1732
 
                new_conflicts.add((c_type, 'Moved existing file to', 
1733
 
                                  conflict[1], conflict[2]))
 
1810
                existing_file, new_file = conflict[1], conflict[2]
 
1811
            new_name = tt.final_name(existing_file)+'.moved'
 
1812
            tt.adjust_path(new_name, final_parent, existing_file)
 
1813
            new_conflicts.add((c_type, 'Moved existing file to', 
 
1814
                               existing_file, new_file))
1734
1815
        elif c_type == 'parent loop':
1735
1816
            # break the loop by undoing one of the ops that caused the loop
1736
1817
            cur = conflict[1]
1752
1833
                try:
1753
1834
                    tt.final_name(trans_id)
1754
1835
                except NoFinalPath:
1755
 
                    file_id = tt.final_file_id(trans_id)
1756
 
                    entry = path_tree.inventory[file_id]
1757
 
                    parent_trans_id = tt.trans_id_file_id(entry.parent_id)
1758
 
                    tt.adjust_path(entry.name, parent_trans_id, trans_id)
 
1836
                    if path_tree is not None:
 
1837
                        file_id = tt.final_file_id(trans_id)
 
1838
                        entry = path_tree.inventory[file_id]
 
1839
                        parent_trans_id = tt.trans_id_file_id(entry.parent_id)
 
1840
                        tt.adjust_path(entry.name, parent_trans_id, trans_id)
1759
1841
        elif c_type == 'unversioned parent':
1760
1842
            tt.version_file(tt.inactive_file_id(conflict[1]), conflict[1])
1761
1843
            new_conflicts.add((c_type, 'Versioned directory', conflict[1]))
1799
1881
 
1800
1882
    def rename(self, from_, to):
1801
1883
        """Rename a file from one path to another.  Functions like os.rename"""
1802
 
        os.rename(from_, to)
 
1884
        try:
 
1885
            os.rename(from_, to)
 
1886
        except OSError, e:
 
1887
            if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
 
1888
                raise errors.FileExists(to, str(e))
 
1889
            raise
1803
1890
        self.past_renames.append((from_, to))
1804
1891
 
1805
1892
    def pre_delete(self, from_, to):