/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
3008.1.29 by Aaron Bentley
Add docstrings, rename TT.__doc
1
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
17
import os
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
18
import errno
1534.7.117 by Aaron Bentley
Simplified permission handling of existing files in transform.
19
from stat import S_ISREG
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
20
import tempfile
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
21
1551.11.12 by Aaron Bentley
Changes from review
22
from bzrlib.lazy_import import lazy_import
23
lazy_import(globals(), """
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
24
from bzrlib import (
25
    bzrdir,
2100.3.31 by Aaron Bentley
Merged bzr.dev (17 tests failing)
26
    delta,
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
27
    errors,
28
    inventory
29
    )
1551.11.12 by Aaron Bentley
Changes from review
30
""")
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
31
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
32
                           ReusingTransform, NotVersionedError, CantMoveRoot,
3006.2.1 by Alexander Belchenko
workaround for bug #81689: give a proper error message instead of traceback when symlink cannot be created (e.g. on Windows)
33
                           ExistingLimbo, ImmortalLimbo, NoFinalPath,
34
                           UnableCreateSymlink)
3368.2.3 by Ian Clatworthy
tests for ..input_file and ..output_lines
35
from bzrlib.filters import filtered_output_lines, filters_for_path
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
36
from bzrlib.inventory import InventoryEntry
1558.12.9 by Aaron Bentley
Handle resolving conflicts with directories properly
37
from bzrlib.osutils import (file_kind, supports_executable, pathjoin, lexists,
3006.2.1 by Alexander Belchenko
workaround for bug #81689: give a proper error message instead of traceback when symlink cannot be created (e.g. on Windows)
38
                            delete_any, has_symlinks)
1551.2.34 by Aaron Bentley
Refactored the revert phases
39
from bzrlib.progress import DummyProgress, ProgressPhase
2687.2.1 by Martin Pool
Rename upcoming release from 0.19 to 0.90
40
from bzrlib.symbol_versioning import (
41
        deprecated_function,
42
        zero_fifteen,
43
        zero_ninety,
44
        )
1534.7.173 by Aaron Bentley
Added conflict warnings to revert
45
from bzrlib.trace import mutter, warning
1551.7.14 by Aaron Bentley
Use specified_file_ids instead of is_inside_any in compare_trees
46
from bzrlib import tree
2255.2.152 by Martin Pool
(broken) merge aaron's workingtree format changes
47
import bzrlib.ui
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
48
import bzrlib.urlutils as urlutils
1534.7.31 by Aaron Bentley
Changed tree root parent to ROOT_PARENT
49
1534.7.167 by Aaron Bentley
PEP8 and comment cleanups
50
1534.7.31 by Aaron Bentley
Changed tree root parent to ROOT_PARENT
51
ROOT_PARENT = "root-parent"
52
1534.7.167 by Aaron Bentley
PEP8 and comment cleanups
53
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
54
def unique_add(map, key, value):
55
    if key in map:
1534.7.5 by Aaron Bentley
Got unique_add under test
56
        raise DuplicateKey(key=key)
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
57
    map[key] = value
58
1534.7.167 by Aaron Bentley
PEP8 and comment cleanups
59
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
60
class _TransformResults(object):
2502.1.5 by Aaron Bentley
Cleanup
61
    def __init__(self, modified_paths, rename_count):
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
62
        object.__init__(self)
63
        self.modified_paths = modified_paths
2502.1.5 by Aaron Bentley
Cleanup
64
        self.rename_count = rename_count
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
65
66
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
67
class TreeTransformBase(object):
3008.1.29 by Aaron Bentley
Add docstrings, rename TT.__doc
68
    """The base class for TreeTransform and TreeTransformBase"""
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
69
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
70
    def __init__(self, tree, limbodir, pb=DummyProgress(),
71
                 case_sensitive=True):
3008.1.29 by Aaron Bentley
Add docstrings, rename TT.__doc
72
        """Constructor.
73
74
        :param tree: The tree that will be transformed, but not necessarily
75
            the output tree.
76
        :param limbodir: A directory where new files can be stored until
77
            they are installed in their proper places
78
        :param pb: A ProgressBar indicating how much progress is being made
79
        :param case_sensitive: If True, the target of the transform is
80
            case sensitive, not just case preserving.
81
        """
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
82
        object.__init__(self)
83
        self._tree = tree
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
84
        self._limbodir = limbodir
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
85
        self._deletiondir = None
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
86
        self._id_number = 0
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
87
        # mapping of trans_id -> new basename
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
88
        self._new_name = {}
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
89
        # mapping of trans_id -> new parent trans_id
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
90
        self._new_parent = {}
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
91
        # mapping of trans_id with new contents -> new file_kind
1534.7.4 by Aaron Bentley
Unified all file types as 'contents'
92
        self._new_contents = {}
2502.1.5 by Aaron Bentley
Cleanup
93
        # A mapping of transform ids to their limbo filename
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
94
        self._limbo_files = {}
2502.1.5 by Aaron Bentley
Cleanup
95
        # A mapping of transform ids to a set of the transform ids of children
2502.1.6 by Aaron Bentley
Update from review comments
96
        # that their limbo directory has
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
97
        self._limbo_children = {}
2502.1.5 by Aaron Bentley
Cleanup
98
        # Map transform ids to maps of child filename to child transform id
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
99
        self._limbo_children_names = {}
2502.1.5 by Aaron Bentley
Cleanup
100
        # List of transform ids that need to be renamed from limbo into place
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
101
        self._needs_rename = set()
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
102
        # Set of trans_ids whose contents will be removed
1534.7.34 by Aaron Bentley
Proper conflicts for removals
103
        self._removed_contents = set()
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
104
        # Mapping of trans_id -> new execute-bit value
1534.7.25 by Aaron Bentley
Added set_executability
105
        self._new_executability = {}
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
106
        # Mapping of trans_id -> new tree-reference value
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
107
        self._new_reference_revision = {}
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
108
        # Mapping of trans_id -> new file_id
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
109
        self._new_id = {}
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
110
        # Mapping of old file-id -> trans_id
1534.7.143 by Aaron Bentley
Prevented get_trans_id from automatically versioning file ids
111
        self._non_present_ids = {}
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
112
        # Mapping of new file_id -> trans_id
1534.7.75 by Aaron Bentley
Added reverse-lookup for versioned files and get_trans_id
113
        self._r_new_id = {}
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
114
        # Set of file_ids that will be removed
1534.7.39 by Aaron Bentley
Ensured that files can be unversioned (de-versioned?)
115
        self._removed_id = set()
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
116
        # Mapping of path in old tree -> trans_id
1534.7.7 by Aaron Bentley
Added support for all-file path ids
117
        self._tree_path_ids = {}
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
118
        # Mapping trans_id -> path in old tree
1534.7.8 by Aaron Bentley
Added TreeTransform.final_kind
119
        self._tree_id_paths = {}
2502.1.5 by Aaron Bentley
Cleanup
120
        # Cache of realpath results, to speed up canonical_path
1534.10.31 by Aaron Bentley
Add caching to speed canonical_path
121
        self._realpaths = {}
2502.1.5 by Aaron Bentley
Cleanup
122
        # Cache of relpath results, to speed up canonical_path
1534.10.31 by Aaron Bentley
Add caching to speed canonical_path
123
        self._relpaths = {}
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
124
        # The trans_id that will be used as the tree root
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
125
        self._new_root = self.trans_id_tree_file_id(tree.get_root_id())
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
126
        # Indictor of whether the transform has been applied
3008.1.29 by Aaron Bentley
Add docstrings, rename TT.__doc
127
        self._done = False
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
128
        # A progress bar
1534.9.1 by Aaron Bentley
Added progress bars to merge
129
        self._pb = pb
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
130
        # Whether the target is case sensitive
131
        self._case_sensitive_target = case_sensitive
1551.19.1 by Aaron Bentley
Documentation update for TreeTransform, suggested by Michael Hudson
132
        # A counter of how many files have been renamed
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
133
        self.rename_count = 0
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
134
1534.7.132 by Aaron Bentley
Got cooked conflicts working
135
    def __get_root(self):
136
        return self._new_root
137
138
    root = property(__get_root)
139
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
140
    def finalize(self):
2502.1.6 by Aaron Bentley
Update from review comments
141
        """Release the working tree lock, if held, clean up limbo dir.
142
143
        This is required if apply has not been invoked, but can be invoked
144
        even after apply.
145
        """
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
146
        if self._tree is None:
147
            return
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
148
        try:
2502.1.7 by Aaron Bentley
Fix finalize deletion ordering
149
            entries = [(self._limbo_name(t), t, k) for t, k in
150
                       self._new_contents.iteritems()]
2502.1.8 by Aaron Bentley
Updates from review comments
151
            entries.sort(reverse=True)
152
            for path, trans_id, kind in entries:
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
153
                if kind == "directory":
154
                    os.rmdir(path)
155
                else:
156
                    os.unlink(path)
157
            try:
158
                os.rmdir(self._limbodir)
159
            except OSError:
160
                # We don't especially care *why* the dir is immortal.
161
                raise ImmortalLimbo(self._limbodir)
2733.2.11 by Aaron Bentley
Detect irregularities with the pending-deletion directory
162
            try:
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
163
                if self._deletiondir is not None:
164
                    os.rmdir(self._deletiondir)
2733.2.11 by Aaron Bentley
Detect irregularities with the pending-deletion directory
165
            except OSError:
166
                raise errors.ImmortalPendingDeletion(self._deletiondir)
1534.7.162 by Aaron Bentley
Handle failures creating/deleting the Limbo directory
167
        finally:
168
            self._tree.unlock()
169
            self._tree = None
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
170
171
    def _assign_id(self):
172
        """Produce a new tranform id"""
173
        new_id = "new-%s" % self._id_number
174
        self._id_number +=1
175
        return new_id
176
177
    def create_path(self, name, parent):
178
        """Assign a transaction id to a new path"""
179
        trans_id = self._assign_id()
180
        unique_add(self._new_name, trans_id, name)
181
        unique_add(self._new_parent, trans_id, parent)
182
        return trans_id
183
1534.7.6 by Aaron Bentley
Added conflict handling
184
    def adjust_path(self, name, parent, trans_id):
1534.7.21 by Aaron Bentley
Updated docstrings
185
        """Change the path that is assigned to a transaction id."""
1534.7.66 by Aaron Bentley
Ensured we don't accidentally move the root directory
186
        if trans_id == self._new_root:
187
            raise CantMoveRoot
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
188
        previous_parent = self._new_parent.get(trans_id)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
189
        previous_name = self._new_name.get(trans_id)
1534.7.6 by Aaron Bentley
Added conflict handling
190
        self._new_name[trans_id] = name
191
        self._new_parent[trans_id] = parent
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
192
        if (trans_id in self._limbo_files and
193
            trans_id not in self._needs_rename):
194
            self._rename_in_limbo([trans_id])
195
            self._limbo_children[previous_parent].remove(trans_id)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
196
            del self._limbo_children_names[previous_parent][previous_name]
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
197
198
    def _rename_in_limbo(self, trans_ids):
2502.1.6 by Aaron Bentley
Update from review comments
199
        """Fix limbo names so that the right final path is produced.
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
200
201
        This means we outsmarted ourselves-- we tried to avoid renaming
2502.1.6 by Aaron Bentley
Update from review comments
202
        these files later by creating them with their final names in their
203
        final parents.  But now the previous name or parent is no longer
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
204
        suitable, so we have to rename them.
2502.1.14 by Aaron Bentley
Style update suggested by Robert
205
206
        Even for trans_ids that have no new contents, we must remove their
207
        entries from _limbo_files, because they are now stale.
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
208
        """
209
        for trans_id in trans_ids:
2502.1.14 by Aaron Bentley
Style update suggested by Robert
210
            old_path = self._limbo_files.pop(trans_id)
2502.1.13 by Aaron Bentley
Updates from review
211
            if trans_id not in self._new_contents:
212
                continue
2502.1.14 by Aaron Bentley
Style update suggested by Robert
213
            new_path = self._limbo_name(trans_id)
2502.1.13 by Aaron Bentley
Updates from review
214
            os.rename(old_path, new_path)
1534.7.6 by Aaron Bentley
Added conflict handling
215
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
216
    def adjust_root_path(self, name, parent):
217
        """Emulate moving the root by moving all children, instead.
218
        
219
        We do this by undoing the association of root's transaction id with the
220
        current tree.  This allows us to create a new directory with that
1534.7.69 by Aaron Bentley
Got real root moves working
221
        transaction id.  We unversion the root directory and version the 
222
        physically new directory, and hope someone versions the tree root
223
        later.
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
224
        """
225
        old_root = self._new_root
226
        old_root_file_id = self.final_file_id(old_root)
227
        # force moving all children of root
228
        for child_id in self.iter_tree_children(old_root):
229
            if child_id != parent:
230
                self.adjust_path(self.final_name(child_id), 
231
                                 self.final_parent(child_id), child_id)
1534.7.69 by Aaron Bentley
Got real root moves working
232
            file_id = self.final_file_id(child_id)
233
            if file_id is not None:
234
                self.unversion_file(child_id)
235
            self.version_file(file_id, child_id)
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
236
        
237
        # the physical root needs a new transaction id
238
        self._tree_path_ids.pop("")
239
        self._tree_id_paths.pop(old_root)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
240
        self._new_root = self.trans_id_tree_file_id(self._tree.get_root_id())
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
241
        if parent == old_root:
242
            parent = self._new_root
243
        self.adjust_path(name, parent, old_root)
244
        self.create_directory(old_root)
1534.7.69 by Aaron Bentley
Got real root moves working
245
        self.version_file(old_root_file_id, old_root)
246
        self.unversion_file(self._new_root)
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
247
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
248
    def trans_id_tree_file_id(self, inventory_id):
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
249
        """Determine the transaction id of a working tree file.
250
        
251
        This reflects only files that already exist, not ones that will be
252
        added by transactions.
253
        """
3146.8.1 by Aaron Bentley
Fix two tree.inventory uses in checkout
254
        path = self._tree.id2path(inventory_id)
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
255
        return self.trans_id_tree_path(path)
1534.7.7 by Aaron Bentley
Added support for all-file path ids
256
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
257
    def trans_id_file_id(self, file_id):
1534.7.156 by Aaron Bentley
PEP8 fixes
258
        """Determine or set the transaction id associated with a file ID.
1534.7.75 by Aaron Bentley
Added reverse-lookup for versioned files and get_trans_id
259
        A new id is only created for file_ids that were never present.  If
260
        a transaction has been unversioned, it is deliberately still returned.
261
        (this will likely lead to an unversioned parent conflict.)
262
        """
263
        if file_id in self._r_new_id and self._r_new_id[file_id] is not None:
264
            return self._r_new_id[file_id]
265
        elif file_id in self._tree.inventory:
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
266
            return self.trans_id_tree_file_id(file_id)
1534.7.143 by Aaron Bentley
Prevented get_trans_id from automatically versioning file ids
267
        elif file_id in self._non_present_ids:
268
            return self._non_present_ids[file_id]
1534.7.75 by Aaron Bentley
Added reverse-lookup for versioned files and get_trans_id
269
        else:
270
            trans_id = self._assign_id()
1534.7.143 by Aaron Bentley
Prevented get_trans_id from automatically versioning file ids
271
            self._non_present_ids[file_id] = trans_id
1534.7.75 by Aaron Bentley
Added reverse-lookup for versioned files and get_trans_id
272
            return trans_id
273
1534.7.12 by Aaron Bentley
Added canonical_path function
274
    def canonical_path(self, path):
275
        """Get the canonical tree-relative path"""
276
        # don't follow final symlinks
1534.10.31 by Aaron Bentley
Add caching to speed canonical_path
277
        abs = self._tree.abspath(path)
278
        if abs in self._relpaths:
279
            return self._relpaths[abs]
280
        dirname, basename = os.path.split(abs)
281
        if dirname not in self._realpaths:
282
            self._realpaths[dirname] = os.path.realpath(dirname)
283
        dirname = self._realpaths[dirname]
284
        abs = pathjoin(dirname, basename)
285
        if dirname in self._relpaths:
286
            relpath = pathjoin(self._relpaths[dirname], basename)
1534.10.32 by Aaron Bentley
Test and fix case where name has trailing slash
287
            relpath = relpath.rstrip('/\\')
1534.10.31 by Aaron Bentley
Add caching to speed canonical_path
288
        else:
289
            relpath = self._tree.relpath(abs)
290
        self._relpaths[abs] = relpath
291
        return relpath
1534.7.12 by Aaron Bentley
Added canonical_path function
292
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
293
    def trans_id_tree_path(self, path):
1534.7.7 by Aaron Bentley
Added support for all-file path ids
294
        """Determine (and maybe set) the transaction ID for a tree path."""
1534.7.12 by Aaron Bentley
Added canonical_path function
295
        path = self.canonical_path(path)
1534.7.7 by Aaron Bentley
Added support for all-file path ids
296
        if path not in self._tree_path_ids:
297
            self._tree_path_ids[path] = self._assign_id()
1534.7.8 by Aaron Bentley
Added TreeTransform.final_kind
298
            self._tree_id_paths[self._tree_path_ids[path]] = path
1534.7.7 by Aaron Bentley
Added support for all-file path ids
299
        return self._tree_path_ids[path]
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
300
1534.7.16 by Aaron Bentley
Added get_tree_parent
301
    def get_tree_parent(self, trans_id):
1534.7.31 by Aaron Bentley
Changed tree root parent to ROOT_PARENT
302
        """Determine id of the parent in the tree."""
1534.7.16 by Aaron Bentley
Added get_tree_parent
303
        path = self._tree_id_paths[trans_id]
304
        if path == "":
1534.7.31 by Aaron Bentley
Changed tree root parent to ROOT_PARENT
305
            return ROOT_PARENT
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
306
        return self.trans_id_tree_path(os.path.dirname(path))
1534.7.16 by Aaron Bentley
Added get_tree_parent
307
1534.7.117 by Aaron Bentley
Simplified permission handling of existing files in transform.
308
    def create_file(self, contents, trans_id, mode_id=None):
1534.7.21 by Aaron Bentley
Updated docstrings
309
        """Schedule creation of a new file.
310
311
        See also new_file.
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
312
        
313
        Contents is an iterator of strings, all of which will be written
1534.7.21 by Aaron Bentley
Updated docstrings
314
        to the target destination.
1534.7.117 by Aaron Bentley
Simplified permission handling of existing files in transform.
315
316
        New file takes the permissions of any existing file with that id,
317
        unless mode_id is specified.
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
318
        """
1711.7.22 by John Arbash Meinel
transform: cleanup the temporary file even if unique_add fails.
319
        name = self._limbo_name(trans_id)
320
        f = open(name, 'wb')
1711.7.8 by John Arbash Meinel
Use try/finally inside create_file for TreeTransform to ensure the file handle gets closed
321
        try:
1711.7.22 by John Arbash Meinel
transform: cleanup the temporary file even if unique_add fails.
322
            try:
323
                unique_add(self._new_contents, trans_id, 'file')
324
            except:
325
                # Clean up the file, it never got registered so
326
                # TreeTransform.finalize() won't clean it up.
327
                f.close()
328
                os.unlink(name)
329
                raise
330
3368.2.3 by Ian Clatworthy
tests for ..input_file and ..output_lines
331
            f.writelines(filtered_output_lines(contents,
332
                filters_for_path(name)))
1711.7.8 by John Arbash Meinel
Use try/finally inside create_file for TreeTransform to ensure the file handle gets closed
333
        finally:
334
            f.close()
1534.7.117 by Aaron Bentley
Simplified permission handling of existing files in transform.
335
        self._set_mode(trans_id, mode_id, S_ISREG)
336
337
    def _set_mode(self, trans_id, mode_id, typefunc):
1534.7.157 by Aaron Bentley
Added more docs
338
        """Set the mode of new file contents.
339
        The mode_id is the existing file to get the mode from (often the same
340
        as trans_id).  The operation is only performed if there's a mode match
341
        according to typefunc.
342
        """
1534.7.117 by Aaron Bentley
Simplified permission handling of existing files in transform.
343
        if mode_id is None:
344
            mode_id = trans_id
345
        try:
346
            old_path = self._tree_id_paths[mode_id]
347
        except KeyError:
348
            return
349
        try:
2027.1.1 by John Arbash Meinel
Fix bug #56549, and write a direct test that the right path is being statted
350
            mode = os.stat(self._tree.abspath(old_path)).st_mode
1534.7.117 by Aaron Bentley
Simplified permission handling of existing files in transform.
351
        except OSError, e:
352
            if e.errno == errno.ENOENT:
353
                return
354
            else:
355
                raise
356
        if typefunc(mode):
357
            os.chmod(self._limbo_name(trans_id), mode)
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
358
3136.1.1 by Aaron Bentley
Add support for hardlinks to TreeTransform
359
    def create_hardlink(self, path, trans_id):
360
        """Schedule creation of a hard link"""
361
        name = self._limbo_name(trans_id)
3136.1.10 by Aaron Bentley
Clean error if filesystem does not support hard-links
362
        try:
363
            os.link(path, name)
364
        except OSError, e:
365
            if e.errno != errno.EPERM:
366
                raise
367
            raise errors.HardLinkNotSupported(path)
3136.1.1 by Aaron Bentley
Add support for hardlinks to TreeTransform
368
        try:
369
            unique_add(self._new_contents, trans_id, 'file')
370
        except:
371
            # Clean up the file, it never got registered so
372
            # TreeTransform.finalize() won't clean it up.
373
            os.unlink(name)
374
            raise
375
1534.7.20 by Aaron Bentley
Added directory handling
376
    def create_directory(self, trans_id):
1534.7.21 by Aaron Bentley
Updated docstrings
377
        """Schedule creation of a new directory.
378
        
379
        See also new_directory.
380
        """
1534.7.73 by Aaron Bentley
Changed model again. Now iterator is used immediately.
381
        os.mkdir(self._limbo_name(trans_id))
382
        unique_add(self._new_contents, trans_id, 'directory')
1534.7.20 by Aaron Bentley
Added directory handling
383
1534.7.22 by Aaron Bentley
Added symlink support
384
    def create_symlink(self, target, trans_id):
385
        """Schedule creation of a new symbolic link.
386
387
        target is a bytestring.
388
        See also new_symlink.
389
        """
3006.2.1 by Alexander Belchenko
workaround for bug #81689: give a proper error message instead of traceback when symlink cannot be created (e.g. on Windows)
390
        if has_symlinks():
391
            os.symlink(target, self._limbo_name(trans_id))
392
            unique_add(self._new_contents, trans_id, 'symlink')
393
        else:
3006.2.2 by Alexander Belchenko
tests added.
394
            try:
395
                path = FinalPaths(self).get_path(trans_id)
396
            except KeyError:
397
                path = None
398
            raise UnableCreateSymlink(path=path)
1534.7.22 by Aaron Bentley
Added symlink support
399
1534.7.129 by Aaron Bentley
Converted test cases to Tree Transform
400
    def cancel_creation(self, trans_id):
1534.7.157 by Aaron Bentley
Added more docs
401
        """Cancel the creation of new file contents."""
1534.7.129 by Aaron Bentley
Converted test cases to Tree Transform
402
        del self._new_contents[trans_id]
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
403
        children = self._limbo_children.get(trans_id)
404
        # if this is a limbo directory with children, move them before removing
405
        # the directory
406
        if children is not None:
407
            self._rename_in_limbo(children)
408
            del self._limbo_children[trans_id]
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
409
            del self._limbo_children_names[trans_id]
1558.12.9 by Aaron Bentley
Handle resolving conflicts with directories properly
410
        delete_any(self._limbo_name(trans_id))
1534.7.129 by Aaron Bentley
Converted test cases to Tree Transform
411
1534.7.34 by Aaron Bentley
Proper conflicts for removals
412
    def delete_contents(self, trans_id):
413
        """Schedule the contents of a path entry for deletion"""
1534.7.130 by Aaron Bentley
More conflict handling, test porting
414
        self.tree_kind(trans_id)
1534.7.34 by Aaron Bentley
Proper conflicts for removals
415
        self._removed_contents.add(trans_id)
416
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
417
    def cancel_deletion(self, trans_id):
418
        """Cancel a scheduled deletion"""
419
        self._removed_contents.remove(trans_id)
420
1534.7.39 by Aaron Bentley
Ensured that files can be unversioned (de-versioned?)
421
    def unversion_file(self, trans_id):
422
        """Schedule a path entry to become unversioned"""
423
        self._removed_id.add(trans_id)
424
425
    def delete_versioned(self, trans_id):
426
        """Delete and unversion a versioned file"""
427
        self.delete_contents(trans_id)
428
        self.unversion_file(trans_id)
429
1534.7.25 by Aaron Bentley
Added set_executability
430
    def set_executability(self, executability, trans_id):
1534.7.167 by Aaron Bentley
PEP8 and comment cleanups
431
        """Schedule setting of the 'execute' bit
432
        To unschedule, set to None
433
        """
1534.7.26 by Aaron Bentley
Added conflicts for setting executability on unversioned/non-file entries
434
        if executability is None:
435
            del self._new_executability[trans_id]
436
        else:
437
            unique_add(self._new_executability, trans_id, executability)
1534.7.25 by Aaron Bentley
Added set_executability
438
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
439
    def set_tree_reference(self, revision_id, trans_id):
440
        """Set the reference associated with a directory"""
441
        unique_add(self._new_reference_revision, trans_id, revision_id)
442
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
443
    def version_file(self, file_id, trans_id):
1534.7.21 by Aaron Bentley
Updated docstrings
444
        """Schedule a file to become versioned."""
1534.7.148 by Aaron Bentley
Handled the remaining file versioning case
445
        assert file_id is not None
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
446
        unique_add(self._new_id, trans_id, file_id)
1534.7.75 by Aaron Bentley
Added reverse-lookup for versioned files and get_trans_id
447
        unique_add(self._r_new_id, file_id, trans_id)
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
448
1534.7.105 by Aaron Bentley
Got merge with rename working
449
    def cancel_versioning(self, trans_id):
450
        """Undo a previous versioning of a file"""
451
        file_id = self._new_id[trans_id]
452
        del self._new_id[trans_id]
453
        del self._r_new_id[file_id]
454
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
455
    def new_paths(self):
1534.7.21 by Aaron Bentley
Updated docstrings
456
        """Determine the paths of all new and changed files"""
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
457
        new_ids = set()
1534.7.132 by Aaron Bentley
Got cooked conflicts working
458
        fp = FinalPaths(self)
1534.7.4 by Aaron Bentley
Unified all file types as 'contents'
459
        for id_set in (self._new_name, self._new_parent, self._new_contents,
1534.7.25 by Aaron Bentley
Added set_executability
460
                       self._new_id, self._new_executability):
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
461
            new_ids.update(id_set)
462
        new_paths = [(fp.get_path(t), t) for t in new_ids]
463
        new_paths.sort()
464
        return new_paths
1534.7.6 by Aaron Bentley
Added conflict handling
465
1534.7.34 by Aaron Bentley
Proper conflicts for removals
466
    def tree_kind(self, trans_id):
1534.7.40 by Aaron Bentley
Updated docs
467
        """Determine the file kind in the working tree.
468
469
        Raises NoSuchFile if the file does not exist
470
        """
1534.7.34 by Aaron Bentley
Proper conflicts for removals
471
        path = self._tree_id_paths.get(trans_id)
472
        if path is None:
473
            raise NoSuchFile(None)
474
        try:
475
            return file_kind(self._tree.abspath(path))
476
        except OSError, e:
477
            if e.errno != errno.ENOENT:
478
                raise
479
            else:
480
                raise NoSuchFile(path)
481
1534.7.8 by Aaron Bentley
Added TreeTransform.final_kind
482
    def final_kind(self, trans_id):
1534.7.156 by Aaron Bentley
PEP8 fixes
483
        """Determine the final file kind, after any changes applied.
1534.7.8 by Aaron Bentley
Added TreeTransform.final_kind
484
        
485
        Raises NoSuchFile if the file does not exist/has no contents.
486
        (It is conceivable that a path would be created without the
487
        corresponding contents insertion command)
488
        """
489
        if trans_id in self._new_contents:
1534.7.73 by Aaron Bentley
Changed model again. Now iterator is used immediately.
490
            return self._new_contents[trans_id]
1534.7.34 by Aaron Bentley
Proper conflicts for removals
491
        elif trans_id in self._removed_contents:
492
            raise NoSuchFile(None)
1534.7.8 by Aaron Bentley
Added TreeTransform.final_kind
493
        else:
1534.7.34 by Aaron Bentley
Proper conflicts for removals
494
            return self.tree_kind(trans_id)
1534.7.8 by Aaron Bentley
Added TreeTransform.final_kind
495
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
496
    def tree_file_id(self, trans_id):
1534.7.41 by Aaron Bentley
Got inventory ID movement working
497
        """Determine the file id associated with the trans_id in the tree"""
498
        try:
499
            path = self._tree_id_paths[trans_id]
500
        except KeyError:
501
            # the file is a new, unversioned file, or invalid trans_id
502
            return None
503
        # the file is old; the old id is still valid
1534.7.68 by Aaron Bentley
Got semi-reasonable root directory renaming working
504
        if self._new_root == trans_id:
2946.3.3 by John Arbash Meinel
Prefer tree.get_root_id() as more explicit than tree.path2id('')
505
            return self._tree.get_root_id()
1534.7.148 by Aaron Bentley
Handled the remaining file versioning case
506
        return self._tree.inventory.path2id(path)
1534.7.41 by Aaron Bentley
Got inventory ID movement working
507
1534.7.13 by Aaron Bentley
Implemented final_file_id
508
    def final_file_id(self, trans_id):
1534.7.156 by Aaron Bentley
PEP8 fixes
509
        """Determine the file id after any changes are applied, or None.
1534.7.21 by Aaron Bentley
Updated docstrings
510
        
511
        None indicates that the file will not be versioned after changes are
512
        applied.
513
        """
1534.7.13 by Aaron Bentley
Implemented final_file_id
514
        try:
515
            # there is a new id for this file
1534.7.148 by Aaron Bentley
Handled the remaining file versioning case
516
            assert self._new_id[trans_id] is not None
1534.7.13 by Aaron Bentley
Implemented final_file_id
517
            return self._new_id[trans_id]
518
        except KeyError:
1534.7.39 by Aaron Bentley
Ensured that files can be unversioned (de-versioned?)
519
            if trans_id in self._removed_id:
520
                return None
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
521
        return self.tree_file_id(trans_id)
1534.7.13 by Aaron Bentley
Implemented final_file_id
522
1534.7.148 by Aaron Bentley
Handled the remaining file versioning case
523
    def inactive_file_id(self, trans_id):
1534.7.157 by Aaron Bentley
Added more docs
524
        """Return the inactive file_id associated with a transaction id.
1534.7.148 by Aaron Bentley
Handled the remaining file versioning case
525
        That is, the one in the tree or in non_present_ids.
526
        The file_id may actually be active, too.
527
        """
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
528
        file_id = self.tree_file_id(trans_id)
1534.7.148 by Aaron Bentley
Handled the remaining file versioning case
529
        if file_id is not None:
530
            return file_id
531
        for key, value in self._non_present_ids.iteritems():
532
            if value == trans_id:
533
                return key
534
1534.7.17 by Aaron Bentley
Added final_parent function
535
    def final_parent(self, trans_id):
1534.7.156 by Aaron Bentley
PEP8 fixes
536
        """Determine the parent file_id, after any changes are applied.
1534.7.21 by Aaron Bentley
Updated docstrings
537
1534.7.31 by Aaron Bentley
Changed tree root parent to ROOT_PARENT
538
        ROOT_PARENT is returned for the tree root.
1534.7.21 by Aaron Bentley
Updated docstrings
539
        """
1534.7.17 by Aaron Bentley
Added final_parent function
540
        try:
541
            return self._new_parent[trans_id]
542
        except KeyError:
543
            return self.get_tree_parent(trans_id)
544
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
545
    def final_name(self, trans_id):
1534.7.40 by Aaron Bentley
Updated docs
546
        """Determine the final filename, after all changes are applied."""
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
547
        try:
548
            return self._new_name[trans_id]
549
        except KeyError:
1731.1.33 by Aaron Bentley
Revert no-special-root changes
550
            try:
551
                return os.path.basename(self._tree_id_paths[trans_id])
552
            except KeyError:
553
                raise NoFinalPath(trans_id, self)
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
554
1534.10.28 by Aaron Bentley
Use numbered backup files
555
    def by_parent(self):
1534.7.40 by Aaron Bentley
Updated docs
556
        """Return a map of parent: children for known parents.
557
        
558
        Only new paths and parents of tree files with assigned ids are used.
559
        """
1534.7.6 by Aaron Bentley
Added conflict handling
560
        by_parent = {}
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
561
        items = list(self._new_parent.iteritems())
1534.7.76 by Aaron Bentley
Fixed final_parent, for the case where finding a parent adds tree id paths.
562
        items.extend((t, self.final_parent(t)) for t in 
563
                      self._tree_id_paths.keys())
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
564
        for trans_id, parent_id in items:
1534.7.6 by Aaron Bentley
Added conflict handling
565
            if parent_id not in by_parent:
566
                by_parent[parent_id] = set()
567
            by_parent[parent_id].add(trans_id)
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
568
        return by_parent
1534.7.11 by Aaron Bentley
Refactored conflict handling
569
1534.7.57 by Aaron Bentley
Enhanced conflict resolution.
570
    def path_changed(self, trans_id):
1534.7.157 by Aaron Bentley
Added more docs
571
        """Return True if a trans_id's path has changed."""
1711.9.11 by John Arbash Meinel
change return foo in bar to return (foo in bar)
572
        return (trans_id in self._new_name) or (trans_id in self._new_parent)
1534.7.57 by Aaron Bentley
Enhanced conflict resolution.
573
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
574
    def new_contents(self, trans_id):
575
        return (trans_id in self._new_contents)
576
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
577
    def find_conflicts(self):
1534.7.40 by Aaron Bentley
Updated docs
578
        """Find any violations of inventory or filesystem invariants"""
3008.1.29 by Aaron Bentley
Add docstrings, rename TT.__doc
579
        if self._done is True:
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
580
            raise ReusingTransform()
581
        conflicts = []
582
        # ensure all children of all existent parents are known
583
        # all children of non-existent parents are known, by definition.
584
        self._add_tree_children()
1534.10.28 by Aaron Bentley
Use numbered backup files
585
        by_parent = self.by_parent()
1534.7.15 by Aaron Bentley
Add conflict types related to versioning
586
        conflicts.extend(self._unversioned_parents(by_parent))
1534.7.19 by Aaron Bentley
Added tests for parent loops
587
        conflicts.extend(self._parent_loops())
1534.7.11 by Aaron Bentley
Refactored conflict handling
588
        conflicts.extend(self._duplicate_entries(by_parent))
1534.7.50 by Aaron Bentley
Detect duplicate inventory ids
589
        conflicts.extend(self._duplicate_ids())
1534.7.11 by Aaron Bentley
Refactored conflict handling
590
        conflicts.extend(self._parent_type_conflicts(by_parent))
1534.7.15 by Aaron Bentley
Add conflict types related to versioning
591
        conflicts.extend(self._improper_versioning())
1534.7.26 by Aaron Bentley
Added conflicts for setting executability on unversioned/non-file entries
592
        conflicts.extend(self._executability_conflicts())
1534.7.152 by Aaron Bentley
Fixed overwrites
593
        conflicts.extend(self._overwrite_conflicts())
1534.7.15 by Aaron Bentley
Add conflict types related to versioning
594
        return conflicts
595
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
596
    def _add_tree_children(self):
1534.7.156 by Aaron Bentley
PEP8 fixes
597
        """Add all the children of all active parents to the known paths.
1534.7.40 by Aaron Bentley
Updated docs
598
599
        Active parents are those which gain children, and those which are
600
        removed.  This is a necessary first step in detecting conflicts.
601
        """
1534.10.28 by Aaron Bentley
Use numbered backup files
602
        parents = self.by_parent().keys()
1534.7.34 by Aaron Bentley
Proper conflicts for removals
603
        parents.extend([t for t in self._removed_contents if 
604
                        self.tree_kind(t) == 'directory'])
1534.7.50 by Aaron Bentley
Detect duplicate inventory ids
605
        for trans_id in self._removed_id:
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
606
            file_id = self.tree_file_id(trans_id)
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
607
            if file_id is not None:
608
                if self._tree.inventory[file_id].kind == 'directory':
609
                    parents.append(trans_id)
610
            elif self.tree_kind(trans_id) == 'directory':
1534.7.50 by Aaron Bentley
Detect duplicate inventory ids
611
                parents.append(trans_id)
612
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
613
        for parent_id in parents:
1534.7.67 by Aaron Bentley
Refactored _add_tree_children
614
            # ensure that all children are registered with the transaction
615
            list(self.iter_tree_children(parent_id))
616
617
    def iter_tree_children(self, parent_id):
618
        """Iterate through the entry's tree children, if any"""
619
        try:
620
            path = self._tree_id_paths[parent_id]
621
        except KeyError:
622
            return
623
        try:
624
            children = os.listdir(self._tree.abspath(path))
625
        except OSError, e:
3144.4.1 by Aaron Bentley
Handle trying to list parents of a non-directory
626
            if e.errno not in (errno.ENOENT, errno.ESRCH, errno.ENOTDIR):
1534.7.67 by Aaron Bentley
Refactored _add_tree_children
627
                raise
628
            return
629
            
630
        for child in children:
631
            childpath = joinpath(path, child)
1534.7.180 by Aaron Bentley
Merge from mainline
632
            if self._tree.is_control_filename(childpath):
1534.7.67 by Aaron Bentley
Refactored _add_tree_children
633
                continue
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
634
            yield self.trans_id_tree_path(childpath)
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
635
1534.10.28 by Aaron Bentley
Use numbered backup files
636
    def has_named_child(self, by_parent, parent_id, name):
637
        try:
638
            children = by_parent[parent_id]
639
        except KeyError:
640
            children = []
641
        for child in children:
642
            if self.final_name(child) == name:
643
                return True
644
        try:
645
            path = self._tree_id_paths[parent_id]
646
        except KeyError:
647
            return False
648
        childpath = joinpath(path, name)
649
        child_id = self._tree_path_ids.get(childpath)
650
        if child_id is None:
651
            return lexists(self._tree.abspath(childpath))
652
        else:
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
653
            if self.final_parent(child_id) != parent_id:
1534.10.28 by Aaron Bentley
Use numbered backup files
654
                return False
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
655
            if child_id in self._removed_contents:
1534.10.28 by Aaron Bentley
Use numbered backup files
656
                # XXX What about dangling file-ids?
657
                return False
658
            else:
659
                return True
660
1534.7.19 by Aaron Bentley
Added tests for parent loops
661
    def _parent_loops(self):
662
        """No entry should be its own ancestor"""
663
        conflicts = []
664
        for trans_id in self._new_parent:
665
            seen = set()
666
            parent_id = trans_id
1534.7.31 by Aaron Bentley
Changed tree root parent to ROOT_PARENT
667
            while parent_id is not ROOT_PARENT:
1534.7.19 by Aaron Bentley
Added tests for parent loops
668
                seen.add(parent_id)
1731.1.33 by Aaron Bentley
Revert no-special-root changes
669
                try:
670
                    parent_id = self.final_parent(parent_id)
671
                except KeyError:
672
                    break
1534.7.19 by Aaron Bentley
Added tests for parent loops
673
                if parent_id == trans_id:
674
                    conflicts.append(('parent loop', trans_id))
675
                if parent_id in seen:
676
                    break
677
        return conflicts
678
1534.7.15 by Aaron Bentley
Add conflict types related to versioning
679
    def _unversioned_parents(self, by_parent):
680
        """If parent directories are versioned, children must be versioned."""
681
        conflicts = []
682
        for parent_id, children in by_parent.iteritems():
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
683
            if parent_id is ROOT_PARENT:
684
                continue
1534.7.15 by Aaron Bentley
Add conflict types related to versioning
685
            if self.final_file_id(parent_id) is not None:
686
                continue
687
            for child_id in children:
688
                if self.final_file_id(child_id) is not None:
689
                    conflicts.append(('unversioned parent', parent_id))
690
                    break;
691
        return conflicts
692
693
    def _improper_versioning(self):
1534.7.156 by Aaron Bentley
PEP8 fixes
694
        """Cannot version a file with no contents, or a bad type.
1534.7.15 by Aaron Bentley
Add conflict types related to versioning
695
        
696
        However, existing entries with no contents are okay.
697
        """
698
        conflicts = []
699
        for trans_id in self._new_id.iterkeys():
700
            try:
701
                kind = self.final_kind(trans_id)
702
            except NoSuchFile:
703
                conflicts.append(('versioning no contents', trans_id))
704
                continue
705
            if not InventoryEntry.versionable_kind(kind):
1534.7.20 by Aaron Bentley
Added directory handling
706
                conflicts.append(('versioning bad kind', trans_id, kind))
1534.7.11 by Aaron Bentley
Refactored conflict handling
707
        return conflicts
708
1534.7.26 by Aaron Bentley
Added conflicts for setting executability on unversioned/non-file entries
709
    def _executability_conflicts(self):
1534.7.40 by Aaron Bentley
Updated docs
710
        """Check for bad executability changes.
711
        
712
        Only versioned files may have their executability set, because
713
        1. only versioned entries can have executability under windows
714
        2. only files can be executable.  (The execute bit on a directory
715
           does not indicate searchability)
716
        """
1534.7.26 by Aaron Bentley
Added conflicts for setting executability on unversioned/non-file entries
717
        conflicts = []
718
        for trans_id in self._new_executability:
719
            if self.final_file_id(trans_id) is None:
720
                conflicts.append(('unversioned executability', trans_id))
1534.7.34 by Aaron Bentley
Proper conflicts for removals
721
            else:
722
                try:
723
                    non_file = self.final_kind(trans_id) != "file"
724
                except NoSuchFile:
725
                    non_file = True
726
                if non_file is True:
727
                    conflicts.append(('non-file executability', trans_id))
1534.7.26 by Aaron Bentley
Added conflicts for setting executability on unversioned/non-file entries
728
        return conflicts
729
1534.7.152 by Aaron Bentley
Fixed overwrites
730
    def _overwrite_conflicts(self):
731
        """Check for overwrites (not permitted on Win32)"""
732
        conflicts = []
733
        for trans_id in self._new_contents:
734
            try:
735
                self.tree_kind(trans_id)
736
            except NoSuchFile:
737
                continue
738
            if trans_id not in self._removed_contents:
739
                conflicts.append(('overwrite', trans_id,
740
                                 self.final_name(trans_id)))
741
        return conflicts
742
1534.7.11 by Aaron Bentley
Refactored conflict handling
743
    def _duplicate_entries(self, by_parent):
744
        """No directory may have two entries with the same name."""
745
        conflicts = []
2590.2.16 by Aaron Bentley
Shortcut duplicate_entries conflict check if no new names introduced
746
        if (self._new_name, self._new_parent) == ({}, {}):
747
            return conflicts
1534.7.6 by Aaron Bentley
Added conflict handling
748
        for children in by_parent.itervalues():
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
749
            name_ids = [(self.final_name(t), t) for t in children]
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
750
            if not self._case_sensitive_target:
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
751
                name_ids = [(n.lower(), t) for n, t in name_ids]
1534.7.6 by Aaron Bentley
Added conflict handling
752
            name_ids.sort()
753
            last_name = None
754
            last_trans_id = None
755
            for name, trans_id in name_ids:
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
756
                try:
757
                    kind = self.final_kind(trans_id)
758
                except NoSuchFile:
759
                    kind = None
760
                file_id = self.final_file_id(trans_id)
761
                if kind is None and file_id is None:
762
                    continue
1534.7.6 by Aaron Bentley
Added conflict handling
763
                if name == last_name:
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
764
                    conflicts.append(('duplicate', last_trans_id, trans_id,
765
                    name))
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
766
                last_name = name
767
                last_trans_id = trans_id
1534.7.11 by Aaron Bentley
Refactored conflict handling
768
        return conflicts
769
1534.7.50 by Aaron Bentley
Detect duplicate inventory ids
770
    def _duplicate_ids(self):
771
        """Each inventory id may only be used once"""
772
        conflicts = []
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
773
        removed_tree_ids = set((self.tree_file_id(trans_id) for trans_id in
1534.7.50 by Aaron Bentley
Detect duplicate inventory ids
774
                                self._removed_id))
3146.8.16 by Aaron Bentley
Updates from review
775
        all_ids = self._tree.all_file_ids()
776
        active_tree_ids = all_ids.difference(removed_tree_ids)
1534.7.50 by Aaron Bentley
Detect duplicate inventory ids
777
        for trans_id, file_id in self._new_id.iteritems():
778
            if file_id in active_tree_ids:
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
779
                old_trans_id = self.trans_id_tree_file_id(file_id)
1534.7.50 by Aaron Bentley
Detect duplicate inventory ids
780
                conflicts.append(('duplicate id', old_trans_id, trans_id))
781
        return conflicts
782
1534.7.11 by Aaron Bentley
Refactored conflict handling
783
    def _parent_type_conflicts(self, by_parent):
784
        """parents must have directory 'contents'."""
785
        conflicts = []
1534.7.37 by Aaron Bentley
Allowed removed dirs to have content-free children.
786
        for parent_id, children in by_parent.iteritems():
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
787
            if parent_id is ROOT_PARENT:
788
                continue
1534.7.37 by Aaron Bentley
Allowed removed dirs to have content-free children.
789
            if not self._any_contents(children):
790
                continue
791
            for child in children:
792
                try:
793
                    self.final_kind(child)
794
                except NoSuchFile:
795
                    continue
1534.7.10 by Aaron Bentley
Implemented missing parent and non-directory parent conflicts
796
            try:
797
                kind = self.final_kind(parent_id)
798
            except NoSuchFile:
799
                kind = None
800
            if kind is None:
801
                conflicts.append(('missing parent', parent_id))
802
            elif kind != "directory":
803
                conflicts.append(('non-directory parent', parent_id))
1534.7.6 by Aaron Bentley
Added conflict handling
804
        return conflicts
1534.7.37 by Aaron Bentley
Allowed removed dirs to have content-free children.
805
806
    def _any_contents(self, trans_ids):
807
        """Return true if any of the trans_ids, will have contents."""
808
        for trans_id in trans_ids:
809
            try:
810
                kind = self.final_kind(trans_id)
811
            except NoSuchFile:
812
                continue
813
            return True
814
        return False
1534.7.35 by Aaron Bentley
Got file renaming working
815
2502.1.14 by Aaron Bentley
Style update suggested by Robert
816
    def _limbo_name(self, trans_id):
1534.7.72 by Aaron Bentley
Moved new content generation to pre-renames
817
        """Generate the limbo name of a file"""
2502.1.14 by Aaron Bentley
Style update suggested by Robert
818
        limbo_name = self._limbo_files.get(trans_id)
819
        if limbo_name is not None:
820
            return limbo_name
2502.1.3 by Aaron Bentley
Don't cause errors when creating contents for trans_ids with no parent/name
821
        parent = self._new_parent.get(trans_id)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
822
        # if the parent directory is already in limbo (e.g. when building a
823
        # tree), choose a limbo name inside the parent, to reduce further
824
        # renames.
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
825
        use_direct_path = False
826
        if self._new_contents.get(parent) == 'directory':
827
            filename = self._new_name.get(trans_id)
828
            if filename is not None:
829
                if parent not in self._limbo_children:
830
                    self._limbo_children[parent] = set()
831
                    self._limbo_children_names[parent] = {}
832
                    use_direct_path = True
2502.1.6 by Aaron Bentley
Update from review comments
833
                # the direct path can only be used if no other file has
834
                # already taken this pathname, i.e. if the name is unused, or
835
                # if it is already associated with this trans_id.
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
836
                elif self._case_sensitive_target:
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
837
                    if (self._limbo_children_names[parent].get(filename)
838
                        in (trans_id, None)):
839
                        use_direct_path = True
840
                else:
841
                    for l_filename, l_trans_id in\
842
                        self._limbo_children_names[parent].iteritems():
843
                        if l_trans_id == trans_id:
844
                            continue
845
                        if l_filename.lower() == filename.lower():
846
                            break
847
                    else:
848
                        use_direct_path = True
849
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
850
        if use_direct_path:
851
            limbo_name = pathjoin(self._limbo_files[parent], filename)
2502.1.2 by Aaron Bentley
Make the limited-renames functionality safer in the general case
852
            self._limbo_children[parent].add(trans_id)
2502.1.4 by Aaron Bentley
Ensure we only reuse limbo names appropriately
853
            self._limbo_children_names[parent][filename] = trans_id
2502.1.1 by Aaron Bentley
Ensure renames only root children are renamed when building trees
854
        else:
855
            limbo_name = pathjoin(self._limbodir, trans_id)
856
            self._needs_rename.add(trans_id)
857
        self._limbo_files[trans_id] = limbo_name
858
        return limbo_name
1534.7.72 by Aaron Bentley
Moved new content generation to pre-renames
859
2376.2.4 by Aaron Bentley
Switch TreeTransform to use WT.apply_inventory_delta
860
    def _set_executability(self, path, entry, trans_id):
1534.7.40 by Aaron Bentley
Updated docs
861
        """Set the executability of versioned files """
1534.7.25 by Aaron Bentley
Added set_executability
862
        new_executability = self._new_executability[trans_id]
2376.2.4 by Aaron Bentley
Switch TreeTransform to use WT.apply_inventory_delta
863
        entry.executable = new_executability
1534.7.25 by Aaron Bentley
Added set_executability
864
        if supports_executable():
865
            abspath = self._tree.abspath(path)
866
            current_mode = os.stat(abspath).st_mode
867
            if new_executability:
868
                umask = os.umask(0)
869
                os.umask(umask)
870
                to_mode = current_mode | (0100 & ~umask)
871
                # Enable x-bit for others only if they can read it.
872
                if current_mode & 0004:
873
                    to_mode |= 0001 & ~umask
874
                if current_mode & 0040:
875
                    to_mode |= 0010 & ~umask
876
            else:
877
                to_mode = current_mode & ~0111
878
            os.chmod(abspath, to_mode)
879
1534.7.23 by Aaron Bentley
Transform.new_entry -> Transform._new_entry
880
    def _new_entry(self, name, parent_id, file_id):
1534.7.21 by Aaron Bentley
Updated docstrings
881
        """Helper function to create a new filesystem entry."""
1534.7.2 by Aaron Bentley
Added convenience function
882
        trans_id = self.create_path(name, parent_id)
883
        if file_id is not None:
884
            self.version_file(file_id, trans_id)
885
        return trans_id
886
1534.7.27 by Aaron Bentley
Added execute bit to new_file method
887
    def new_file(self, name, parent_id, contents, file_id=None, 
888
                 executable=None):
1534.7.156 by Aaron Bentley
PEP8 fixes
889
        """Convenience method to create files.
1534.7.21 by Aaron Bentley
Updated docstrings
890
        
891
        name is the name of the file to create.
892
        parent_id is the transaction id of the parent directory of the file.
893
        contents is an iterator of bytestrings, which will be used to produce
894
        the file.
1740.2.4 by Aaron Bentley
Update transform tests and docs
895
        :param file_id: The inventory ID of the file, if it is to be versioned.
896
        :param executable: Only valid when a file_id has been supplied.
1534.7.21 by Aaron Bentley
Updated docstrings
897
        """
1534.7.23 by Aaron Bentley
Transform.new_entry -> Transform._new_entry
898
        trans_id = self._new_entry(name, parent_id, file_id)
1740.2.4 by Aaron Bentley
Update transform tests and docs
899
        # TODO: rather than scheduling a set_executable call,
900
        # have create_file create the file with the right mode.
1534.7.20 by Aaron Bentley
Added directory handling
901
        self.create_file(contents, trans_id)
1534.7.27 by Aaron Bentley
Added execute bit to new_file method
902
        if executable is not None:
903
            self.set_executability(executable, trans_id)
1534.7.20 by Aaron Bentley
Added directory handling
904
        return trans_id
905
906
    def new_directory(self, name, parent_id, file_id=None):
1534.7.156 by Aaron Bentley
PEP8 fixes
907
        """Convenience method to create directories.
1534.7.21 by Aaron Bentley
Updated docstrings
908
909
        name is the name of the directory to create.
910
        parent_id is the transaction id of the parent directory of the
911
        directory.
912
        file_id is the inventory ID of the directory, if it is to be versioned.
913
        """
1534.7.23 by Aaron Bentley
Transform.new_entry -> Transform._new_entry
914
        trans_id = self._new_entry(name, parent_id, file_id)
1534.7.20 by Aaron Bentley
Added directory handling
915
        self.create_directory(trans_id)
916
        return trans_id 
917
1534.7.22 by Aaron Bentley
Added symlink support
918
    def new_symlink(self, name, parent_id, target, file_id=None):
1534.7.156 by Aaron Bentley
PEP8 fixes
919
        """Convenience method to create symbolic link.
1534.7.22 by Aaron Bentley
Added symlink support
920
        
921
        name is the name of the symlink to create.
922
        parent_id is the transaction id of the parent directory of the symlink.
923
        target is a bytestring of the target of the symlink.
924
        file_id is the inventory ID of the file, if it is to be versioned.
925
        """
1534.7.23 by Aaron Bentley
Transform.new_entry -> Transform._new_entry
926
        trans_id = self._new_entry(name, parent_id, file_id)
1534.7.22 by Aaron Bentley
Added symlink support
927
        self.create_symlink(target, trans_id)
928
        return trans_id
929
1551.11.12 by Aaron Bentley
Changes from review
930
    def _affected_ids(self):
931
        """Return the set of transform ids affected by the transform"""
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
932
        trans_ids = set(self._removed_id)
933
        trans_ids.update(self._new_id.keys())
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
934
        trans_ids.update(self._removed_contents)
935
        trans_ids.update(self._new_contents.keys())
936
        trans_ids.update(self._new_executability.keys())
937
        trans_ids.update(self._new_name.keys())
938
        trans_ids.update(self._new_parent.keys())
1551.11.12 by Aaron Bentley
Changes from review
939
        return trans_ids
940
941
    def _get_file_id_maps(self):
942
        """Return mapping of file_ids to trans_ids in the to and from states"""
943
        trans_ids = self._affected_ids()
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
944
        from_trans_ids = {}
945
        to_trans_ids = {}
946
        # Build up two dicts: trans_ids associated with file ids in the
947
        # FROM state, vs the TO state.
948
        for trans_id in trans_ids:
949
            from_file_id = self.tree_file_id(trans_id)
950
            if from_file_id is not None:
951
                from_trans_ids[from_file_id] = trans_id
952
            to_file_id = self.final_file_id(trans_id)
953
            if to_file_id is not None:
954
                to_trans_ids[to_file_id] = trans_id
1551.11.12 by Aaron Bentley
Changes from review
955
        return from_trans_ids, to_trans_ids
956
957
    def _from_file_data(self, from_trans_id, from_versioned, file_id):
958
        """Get data about a file in the from (tree) state
959
960
        Return a (name, parent, kind, executable) tuple
961
        """
962
        from_path = self._tree_id_paths.get(from_trans_id)
963
        if from_versioned:
964
            # get data from working tree if versioned
965
            from_entry = self._tree.inventory[file_id]
966
            from_name = from_entry.name
967
            from_parent = from_entry.parent_id
968
        else:
969
            from_entry = None
970
            if from_path is None:
971
                # File does not exist in FROM state
972
                from_name = None
973
                from_parent = None
974
            else:
975
                # File exists, but is not versioned.  Have to use path-
976
                # splitting stuff
977
                from_name = os.path.basename(from_path)
978
                tree_parent = self.get_tree_parent(from_trans_id)
979
                from_parent = self.tree_file_id(tree_parent)
980
        if from_path is not None:
981
            from_kind, from_executable, from_stats = \
982
                self._tree._comparison_data(from_entry, from_path)
983
        else:
984
            from_kind = None
985
            from_executable = False
986
        return from_name, from_parent, from_kind, from_executable
987
988
    def _to_file_data(self, to_trans_id, from_trans_id, from_executable):
989
        """Get data about a file in the to (target) state
990
991
        Return a (name, parent, kind, executable) tuple
992
        """
993
        to_name = self.final_name(to_trans_id)
994
        try:
995
            to_kind = self.final_kind(to_trans_id)
996
        except NoSuchFile:
997
            to_kind = None
998
        to_parent = self.final_file_id(self.final_parent(to_trans_id))
999
        if to_trans_id in self._new_executability:
1000
            to_executable = self._new_executability[to_trans_id]
1001
        elif to_trans_id == from_trans_id:
1002
            to_executable = from_executable
1003
        else:
1004
            to_executable = False
1005
        return to_name, to_parent, to_kind, to_executable
1006
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1007
    def iter_changes(self):
1008
        """Produce output in the same format as Tree.iter_changes.
1551.11.12 by Aaron Bentley
Changes from review
1009
1010
        Will produce nonsensical results if invoked while inventory/filesystem
1011
        conflicts (as reported by TreeTransform.find_conflicts()) are present.
1012
1013
        This reads the Transform, but only reproduces changes involving a
1014
        file_id.  Files that are not versioned in either of the FROM or TO
1015
        states are not reflected.
1016
        """
1017
        final_paths = FinalPaths(self)
1018
        from_trans_ids, to_trans_ids = self._get_file_id_maps()
1551.11.4 by Aaron Bentley
Sort output of Transform.iter_changes by path
1019
        results = []
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
1020
        # Now iterate through all active file_ids
1021
        for file_id in set(from_trans_ids.keys() + to_trans_ids.keys()):
1551.11.7 by Aaron Bentley
Stop modified flag bleeding into later changes
1022
            modified = False
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
1023
            from_trans_id = from_trans_ids.get(file_id)
1024
            # find file ids, and determine versioning state
1025
            if from_trans_id is None:
1026
                from_versioned = False
1027
                from_trans_id = to_trans_ids[file_id]
1028
            else:
1029
                from_versioned = True
1030
            to_trans_id = to_trans_ids.get(file_id)
1031
            if to_trans_id is None:
1032
                to_versioned = False
1033
                to_trans_id = from_trans_id
1034
            else:
1035
                to_versioned = True
1551.11.12 by Aaron Bentley
Changes from review
1036
1037
            from_name, from_parent, from_kind, from_executable = \
1038
                self._from_file_data(from_trans_id, from_versioned, file_id)
1039
1040
            to_name, to_parent, to_kind, to_executable = \
1041
                self._to_file_data(to_trans_id, from_trans_id, from_executable)
1042
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
1043
            if not from_versioned:
1044
                from_path = None
1045
            else:
1046
                from_path = self._tree_id_paths.get(from_trans_id)
1047
            if not to_versioned:
1048
                to_path = None
1049
            else:
1050
                to_path = final_paths.get_path(to_trans_id)
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
1051
            if from_kind != to_kind:
1052
                modified = True
1551.10.37 by Aaron Bentley
recommit of TreeTransform._iter_changes fix with missing files
1053
            elif to_kind in ('file', 'symlink') and (
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1054
                to_trans_id != from_trans_id or
1055
                to_trans_id in self._new_contents):
1056
                modified = True
1057
            if (not modified and from_versioned == to_versioned and
1058
                from_parent==to_parent and from_name == to_name and
1059
                from_executable == to_executable):
1060
                continue
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
1061
            results.append((file_id, (from_path, to_path), modified,
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
1062
                   (from_versioned, to_versioned),
1551.11.2 by Aaron Bentley
Get kind change detection working for iter_changes
1063
                   (from_parent, to_parent),
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
1064
                   (from_name, to_name),
1065
                   (from_kind, to_kind),
1551.11.4 by Aaron Bentley
Sort output of Transform.iter_changes by path
1066
                   (from_executable, to_executable)))
1067
        return iter(sorted(results, key=lambda x:x[1]))
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
1068
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1069
    def get_preview_tree(self):
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1070
        """Return a tree representing the result of the transform.
1071
1072
        This tree only supports the subset of Tree functionality required
1073
        by show_diff_trees.  It must only be compared to tt._tree.
1074
        """
1075
        return _PreviewTree(self)
1076
1551.11.1 by Aaron Bentley
Initial work on converting TreeTransform to iter_changes format
1077
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1078
class TreeTransform(TreeTransformBase):
1079
    """Represent a tree transformation.
3008.1.19 by Aaron Bentley
Remove trailing whitespace
1080
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1081
    This object is designed to support incremental generation of the transform,
1082
    in any order.
1083
1084
    However, it gives optimum performance when parent directories are created
1085
    before their contents.  The transform is then able to put child files
1086
    directly in their parent directory, avoiding later renames.
3008.1.19 by Aaron Bentley
Remove trailing whitespace
1087
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1088
    It is easy to produce malformed transforms, but they are generally
1089
    harmless.  Attempting to apply a malformed transform will cause an
3008.1.19 by Aaron Bentley
Remove trailing whitespace
1090
    exception to be raised before any modifications are made to the tree.
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1091
3008.1.19 by Aaron Bentley
Remove trailing whitespace
1092
    Many kinds of malformed transforms can be corrected with the
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1093
    resolve_conflicts function.  The remaining ones indicate programming error,
1094
    such as trying to create a file with no path.
1095
1096
    Two sets of file creation methods are supplied.  Convenience methods are:
1097
     * new_file
1098
     * new_directory
1099
     * new_symlink
1100
1101
    These are composed of the low-level methods:
1102
     * create_path
1103
     * create_file or create_directory or create_symlink
1104
     * version_file
1105
     * set_executability
3008.1.13 by Michael Hudson
merge bzr.dev
1106
1107
    Transform/Transaction ids
1108
    -------------------------
1109
    trans_ids are temporary ids assigned to all files involved in a transform.
1110
    It's possible, even common, that not all files in the Tree have trans_ids.
1111
1112
    trans_ids are used because filenames and file_ids are not good enough
1113
    identifiers; filenames change, and not all files have file_ids.  File-ids
1114
    are also associated with trans-ids, so that moving a file moves its
1115
    file-id.
1116
1117
    trans_ids are only valid for the TreeTransform that generated them.
1118
1119
    Limbo
1120
    -----
1121
    Limbo is a temporary directory use to hold new versions of files.
1122
    Files are added to limbo by create_file, create_directory, create_symlink,
1123
    and their convenience variants (new_*).  Files may be removed from limbo
1124
    using cancel_creation.  Files are renamed from limbo into their final
1125
    location as part of TreeTransform.apply
1126
1127
    Limbo must be cleaned up, by either calling TreeTransform.apply or
1128
    calling TreeTransform.finalize.
1129
1130
    Files are placed into limbo inside their parent directories, where
1131
    possible.  This reduces subsequent renames, and makes operations involving
1132
    lots of files faster.  This optimization is only possible if the parent
1133
    directory is created *before* creating any of its children, so avoid
1134
    creating children before parents, where possible.
1135
1136
    Pending-deletion
1137
    ----------------
1138
    This temporary directory is used by _FileMover for storing files that are
1139
    about to be deleted.  In case of rollback, the files will be restored.
1140
    FileMover does not delete files until it is sure that a rollback will not
3008.1.19 by Aaron Bentley
Remove trailing whitespace
1141
    happen.
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1142
    """
1143
    def __init__(self, tree, pb=DummyProgress()):
1144
        """Note: a tree_write lock is taken on the tree.
1145
1146
        Use TreeTransform.finalize() to release the lock (can be omitted if
1147
        TreeTransform.apply() called).
1148
        """
1149
        tree.lock_tree_write()
3008.1.13 by Michael Hudson
merge bzr.dev
1150
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1151
        try:
1152
            control_files = tree._control_files
1153
            limbodir = urlutils.local_path_from_url(
1154
                control_files.controlfilename('limbo'))
1155
            try:
1156
                os.mkdir(limbodir)
1157
            except OSError, e:
1158
                if e.errno == errno.EEXIST:
1159
                    raise ExistingLimbo(limbodir)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
1160
            deletiondir = urlutils.local_path_from_url(
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1161
                control_files.controlfilename('pending-deletion'))
1162
            try:
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
1163
                os.mkdir(deletiondir)
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1164
            except OSError, e:
1165
                if e.errno == errno.EEXIST:
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
1166
                    raise errors.ExistingPendingDeletion(deletiondir)
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1167
        except:
1168
            tree.unlock()
1169
            raise
3008.1.13 by Michael Hudson
merge bzr.dev
1170
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
1171
        TreeTransformBase.__init__(self, tree, limbodir, pb,
1172
                                   tree.case_sensitive)
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
1173
        self._deletiondir = deletiondir
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1174
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1175
    def apply(self, no_conflicts=False, _mover=None):
1176
        """Apply all changes to the inventory and filesystem.
3008.1.19 by Aaron Bentley
Remove trailing whitespace
1177
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1178
        If filesystem or inventory conflicts are present, MalformedTransform
1179
        will be thrown.
1180
1181
        If apply succeeds, finalize is not necessary.
1182
1183
        :param no_conflicts: if True, the caller guarantees there are no
1184
            conflicts, so no check is made.
1185
        :param _mover: Supply an alternate FileMover, for testing
1186
        """
1187
        if not no_conflicts:
1188
            conflicts = self.find_conflicts()
1189
            if len(conflicts) != 0:
1190
                raise MalformedTransform(conflicts=conflicts)
1191
        inventory_delta = []
1192
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1193
        try:
1194
            if _mover is None:
1195
                mover = _FileMover()
1196
            else:
1197
                mover = _mover
1198
            try:
1199
                child_pb.update('Apply phase', 0, 2)
3146.8.18 by Aaron Bentley
Merge with dev
1200
                self._apply_removals(inventory_delta, mover)
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1201
                child_pb.update('Apply phase', 1, 2)
3146.8.18 by Aaron Bentley
Merge with dev
1202
                modified_paths = self._apply_insertions(inventory_delta, mover)
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1203
            except:
1204
                mover.rollback()
1205
                raise
1206
            else:
1207
                mover.apply_deletions()
1208
        finally:
1209
            child_pb.finished()
1210
        self._tree.apply_inventory_delta(inventory_delta)
3008.1.29 by Aaron Bentley
Add docstrings, rename TT.__doc
1211
        self._done = True
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1212
        self.finalize()
1213
        return _TransformResults(modified_paths, self.rename_count)
1214
3146.8.18 by Aaron Bentley
Merge with dev
1215
    def _apply_removals(self, inventory_delta, mover):
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1216
        """Perform tree operations that remove directory/inventory names.
3008.1.19 by Aaron Bentley
Remove trailing whitespace
1217
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1218
        That is, delete files that are to be deleted, and put any files that
1219
        need renaming into limbo.  This must be done in strict child-to-parent
1220
        order.
1221
        """
1222
        tree_paths = list(self._tree_path_ids.iteritems())
1223
        tree_paths.sort(reverse=True)
1224
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1225
        try:
1226
            for num, data in enumerate(tree_paths):
1227
                path, trans_id = data
1228
                child_pb.update('removing file', num, len(tree_paths))
1229
                full_path = self._tree.abspath(path)
1230
                if trans_id in self._removed_contents:
1231
                    mover.pre_delete(full_path, os.path.join(self._deletiondir,
1232
                                     trans_id))
1233
                elif trans_id in self._new_name or trans_id in \
1234
                    self._new_parent:
1235
                    try:
1236
                        mover.rename(full_path, self._limbo_name(trans_id))
1237
                    except OSError, e:
1238
                        if e.errno != errno.ENOENT:
1239
                            raise
1240
                    else:
1241
                        self.rename_count += 1
1242
                if trans_id in self._removed_id:
1243
                    if trans_id == self._new_root:
1244
                        file_id = self._tree.get_root_id()
1245
                    else:
1246
                        file_id = self.tree_file_id(trans_id)
3146.8.18 by Aaron Bentley
Merge with dev
1247
                    assert file_id is not None
1248
                    # File-id isn't really being deleted, just moved
1249
                    if file_id in self._r_new_id:
1250
                        continue
1251
                    inventory_delta.append((path, None, file_id, None))
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1252
        finally:
1253
            child_pb.finished()
1254
3146.8.18 by Aaron Bentley
Merge with dev
1255
    def _apply_insertions(self, inventory_delta, mover):
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1256
        """Perform tree operations that insert directory/inventory names.
3008.1.19 by Aaron Bentley
Remove trailing whitespace
1257
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1258
        That is, create any files that need to be created, and restore from
1259
        limbo any files that needed renaming.  This must be done in strict
1260
        parent-to-child order.
1261
        """
1262
        new_paths = self.new_paths()
1263
        modified_paths = []
1264
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
3008.1.16 by Aaron Bentley
Merge with bzr.dev
1265
        completed_new = []
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1266
        try:
1267
            for num, (path, trans_id) in enumerate(new_paths):
1268
                new_entry = None
1269
                child_pb.update('adding file', num, len(new_paths))
1270
                if trans_id in self._new_contents or \
1271
                    self.path_changed(trans_id):
1272
                    full_path = self._tree.abspath(path)
1273
                    if trans_id in self._needs_rename:
1274
                        try:
1275
                            mover.rename(self._limbo_name(trans_id), full_path)
1276
                        except OSError, e:
1277
                            # We may be renaming a dangling inventory id
1278
                            if e.errno != errno.ENOENT:
1279
                                raise
1280
                        else:
1281
                            self.rename_count += 1
1282
                    if trans_id in self._new_contents:
1283
                        modified_paths.append(full_path)
3008.1.16 by Aaron Bentley
Merge with bzr.dev
1284
                        completed_new.append(trans_id)
3146.8.18 by Aaron Bentley
Merge with dev
1285
                file_id = self.final_file_id(trans_id)
1286
                if file_id is not None and (trans_id in self._new_id or
1287
                    trans_id in self._new_name or trans_id in self._new_parent
1288
                    or trans_id in self._new_executability):
1289
                    try:
1290
                        kind = self.final_kind(trans_id)
1291
                    except NoSuchFile:
1292
                        kind = self._tree.stored_kind(file_id)
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1293
                    if trans_id in self._new_reference_revision:
1294
                        new_entry = inventory.TreeReference(
3146.8.18 by Aaron Bentley
Merge with dev
1295
                            self.final_file_id(trans_id),
3146.8.20 by Aaron Bentley
Fix trailing whitespace
1296
                            self._new_name[trans_id],
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1297
                            self.final_file_id(self._new_parent[trans_id]),
1298
                            None, self._new_reference_revision[trans_id])
1299
                    else:
1300
                        new_entry = inventory.make_entry(kind,
1301
                            self.final_name(trans_id),
1302
                            self.final_file_id(self.final_parent(trans_id)),
3146.8.18 by Aaron Bentley
Merge with dev
1303
                            self.final_file_id(trans_id))
1304
                    try:
1305
                        old_path = self._tree.id2path(new_entry.file_id)
1306
                    except errors.NoSuchId:
1307
                        old_path = None
1308
                    inventory_delta.append((old_path, path, new_entry.file_id,
1309
                                            new_entry))
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1310
1311
                if trans_id in self._new_executability:
1312
                    self._set_executability(path, new_entry, trans_id)
1313
        finally:
1314
            child_pb.finished()
3008.1.16 by Aaron Bentley
Merge with bzr.dev
1315
        for trans_id in completed_new:
1316
            del self._new_contents[trans_id]
3008.1.7 by Michael Hudson
move apply and helpers to TreeTransform from TreeTransformBase
1317
        return modified_paths
1318
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1319
1320
class TransformPreview(TreeTransformBase):
3008.1.29 by Aaron Bentley
Add docstrings, rename TT.__doc
1321
    """A TreeTransform for generating preview trees.
1322
1323
    Unlike TreeTransform, this version works when the input tree is a
1324
    RevisionTree, rather than a WorkingTree.  As a result, it tends to ignore
1325
    unversioned files in the input tree.
1326
    """
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1327
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
1328
    def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
3199.1.5 by Vincent Ladeuil
Fix two more leaking tmp dirs, by reworking TransformPreview lock handling.
1329
        tree.lock_read()
3199.1.4 by Vincent Ladeuil
Fix 16 leaked tmp dirs. Probably indicates a lock handling problem with TransformPreview
1330
        limbodir = tempfile.mkdtemp(prefix='bzr-limbo-')
3008.1.15 by Aaron Bentley
Make case_sensitive an aspect of the transform, not the source tree
1331
        TreeTransformBase.__init__(self, tree, limbodir, pb, case_sensitive)
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1332
1333
    def canonical_path(self, path):
1334
        return path
1335
3008.1.9 by Michael Hudson
wanton hacking that lets me write an efficient version of get_diff_as_merged
1336
    def tree_kind(self, trans_id):
1337
        path = self._tree_id_paths.get(trans_id)
3008.2.1 by Aaron Bentley
Ensure conflict resolution works
1338
        if path is None:
1339
            raise NoSuchFile(None)
3008.1.9 by Michael Hudson
wanton hacking that lets me write an efficient version of get_diff_as_merged
1340
        file_id = self._tree.path2id(path)
1341
        return self._tree.kind(file_id)
1342
1343
    def _set_mode(self, trans_id, mode_id, typefunc):
1344
        """Set the mode of new file contents.
1345
        The mode_id is the existing file to get the mode from (often the same
1346
        as trans_id).  The operation is only performed if there's a mode match
1347
        according to typefunc.
1348
        """
1349
        # is it ok to ignore this?  probably
1350
        pass
1351
1352
    def iter_tree_children(self, parent_id):
1353
        """Iterate through the entry's tree children, if any"""
3008.2.1 by Aaron Bentley
Ensure conflict resolution works
1354
        try:
1355
            path = self._tree_id_paths[parent_id]
1356
        except KeyError:
1357
            return
1358
        file_id = self.tree_file_id(parent_id)
1359
        for child in self._tree.inventory[file_id].children.iterkeys():
1360
            childpath = joinpath(path, child)
1361
            yield self.trans_id_tree_path(childpath)
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1362
3008.1.29 by Aaron Bentley
Add docstrings, rename TT.__doc
1363
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1364
class _PreviewTree(object):
3008.1.29 by Aaron Bentley
Add docstrings, rename TT.__doc
1365
    """Partial implementation of Tree to support show_diff_trees"""
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1366
1367
    def __init__(self, transform):
1368
        self._transform = transform
1369
1370
    def lock_read(self):
3008.1.18 by Aaron Bentley
Get supported PreviewTree functionality under test
1371
        # Perhaps in theory, this should lock the TreeTransform?
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1372
        pass
1373
1374
    def unlock(self):
1375
        pass
1376
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1377
    def iter_changes(self, from_tree, include_unchanged=False,
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1378
                      specific_files=None, pb=None, extra_trees=None,
1379
                      require_versioned=True, want_unversioned=False):
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1380
        """See InterTree.iter_changes.
3008.1.17 by Aaron Bentley
Test unused parameters of preview_tree._iter_changes
1381
1382
        This implementation does not support include_unchanged, specific_files,
1383
        or want_unversioned.  extra_trees, require_versioned, and pb are
1384
        ignored.
1385
        """
1386
        if from_tree is not self._transform._tree:
1387
            raise ValueError('from_tree must be transform source tree.')
1388
        if include_unchanged:
1389
            raise ValueError('include_unchanged is not supported')
1390
        if specific_files is not None:
1391
            raise ValueError('specific_files is not supported')
1392
        if want_unversioned:
1393
            raise ValueError('want_unversioned is not supported')
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1394
        return self._transform.iter_changes()
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1395
3008.1.3 by Aaron Bentley
Support basic diff
1396
    def kind(self, file_id):
1397
        trans_id = self._transform.trans_id_file_id(file_id)
1398
        return self._transform.final_kind(trans_id)
1399
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1400
    def get_file_mtime(self, file_id, path=None):
3008.1.20 by Aaron Bentley
Add some docstrings
1401
        """See Tree.get_file_mtime"""
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1402
        trans_id = self._transform.trans_id_file_id(file_id)
1403
        name = self._transform._limbo_name(trans_id)
1404
        return os.stat(name).st_mtime
1405
3008.1.3 by Aaron Bentley
Support basic diff
1406
    def get_file(self, file_id):
3008.1.20 by Aaron Bentley
Add some docstrings
1407
        """See Tree.get_file"""
3008.1.3 by Aaron Bentley
Support basic diff
1408
        trans_id = self._transform.trans_id_file_id(file_id)
1409
        name = self._transform._limbo_name(trans_id)
1410
        return open(name, 'rb')
1411
3228.1.1 by James Henstridge
* Add get_symlink_target() method to bzrlib.transform._PreviewTree, so
1412
    def get_symlink_target(self, file_id):
1413
        """See Tree.get_symlink_target"""
1414
        trans_id = self._transform.trans_id_file_id(file_id)
1415
        name = self._transform._limbo_name(trans_id)
1416
        return os.readlink(name)
1417
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1418
    def paths2ids(self, specific_files, trees=None, require_versioned=False):
3008.1.29 by Aaron Bentley
Add docstrings, rename TT.__doc
1419
        """See Tree.paths2ids"""
3008.1.1 by Aaron Bentley
Start work allowing previews of transforms
1420
        return 'not_empty'
1421
1422
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
1423
def joinpath(parent, child):
1534.7.40 by Aaron Bentley
Updated docs
1424
    """Join tree-relative paths, handling the tree root specially"""
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
1425
    if parent is None or parent == "":
1426
        return child
1427
    else:
1534.7.166 by Aaron Bentley
Swapped os.path.join for pathjoin everywhere
1428
        return pathjoin(parent, child)
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
1429
1534.7.167 by Aaron Bentley
PEP8 and comment cleanups
1430
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
1431
class FinalPaths(object):
1759.2.2 by Jelmer Vernooij
Revert some of my spelling fixes and fix some typos after review by Aaron.
1432
    """Make path calculation cheap by memoizing paths.
1534.7.21 by Aaron Bentley
Updated docstrings
1433
1434
    The underlying tree must not be manipulated between calls, or else
1435
    the results will likely be incorrect.
1436
    """
1534.7.132 by Aaron Bentley
Got cooked conflicts working
1437
    def __init__(self, transform):
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
1438
        object.__init__(self)
1439
        self._known_paths = {}
1534.7.33 by Aaron Bentley
Fixed naming
1440
        self.transform = transform
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
1441
1442
    def _determine_path(self, trans_id):
1534.7.132 by Aaron Bentley
Got cooked conflicts working
1443
        if trans_id == self.transform.root:
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
1444
            return ""
1534.7.33 by Aaron Bentley
Fixed naming
1445
        name = self.transform.final_name(trans_id)
1446
        parent_id = self.transform.final_parent(trans_id)
1534.7.132 by Aaron Bentley
Got cooked conflicts working
1447
        if parent_id == self.transform.root:
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
1448
            return name
1449
        else:
1534.7.166 by Aaron Bentley
Swapped os.path.join for pathjoin everywhere
1450
            return pathjoin(self.get_path(parent_id), name)
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
1451
1452
    def get_path(self, trans_id):
1534.7.157 by Aaron Bentley
Added more docs
1453
        """Find the final path associated with a trans_id"""
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
1454
        if trans_id not in self._known_paths:
1455
            self._known_paths[trans_id] = self._determine_path(trans_id)
1456
        return self._known_paths[trans_id]
1534.7.28 by Aaron Bentley
Nearly-working build_tree replacement
1457
3006.2.2 by Alexander Belchenko
tests added.
1458
1534.7.30 by Aaron Bentley
Factored out topological id sorting
1459
def topology_sorted_ids(tree):
1534.7.40 by Aaron Bentley
Updated docs
1460
    """Determine the topological order of the ids in a tree"""
1534.7.30 by Aaron Bentley
Factored out topological id sorting
1461
    file_ids = list(tree)
1462
    file_ids.sort(key=tree.id2path)
1463
    return file_ids
1534.7.28 by Aaron Bentley
Nearly-working build_tree replacement
1464
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1465
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1466
def build_tree(tree, wt, accelerator_tree=None, hardlink=False):
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1467
    """Create working tree for a branch, using a TreeTransform.
1468
    
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1469
    This function should be used on empty trees, having a tree root at most.
1470
    (see merge and revert functionality for working with existing trees)
1471
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1472
    Existing files are handled like so:
1473
    
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1474
    - Existing bzrdirs take precedence over creating new items.  They are
1475
      created as '%s.diverted' % name.
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1476
    - Otherwise, if the content on disk matches the content we are building,
1477
      it is silently replaced.
1478
    - Otherwise, conflict resolution will move the old file to 'oldname.moved'.
3123.5.17 by Aaron Bentley
Update docs
1479
1480
    :param tree: The tree to convert wt into a copy of
1481
    :param wt: The working tree that files will be placed into
1482
    :param accelerator_tree: A tree which can be used for retrieving file
1483
        contents more quickly than tree itself, i.e. a workingtree.  tree
1484
        will be used for cases where accelerator_tree's content is different.
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1485
    :param hardlink: If true, hard-link files to accelerator_tree, where
3136.1.4 by Aaron Bentley
Avoid id2abspath calls
1486
        possible.  accelerator_tree must implement abspath, i.e. be a
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1487
        working tree.
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1488
    """
2255.7.51 by Robert Collins
Lock build_tree trees in write-first order, to support older formats that dont do lock_tree_write nicely.
1489
    wt.lock_tree_write()
2255.7.49 by Robert Collins
Lock trees passed in to build_tree.
1490
    try:
2255.7.51 by Robert Collins
Lock build_tree trees in write-first order, to support older formats that dont do lock_tree_write nicely.
1491
        tree.lock_read()
2255.7.49 by Robert Collins
Lock trees passed in to build_tree.
1492
        try:
3123.5.1 by Aaron Bentley
Make build-tree able to use an additional 'accelerator' tree
1493
            if accelerator_tree is not None:
1494
                accelerator_tree.lock_read()
1495
            try:
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1496
                return _build_tree(tree, wt, accelerator_tree, hardlink)
3123.5.1 by Aaron Bentley
Make build-tree able to use an additional 'accelerator' tree
1497
            finally:
1498
                if accelerator_tree is not None:
1499
                    accelerator_tree.unlock()
2255.7.49 by Robert Collins
Lock trees passed in to build_tree.
1500
        finally:
2255.7.51 by Robert Collins
Lock build_tree trees in write-first order, to support older formats that dont do lock_tree_write nicely.
1501
            tree.unlock()
2255.7.49 by Robert Collins
Lock trees passed in to build_tree.
1502
    finally:
2255.7.51 by Robert Collins
Lock build_tree trees in write-first order, to support older formats that dont do lock_tree_write nicely.
1503
        wt.unlock()
2255.7.49 by Robert Collins
Lock trees passed in to build_tree.
1504
3006.2.2 by Alexander Belchenko
tests added.
1505
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1506
def _build_tree(tree, wt, accelerator_tree, hardlink):
2255.7.49 by Robert Collins
Lock trees passed in to build_tree.
1507
    """See build_tree."""
3146.8.17 by Aaron Bentley
Change to explicit file_id list
1508
    for num, _unused in enumerate(wt.all_file_ids()):
3146.8.16 by Aaron Bentley
Updates from review
1509
        if num > 0:  # more than just a root
1510
            raise errors.WorkingTreeAlreadyPopulated(base=wt.basedir)
1534.7.28 by Aaron Bentley
Nearly-working build_tree replacement
1511
    file_trans_id = {}
1558.11.1 by Aaron Bentley
Progress indicator for tree builts
1512
    top_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1513
    pp = ProgressPhase("Build phase", 2, top_pb)
2255.2.183 by Martin Pool
add missing _must_be_locked and a better message
1514
    if tree.inventory.root is not None:
2502.1.6 by Aaron Bentley
Update from review comments
1515
        # This is kind of a hack: we should be altering the root
1516
        # as part of the regular tree shape diff logic.
1517
        # The conditional test here is to avoid doing an
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
1518
        # expensive operation (flush) every time the root id
1519
        # is set within the tree, nor setting the root and thus
1520
        # marking the tree as dirty, because we use two different
1521
        # idioms here: tree interfaces and inventory interfaces.
2946.3.3 by John Arbash Meinel
Prefer tree.get_root_id() as more explicit than tree.path2id('')
1522
        if wt.get_root_id() != tree.get_root_id():
1523
            wt.set_root_id(tree.get_root_id())
2255.2.194 by Robert Collins
[BROKEN] Many updates to stop using experimental formats in tests.
1524
            wt.flush()
1534.7.28 by Aaron Bentley
Nearly-working build_tree replacement
1525
    tt = TreeTransform(wt)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1526
    divert = set()
1534.7.28 by Aaron Bentley
Nearly-working build_tree replacement
1527
    try:
1558.11.1 by Aaron Bentley
Progress indicator for tree builts
1528
        pp.next_phase()
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1529
        file_trans_id[wt.get_root_id()] = \
1530
            tt.trans_id_tree_file_id(wt.get_root_id())
1558.11.1 by Aaron Bentley
Progress indicator for tree builts
1531
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
1532
        try:
2708.1.2 by Aaron Bentley
Use extract_files_bytes for build_tree
1533
            deferred_contents = []
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1534
            num = 0
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1535
            for num, (tree_path, entry) in \
1536
                enumerate(tree.inventory.iter_entries_by_dir()):
2708.1.8 by Aaron Bentley
rename extract_files_bytest to iter_files_bytes, fix build_tree / progress
1537
                pb.update("Building tree", num - len(deferred_contents),
1538
                          len(tree.inventory))
1558.11.1 by Aaron Bentley
Progress indicator for tree builts
1539
                if entry.parent_id is None:
1540
                    continue
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1541
                reparent = False
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1542
                file_id = entry.file_id
1543
                target_path = wt.abspath(tree_path)
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1544
                try:
1545
                    kind = file_kind(target_path)
1546
                except NoSuchFile:
1547
                    pass
1548
                else:
1549
                    if kind == "directory":
1550
                        try:
1551
                            bzrdir.BzrDir.open(target_path)
1552
                        except errors.NotBranchError:
1553
                            pass
1554
                        else:
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1555
                            divert.add(file_id)
1556
                    if (file_id not in divert and
1557
                        _content_match(tree, entry, file_id, kind,
1558
                        target_path)):
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1559
                        tt.delete_contents(tt.trans_id_tree_path(tree_path))
1560
                        if kind == 'directory':
1561
                            reparent = True
1558.11.1 by Aaron Bentley
Progress indicator for tree builts
1562
                if entry.parent_id not in file_trans_id:
2255.2.183 by Martin Pool
add missing _must_be_locked and a better message
1563
                    raise AssertionError(
1564
                        'entry %s parent id %r is not in file_trans_id %r'
1565
                        % (entry, entry.parent_id, file_trans_id))
1558.11.1 by Aaron Bentley
Progress indicator for tree builts
1566
                parent_id = file_trans_id[entry.parent_id]
2708.1.8 by Aaron Bentley
rename extract_files_bytest to iter_files_bytes, fix build_tree / progress
1567
                if entry.kind == 'file':
2708.1.2 by Aaron Bentley
Use extract_files_bytes for build_tree
1568
                    # We *almost* replicate new_by_entry, so that we can defer
1569
                    # getting the file text, and get them all at once.
1570
                    trans_id = tt.create_path(entry.name, parent_id)
2708.1.8 by Aaron Bentley
rename extract_files_bytest to iter_files_bytes, fix build_tree / progress
1571
                    file_trans_id[file_id] = trans_id
1572
                    tt.version_file(entry.file_id, trans_id)
2708.1.2 by Aaron Bentley
Use extract_files_bytes for build_tree
1573
                    executable = tree.is_executable(entry.file_id, tree_path)
1574
                    if executable is not None:
2708.1.8 by Aaron Bentley
rename extract_files_bytest to iter_files_bytes, fix build_tree / progress
1575
                        tt.set_executability(executable, trans_id)
2708.1.2 by Aaron Bentley
Use extract_files_bytes for build_tree
1576
                    deferred_contents.append((entry.file_id, trans_id))
1577
                else:
1578
                    file_trans_id[file_id] = new_by_entry(tt, entry, parent_id,
1579
                                                          tree)
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1580
                if reparent:
1581
                    new_trans_id = file_trans_id[file_id]
1582
                    old_parent = tt.trans_id_tree_path(tree_path)
1583
                    _reparent_children(tt, old_parent, new_trans_id)
3136.1.11 by Aaron Bentley
Updates from review
1584
            offset = num + 1 - len(deferred_contents)
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1585
            _create_files(tt, tree, deferred_contents, pb, offset,
1586
                          accelerator_tree, hardlink)
1558.11.1 by Aaron Bentley
Progress indicator for tree builts
1587
        finally:
1588
            pb.finished()
1589
        pp.next_phase()
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1590
        divert_trans = set(file_trans_id[f] for f in divert)
1591
        resolver = lambda t, c: resolve_checkout(t, c, divert_trans)
1592
        raw_conflicts = resolve_conflicts(tt, pass_func=resolver)
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1593
        conflicts = cook_conflicts(raw_conflicts, tt)
1594
        for conflict in conflicts:
1595
            warning(conflict)
1596
        try:
1597
            wt.add_conflicts(conflicts)
1598
        except errors.UnsupportedOperation:
1599
            pass
3123.5.7 by Aaron Bentley
Avoid redundant conflict check
1600
        result = tt.apply(no_conflicts=True)
1534.7.47 by Aaron Bentley
Started work on 'revert'
1601
    finally:
1602
        tt.finalize()
1558.11.1 by Aaron Bentley
Progress indicator for tree builts
1603
        top_pb.finished()
2502.1.5 by Aaron Bentley
Cleanup
1604
    return result
1534.7.47 by Aaron Bentley
Started work on 'revert'
1605
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1606
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1607
def _create_files(tt, tree, desired_files, pb, offset, accelerator_tree,
1608
                  hardlink):
1609
    total = len(desired_files) + offset
3123.5.5 by Aaron Bentley
Split out _iter_files_bytes_accelerated
1610
    if accelerator_tree is None:
1611
        new_desired_files = desired_files
1612
    else:
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1613
        iter = accelerator_tree.iter_changes(tree, include_unchanged=True)
3137.1.1 by Aaron Bentley
Fix build_tree acceleration when file is moved in accelerator_tree
1614
        unchanged = dict((f, p[1]) for (f, p, c, v, d, n, k, e)
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1615
                         in iter if not (c or e[0] != e[1]))
3123.5.5 by Aaron Bentley
Split out _iter_files_bytes_accelerated
1616
        new_desired_files = []
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1617
        count = 0
1618
        for file_id, trans_id in desired_files:
3123.5.13 by Aaron Bentley
Accelerate further by using iter_changes
1619
            accelerator_path = unchanged.get(file_id)
3123.5.12 by Aaron Bentley
Try to optimize iter_changes_accelerated
1620
            if accelerator_path is None:
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1621
                new_desired_files.append((file_id, trans_id))
3123.5.12 by Aaron Bentley
Try to optimize iter_changes_accelerated
1622
                continue
3136.1.11 by Aaron Bentley
Updates from review
1623
            pb.update('Adding file contents', count + offset, total)
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1624
            if hardlink:
3136.1.4 by Aaron Bentley
Avoid id2abspath calls
1625
                tt.create_hardlink(accelerator_tree.abspath(accelerator_path),
3136.1.2 by Aaron Bentley
Implement hard-linking for build_tree
1626
                                   trans_id)
1627
            else:
1628
                contents = accelerator_tree.get_file(file_id, accelerator_path)
1629
                try:
1630
                    tt.create_file(contents, trans_id)
1631
                finally:
1632
                    contents.close()
1633
            count += 1
1634
        offset += count
1635
    for count, (trans_id, contents) in enumerate(tree.iter_files_bytes(
1636
                                                 new_desired_files)):
1637
        tt.create_file(contents, trans_id)
3136.1.11 by Aaron Bentley
Updates from review
1638
        pb.update('Adding file contents', count + offset, total)
3123.5.5 by Aaron Bentley
Split out _iter_files_bytes_accelerated
1639
1640
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1641
def _reparent_children(tt, old_parent, new_parent):
1642
    for child in tt.iter_tree_children(old_parent):
1643
        tt.adjust_path(tt.final_name(child), new_parent, child)
1644
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
1645
def _reparent_transform_children(tt, old_parent, new_parent):
1646
    by_parent = tt.by_parent()
1647
    for child in by_parent[old_parent]:
1648
        tt.adjust_path(tt.final_name(child), new_parent, child)
1551.19.32 by Aaron Bentley
Don't traceback when adding files to a deleted root (abentley, #210092)
1649
    return by_parent[old_parent]
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1650
1651
def _content_match(tree, entry, file_id, kind, target_path):
1652
    if entry.kind != kind:
1653
        return False
1654
    if entry.kind == "directory":
1655
        return True
1656
    if entry.kind == "file":
1657
        if tree.get_file(file_id).read() == file(target_path, 'rb').read():
1658
            return True
1659
    elif entry.kind == "symlink":
1660
        if tree.get_symlink_target(file_id) == os.readlink(target_path):
1661
            return True
1662
    return False
1663
1664
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1665
def resolve_checkout(tt, conflicts, divert):
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1666
    new_conflicts = set()
1667
    for c_type, conflict in ((c[0], c) for c in conflicts):
1668
        # Anything but a 'duplicate' would indicate programmer error
1669
        assert c_type == 'duplicate', c_type
1670
        # Now figure out which is new and which is old
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1671
        if tt.new_contents(conflict[1]):
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1672
            new_file = conflict[1]
1673
            old_file = conflict[2]
1674
        else:
1675
            new_file = conflict[2]
1676
            old_file = conflict[1]
1677
1678
        # We should only get here if the conflict wasn't completely
1679
        # resolved
1680
        final_parent = tt.final_parent(old_file)
1966.1.2 by Aaron Bentley
Divert files instead of failing to create them, update from review
1681
        if new_file in divert:
1682
            new_name = tt.final_name(old_file)+'.diverted'
1683
            tt.adjust_path(new_name, final_parent, new_file)
1684
            new_conflicts.add((c_type, 'Diverted to',
1685
                               new_file, old_file))
1686
        else:
1687
            new_name = tt.final_name(old_file)+'.moved'
1688
            tt.adjust_path(new_name, final_parent, old_file)
1689
            new_conflicts.add((c_type, 'Moved existing file to',
1690
                               old_file, new_file))
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1691
    return new_conflicts
1692
1693
1534.7.47 by Aaron Bentley
Started work on 'revert'
1694
def new_by_entry(tt, entry, parent_id, tree):
1534.7.157 by Aaron Bentley
Added more docs
1695
    """Create a new file according to its inventory entry"""
1534.7.47 by Aaron Bentley
Started work on 'revert'
1696
    name = entry.name
1697
    kind = entry.kind
1698
    if kind == 'file':
1534.7.79 by Aaron Bentley
Stopped calling get_file_lines on WorkingTree
1699
        contents = tree.get_file(entry.file_id).readlines()
1534.7.47 by Aaron Bentley
Started work on 'revert'
1700
        executable = tree.is_executable(entry.file_id)
1701
        return tt.new_file(name, parent_id, contents, entry.file_id, 
1702
                           executable)
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
1703
    elif kind in ('directory', 'tree-reference'):
1704
        trans_id = tt.new_directory(name, parent_id, entry.file_id)
1705
        if kind == 'tree-reference':
1706
            tt.set_tree_reference(entry.reference_revision, trans_id)
1707
        return trans_id 
1534.7.47 by Aaron Bentley
Started work on 'revert'
1708
    elif kind == 'symlink':
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
1709
        target = tree.get_symlink_target(entry.file_id)
1710
        return tt.new_symlink(name, parent_id, target, entry.file_id)
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
1711
    else:
1712
        raise errors.BadFileKindError(name, kind)
1534.7.47 by Aaron Bentley
Started work on 'revert'
1713
3006.2.2 by Alexander Belchenko
tests added.
1714
1534.7.117 by Aaron Bentley
Simplified permission handling of existing files in transform.
1715
def create_by_entry(tt, entry, tree, trans_id, lines=None, mode_id=None):
1534.7.157 by Aaron Bentley
Added more docs
1716
    """Create new file contents according to an inventory entry."""
1534.7.47 by Aaron Bentley
Started work on 'revert'
1717
    if entry.kind == "file":
1963.2.6 by Robey Pointer
pychecker is on crack; go back to using 'is None'.
1718
        if lines is None:
1534.7.97 by Aaron Bentley
Ensured foo.BASE is a directory if there's a conflict
1719
            lines = tree.get_file(entry.file_id).readlines()
1534.7.117 by Aaron Bentley
Simplified permission handling of existing files in transform.
1720
        tt.create_file(lines, trans_id, mode_id=mode_id)
1534.7.47 by Aaron Bentley
Started work on 'revert'
1721
    elif entry.kind == "symlink":
1534.7.101 by Aaron Bentley
Got conflicts on symlinks working properly
1722
        tt.create_symlink(tree.get_symlink_target(entry.file_id), trans_id)
1534.7.47 by Aaron Bentley
Started work on 'revert'
1723
    elif entry.kind == "directory":
1534.7.51 by Aaron Bentley
New approach to revert
1724
        tt.create_directory(trans_id)
1534.7.47 by Aaron Bentley
Started work on 'revert'
1725
3006.2.2 by Alexander Belchenko
tests added.
1726
1534.7.89 by Aaron Bentley
Handle all content types in three-way
1727
def create_entry_executability(tt, entry, trans_id):
1534.7.157 by Aaron Bentley
Added more docs
1728
    """Set the executability of a trans_id according to an inventory entry"""
1534.7.89 by Aaron Bentley
Handle all content types in three-way
1729
    if entry.kind == "file":
1730
        tt.set_executability(entry.executable, trans_id)
1534.7.47 by Aaron Bentley
Started work on 'revert'
1731
1534.7.157 by Aaron Bentley
Added more docs
1732
2255.7.48 by Robert Collins
Deprecated and make work with DirState trees 'transform.find_interesting'.
1733
@deprecated_function(zero_fifteen)
1534.7.55 by Aaron Bentley
Fixed up the change detection
1734
def find_interesting(working_tree, target_tree, filenames):
2255.7.48 by Robert Collins
Deprecated and make work with DirState trees 'transform.find_interesting'.
1735
    """Find the ids corresponding to specified filenames.
1736
    
1737
    Deprecated: Please use tree1.paths2ids(filenames, [tree2]).
1738
    """
1739
    working_tree.lock_read()
1740
    try:
1741
        target_tree.lock_read()
1742
        try:
1743
            return working_tree.paths2ids(filenames, [target_tree])
1744
        finally:
1745
            target_tree.unlock()
1746
    finally:
1747
        working_tree.unlock()
1534.7.55 by Aaron Bentley
Fixed up the change detection
1748
1749
2687.2.1 by Martin Pool
Rename upcoming release from 0.19 to 0.90
1750
@deprecated_function(zero_ninety)
1534.7.56 by Aaron Bentley
Implemented the backup file detritus
1751
def change_entry(tt, file_id, working_tree, target_tree, 
1534.10.28 by Aaron Bentley
Use numbered backup files
1752
                 trans_id_file_id, backups, trans_id, by_parent):
1534.7.157 by Aaron Bentley
Added more docs
1753
    """Replace a file_id's contents with those from a target tree."""
2625.4.3 by Ian Clatworthy
Add a test to ensure the deprecation worked.
1754
    if file_id is None and target_tree is None:
1755
        # skip the logic altogether in the deprecation test
1756
        return
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
1757
    e_trans_id = trans_id_file_id(file_id)
1534.7.55 by Aaron Bentley
Fixed up the change detection
1758
    entry = target_tree.inventory[file_id]
1759
    has_contents, contents_mod, meta_mod, = _entry_changes(file_id, entry, 
1760
                                                           working_tree)
1761
    if contents_mod:
1534.7.117 by Aaron Bentley
Simplified permission handling of existing files in transform.
1762
        mode_id = e_trans_id
1534.7.55 by Aaron Bentley
Fixed up the change detection
1763
        if has_contents:
1534.7.56 by Aaron Bentley
Implemented the backup file detritus
1764
            if not backups:
1765
                tt.delete_contents(e_trans_id)
1766
            else:
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
1767
                parent_trans_id = trans_id_file_id(entry.parent_id)
1534.10.28 by Aaron Bentley
Use numbered backup files
1768
                backup_name = get_backup_name(entry, by_parent,
1769
                                              parent_trans_id, tt)
1770
                tt.adjust_path(backup_name, parent_trans_id, e_trans_id)
1534.7.56 by Aaron Bentley
Implemented the backup file detritus
1771
                tt.unversion_file(e_trans_id)
1772
                e_trans_id = tt.create_path(entry.name, parent_trans_id)
1773
                tt.version_file(file_id, e_trans_id)
1774
                trans_id[file_id] = e_trans_id
1534.7.117 by Aaron Bentley
Simplified permission handling of existing files in transform.
1775
        create_by_entry(tt, entry, target_tree, e_trans_id, mode_id=mode_id)
1534.7.89 by Aaron Bentley
Handle all content types in three-way
1776
        create_entry_executability(tt, entry, e_trans_id)
1777
1711.4.26 by John Arbash Meinel
Fix #45010 correctly. Don't forget the execute bit.
1778
    elif meta_mod:
1779
        tt.set_executability(entry.executable, e_trans_id)
1534.7.55 by Aaron Bentley
Fixed up the change detection
1780
    if tt.final_name(e_trans_id) != entry.name:
1781
        adjust_path  = True
1782
    else:
1783
        parent_id = tt.final_parent(e_trans_id)
1784
        parent_file_id = tt.final_file_id(parent_id)
1785
        if parent_file_id != entry.parent_id:
1786
            adjust_path = True
1787
        else:
1788
            adjust_path = False
1789
    if adjust_path:
1534.7.181 by Aaron Bentley
Renamed a bunch of functions
1790
        parent_trans_id = trans_id_file_id(entry.parent_id)
1534.7.56 by Aaron Bentley
Implemented the backup file detritus
1791
        tt.adjust_path(entry.name, parent_trans_id, e_trans_id)
1534.7.55 by Aaron Bentley
Fixed up the change detection
1792
1793
1534.10.28 by Aaron Bentley
Use numbered backup files
1794
def get_backup_name(entry, by_parent, parent_trans_id, tt):
2012.1.12 by Aaron Bentley
Use iter_changes for revert
1795
    return _get_backup_name(entry.name, by_parent, parent_trans_id, tt)
1796
1797
1798
def _get_backup_name(name, by_parent, parent_trans_id, tt):
1534.10.28 by Aaron Bentley
Use numbered backup files
1799
    """Produce a backup-style name that appears to be available"""
1800
    def name_gen():
1801
        counter = 1
1802
        while True:
2012.1.12 by Aaron Bentley
Use iter_changes for revert
1803
            yield "%s.~%d~" % (name, counter)
1534.10.28 by Aaron Bentley
Use numbered backup files
1804
            counter += 1
2012.1.12 by Aaron Bentley
Use iter_changes for revert
1805
    for new_name in name_gen():
1806
        if not tt.has_named_child(by_parent, parent_trans_id, new_name):
1807
            return new_name
1808
1534.10.28 by Aaron Bentley
Use numbered backup files
1809
1534.7.55 by Aaron Bentley
Fixed up the change detection
1810
def _entry_changes(file_id, entry, working_tree):
1534.7.156 by Aaron Bentley
PEP8 fixes
1811
    """Determine in which ways the inventory entry has changed.
1534.7.55 by Aaron Bentley
Fixed up the change detection
1812
1813
    Returns booleans: has_contents, content_mod, meta_mod
1814
    has_contents means there are currently contents, but they differ
1815
    contents_mod means contents need to be modified
1816
    meta_mod means the metadata needs to be modified
1817
    """
1818
    cur_entry = working_tree.inventory[file_id]
1819
    try:
1820
        working_kind = working_tree.kind(file_id)
1821
        has_contents = True
1757.2.4 by Robert Collins
Teach file_kind about NoSuchFile, reducing duplicate code, and add user files before entering the main loop in smart_add.
1822
    except NoSuchFile:
1534.7.55 by Aaron Bentley
Fixed up the change detection
1823
        has_contents = False
1824
        contents_mod = True
1825
        meta_mod = False
1826
    if has_contents is True:
1731.1.1 by Aaron Bentley
Make root entry an InventoryDirectory, make EmptyTree really empty
1827
        if entry.kind != working_kind:
1534.7.55 by Aaron Bentley
Fixed up the change detection
1828
            contents_mod, meta_mod = True, False
1829
        else:
1830
            cur_entry._read_tree_state(working_tree.id2path(file_id), 
1831
                                       working_tree)
1832
            contents_mod, meta_mod = entry.detect_changes(cur_entry)
1534.7.175 by Aaron Bentley
Ensured revert writes a normal inventory
1833
            cur_entry._forget_tree_state()
1534.7.55 by Aaron Bentley
Fixed up the change detection
1834
    return has_contents, contents_mod, meta_mod
1835
1534.7.56 by Aaron Bentley
Implemented the backup file detritus
1836
2255.2.53 by Robert Collins
Teach TreeTransform to lock basis_trees if it acquires them, fixing revert on a dirstate working tree.
1837
def revert(working_tree, target_tree, filenames, backups=False,
2225.1.1 by Aaron Bentley
Added revert change display, with tests
1838
           pb=DummyProgress(), change_reporter=None):
1534.7.157 by Aaron Bentley
Added more docs
1839
    """Revert a working tree's contents to those of a target tree."""
2255.2.53 by Robert Collins
Teach TreeTransform to lock basis_trees if it acquires them, fixing revert on a dirstate working tree.
1840
    target_tree.lock_read()
1534.9.7 by Aaron Bentley
Show progress bars in revert
1841
    tt = TreeTransform(working_tree, pb)
1534.7.47 by Aaron Bentley
Started work on 'revert'
1842
    try:
2012.1.12 by Aaron Bentley
Use iter_changes for revert
1843
        pp = ProgressPhase("Revert phase", 3, pb)
1844
        pp.next_phase()
1845
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1846
        try:
2499.1.1 by Aaron Bentley
Revert does not try to preserve file contents produced by revert
1847
            merge_modified = _alter_files(working_tree, target_tree, tt,
1848
                                          child_pb, filenames, backups)
1551.2.31 by Aaron Bentley
Got merge and revert using nested pbs
1849
        finally:
1850
            child_pb.finished()
1551.2.34 by Aaron Bentley
Refactored the revert phases
1851
        pp.next_phase()
1551.2.31 by Aaron Bentley
Got merge and revert using nested pbs
1852
        child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
1853
        try:
1551.19.5 by Aaron Bentley
Fix revert when parent dir is missing
1854
            raw_conflicts = resolve_conflicts(tt, child_pb,
1855
                lambda t, c: conflict_pass(t, c, target_tree))
1551.2.31 by Aaron Bentley
Got merge and revert using nested pbs
1856
        finally:
1857
            child_pb.finished()
1558.7.13 by Aaron Bentley
WorkingTree.revert returns conflicts
1858
        conflicts = cook_conflicts(raw_conflicts, tt)
1551.11.5 by Aaron Bentley
cleanup
1859
        if change_reporter:
1551.10.25 by Aaron Bentley
Make ChangeReporter private
1860
            change_reporter = delta._ChangeReporter(
2255.7.98 by Robert Collins
Merge bzr.dev.
1861
                unversioned_filter=working_tree.is_ignored)
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1862
            delta.report_changes(tt.iter_changes(), change_reporter)
1551.11.6 by Aaron Bentley
Emit change listings before conflict warnings
1863
        for conflict in conflicts:
1864
            warning(conflict)
1865
        pp.next_phase()
1534.7.28 by Aaron Bentley
Nearly-working build_tree replacement
1866
        tt.apply()
2499.1.1 by Aaron Bentley
Revert does not try to preserve file contents produced by revert
1867
        working_tree.set_merge_modified(merge_modified)
1534.7.28 by Aaron Bentley
Nearly-working build_tree replacement
1868
    finally:
2255.2.53 by Robert Collins
Teach TreeTransform to lock basis_trees if it acquires them, fixing revert on a dirstate working tree.
1869
        target_tree.unlock()
1534.7.28 by Aaron Bentley
Nearly-working build_tree replacement
1870
        tt.finalize()
1534.9.4 by Aaron Bentley
Added progress bars to revert.
1871
        pb.clear()
1558.7.13 by Aaron Bentley
WorkingTree.revert returns conflicts
1872
    return conflicts
1534.7.51 by Aaron Bentley
New approach to revert
1873
1534.7.57 by Aaron Bentley
Enhanced conflict resolution.
1874
2255.2.149 by Robert Collins
Crufty but existing _iter_changes implementation for WorkingTreeFormat4.
1875
def _alter_files(working_tree, target_tree, tt, pb, specific_files,
1551.11.3 by Aaron Bentley
Use tree transform to emit upcoming change list
1876
                 backups):
2012.1.12 by Aaron Bentley
Use iter_changes for revert
1877
    merge_modified = working_tree.merge_modified()
3254.1.1 by Aaron Bentley
Make Tree.iter_changes a public method
1878
    change_list = target_tree.iter_changes(working_tree,
2255.2.149 by Robert Collins
Crufty but existing _iter_changes implementation for WorkingTreeFormat4.
1879
        specific_files=specific_files, pb=pb)
2012.1.12 by Aaron Bentley
Use iter_changes for revert
1880
    if target_tree.inventory.root is None:
1881
        skip_root = True
1882
    else:
1883
        skip_root = False
1884
    basis_tree = None
2255.2.53 by Robert Collins
Teach TreeTransform to lock basis_trees if it acquires them, fixing revert on a dirstate working tree.
1885
    try:
2708.1.5 by Aaron Bentley
Use Tree.extract_files_bytes in revert
1886
        deferred_files = []
2255.2.53 by Robert Collins
Teach TreeTransform to lock basis_trees if it acquires them, fixing revert on a dirstate working tree.
1887
        for id_num, (file_id, path, changed_content, versioned, parent, name,
1888
                kind, executable) in enumerate(change_list):
1889
            if skip_root and file_id[0] is not None and parent[0] is None:
1890
                continue
1891
            trans_id = tt.trans_id_file_id(file_id)
1892
            mode_id = None
1893
            if changed_content:
1894
                keep_content = False
1895
                if kind[0] == 'file' and (backups or kind[1] is None):
1896
                    wt_sha1 = working_tree.get_file_sha1(file_id)
1897
                    if merge_modified.get(file_id) != wt_sha1:
2502.1.6 by Aaron Bentley
Update from review comments
1898
                        # acquire the basis tree lazily to prevent the
1899
                        # expense of accessing it when it's not needed ?
1900
                        # (Guessing, RBC, 200702)
2255.2.53 by Robert Collins
Teach TreeTransform to lock basis_trees if it acquires them, fixing revert on a dirstate working tree.
1901
                        if basis_tree is None:
1902
                            basis_tree = working_tree.basis_tree()
1903
                            basis_tree.lock_read()
1904
                        if file_id in basis_tree:
1905
                            if wt_sha1 != basis_tree.get_file_sha1(file_id):
1906
                                keep_content = True
1907
                        elif kind[1] is None and not versioned[1]:
2012.1.12 by Aaron Bentley
Use iter_changes for revert
1908
                            keep_content = True
2255.2.53 by Robert Collins
Teach TreeTransform to lock basis_trees if it acquires them, fixing revert on a dirstate working tree.
1909
                if kind[0] is not None:
1910
                    if not keep_content:
1911
                        tt.delete_contents(trans_id)
1912
                    elif kind[1] is not None:
1913
                        parent_trans_id = tt.trans_id_file_id(parent[0])
1914
                        by_parent = tt.by_parent()
1915
                        backup_name = _get_backup_name(name[0], by_parent,
1916
                                                       parent_trans_id, tt)
1917
                        tt.adjust_path(backup_name, parent_trans_id, trans_id)
1918
                        new_trans_id = tt.create_path(name[0], parent_trans_id)
1919
                        if versioned == (True, True):
1920
                            tt.unversion_file(trans_id)
1921
                            tt.version_file(file_id, new_trans_id)
1922
                        # New contents should have the same unix perms as old
1923
                        # contents
1924
                        mode_id = trans_id
1925
                        trans_id = new_trans_id
1926
                if kind[1] == 'directory':
1927
                    tt.create_directory(trans_id)
1928
                elif kind[1] == 'symlink':
1929
                    tt.create_symlink(target_tree.get_symlink_target(file_id),
1930
                                      trans_id)
1931
                elif kind[1] == 'file':
2708.1.6 by Aaron Bentley
Turn extract_files_bytes into an iterator
1932
                    deferred_files.append((file_id, (trans_id, mode_id)))
2499.1.1 by Aaron Bentley
Revert does not try to preserve file contents produced by revert
1933
                    if basis_tree is None:
1934
                        basis_tree = working_tree.basis_tree()
1935
                        basis_tree.lock_read()
1936
                    new_sha1 = target_tree.get_file_sha1(file_id)
1937
                    if (file_id in basis_tree and new_sha1 ==
1938
                        basis_tree.get_file_sha1(file_id)):
1939
                        if file_id in merge_modified:
1940
                            del merge_modified[file_id]
1941
                    else:
1942
                        merge_modified[file_id] = new_sha1
1943
2255.2.53 by Robert Collins
Teach TreeTransform to lock basis_trees if it acquires them, fixing revert on a dirstate working tree.
1944
                    # preserve the execute bit when backing up
1945
                    if keep_content and executable[0] == executable[1]:
1946
                        tt.set_executability(executable[1], trans_id)
1947
                else:
1948
                    assert kind[1] is None
1949
            if versioned == (False, True):
1950
                tt.version_file(file_id, trans_id)
1951
            if versioned == (True, False):
1952
                tt.unversion_file(trans_id)
1953
            if (name[1] is not None and 
1954
                (name[0] != name[1] or parent[0] != parent[1])):
1955
                tt.adjust_path(
1956
                    name[1], tt.trans_id_file_id(parent[1]), trans_id)
1957
            if executable[0] != executable[1] and kind[1] == "file":
1958
                tt.set_executability(executable[1], trans_id)
2708.1.8 by Aaron Bentley
rename extract_files_bytest to iter_files_bytes, fix build_tree / progress
1959
        for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2708.1.6 by Aaron Bentley
Turn extract_files_bytes into an iterator
1960
            deferred_files):
1961
            tt.create_file(bytes, trans_id, mode_id)
2255.2.53 by Robert Collins
Teach TreeTransform to lock basis_trees if it acquires them, fixing revert on a dirstate working tree.
1962
    finally:
1963
        if basis_tree is not None:
1964
            basis_tree.unlock()
2499.1.1 by Aaron Bentley
Revert does not try to preserve file contents produced by revert
1965
    return merge_modified
2012.1.12 by Aaron Bentley
Use iter_changes for revert
1966
1967
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1968
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
1534.7.57 by Aaron Bentley
Enhanced conflict resolution.
1969
    """Make many conflict-resolution attempts, but die if they fail"""
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1970
    if pass_func is None:
1971
        pass_func = conflict_pass
1534.7.169 by Aaron Bentley
Add filesystem/inventory conflicts to conflict output
1972
    new_conflicts = set()
1534.9.1 by Aaron Bentley
Added progress bars to merge
1973
    try:
1974
        for n in range(10):
1975
            pb.update('Resolution pass', n+1, 10)
1976
            conflicts = tt.find_conflicts()
1977
            if len(conflicts) == 0:
1978
                return new_conflicts
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
1979
            new_conflicts.update(pass_func(tt, conflicts))
1534.9.1 by Aaron Bentley
Added progress bars to merge
1980
        raise MalformedTransform(conflicts=conflicts)
1981
    finally:
1982
        pb.clear()
1534.7.57 by Aaron Bentley
Enhanced conflict resolution.
1983
1984
2590.2.8 by Aaron Bentley
Restore conflict handling changes
1985
def conflict_pass(tt, conflicts, path_tree=None):
1986
    """Resolve some classes of conflicts.
1987
1988
    :param tt: The transform to resolve conflicts in
1989
    :param conflicts: The conflicts to resolve
1990
    :param path_tree: A Tree to get supplemental paths from
1991
    """
1534.7.169 by Aaron Bentley
Add filesystem/inventory conflicts to conflict output
1992
    new_conflicts = set()
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
1993
    for c_type, conflict in ((c[0], c) for c in conflicts):
1994
        if c_type == 'duplicate id':
1534.7.51 by Aaron Bentley
New approach to revert
1995
            tt.unversion_file(conflict[1])
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
1996
            new_conflicts.add((c_type, 'Unversioned existing file',
1997
                               conflict[1], conflict[2], ))
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
1998
        elif c_type == 'duplicate':
1534.7.57 by Aaron Bentley
Enhanced conflict resolution.
1999
            # files that were renamed take precedence
2000
            final_parent = tt.final_parent(conflict[1])
2001
            if tt.path_changed(conflict[1]):
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
2002
                existing_file, new_file = conflict[2], conflict[1]
1534.7.57 by Aaron Bentley
Enhanced conflict resolution.
2003
            else:
3034.4.1 by Aaron Bentley
Start handling case-insensitivity
2004
                existing_file, new_file = conflict[1], conflict[2]
2005
            new_name = tt.final_name(existing_file)+'.moved'
2006
            tt.adjust_path(new_name, final_parent, existing_file)
2007
            new_conflicts.add((c_type, 'Moved existing file to', 
2008
                               existing_file, new_file))
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
2009
        elif c_type == 'parent loop':
2010
            # break the loop by undoing one of the ops that caused the loop
2011
            cur = conflict[1]
2012
            while not tt.path_changed(cur):
2013
                cur = tt.final_parent(cur)
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
2014
            new_conflicts.add((c_type, 'Cancelled move', cur,
2015
                               tt.final_parent(cur),))
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
2016
            tt.adjust_path(tt.final_name(cur), tt.get_tree_parent(cur), cur)
1534.7.169 by Aaron Bentley
Add filesystem/inventory conflicts to conflict output
2017
            
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
2018
        elif c_type == 'missing parent':
1534.7.128 by Aaron Bentley
Got missing contents test working
2019
            trans_id = conflict[1]
2020
            try:
2021
                tt.cancel_deletion(trans_id)
1551.8.22 by Aaron Bentley
Improve message when OTHER deletes an in-use tree
2022
                new_conflicts.add(('deleting parent', 'Not deleting', 
2023
                                   trans_id))
1534.7.128 by Aaron Bentley
Got missing contents test working
2024
            except KeyError:
1551.19.32 by Aaron Bentley
Don't traceback when adding files to a deleted root (abentley, #210092)
2025
                create = True
2590.2.8 by Aaron Bentley
Restore conflict handling changes
2026
                try:
2027
                    tt.final_name(trans_id)
2028
                except NoFinalPath:
1551.19.6 by Aaron Bentley
Revert doesn't crash restoring a file from a deleted directory
2029
                    if path_tree is not None:
2030
                        file_id = tt.final_file_id(trans_id)
1551.19.32 by Aaron Bentley
Don't traceback when adding files to a deleted root (abentley, #210092)
2031
                        if file_id is None:
2032
                            file_id = tt.inactive_file_id(trans_id)
1551.19.6 by Aaron Bentley
Revert doesn't crash restoring a file from a deleted directory
2033
                        entry = path_tree.inventory[file_id]
1551.19.32 by Aaron Bentley
Don't traceback when adding files to a deleted root (abentley, #210092)
2034
                        # special-case the other tree root (move its
2035
                        # children to current root)
2036
                        if entry.parent_id is None:
2037
                            create=False
2038
                            moved = _reparent_transform_children(
2039
                                tt, trans_id, tt.root)
2040
                            for child in moved:
2041
                                new_conflicts.add((c_type, 'Moved to root',
2042
                                                   child))
2043
                        else:
2044
                            parent_trans_id = tt.trans_id_file_id(
2045
                                entry.parent_id)
2046
                            tt.adjust_path(entry.name, parent_trans_id,
2047
                                           trans_id)
2048
                if create:
2049
                    tt.create_directory(trans_id)
2050
                    new_conflicts.add((c_type, 'Created directory', trans_id))
1534.7.61 by Aaron Bentley
Handled parent loops, missing parents, unversioned parents
2051
        elif c_type == 'unversioned parent':
1551.19.32 by Aaron Bentley
Don't traceback when adding files to a deleted root (abentley, #210092)
2052
            file_id = tt.inactive_file_id(conflict[1])
2053
            # special-case the other tree root (move its children instead)
2054
            if path_tree and file_id in path_tree:
2055
                if path_tree.inventory[file_id].parent_id is None:
2056
                    continue
2057
            tt.version_file(file_id, conflict[1])
1534.7.171 by Aaron Bentley
Implemented stringifying filesystem conflicts
2058
            new_conflicts.add((c_type, 'Versioned directory', conflict[1]))
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
2059
        elif c_type == 'non-directory parent':
2060
            parent_id = conflict[1]
2061
            parent_parent = tt.final_parent(parent_id)
2062
            parent_name = tt.final_name(parent_id)
2063
            parent_file_id = tt.final_file_id(parent_id)
2064
            new_parent_id = tt.new_directory(parent_name + '.new',
2065
                parent_parent, parent_file_id)
2066
            _reparent_transform_children(tt, parent_id, new_parent_id)
3146.8.19 by Aaron Bentley
Merge with bzr.dev
2067
            if parent_file_id is not None:
2068
                tt.unversion_file(parent_id)
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
2069
            new_conflicts.add((c_type, 'Created directory', new_parent_id))
1534.7.169 by Aaron Bentley
Add filesystem/inventory conflicts to conflict output
2070
    return new_conflicts
2071
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
2072
1534.7.169 by Aaron Bentley
Add filesystem/inventory conflicts to conflict output
2073
def cook_conflicts(raw_conflicts, tt):
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
2074
    """Generate a list of cooked conflicts, sorted by file path"""
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
2075
    from bzrlib.conflicts import Conflict
2076
    conflict_iter = iter_cook_conflicts(raw_conflicts, tt)
2077
    return sorted(conflict_iter, key=Conflict.sort_key)
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
2078
1534.7.169 by Aaron Bentley
Add filesystem/inventory conflicts to conflict output
2079
2080
def iter_cook_conflicts(raw_conflicts, tt):
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
2081
    from bzrlib.conflicts import Conflict
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
2082
    fp = FinalPaths(tt)
1534.7.169 by Aaron Bentley
Add filesystem/inventory conflicts to conflict output
2083
    for conflict in raw_conflicts:
2084
        c_type = conflict[0]
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
2085
        action = conflict[1]
2086
        modified_path = fp.get_path(conflict[2])
2087
        modified_id = tt.final_file_id(conflict[2])
2088
        if len(conflict) == 3:
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
2089
            yield Conflict.factory(c_type, action=action, path=modified_path,
2090
                                     file_id=modified_id)
2091
             
1534.7.170 by Aaron Bentley
Cleaned up filesystem conflict handling
2092
        else:
2093
            conflicting_path = fp.get_path(conflict[3])
2094
            conflicting_id = tt.final_file_id(conflict[3])
1534.10.19 by Aaron Bentley
Stanza conversion, cooking
2095
            yield Conflict.factory(c_type, action=action, path=modified_path,
2096
                                   file_id=modified_id, 
2097
                                   conflict_path=conflicting_path,
2098
                                   conflict_file_id=conflicting_id)
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
2099
2100
2101
class _FileMover(object):
2733.2.9 by Aaron Bentley
Update docstrings
2102
    """Moves and deletes files for TreeTransform, tracking operations"""
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
2103
2104
    def __init__(self):
2105
        self.past_renames = []
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2106
        self.pending_deletions = []
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
2107
2108
    def rename(self, from_, to):
2733.2.9 by Aaron Bentley
Update docstrings
2109
        """Rename a file from one path to another.  Functions like os.rename"""
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
2110
        try:
2111
            os.rename(from_, to)
2112
        except OSError, e:
3063.1.3 by Aaron Bentley
Update for Linux
2113
            if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
2114
                raise errors.FileExists(to, str(e))
2115
            raise
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
2116
        self.past_renames.append((from_, to))
2117
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2118
    def pre_delete(self, from_, to):
2733.2.9 by Aaron Bentley
Update docstrings
2119
        """Rename a file out of the way and mark it for deletion.
2120
2121
        Unlike os.unlink, this works equally well for files and directories.
2122
        :param from_: The current file path
2123
        :param to: A temporary path for the file
2124
        """
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2125
        self.rename(from_, to)
2126
        self.pending_deletions.append(to)
2127
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
2128
    def rollback(self):
2733.2.9 by Aaron Bentley
Update docstrings
2129
        """Reverse all renames that have been performed"""
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
2130
        for from_, to in reversed(self.past_renames):
2131
            os.rename(to, from_)
2733.2.12 by Aaron Bentley
Updates from review
2132
        # after rollback, don't reuse _FileMover
2133
        past_renames = None
2134
        pending_deletions = None
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2135
2136
    def apply_deletions(self):
2733.2.9 by Aaron Bentley
Update docstrings
2137
        """Apply all marked deletions"""
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
2138
        for path in self.pending_deletions:
2139
            delete_any(path)
2733.2.12 by Aaron Bentley
Updates from review
2140
        # after apply_deletions, don't reuse _FileMover
2141
        past_renames = None
2142
        pending_deletions = None