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