/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5557.1.7 by John Arbash Meinel
Merge in the bzr.dev 5582
1
# Copyright (C) 2006-2011 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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
16
7479.2.1 by Jelmer Vernooij
Drop python2 support.
17
import contextlib
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
18
import os
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
19
import errno
3363.2.27 by Aaron Bentley
Make path_content_summary a core API
20
from stat import S_ISREG, S_IEXEC
4934.1.1 by John Arbash Meinel
Basic implementation for windows and bug #488724.
21
import time
1534.7.106 by Aaron Bentley
Cleaned up imports, added copyright statements
22
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
23
from . import (
6449.6.5 by Jelmer Vernooij
Merge bzr.transform.orphan_policy move to config stacks.
24
    config as _mod_config,
7490.83.1 by Jelmer Vernooij
Add abstract methods.
25
    controldir,
5409.1.7 by Vincent Ladeuil
First orphaning implementation (some tests lacking).
26
    errors,
27
    lazy_import,
7490.85.2 by Jelmer Vernooij
Split PreviewTree.
28
    lock,
7490.83.1 by Jelmer Vernooij
Add abstract methods.
29
    osutils,
5409.1.14 by Vincent Ladeuil
Prepare for more ways to handle orphans.
30
    registry,
5753.2.3 by Jelmer Vernooij
Fix transform tests.
31
    trace,
5409.1.7 by Vincent Ladeuil
First orphaning implementation (some tests lacking).
32
    )
33
lazy_import.lazy_import(globals(), """
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
34
from breezy import (
0.13.8 by Aaron Bentley
Integrate serialization into TreeTransforms
35
    multiparent,
3363.2.4 by Aaron Bentley
Get significant portions of PreviewTree implemented and passing tests
36
    revision as _mod_revision,
4961.2.9 by Martin Pool
Rip out most remaining uses of DummyProgressBar
37
    ui,
5582.10.4 by Jelmer Vernooij
Fix a bunch of tests.
38
    urlutils,
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
39
    )
7490.83.1 by Jelmer Vernooij
Add abstract methods.
40
from breezy.i18n import gettext
7490.83.5 by Jelmer Vernooij
Fix import tariff test.
41
""")
7490.83.1 by Jelmer Vernooij
Add abstract methods.
42
7490.69.1 by Jelmer Vernooij
Move some more tests to breezy.bzr.
43
from .errors import (DuplicateKey,
44
                     BzrError, InternalBzrError)
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
45
from .filters import filtered_output_bytes, ContentFilterContext
46
from .mutabletree import MutableTree
47
from .osutils import (
3363.2.19 by Aaron Bentley
Make PreviewTree.path2id correct
48
    delete_any,
49
    file_kind,
50
    pathjoin,
3363.15.4 by Aaron Bentley
Implement PreviewTree.get_file_sha1 properly
51
    sha_file,
3363.2.19 by Aaron Bentley
Make PreviewTree.path2id correct
52
    splitpath,
7122.6.3 by Jelmer Vernooij
Merge trunk.
53
    supports_symlinks,
5579.3.1 by Jelmer Vernooij
Remove unused imports.
54
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
55
from .progress import ProgressPhase
6883.5.3 by Jelmer Vernooij
Add find_previous_path.
56
from .tree import (
7357.1.8 by Jelmer Vernooij
Remove the InterTree object.
57
    InterTree,
7490.83.7 by Jelmer Vernooij
Key by path rather than by file id.
58
    find_previous_path,
6883.5.3 by Jelmer Vernooij
Add find_previous_path.
59
    )
1534.7.31 by Aaron Bentley
Changed tree root parent to ROOT_PARENT
60
1534.7.167 by Aaron Bentley
PEP8 and comment cleanups
61
1534.7.31 by Aaron Bentley
Changed tree root parent to ROOT_PARENT
62
ROOT_PARENT = "root-parent"
63
7143.15.2 by Jelmer Vernooij
Run autopep8.
64
7490.69.1 by Jelmer Vernooij
Move some more tests to breezy.bzr.
65
class NoFinalPath(BzrError):
66
67
    _fmt = ("No final name for trans_id %(trans_id)r\n"
68
            "root trans-id: %(root_trans_id)r\n")
69
70
    def __init__(self, trans_id, transform):
71
        self.trans_id = trans_id
72
        self.root_trans_id = transform.root
73
74
75
class ReusingTransform(BzrError):
76
77
    _fmt = "Attempt to reuse a transform that has already been applied."
78
79
80
class MalformedTransform(InternalBzrError):
81
82
    _fmt = "Tree transform is malformed %(conflicts)r"
83
84
7490.77.4 by Jelmer Vernooij
Move some errors around.
85
class CantMoveRoot(BzrError):
86
87
    _fmt = "Moving the root directory is not supported at this time"
88
89
7490.69.1 by Jelmer Vernooij
Move some more tests to breezy.bzr.
90
class ImmortalLimbo(BzrError):
91
92
    _fmt = """Unable to delete transform temporary directory %(limbo_dir)s.
93
    Please examine %(limbo_dir)s to see if it contains any files you wish to
94
    keep, and delete it when you are done."""
95
96
    def __init__(self, limbo_dir):
97
        BzrError.__init__(self)
98
        self.limbo_dir = limbo_dir
99
100
7490.77.4 by Jelmer Vernooij
Move some errors around.
101
class TransformRenameFailed(BzrError):
102
103
    _fmt = "Failed to rename %(from_path)s to %(to_path)s: %(why)s"
104
105
    def __init__(self, from_path, to_path, why, errno):
106
        self.from_path = from_path
107
        self.to_path = to_path
108
        self.why = why
109
        self.errno = errno
110
111
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
112
def unique_add(map, key, value):
113
    if key in map:
1534.7.5 by Aaron Bentley
Got unique_add under test
114
        raise DuplicateKey(key=key)
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
115
    map[key] = value
116
1534.7.167 by Aaron Bentley
PEP8 and comment cleanups
117
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
118
class _TransformResults(object):
7490.77.14 by Jelmer Vernooij
Split transform.
119
2502.1.5 by Aaron Bentley
Cleanup
120
    def __init__(self, modified_paths, rename_count):
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
121
        object.__init__(self)
122
        self.modified_paths = modified_paths
2502.1.5 by Aaron Bentley
Cleanup
123
        self.rename_count = rename_count
1534.7.191 by Aaron Bentley
Got transform.apply to list modified paths
124
125
7490.77.17 by Jelmer Vernooij
Rationalize TreeTransform class hierarchy.
126
class TreeTransform(object):
127
    """Represent a tree transformation.
128
129
    This object is designed to support incremental generation of the transform,
130
    in any order.
131
132
    However, it gives optimum performance when parent directories are created
133
    before their contents.  The transform is then able to put child files
134
    directly in their parent directory, avoiding later renames.
135
136
    It is easy to produce malformed transforms, but they are generally
137
    harmless.  Attempting to apply a malformed transform will cause an
138
    exception to be raised before any modifications are made to the tree.
139
140
    Many kinds of malformed transforms can be corrected with the
141
    resolve_conflicts function.  The remaining ones indicate programming error,
142
    such as trying to create a file with no path.
143
144
    Two sets of file creation methods are supplied.  Convenience methods are:
145
     * new_file
146
     * new_directory
147
     * new_symlink
148
149
    These are composed of the low-level methods:
150
     * create_path
151
     * create_file or create_directory or create_symlink
152
     * version_file
153
     * set_executability
154
155
    Transform/Transaction ids
156
    -------------------------
157
    trans_ids are temporary ids assigned to all files involved in a transform.
158
    It's possible, even common, that not all files in the Tree have trans_ids.
159
160
    trans_ids are only valid for the TreeTransform that generated them.
161
    """
7490.77.14 by Jelmer Vernooij
Split transform.
162
163
    def __init__(self, tree, pb=None):
164
        self._tree = tree
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
165
        # A progress bar
166
        self._pb = pb
7490.77.14 by Jelmer Vernooij
Split transform.
167
        self._id_number = 0
168
        # Mapping of path in old tree -> trans_id
169
        self._tree_path_ids = {}
170
        # Mapping trans_id -> path in old tree
171
        self._tree_id_paths = {}
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
172
        # mapping of trans_id -> new basename
173
        self._new_name = {}
174
        # mapping of trans_id -> new parent trans_id
175
        self._new_parent = {}
176
        # mapping of trans_id with new contents -> new file_kind
177
        self._new_contents = {}
178
        # Set of trans_ids whose contents will be removed
179
        self._removed_contents = set()
180
        # Mapping of trans_id -> new execute-bit value
181
        self._new_executability = {}
182
        # Mapping of trans_id -> new tree-reference value
183
        self._new_reference_revision = {}
184
        # Set of trans_ids that will be removed
185
        self._removed_id = set()
186
        # Indicator of whether the transform has been applied
187
        self._done = False
7490.77.14 by Jelmer Vernooij
Split transform.
188
189
    def __enter__(self):
190
        """Support Context Manager API."""
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
191
        return self
7490.77.14 by Jelmer Vernooij
Split transform.
192
193
    def __exit__(self, exc_type, exc_val, exc_tb):
194
        """Support Context Manager API."""
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
195
        self.finalize()
7490.77.14 by Jelmer Vernooij
Split transform.
196
197
    def iter_tree_children(self, trans_id):
198
        """Iterate through the entry's tree children, if any.
199
200
        :param trans_id: trans id to iterate
201
        :returns: Iterator over paths
202
        """
203
        raise NotImplementedError(self.iter_tree_children)
204
205
    def canonical_path(self, path):
206
        return path
207
208
    def tree_kind(self, trans_id):
209
        raise NotImplementedError(self.tree_kind)
210
7490.77.16 by Jelmer Vernooij
Move more generic code.
211
    def by_parent(self):
212
        """Return a map of parent: children for known parents.
213
214
        Only new paths and parents of tree files with assigned ids are used.
215
        """
216
        by_parent = {}
7518.1.2 by Jelmer Vernooij
Fix imports of sixish.
217
        items = list(self._new_parent.items())
7490.77.16 by Jelmer Vernooij
Move more generic code.
218
        items.extend((t, self.final_parent(t))
219
                     for t in list(self._tree_id_paths))
220
        for trans_id, parent_id in items:
221
            if parent_id not in by_parent:
222
                by_parent[parent_id] = set()
223
            by_parent[parent_id].add(trans_id)
224
        return by_parent
225
7490.77.14 by Jelmer Vernooij
Split transform.
226
    def finalize(self):
227
        """Release the working tree lock, if held.
228
229
        This is required if apply has not been invoked, but can be invoked
230
        even after apply.
231
        """
232
        raise NotImplementedError(self.finalize)
233
234
    def create_path(self, name, parent):
235
        """Assign a transaction id to a new path"""
7490.133.5 by Jelmer Vernooij
Make assign_id public, fix some more GitTransform tests.
236
        trans_id = self.assign_id()
7490.77.16 by Jelmer Vernooij
Move more generic code.
237
        unique_add(self._new_name, trans_id, name)
238
        unique_add(self._new_parent, trans_id, parent)
239
        return trans_id
7490.77.14 by Jelmer Vernooij
Split transform.
240
241
    def adjust_path(self, name, parent, trans_id):
242
        """Change the path that is assigned to a transaction id."""
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
243
        if parent is None:
244
            raise ValueError("Parent trans-id may not be None")
7490.133.4 by Jelmer Vernooij
q
245
        if trans_id == self.root:
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
246
            raise CantMoveRoot
247
        self._new_name[trans_id] = name
248
        self._new_parent[trans_id] = parent
7490.77.14 by Jelmer Vernooij
Split transform.
249
250
    def adjust_root_path(self, name, parent):
251
        """Emulate moving the root by moving all children, instead.
252
253
        We do this by undoing the association of root's transaction id with the
254
        current tree.  This allows us to create a new directory with that
255
        transaction id.  We unversion the root directory and version the
256
        physically new directory, and hope someone versions the tree root
257
        later.
258
        """
259
        raise NotImplementedError(self.adjust_root_path)
260
261
    def fixup_new_roots(self):
262
        """Reinterpret requests to change the root directory
263
264
        Instead of creating a root directory, or moving an existing directory,
265
        all the attributes and children of the new root are applied to the
266
        existing root directory.
267
268
        This means that the old root trans-id becomes obsolete, so it is
269
        recommended only to invoke this after the root trans-id has become
270
        irrelevant.
271
        """
272
        raise NotImplementedError(self.fixup_new_roots)
273
7490.133.5 by Jelmer Vernooij
Make assign_id public, fix some more GitTransform tests.
274
    def assign_id(self):
7490.77.14 by Jelmer Vernooij
Split transform.
275
        """Produce a new tranform id"""
276
        new_id = "new-%s" % self._id_number
277
        self._id_number += 1
278
        return new_id
279
280
    def trans_id_tree_path(self, path):
281
        """Determine (and maybe set) the transaction ID for a tree path."""
282
        path = self.canonical_path(path)
283
        if path not in self._tree_path_ids:
7490.133.5 by Jelmer Vernooij
Make assign_id public, fix some more GitTransform tests.
284
            self._tree_path_ids[path] = self.assign_id()
7490.77.14 by Jelmer Vernooij
Split transform.
285
            self._tree_id_paths[self._tree_path_ids[path]] = path
286
        return self._tree_path_ids[path]
287
288
    def get_tree_parent(self, trans_id):
289
        """Determine id of the parent in the tree."""
290
        path = self._tree_id_paths[trans_id]
291
        if path == "":
292
            return ROOT_PARENT
293
        return self.trans_id_tree_path(os.path.dirname(path))
294
295
    def delete_contents(self, trans_id):
296
        """Schedule the contents of a path entry for deletion"""
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
297
        kind = self.tree_kind(trans_id)
298
        if kind is not None:
299
            self._removed_contents.add(trans_id)
7490.77.14 by Jelmer Vernooij
Split transform.
300
301
    def cancel_deletion(self, trans_id):
302
        """Cancel a scheduled deletion"""
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
303
        self._removed_contents.remove(trans_id)
7490.77.14 by Jelmer Vernooij
Split transform.
304
305
    def delete_versioned(self, trans_id):
306
        """Delete and unversion a versioned file"""
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
307
        self.delete_contents(trans_id)
308
        self.unversion_file(trans_id)
7490.77.14 by Jelmer Vernooij
Split transform.
309
310
    def set_executability(self, executability, trans_id):
311
        """Schedule setting of the 'execute' bit
312
        To unschedule, set to None
313
        """
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
314
        if executability is None:
315
            del self._new_executability[trans_id]
316
        else:
317
            unique_add(self._new_executability, trans_id, executability)
7490.77.14 by Jelmer Vernooij
Split transform.
318
319
    def set_tree_reference(self, revision_id, trans_id):
320
        """Set the reference associated with a directory"""
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
321
        unique_add(self._new_reference_revision, trans_id, revision_id)
7490.77.14 by Jelmer Vernooij
Split transform.
322
323
    def version_file(self, trans_id, file_id=None):
324
        """Schedule a file to become versioned."""
325
        raise NotImplementedError(self.version_file)
326
327
    def cancel_versioning(self, trans_id):
328
        """Undo a previous versioning of a file"""
329
        raise NotImplementedError(self.cancel_versioning)
330
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
331
    def unversion_file(self, trans_id):
332
        """Schedule a path entry to become unversioned"""
333
        self._removed_id.add(trans_id)
334
7490.77.14 by Jelmer Vernooij
Split transform.
335
    def new_paths(self, filesystem_only=False):
336
        """Determine the paths of all new and changed files.
337
338
        :param filesystem_only: if True, only calculate values for files
339
            that require renames or execute bit changes.
340
        """
341
        raise NotImplementedError(self.new_paths)
342
343
    def final_kind(self, trans_id):
344
        """Determine the final file kind, after any changes applied.
345
346
        :return: None if the file does not exist/has no contents.  (It is
347
            conceivable that a path would be created without the corresponding
348
            contents insertion command)
349
        """
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
350
        if trans_id in self._new_contents:
7490.133.25 by Jelmer Vernooij
More fixes.
351
            if trans_id in self._new_reference_revision:
352
                return 'tree-reference'
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
353
            return self._new_contents[trans_id]
354
        elif trans_id in self._removed_contents:
355
            return None
356
        else:
357
            return self.tree_kind(trans_id)
7490.77.14 by Jelmer Vernooij
Split transform.
358
359
    def tree_path(self, trans_id):
360
        """Determine the tree path associated with the trans_id."""
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
361
        return self._tree_id_paths.get(trans_id)
7490.77.14 by Jelmer Vernooij
Split transform.
362
363
    def final_is_versioned(self, trans_id):
364
        raise NotImplementedError(self.final_is_versioned)
365
366
    def final_parent(self, trans_id):
367
        """Determine the parent file_id, after any changes are applied.
368
369
        ROOT_PARENT is returned for the tree root.
370
        """
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
371
        try:
372
            return self._new_parent[trans_id]
373
        except KeyError:
374
            return self.get_tree_parent(trans_id)
7490.77.14 by Jelmer Vernooij
Split transform.
375
376
    def final_name(self, trans_id):
377
        """Determine the final filename, after all changes are applied."""
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
378
        try:
379
            return self._new_name[trans_id]
380
        except KeyError:
381
            try:
382
                return os.path.basename(self._tree_id_paths[trans_id])
383
            except KeyError:
384
                raise NoFinalPath(trans_id, self)
7490.77.14 by Jelmer Vernooij
Split transform.
385
386
    def path_changed(self, trans_id):
387
        """Return True if a trans_id's path has changed."""
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
388
        return (trans_id in self._new_name) or (trans_id in self._new_parent)
7490.77.14 by Jelmer Vernooij
Split transform.
389
390
    def new_contents(self, trans_id):
7490.77.15 by Jelmer Vernooij
Factor out non-version things.
391
        return (trans_id in self._new_contents)
7490.77.14 by Jelmer Vernooij
Split transform.
392
7490.129.2 by Jelmer Vernooij
Move cook conflict implementation into breezy.bzr.transform.
393
    def find_raw_conflicts(self):
7490.77.14 by Jelmer Vernooij
Split transform.
394
        """Find any violations of inventory or filesystem invariants"""
7490.129.2 by Jelmer Vernooij
Move cook conflict implementation into breezy.bzr.transform.
395
        raise NotImplementedError(self.find_raw_conflicts)
7490.77.14 by Jelmer Vernooij
Split transform.
396
397
    def new_file(self, name, parent_id, contents, file_id=None,
398
                 executable=None, sha1=None):
399
        """Convenience method to create files.
400
401
        name is the name of the file to create.
402
        parent_id is the transaction id of the parent directory of the file.
403
        contents is an iterator of bytestrings, which will be used to produce
404
        the file.
405
        :param file_id: The inventory ID of the file, if it is to be versioned.
406
        :param executable: Only valid when a file_id has been supplied.
407
        """
408
        raise NotImplementedError(self.new_file)
409
410
    def new_directory(self, name, parent_id, file_id=None):
411
        """Convenience method to create directories.
412
413
        name is the name of the directory to create.
414
        parent_id is the transaction id of the parent directory of the
415
        directory.
416
        file_id is the inventory ID of the directory, if it is to be versioned.
417
        """
418
        raise NotImplementedError(self.new_directory)
419
420
    def new_symlink(self, name, parent_id, target, file_id=None):
421
        """Convenience method to create symbolic link.
422
423
        name is the name of the symlink to create.
424
        parent_id is the transaction id of the parent directory of the symlink.
425
        target is a bytestring of the target of the symlink.
426
        file_id is the inventory ID of the file, if it is to be versioned.
427
        """
428
        raise NotImplementedError(self.new_symlink)
429
430
    def new_orphan(self, trans_id, parent_id):
431
        """Schedule an item to be orphaned.
432
433
        When a directory is about to be removed, its children, if they are not
434
        versioned are moved out of the way: they don't have a parent anymore.
435
436
        :param trans_id: The trans_id of the existing item.
437
        :param parent_id: The parent trans_id of the item.
438
        """
439
        raise NotImplementedError(self.new_orphan)
440
441
    def iter_changes(self):
442
        """Produce output in the same format as Tree.iter_changes.
443
444
        Will produce nonsensical results if invoked while inventory/filesystem
7490.129.2 by Jelmer Vernooij
Move cook conflict implementation into breezy.bzr.transform.
445
        conflicts (as reported by TreeTransform.find_raw_conflicts()) are present.
7490.77.14 by Jelmer Vernooij
Split transform.
446
447
        This reads the Transform, but only reproduces changes involving a
448
        file_id.  Files that are not versioned in either of the FROM or TO
449
        states are not reflected.
450
        """
451
        raise NotImplementedError(self.iter_changes)
452
453
    def get_preview_tree(self):
454
        """Return a tree representing the result of the transform.
455
456
        The tree is a snapshot, and altering the TreeTransform will invalidate
457
        it.
458
        """
459
        raise NotImplementedError(self.get_preview_tree)
460
461
    def commit(self, branch, message, merge_parents=None, strict=False,
462
               timestamp=None, timezone=None, committer=None, authors=None,
463
               revprops=None, revision_id=None):
464
        """Commit the result of this TreeTransform to a branch.
465
466
        :param branch: The branch to commit to.
467
        :param message: The message to attach to the commit.
468
        :param merge_parents: Additional parent revision-ids specified by
469
            pending merges.
470
        :param strict: If True, abort the commit if there are unversioned
471
            files.
472
        :param timestamp: if not None, seconds-since-epoch for the time and
473
            date.  (May be a float.)
474
        :param timezone: Optional timezone for timestamp, as an offset in
475
            seconds.
476
        :param committer: Optional committer in email-id format.
477
            (e.g. "J Random Hacker <jrandom@example.com>")
478
        :param authors: Optional list of authors in email-id format.
479
        :param revprops: Optional dictionary of revision properties.
480
        :param revision_id: Optional revision id.  (Specifying a revision-id
481
            may reduce performance for some non-native formats.)
482
        :return: The revision_id of the revision committed.
483
        """
484
        raise NotImplementedError(self.commit)
485
486
    def create_file(self, contents, trans_id, mode_id=None, sha1=None):
487
        """Schedule creation of a new file.
488
489
        :seealso: new_file.
490
491
        :param contents: an iterator of strings, all of which will be written
492
            to the target destination.
493
        :param trans_id: TreeTransform handle
494
        :param mode_id: If not None, force the mode of the target file to match
495
            the mode of the object referenced by mode_id.
496
            Otherwise, we will try to preserve mode bits of an existing file.
497
        :param sha1: If the sha1 of this content is already known, pass it in.
498
            We can use it to prevent future sha1 computations.
499
        """
500
        raise NotImplementedError(self.create_file)
501
502
    def create_directory(self, trans_id):
503
        """Schedule creation of a new directory.
504
505
        See also new_directory.
506
        """
507
        raise NotImplementedError(self.create_directory)
508
509
    def create_symlink(self, target, trans_id):
510
        """Schedule creation of a new symbolic link.
511
512
        target is a bytestring.
513
        See also new_symlink.
514
        """
515
        raise NotImplementedError(self.create_symlink)
516
517
    def create_hardlink(self, path, trans_id):
518
        """Schedule creation of a hard link"""
519
        raise NotImplementedError(self.create_hardlink)
520
521
    def cancel_creation(self, trans_id):
522
        """Cancel the creation of new file contents."""
523
        raise NotImplementedError(self.cancel_creation)
524
7490.129.1 by Jelmer Vernooij
Make cook_conflicts a member of Transform.
525
    def cook_conflicts(self, raw_conflicts):
526
        """Cook conflicts.
527
        """
528
        raise NotImplementedError(self.cook_conflicts)
529
7490.77.14 by Jelmer Vernooij
Split transform.
530
5409.1.16 by Vincent Ladeuil
Add ``bzrlib.transform.orphan_policy`` and allows ``never`` to restore the previous behaviour.
531
class OrphaningError(errors.BzrError):
532
533
    # Only bugs could lead to such exception being seen by the user
534
    internal_error = True
535
    _fmt = "Error while orphaning %s in %s directory"
536
537
    def __init__(self, orphan, parent):
538
        errors.BzrError.__init__(self)
539
        self.orphan = orphan
540
        self.parent = parent
541
542
543
class OrphaningForbidden(OrphaningError):
544
545
    _fmt = "Policy: %s doesn't allow creating orphans."
546
547
    def __init__(self, policy):
548
        errors.BzrError.__init__(self)
549
        self.policy = policy
5409.1.14 by Vincent Ladeuil
Prepare for more ways to handle orphans.
550
551
552
def move_orphan(tt, orphan_id, parent_id):
553
    """See TreeTransformBase.new_orphan.
554
6681.2.4 by Jelmer Vernooij
More renames.
555
    This creates a new orphan in the `brz-orphans` dir at the root of the
5409.1.14 by Vincent Ladeuil
Prepare for more ways to handle orphans.
556
    `TreeTransform`.
557
558
    :param tt: The TreeTransform orphaning `trans_id`.
559
560
    :param orphan_id: The trans id that should be orphaned.
561
562
    :param parent_id: The orphan parent trans id.
563
    """
564
    # Add the orphan dir if it doesn't exist
6681.2.4 by Jelmer Vernooij
More renames.
565
    orphan_dir_basename = 'brz-orphans'
5409.1.16 by Vincent Ladeuil
Add ``bzrlib.transform.orphan_policy`` and allows ``never`` to restore the previous behaviour.
566
    od_id = tt.trans_id_tree_path(orphan_dir_basename)
5409.1.14 by Vincent Ladeuil
Prepare for more ways to handle orphans.
567
    if tt.final_kind(od_id) is None:
568
        tt.create_directory(od_id)
569
    parent_path = tt._tree_id_paths[parent_id]
570
    # Find a name that doesn't exist yet in the orphan dir
571
    actual_name = tt.final_name(orphan_id)
572
    new_name = tt._available_backup_name(actual_name, od_id)
573
    tt.adjust_path(new_name, od_id, orphan_id)
5753.2.3 by Jelmer Vernooij
Fix transform tests.
574
    trace.warning('%s has been orphaned in %s'
5409.1.16 by Vincent Ladeuil
Add ``bzrlib.transform.orphan_policy`` and allows ``never`` to restore the previous behaviour.
575
                  % (joinpath(parent_path, actual_name), orphan_dir_basename))
576
577
578
def refuse_orphan(tt, orphan_id, parent_id):
579
    """See TreeTransformBase.new_orphan.
580
5409.1.23 by Vincent Ladeuil
Add more doc and fix rst typos
581
    This refuses to create orphan, letting the caller handle the conflict.
5409.1.16 by Vincent Ladeuil
Add ``bzrlib.transform.orphan_policy`` and allows ``never`` to restore the previous behaviour.
582
    """
583
    raise OrphaningForbidden('never')
5409.1.14 by Vincent Ladeuil
Prepare for more ways to handle orphans.
584
585
586
orphaning_registry = registry.Registry()
5409.1.25 by Vincent Ladeuil
Better docs.
587
orphaning_registry.register(
6973.11.9 by Jelmer Vernooij
Fix tests.
588
    u'conflict', refuse_orphan,
5409.1.25 by Vincent Ladeuil
Better docs.
589
    'Leave orphans in place and create a conflict on the directory.')
590
orphaning_registry.register(
6973.11.9 by Jelmer Vernooij
Fix tests.
591
    u'move', move_orphan,
6681.2.4 by Jelmer Vernooij
More renames.
592
    'Move orphans into the brz-orphans directory.')
6973.11.9 by Jelmer Vernooij
Fix tests.
593
orphaning_registry._set_default_key(u'conflict')
5409.1.7 by Vincent Ladeuil
First orphaning implementation (some tests lacking).
594
4354.5.1 by Aaron Bentley
Split out a DiskTreeTransform class that manages Limbo.
595
6449.6.5 by Jelmer Vernooij
Merge bzr.transform.orphan_policy move to config stacks.
596
opt_transform_orphan = _mod_config.RegistryOption(
6883.13.1 by Jelmer Vernooij
Rename bzr.transform.orphan_policy -> transform.orphan_policy.
597
    'transform.orphan_policy', orphaning_registry,
6449.6.5 by Jelmer Vernooij
Merge bzr.transform.orphan_policy move to config stacks.
598
    help='Policy for orphaned files during transform operations.',
599
    invalid='warning')
600
601
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
602
def joinpath(parent, child):
1534.7.40 by Aaron Bentley
Updated docs
603
    """Join tree-relative paths, handling the tree root specially"""
1534.7.32 by Aaron Bentley
Got conflict handling working when conflicts involve existing files
604
    if parent is None or parent == "":
605
        return child
606
    else:
1534.7.166 by Aaron Bentley
Swapped os.path.join for pathjoin everywhere
607
        return pathjoin(parent, child)
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
608
1534.7.167 by Aaron Bentley
PEP8 and comment cleanups
609
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
610
class FinalPaths(object):
1759.2.2 by Jelmer Vernooij
Revert some of my spelling fixes and fix some typos after review by Aaron.
611
    """Make path calculation cheap by memoizing paths.
1534.7.21 by Aaron Bentley
Updated docstrings
612
613
    The underlying tree must not be manipulated between calls, or else
614
    the results will likely be incorrect.
615
    """
7143.15.2 by Jelmer Vernooij
Run autopep8.
616
1534.7.132 by Aaron Bentley
Got cooked conflicts working
617
    def __init__(self, transform):
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
618
        object.__init__(self)
619
        self._known_paths = {}
1534.7.33 by Aaron Bentley
Fixed naming
620
        self.transform = transform
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
621
622
    def _determine_path(self, trans_id):
7490.133.5 by Jelmer Vernooij
Make assign_id public, fix some more GitTransform tests.
623
        if trans_id == self.transform.root or trans_id == ROOT_PARENT:
6996 by Jelmer Vernooij
Merge lp:~jelmer/brz/python3-e
624
            return u""
1534.7.33 by Aaron Bentley
Fixed naming
625
        name = self.transform.final_name(trans_id)
626
        parent_id = self.transform.final_parent(trans_id)
1534.7.132 by Aaron Bentley
Got cooked conflicts working
627
        if parent_id == self.transform.root:
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
628
            return name
629
        else:
1534.7.166 by Aaron Bentley
Swapped os.path.join for pathjoin everywhere
630
            return pathjoin(self.get_path(parent_id), name)
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
631
632
    def get_path(self, trans_id):
1534.7.157 by Aaron Bentley
Added more docs
633
        """Find the final path associated with a trans_id"""
1534.7.1 by Aaron Bentley
Got creation of a versioned file working
634
        if trans_id not in self._known_paths:
635
            self._known_paths[trans_id] = self._determine_path(trans_id)
636
        return self._known_paths[trans_id]
1534.7.28 by Aaron Bentley
Nearly-working build_tree replacement
637
3453.2.3 by Aaron Bentley
Enable using a precomputed inventory delta for build_tree.
638
    def get_paths(self, trans_ids):
639
        return [(self.get_path(t), t) for t in trans_ids]
640
641
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
642
def _reparent_children(tt, old_parent, new_parent):
643
    for child in tt.iter_tree_children(old_parent):
644
        tt.adjust_path(tt.final_name(child), new_parent, child)
645
5409.1.7 by Vincent Ladeuil
First orphaning implementation (some tests lacking).
646
3144.4.2 by Aaron Bentley
Handle non-directory parent conflicts (abentley, #177390)
647
def _reparent_transform_children(tt, old_parent, new_parent):
648
    by_parent = tt.by_parent()
649
    for child in by_parent[old_parent]:
650
        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)
651
    return by_parent[old_parent]
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
652
5409.1.7 by Vincent Ladeuil
First orphaning implementation (some tests lacking).
653
6809.4.4 by Jelmer Vernooij
Swap arguments for Tree.is_executable.
654
def new_by_entry(path, tt, entry, parent_id, tree):
1534.7.157 by Aaron Bentley
Added more docs
655
    """Create a new file according to its inventory entry"""
1534.7.47 by Aaron Bentley
Started work on 'revert'
656
    name = entry.name
657
    kind = entry.kind
658
    if kind == 'file':
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
659
        with tree.get_file(path) as f:
660
            executable = tree.is_executable(path)
6977.2.1 by Jelmer Vernooij
Require that get_file implementations are contect managers, simplify file handling in transform.
661
            return tt.new_file(
7143.15.2 by Jelmer Vernooij
Run autopep8.
662
                name, parent_id, osutils.file_iterator(f), entry.file_id,
663
                executable)
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
664
    elif kind in ('directory', 'tree-reference'):
665
        trans_id = tt.new_directory(name, parent_id, entry.file_id)
666
        if kind == 'tree-reference':
667
            tt.set_tree_reference(entry.reference_revision, trans_id)
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
668
        return trans_id
1534.7.47 by Aaron Bentley
Started work on 'revert'
669
    elif kind == 'symlink':
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
670
        target = tree.get_symlink_target(path)
1534.7.183 by Aaron Bentley
Fixed build_tree with symlinks
671
        return tt.new_symlink(name, parent_id, target, entry.file_id)
2100.3.21 by Aaron Bentley
Work on checking out by-reference trees
672
    else:
673
        raise errors.BadFileKindError(name, kind)
1534.7.47 by Aaron Bentley
Started work on 'revert'
674
3006.2.2 by Alexander Belchenko
tests added.
675
7350.5.1 by Jelmer Vernooij
Remove file_id from transform's create_from_tree, get filter tree paths by path.
676
def create_from_tree(tt, trans_id, tree, path, chunks=None,
7143.15.2 by Jelmer Vernooij
Run autopep8.
677
                     filter_tree_path=None):
4443.2.1 by Ian Clatworthy
apply content filters when merging new files
678
    """Create new file contents according to tree contents.
6809.4.7 by Jelmer Vernooij
Swap arguments for get_symlink_target and kind/stored_kind.
679
4443.2.1 by Ian Clatworthy
apply content filters when merging new files
680
    :param filter_tree_path: the tree path to use to lookup
681
      content filters to apply to the bytes output in the working tree.
682
      This only applies if the working tree supports content filtering.
683
    """
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
684
    kind = tree.kind(path)
3363.17.24 by Aaron Bentley
Implement create_by_tree
685
    if kind == 'directory':
686
        tt.create_directory(trans_id)
687
    elif kind == "file":
6977.2.1 by Jelmer Vernooij
Require that get_file implementations are contect managers, simplify file handling in transform.
688
        if chunks is None:
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
689
            f = tree.get_file(path)
6977.2.1 by Jelmer Vernooij
Require that get_file implementations are contect managers, simplify file handling in transform.
690
            chunks = osutils.file_iterator(f)
691
        else:
692
            f = None
693
        try:
694
            wt = tt._tree
695
            if wt.supports_content_filtering() and filter_tree_path is not None:
696
                filters = wt._content_filter_stack(filter_tree_path)
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
697
                chunks = filtered_output_bytes(
698
                    chunks, filters,
699
                    ContentFilterContext(filter_tree_path, tree))
6977.2.1 by Jelmer Vernooij
Require that get_file implementations are contect managers, simplify file handling in transform.
700
            tt.create_file(chunks, trans_id)
701
        finally:
702
            if f is not None:
703
                f.close()
3363.17.24 by Aaron Bentley
Implement create_by_tree
704
    elif kind == "symlink":
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
705
        tt.create_symlink(tree.get_symlink_target(path), trans_id)
3363.17.27 by Aaron Bentley
Add default case for create_from_tree
706
    else:
707
        raise AssertionError('Unknown kind %r' % kind)
3363.17.24 by Aaron Bentley
Implement create_by_tree
708
709
1534.7.89 by Aaron Bentley
Handle all content types in three-way
710
def create_entry_executability(tt, entry, trans_id):
1534.7.157 by Aaron Bentley
Added more docs
711
    """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
712
    if entry.kind == "file":
713
        tt.set_executability(entry.executable, trans_id)
1534.7.47 by Aaron Bentley
Started work on 'revert'
714
1534.7.157 by Aaron Bentley
Added more docs
715
7490.139.7 by Jelmer Vernooij
Move more transform code.
716
def _prepare_revert_transform(es, working_tree, target_tree, tt, filenames,
3363.2.17 by Aaron Bentley
Start implementing post-change PreviewTree functionality
717
                              backups, pp, basis_tree=None,
718
                              merge_modified=None):
6861.4.1 by Jelmer Vernooij
Make progress bars context managers.
719
    with ui.ui_factory.nested_progress_bar() as child_pb:
3363.2.17 by Aaron Bentley
Start implementing post-change PreviewTree functionality
720
        if merge_modified is None:
721
            merge_modified = working_tree.merge_modified()
7490.139.7 by Jelmer Vernooij
Move more transform code.
722
        merge_modified = _alter_files(es, working_tree, target_tree, tt,
3363.2.17 by Aaron Bentley
Start implementing post-change PreviewTree functionality
723
                                      child_pb, filenames, backups,
724
                                      merge_modified, basis_tree)
6861.4.1 by Jelmer Vernooij
Make progress bars context managers.
725
    with ui.ui_factory.nested_progress_bar() as child_pb:
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
726
        raw_conflicts = resolve_conflicts(
727
            tt, child_pb, lambda t, c: conflict_pass(t, c, target_tree))
7490.129.1 by Jelmer Vernooij
Make cook_conflicts a member of Transform.
728
    conflicts = tt.cook_conflicts(raw_conflicts)
3363.2.10 by Aaron Bentley
Refactor _prepare_revert_transform out of revert
729
    return conflicts, merge_modified
730
731
7490.139.7 by Jelmer Vernooij
Move more transform code.
732
def revert(working_tree, target_tree, filenames, backups=False,
733
           pb=None, change_reporter=None, merge_modified=None, basis_tree=None):
734
    """Revert a working tree's contents to those of a target tree."""
7524.2.2 by Jelmer Vernooij
Cleanup is gone.
735
    with contextlib.ExitStack() as es:
7490.139.7 by Jelmer Vernooij
Move more transform code.
736
        pb = es.enter_context(ui.ui_factory.nested_progress_bar())
737
        es.enter_context(target_tree.lock_read())
738
        tt = es.enter_context(working_tree.transform(pb))
739
        pp = ProgressPhase("Revert phase", 3, pb)
740
        conflicts, merge_modified = _prepare_revert_transform(
741
            es, working_tree, target_tree, tt, filenames, backups, pp)
742
        if change_reporter:
743
            from . import delta
744
            change_reporter = delta._ChangeReporter(
745
                unversioned_filter=working_tree.is_ignored)
746
            delta.report_changes(tt.iter_changes(), change_reporter)
747
        for conflict in conflicts:
7524.2.5 by Jelmer Vernooij
Remove sixish data type.
748
            trace.warning(str(conflict))
7490.139.7 by Jelmer Vernooij
Move more transform code.
749
        pp.next_phase()
750
        tt.apply()
751
        if working_tree.supports_merge_modified():
752
            working_tree.set_merge_modified(merge_modified)
753
    return conflicts
754
755
756
def _alter_files(es, working_tree, target_tree, tt, pb, specific_files,
3363.2.17 by Aaron Bentley
Start implementing post-change PreviewTree functionality
757
                 backups, merge_modified, basis_tree=None):
3363.10.25 by Aaron Bentley
_alter_files locks supplied basis_tree
758
    if basis_tree is not None:
7490.139.7 by Jelmer Vernooij
Move more transform code.
759
        es.enter_context(basis_tree.lock_read())
5783.3.1 by John Arbash Meinel
Do an 'obvious' transformation for clarity.
760
    # We ask the working_tree for its changes relative to the target, rather
761
    # than the target changes relative to the working tree. Because WT4 has an
762
    # optimizer to compare itself to a target, but no optimizer for the
763
    # reverse.
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
764
    change_list = working_tree.iter_changes(
765
        target_tree, specific_files=specific_files, pb=pb)
6973.3.1 by Jelmer Vernooij
Avoid using file ids where possible.
766
    if not target_tree.is_versioned(u''):
2012.1.12 by Aaron Bentley
Use iter_changes for revert
767
        skip_root = True
768
    else:
769
        skip_root = False
7490.139.7 by Jelmer Vernooij
Move more transform code.
770
    deferred_files = []
771
    for id_num, change in enumerate(change_list):
772
        target_path, wt_path = change.path
773
        target_versioned, wt_versioned = change.versioned
774
        target_parent = change.parent_id[0]
775
        target_name, wt_name = change.name
776
        target_kind, wt_kind = change.kind
777
        target_executable, wt_executable = change.executable
778
        if skip_root and wt_path == '':
779
            continue
780
        mode_id = None
7490.139.8 by Jelmer Vernooij
Handle duplicate directories entries for git.
781
        if wt_path is not None:
782
            trans_id = tt.trans_id_tree_path(wt_path)
783
        else:
784
            trans_id = tt.assign_id()
7490.139.7 by Jelmer Vernooij
Move more transform code.
785
        if change.changed_content:
786
            keep_content = False
787
            if wt_kind == 'file' and (backups or target_kind is None):
788
                wt_sha1 = working_tree.get_file_sha1(wt_path)
789
                if merge_modified.get(wt_path) != wt_sha1:
790
                    # acquire the basis tree lazily to prevent the
791
                    # expense of accessing it when it's not needed ?
792
                    # (Guessing, RBC, 200702)
2499.1.1 by Aaron Bentley
Revert does not try to preserve file contents produced by revert
793
                    if basis_tree is None:
794
                        basis_tree = working_tree.basis_tree()
7490.139.7 by Jelmer Vernooij
Move more transform code.
795
                        es.enter_context(basis_tree.lock_read())
796
                    basis_inter = InterTree.get(basis_tree, working_tree)
797
                    basis_path = basis_inter.find_source_path(wt_path)
798
                    if basis_path is None:
799
                        if target_kind is None and not target_versioned:
800
                            keep_content = True
2499.1.1 by Aaron Bentley
Revert does not try to preserve file contents produced by revert
801
                    else:
7490.139.7 by Jelmer Vernooij
Move more transform code.
802
                        if wt_sha1 != basis_tree.get_file_sha1(basis_path):
803
                            keep_content = True
804
            if wt_kind is not None:
805
                if not keep_content:
806
                    tt.delete_contents(trans_id)
5783.3.1 by John Arbash Meinel
Do an 'obvious' transformation for clarity.
807
                elif target_kind is not None:
7490.139.7 by Jelmer Vernooij
Move more transform code.
808
                    parent_trans_id = tt.trans_id_tree_path(osutils.dirname(wt_path))
809
                    backup_name = tt._available_backup_name(
810
                        wt_name, parent_trans_id)
811
                    tt.adjust_path(backup_name, parent_trans_id, trans_id)
812
                    new_trans_id = tt.create_path(wt_name, parent_trans_id)
813
                    if wt_versioned and target_versioned:
814
                        tt.unversion_file(trans_id)
815
                        tt.version_file(
816
                            new_trans_id, file_id=getattr(change, 'file_id', None))
817
                    # New contents should have the same unix perms as old
818
                    # contents
819
                    mode_id = trans_id
820
                    trans_id = new_trans_id
821
            if target_kind in ('directory', 'tree-reference'):
822
                tt.create_directory(trans_id)
823
                if target_kind == 'tree-reference':
824
                    revision = target_tree.get_reference_revision(
825
                        target_path)
826
                    tt.set_tree_reference(revision, trans_id)
827
            elif target_kind == 'symlink':
828
                tt.create_symlink(target_tree.get_symlink_target(
829
                    target_path), trans_id)
830
            elif target_kind == 'file':
831
                deferred_files.append(
832
                    (target_path, (trans_id, mode_id, target_path)))
833
                if basis_tree is None:
834
                    basis_tree = working_tree.basis_tree()
835
                    es.enter_context(basis_tree.lock_read())
836
                new_sha1 = target_tree.get_file_sha1(target_path)
837
                basis_inter = InterTree.get(basis_tree, target_tree)
838
                basis_path = basis_inter.find_source_path(target_path)
839
                if (basis_path is not None and
840
                        new_sha1 == basis_tree.get_file_sha1(basis_path)):
841
                    # If the new contents of the file match what is in basis,
842
                    # then there is no need to store in merge_modified.
843
                    if basis_path in merge_modified:
844
                        del merge_modified[basis_path]
845
                else:
846
                    merge_modified[target_path] = new_sha1
847
848
                # preserve the execute bit when backing up
849
                if keep_content and wt_executable == target_executable:
850
                    tt.set_executability(target_executable, trans_id)
851
            elif target_kind is not None:
852
                raise AssertionError(target_kind)
853
        if not wt_versioned and target_versioned:
854
            tt.version_file(
855
                trans_id, file_id=getattr(change, 'file_id', None))
856
        if wt_versioned and not target_versioned:
857
            tt.unversion_file(trans_id)
858
        if (target_name is not None
859
                and (wt_name != target_name or change.is_reparented())):
860
            if target_path == '':
861
                parent_trans = ROOT_PARENT
862
            else:
863
                parent_trans = tt.trans_id_file_id(target_parent)
864
            if wt_path == '' and wt_versioned:
865
                tt.adjust_root_path(target_name, parent_trans)
866
            else:
867
                tt.adjust_path(target_name, parent_trans, trans_id)
868
        if wt_executable != target_executable and target_kind == "file":
869
            tt.set_executability(target_executable, trans_id)
870
    if working_tree.supports_content_filtering():
871
        for (trans_id, mode_id, target_path), bytes in (
872
                target_tree.iter_files_bytes(deferred_files)):
873
            # We're reverting a tree to the target tree so using the
874
            # target tree to find the file path seems the best choice
875
            # here IMO - Ian C 27/Oct/2009
876
            filters = working_tree._content_filter_stack(target_path)
877
            bytes = filtered_output_bytes(
878
                bytes, filters,
879
                ContentFilterContext(target_path, working_tree))
880
            tt.create_file(bytes, trans_id, mode_id)
881
    else:
882
        for (trans_id, mode_id, target_path), bytes in target_tree.iter_files_bytes(
883
                deferred_files):
884
            tt.create_file(bytes, trans_id, mode_id)
885
    tt.fixup_new_roots()
2499.1.1 by Aaron Bentley
Revert does not try to preserve file contents produced by revert
886
    return merge_modified
2012.1.12 by Aaron Bentley
Use iter_changes for revert
887
888
4961.2.9 by Martin Pool
Rip out most remaining uses of DummyProgressBar
889
def resolve_conflicts(tt, pb=None, pass_func=None):
1534.7.57 by Aaron Bentley
Enhanced conflict resolution.
890
    """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
891
    if pass_func is None:
892
        pass_func = conflict_pass
1534.7.169 by Aaron Bentley
Add filesystem/inventory conflicts to conflict output
893
    new_conflicts = set()
6861.4.1 by Jelmer Vernooij
Make progress bars context managers.
894
    with ui.ui_factory.nested_progress_bar() as pb:
1534.9.1 by Aaron Bentley
Added progress bars to merge
895
        for n in range(10):
7143.15.2 by Jelmer Vernooij
Run autopep8.
896
            pb.update(gettext('Resolution pass'), n + 1, 10)
7490.129.2 by Jelmer Vernooij
Move cook conflict implementation into breezy.bzr.transform.
897
            conflicts = tt.find_raw_conflicts()
1534.9.1 by Aaron Bentley
Added progress bars to merge
898
            if len(conflicts) == 0:
899
                return new_conflicts
1966.1.1 by Aaron Bentley
Implement disk-content merge and conflict resolution for build_tree
900
            new_conflicts.update(pass_func(tt, conflicts))
1534.9.1 by Aaron Bentley
Added progress bars to merge
901
        raise MalformedTransform(conflicts=conflicts)
1534.7.57 by Aaron Bentley
Enhanced conflict resolution.
902
903
7490.128.1 by Jelmer Vernooij
Factor out various resolvers.
904
def resolve_duplicate_id(tt, path_tree, c_type, old_trans_id, trans_id):
905
    tt.unversion_file(old_trans_id)
906
    yield (c_type, 'Unversioned existing file', old_trans_id, trans_id)
907
908
909
def resolve_duplicate(tt, path_tree, c_type, last_trans_id, trans_id, name):
910
    # files that were renamed take precedence
911
    final_parent = tt.final_parent(last_trans_id)
912
    if tt.path_changed(last_trans_id):
913
        existing_file, new_file = trans_id, last_trans_id
914
    else:
915
        existing_file, new_file = last_trans_id, trans_id
7490.139.8 by Jelmer Vernooij
Handle duplicate directories entries for git.
916
    if (not tt._tree.has_versioned_directories() and
917
            tt.final_kind(trans_id) == 'directory' and
918
            tt.final_kind(last_trans_id) == 'directory'):
919
        _reparent_transform_children(tt, existing_file, new_file)
920
        tt.delete_contents(existing_file)
921
        tt.unversion_file(existing_file)
922
        tt.cancel_creation(existing_file)
923
    else:
924
        new_name = tt.final_name(existing_file) + '.moved'
925
        tt.adjust_path(new_name, final_parent, existing_file)
926
        yield (c_type, 'Moved existing file to', existing_file, new_file)
7490.128.1 by Jelmer Vernooij
Factor out various resolvers.
927
928
929
def resolve_parent_loop(tt, path_tree, c_type, cur):
930
    # break the loop by undoing one of the ops that caused the loop
931
    while not tt.path_changed(cur):
932
        cur = tt.final_parent(cur)
933
    yield (c_type, 'Cancelled move', cur, tt.final_parent(cur),)
934
    tt.adjust_path(tt.final_name(cur), tt.get_tree_parent(cur), cur)
935
936
937
def resolve_missing_parent(tt, path_tree, c_type, trans_id):
938
    if trans_id in tt._removed_contents:
939
        cancel_deletion = True
940
        orphans = tt._get_potential_orphans(trans_id)
941
        if orphans:
942
            cancel_deletion = False
943
            # All children are orphans
944
            for o in orphans:
945
                try:
946
                    tt.new_orphan(o, trans_id)
947
                except OrphaningError:
948
                    # Something bad happened so we cancel the directory
949
                    # deletion which will leave it in place with a
950
                    # conflict. The user can deal with it from there.
951
                    # Note that this also catch the case where we don't
952
                    # want to create orphans and leave the directory in
953
                    # place.
954
                    cancel_deletion = True
955
                    break
956
        if cancel_deletion:
957
            # Cancel the directory deletion
958
            tt.cancel_deletion(trans_id)
959
            yield ('deleting parent', 'Not deleting', trans_id)
960
    else:
961
        create = True
962
        try:
963
            tt.final_name(trans_id)
964
        except NoFinalPath:
965
            if path_tree is not None:
966
                file_id = tt.final_file_id(trans_id)
967
                if file_id is None:
968
                    file_id = tt.inactive_file_id(trans_id)
969
                _, entry = next(path_tree.iter_entries_by_dir(
970
                    specific_files=[path_tree.id2path(file_id)]))
971
                # special-case the other tree root (move its
972
                # children to current root)
973
                if entry.parent_id is None:
974
                    create = False
975
                    moved = _reparent_transform_children(
976
                        tt, trans_id, tt.root)
977
                    for child in moved:
978
                        yield (c_type, 'Moved to root', child)
979
                else:
980
                    parent_trans_id = tt.trans_id_file_id(
981
                        entry.parent_id)
982
                    tt.adjust_path(entry.name, parent_trans_id,
983
                                   trans_id)
984
        if create:
985
            tt.create_directory(trans_id)
986
            yield (c_type, 'Created directory', trans_id)
987
988
989
def resolve_unversioned_parent(tt, path_tree, c_type, trans_id):
990
    file_id = tt.inactive_file_id(trans_id)
991
    # special-case the other tree root (move its children instead)
992
    if path_tree and path_tree.path2id('') == file_id:
993
        # This is the root entry, skip it
994
        return
995
    tt.version_file(trans_id, file_id=file_id)
996
    yield (c_type, 'Versioned directory', trans_id)
997
998
999
def resolve_non_directory_parent(tt, path_tree, c_type, parent_id):
1000
    parent_parent = tt.final_parent(parent_id)
1001
    parent_name = tt.final_name(parent_id)
7490.133.20 by Jelmer Vernooij
Some more test fixes.
1002
    # TODO(jelmer): Make this code transform-specific
1003
    if tt._tree.supports_setting_file_ids():
1004
        parent_file_id = tt.final_file_id(parent_id)
1005
    else:
1006
        parent_file_id = b'DUMMY'
7490.128.1 by Jelmer Vernooij
Factor out various resolvers.
1007
    new_parent_id = tt.new_directory(parent_name + '.new',
1008
                                     parent_parent, parent_file_id)
1009
    _reparent_transform_children(tt, parent_id, new_parent_id)
1010
    if parent_file_id is not None:
1011
        tt.unversion_file(parent_id)
1012
    yield (c_type, 'Created directory', new_parent_id)
1013
1014
1015
def resolve_versioning_no_contents(tt, path_tree, c_type, trans_id):
1016
    tt.cancel_versioning(trans_id)
1017
    return []
1018
1019
1020
CONFLICT_RESOLVERS = {
1021
    'duplicate id': resolve_duplicate_id,
1022
    'duplicate': resolve_duplicate,
1023
    'parent loop': resolve_parent_loop,
1024
    'missing parent': resolve_missing_parent,
1025
    'unversioned parent': resolve_unversioned_parent,
1026
    'non-directory parent': resolve_non_directory_parent,
1027
    'versioning no contents': resolve_versioning_no_contents,
1028
}
1029
1030
2590.2.8 by Aaron Bentley
Restore conflict handling changes
1031
def conflict_pass(tt, conflicts, path_tree=None):
1032
    """Resolve some classes of conflicts.
1033
1034
    :param tt: The transform to resolve conflicts in
1035
    :param conflicts: The conflicts to resolve
1036
    :param path_tree: A Tree to get supplemental paths from
1037
    """
1534.7.169 by Aaron Bentley
Add filesystem/inventory conflicts to conflict output
1038
    new_conflicts = set()
7490.128.1 by Jelmer Vernooij
Factor out various resolvers.
1039
    for conflict in conflicts:
1040
        resolver = CONFLICT_RESOLVERS.get(conflict[0])
1041
        if resolver is None:
1042
            continue
1043
        new_conflicts.update(resolver(tt, path_tree, *conflict))
1534.7.169 by Aaron Bentley
Add filesystem/inventory conflicts to conflict output
1044
    return new_conflicts
1045
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
1046
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
1047
class _FileMover(object):
2733.2.9 by Aaron Bentley
Update docstrings
1048
    """Moves and deletes files for TreeTransform, tracking operations"""
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
1049
1050
    def __init__(self):
1051
        self.past_renames = []
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1052
        self.pending_deletions = []
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
1053
1054
    def rename(self, from_, to):
5186.2.2 by Martin Pool
wrap os.rename to insert the source and destination filenames in any exception that may be raised
1055
        """Rename a file from one path to another."""
3063.1.1 by Alexander Belchenko
Catch OSError 17 (file exists) in final phase of tree transform and show filename to user (#111758).
1056
        try:
5050.15.1 by Martin
Revert change of rename function in transform from r5192 to fix failures on Windows
1057
            os.rename(from_, to)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
1058
        except OSError as e:
3063.1.3 by Aaron Bentley
Update for Linux
1059
            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).
1060
                raise errors.FileExists(to, str(e))
5186.2.5 by Martin Pool
Raise a specific clearer error when a rename fails inside transform
1061
            # normal OSError doesn't include filenames so it's hard to see where
1062
            # the problem is, see https://bugs.launchpad.net/bzr/+bug/491763
7490.77.4 by Jelmer Vernooij
Move some errors around.
1063
            raise TransformRenameFailed(from_, to, str(e), e.errno)
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
1064
        self.past_renames.append((from_, to))
1065
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1066
    def pre_delete(self, from_, to):
2733.2.9 by Aaron Bentley
Update docstrings
1067
        """Rename a file out of the way and mark it for deletion.
1068
1069
        Unlike os.unlink, this works equally well for files and directories.
1070
        :param from_: The current file path
1071
        :param to: A temporary path for the file
1072
        """
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1073
        self.rename(from_, to)
1074
        self.pending_deletions.append(to)
1075
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
1076
    def rollback(self):
2733.2.9 by Aaron Bentley
Update docstrings
1077
        """Reverse all renames that have been performed"""
2733.2.1 by Aaron Bentley
Implement FileMover, to support TreeTransform rollback
1078
        for from_, to in reversed(self.past_renames):
5186.2.7 by Martin Pool
Update other cases where transform detects failure to rename
1079
            try:
5050.15.1 by Martin
Revert change of rename function in transform from r5192 to fix failures on Windows
1080
                os.rename(to, from_)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
1081
            except OSError as e:
7490.77.4 by Jelmer Vernooij
Move some errors around.
1082
                raise TransformRenameFailed(to, from_, str(e), e.errno)
2733.2.12 by Aaron Bentley
Updates from review
1083
        # after rollback, don't reuse _FileMover
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
1084
        self.past_renames = None
1085
        self.pending_deletions = None
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1086
1087
    def apply_deletions(self):
2733.2.9 by Aaron Bentley
Update docstrings
1088
        """Apply all marked deletions"""
2733.2.5 by Aaron Bentley
Implement FileMover.pre_delete and FileMover.apply_deletions
1089
        for path in self.pending_deletions:
1090
            delete_any(path)
2733.2.12 by Aaron Bentley
Updates from review
1091
        # after apply_deletions, don't reuse _FileMover
7143.15.5 by Jelmer Vernooij
More PEP8 fixes.
1092
        self.past_renames = None
1093
        self.pending_deletions = None
6652.1.1 by Jelmer Vernooij
Bundle the link-tree command.
1094
1095
1096
def link_tree(target_tree, source_tree):
1097
    """Where possible, hard-link files in a tree to those in another tree.
1098
1099
    :param target_tree: Tree to change
1100
    :param source_tree: Tree to hard-link from
1101
    """
7490.77.2 by Jelmer Vernooij
Split out git and bzr-specific transforms.
1102
    with target_tree.transform() as tt:
7322.1.6 by Jelmer Vernooij
Use the new attributes on TreeChange.
1103
        for change in target_tree.iter_changes(source_tree, include_unchanged=True):
1104
            if change.changed_content:
1105
                continue
1106
            if change.kind != ('file', 'file'):
1107
                continue
1108
            if change.executable[0] != change.executable[1]:
1109
                continue
1110
            trans_id = tt.trans_id_tree_path(change.path[1])
6652.1.1 by Jelmer Vernooij
Bundle the link-tree command.
1111
            tt.delete_contents(trans_id)
7322.1.6 by Jelmer Vernooij
Use the new attributes on TreeChange.
1112
            tt.create_hardlink(source_tree.abspath(change.path[0]), trans_id)
6652.1.1 by Jelmer Vernooij
Bundle the link-tree command.
1113
        tt.apply()
7490.85.1 by Jelmer Vernooij
Factor out PreviewTree.
1114
1115
1116
class PreviewTree(object):
1117
    """Preview tree."""
1118
1119
    def __init__(self, transform):
1120
        self._transform = transform
7490.85.2 by Jelmer Vernooij
Split PreviewTree.
1121
        self._parent_ids = []
1122
        self.__by_parent = None
1123
        self._path2trans_id_cache = {}
1124
        self._all_children_cache = {}
1125
        self._final_name_cache = {}
1126
7490.133.20 by Jelmer Vernooij
Some more test fixes.
1127
    def supports_setting_file_ids(self):
1128
        raise NotImplementedError(self.supports_setting_file_ids)
1129
7490.85.2 by Jelmer Vernooij
Split PreviewTree.
1130
    @property
1131
    def _by_parent(self):
1132
        if self.__by_parent is None:
1133
            self.__by_parent = self._transform.by_parent()
1134
        return self.__by_parent
1135
1136
    def get_parent_ids(self):
1137
        return self._parent_ids
1138
1139
    def set_parent_ids(self, parent_ids):
1140
        self._parent_ids = parent_ids
1141
1142
    def get_revision_tree(self, revision_id):
1143
        return self._transform._tree.get_revision_tree(revision_id)
1144
1145
    def is_locked(self):
1146
        return False
1147
1148
    def lock_read(self):
1149
        # Perhaps in theory, this should lock the TreeTransform?
1150
        return lock.LogicalLockResult(self.unlock)
1151
1152
    def unlock(self):
1153
        pass
1154
1155
    def _path2trans_id(self, path):
7490.133.9 by Jelmer Vernooij
More improvements to the Git transform implementation.
1156
        """Look up the trans id associated with a path.
1157
1158
        :param path: path to look up, None when the path does not exist
1159
        :return: trans_id
1160
        """
7490.85.2 by Jelmer Vernooij
Split PreviewTree.
1161
        # We must not use None here, because that is a valid value to store.
1162
        trans_id = self._path2trans_id_cache.get(path, object)
1163
        if trans_id is not object:
1164
            return trans_id
1165
        segments = osutils.splitpath(path)
1166
        cur_parent = self._transform.root
1167
        for cur_segment in segments:
1168
            for child in self._all_children(cur_parent):
1169
                final_name = self._final_name_cache.get(child)
1170
                if final_name is None:
1171
                    final_name = self._transform.final_name(child)
1172
                    self._final_name_cache[child] = final_name
1173
                if final_name == cur_segment:
1174
                    cur_parent = child
1175
                    break
1176
            else:
1177
                self._path2trans_id_cache[path] = None
1178
                return None
1179
        self._path2trans_id_cache[path] = cur_parent
1180
        return cur_parent
1181
1182
    def _all_children(self, trans_id):
1183
        children = self._all_children_cache.get(trans_id)
1184
        if children is not None:
1185
            return children
1186
        children = set(self._transform.iter_tree_children(trans_id))
1187
        # children in the _new_parent set are provided by _by_parent.
1188
        children.difference_update(self._transform._new_parent)
1189
        children.update(self._by_parent.get(trans_id, []))
1190
        self._all_children_cache[trans_id] = children
1191
        return children
1192
1193
    def get_file_with_stat(self, path):
1194
        return self.get_file(path), None
1195
1196
    def is_executable(self, path):
1197
        trans_id = self._path2trans_id(path)
1198
        if trans_id is None:
1199
            return False
1200
        try:
1201
            return self._transform._new_executability[trans_id]
1202
        except KeyError:
1203
            try:
1204
                return self._transform._tree.is_executable(path)
1205
            except OSError as e:
1206
                if e.errno == errno.ENOENT:
1207
                    return False
1208
                raise
1209
            except errors.NoSuchFile:
1210
                return False
1211
1212
    def has_filename(self, path):
1213
        trans_id = self._path2trans_id(path)
1214
        if trans_id in self._transform._new_contents:
1215
            return True
1216
        elif trans_id in self._transform._removed_contents:
1217
            return False
1218
        else:
1219
            return self._transform._tree.has_filename(path)
1220
1221
    def get_file_sha1(self, path, stat_value=None):
1222
        trans_id = self._path2trans_id(path)
1223
        if trans_id is None:
1224
            raise errors.NoSuchFile(path)
1225
        kind = self._transform._new_contents.get(trans_id)
1226
        if kind is None:
1227
            return self._transform._tree.get_file_sha1(path)
1228
        if kind == 'file':
1229
            with self.get_file(path) as fileobj:
1230
                return osutils.sha_file(fileobj)
1231
1232
    def get_file_verifier(self, path, stat_value=None):
1233
        trans_id = self._path2trans_id(path)
1234
        if trans_id is None:
1235
            raise errors.NoSuchFile(path)
1236
        kind = self._transform._new_contents.get(trans_id)
1237
        if kind is None:
1238
            return self._transform._tree.get_file_verifier(path)
1239
        if kind == 'file':
1240
            with self.get_file(path) as fileobj:
1241
                return ("SHA1", osutils.sha_file(fileobj))
1242
1243
    def kind(self, path):
1244
        trans_id = self._path2trans_id(path)
1245
        if trans_id is None:
1246
            raise errors.NoSuchFile(path)
1247
        return self._transform.final_kind(trans_id)
1248
1249
    def stored_kind(self, path):
1250
        trans_id = self._path2trans_id(path)
1251
        if trans_id is None:
1252
            raise errors.NoSuchFile(path)
1253
        try:
1254
            return self._transform._new_contents[trans_id]
1255
        except KeyError:
1256
            return self._transform._tree.stored_kind(path)
1257
1258
    def _get_repository(self):
1259
        repo = getattr(self._transform._tree, '_repository', None)
1260
        if repo is None:
1261
            repo = self._transform._tree.branch.repository
1262
        return repo
1263
1264
    def _iter_parent_trees(self):
1265
        for revision_id in self.get_parent_ids():
1266
            try:
1267
                yield self.revision_tree(revision_id)
1268
            except errors.NoSuchRevisionInTree:
1269
                yield self._get_repository().revision_tree(revision_id)
1270
1271
    def get_file_size(self, path):
1272
        """See Tree.get_file_size"""
1273
        trans_id = self._path2trans_id(path)
1274
        if trans_id is None:
1275
            raise errors.NoSuchFile(path)
1276
        kind = self._transform.final_kind(trans_id)
1277
        if kind != 'file':
1278
            return None
1279
        if trans_id in self._transform._new_contents:
1280
            return self._stat_limbo_file(trans_id).st_size
1281
        if self.kind(path) == 'file':
1282
            return self._transform._tree.get_file_size(path)
1283
        else:
1284
            return None
1285
1286
    def get_reference_revision(self, path):
1287
        trans_id = self._path2trans_id(path)
1288
        if trans_id is None:
1289
            raise errors.NoSuchFile(path)
1290
        reference_revision = self._transform._new_reference_revision.get(trans_id)
1291
        if reference_revision is None:
1292
            return self._transform._tree.get_reference_revision(path)
1293
        return reference_revision
1294
1295
    def tree_kind(self, trans_id):
1296
        path = self._tree_id_paths.get(trans_id)
1297
        if path is None:
1298
            return None
1299
        kind = self._tree.path_content_summary(path)[0]
1300
        if kind == 'missing':
1301
            kind = None
1302
        return kind