/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/workingtree.py

  • Committer: Jelmer Vernooij
  • Date: 2017-06-08 23:30:31 UTC
  • mto: This revision was merged to the branch mainline in revision 6690.
  • Revision ID: jelmer@jelmer.uk-20170608233031-3qavls2o7a1pqllj
Update imports.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 
19
19
A WorkingTree represents the editable working copy of a branch.
20
20
Operations which represent the WorkingTree are also done here,
21
 
such as renaming or adding files.
 
21
such as renaming or adding files.  
22
22
 
23
23
At the moment every WorkingTree has its own branch.  Remote
24
24
WorkingTrees aren't supported.
25
25
 
26
 
To get a WorkingTree, call controldir.open_workingtree() or
 
26
To get a WorkingTree, call bzrdir.open_workingtree() or
27
27
WorkingTree.open(dir).
28
28
"""
29
29
 
30
 
import contextlib
 
30
from __future__ import absolute_import
 
31
 
31
32
import errno
32
33
import os
 
34
import re
33
35
import sys
34
36
 
35
37
import breezy
36
38
 
37
39
from .lazy_import import lazy_import
38
40
lazy_import(globals(), """
39
 
import shutil
 
41
from bisect import bisect_left
 
42
import itertools
 
43
import operator
40
44
import stat
41
45
 
42
46
from breezy import (
 
47
    branch,
43
48
    conflicts as _mod_conflicts,
 
49
    controldir,
 
50
    errors,
44
51
    filters as _mod_filters,
 
52
    generate_ids,
 
53
    globbing,
 
54
    ignores,
45
55
    merge,
46
56
    revision as _mod_revision,
 
57
    shelf,
47
58
    transform,
48
59
    transport,
 
60
    ui,
49
61
    views,
50
62
    )
51
 
from breezy.bzr import (
52
 
    generate_ids,
53
 
    )
54
63
""")
55
64
 
56
65
from . import (
57
 
    errors,
58
 
    )
59
 
from .controldir import (
60
 
    ControlComponent,
61
 
    ControlComponentFormatRegistry,
62
 
    ControlComponentFormat,
63
 
    ControlDir,
64
 
    ControlDirFormat,
65
 
    )
66
 
from . import (
67
66
    osutils,
68
67
    )
 
68
from .decorators import needs_read_lock, needs_write_lock
69
69
from .i18n import gettext
70
70
from . import mutabletree
71
 
from .symbol_versioning import deprecated_method, deprecated_in
 
71
from .mutabletree import needs_tree_write_lock
72
72
from .trace import mutter, note
73
73
 
74
74
 
75
 
class SettingFileIdUnsupported(errors.BzrError):
76
 
 
77
 
    _fmt = "This format does not support setting file ids."
78
 
 
79
 
 
80
 
class ShelvingUnsupported(errors.BzrError):
81
 
 
82
 
    _fmt = "This format does not support shelving changes."
83
 
 
84
 
 
85
 
class WorkingTree(mutabletree.MutableTree, ControlComponent):
 
75
ERROR_PATH_NOT_FOUND = 3    # WindowsError errno code, equivalent to ENOENT
 
76
 
 
77
 
 
78
class TreeEntry(object):
 
79
    """An entry that implements the minimum interface used by commands.
 
80
 
 
81
    This needs further inspection, it may be better to have
 
82
    InventoryEntries without ids - though that seems wrong. For now,
 
83
    this is a parallel hierarchy to InventoryEntry, and needs to become
 
84
    one of several things: decorates to that hierarchy, children of, or
 
85
    parents of it.
 
86
    Another note is that these objects are currently only used when there is
 
87
    no InventoryEntry available - i.e. for unversioned objects.
 
88
    Perhaps they should be UnversionedEntry et al. ? - RBC 20051003
 
89
    """
 
90
 
 
91
    def __eq__(self, other):
 
92
        # yes, this us ugly, TODO: best practice __eq__ style.
 
93
        return (isinstance(other, TreeEntry)
 
94
                and other.__class__ == self.__class__)
 
95
 
 
96
    def kind_character(self):
 
97
        return "???"
 
98
 
 
99
 
 
100
class TreeDirectory(TreeEntry):
 
101
    """See TreeEntry. This is a directory in a working tree."""
 
102
 
 
103
    def __eq__(self, other):
 
104
        return (isinstance(other, TreeDirectory)
 
105
                and other.__class__ == self.__class__)
 
106
 
 
107
    def kind_character(self):
 
108
        return "/"
 
109
 
 
110
 
 
111
class TreeFile(TreeEntry):
 
112
    """See TreeEntry. This is a regular file in a working tree."""
 
113
 
 
114
    def __eq__(self, other):
 
115
        return (isinstance(other, TreeFile)
 
116
                and other.__class__ == self.__class__)
 
117
 
 
118
    def kind_character(self):
 
119
        return ''
 
120
 
 
121
 
 
122
class TreeLink(TreeEntry):
 
123
    """See TreeEntry. This is a symlink in a working tree."""
 
124
 
 
125
    def __eq__(self, other):
 
126
        return (isinstance(other, TreeLink)
 
127
                and other.__class__ == self.__class__)
 
128
 
 
129
    def kind_character(self):
 
130
        return ''
 
131
 
 
132
 
 
133
class WorkingTree(mutabletree.MutableTree,
 
134
    controldir.ControlComponent):
86
135
    """Working copy tree.
87
136
 
88
137
    :ivar basedir: The root of the tree on disk. This is a unicode path object
98
147
                 _internal=False,
99
148
                 _transport=None,
100
149
                 _format=None,
101
 
                 _controldir=None):
 
150
                 _bzrdir=None):
102
151
        """Construct a WorkingTree instance. This is not a public API.
103
152
 
104
153
        :param branch: A branch to override probing for the branch.
105
154
        """
106
155
        self._format = _format
107
 
        self.controldir = _controldir
 
156
        self.bzrdir = _bzrdir
108
157
        if not _internal:
109
 
            raise errors.BzrError("Please use controldir.open_workingtree or "
110
 
                                  "WorkingTree.open() to obtain a WorkingTree.")
 
158
            raise errors.BzrError("Please use bzrdir.open_workingtree or "
 
159
                "WorkingTree.open() to obtain a WorkingTree.")
111
160
        basedir = osutils.safe_unicode(basedir)
112
161
        mutter("opening working tree %r", basedir)
113
162
        if branch is not None:
114
163
            self._branch = branch
115
164
        else:
116
 
            self._branch = self.controldir.open_branch()
 
165
            self._branch = self.bzrdir.open_branch()
117
166
        self.basedir = osutils.realpath(basedir)
118
167
        self._transport = _transport
119
168
        self._rules_searcher = None
121
170
 
122
171
    @property
123
172
    def user_transport(self):
124
 
        return self.controldir.user_transport
 
173
        return self.bzrdir.user_transport
125
174
 
126
175
    @property
127
176
    def control_transport(self):
128
177
        return self._transport
129
178
 
130
 
    def supports_symlinks(self):
131
 
        return osutils.supports_symlinks(self.basedir)
132
 
 
133
179
    def is_control_filename(self, filename):
134
180
        """True if filename is the name of a control file in this tree.
135
181
 
140
186
        that bzr controls in this tree. I.E. a random .bzr directory placed
141
187
        on disk will not be a control file for this tree.
142
188
        """
143
 
        return self.controldir.is_control_filename(filename)
 
189
        return self.bzrdir.is_control_filename(filename)
144
190
 
145
191
    branch = property(
146
192
        fget=lambda self: self._branch,
154
200
        """See `Tree.has_versioned_directories`."""
155
201
        return self._format.supports_versioned_directories
156
202
 
157
 
    def supports_merge_modified(self):
158
 
        """Indicate whether this workingtree supports storing merge_modified.
159
 
        """
160
 
        return self._format.supports_merge_modified
161
 
 
162
203
    def _supports_executable(self):
163
 
        return osutils.supports_executable(self.basedir)
 
204
        if sys.platform == 'win32':
 
205
            return False
 
206
        # FIXME: Ideally this should check the file system
 
207
        return True
164
208
 
165
209
    def break_lock(self):
166
210
        """Break a lock if one is present from another instance.
184
228
    def supports_views(self):
185
229
        return self.views.supports_views()
186
230
 
187
 
    def supports_setting_file_ids(self):
188
 
        return self._format.supports_setting_file_ids
189
 
 
190
231
    def get_config_stack(self):
191
232
        """Retrieve the config stack for this tree.
192
233
 
202
243
        """
203
244
        if path is None:
204
245
            path = osutils.getcwd()
205
 
        control = ControlDir.open(path, _unsupported=_unsupported)
 
246
        control = controldir.ControlDir.open(path, _unsupported=_unsupported)
206
247
        return control.open_workingtree(unsupported=_unsupported)
207
248
 
208
249
    @staticmethod
220
261
        """
221
262
        if path is None:
222
263
            path = osutils.getcwd()
223
 
        control, relpath = ControlDir.open_containing(path)
 
264
        control, relpath = controldir.ControlDir.open_containing(path)
224
265
        return control.open_workingtree(), relpath
225
266
 
226
267
    @staticmethod
279
320
        # self.relpath exists as a "thunk" to osutils, but canonical_relpath
280
321
        # doesn't - fix that up here before we enter the loop.
281
322
        if canonicalize:
282
 
            def fixer(p):
283
 
                return osutils.canonical_relpath(self.basedir, p)
 
323
            fixer = lambda p: osutils.canonical_relpath(self.basedir, p)
284
324
        else:
285
325
            fixer = self.relpath
286
326
        for filename in file_list:
287
327
            relpath = fixer(osutils.dereference_path(filename))
288
328
            if view_files and not osutils.is_inside_any(view_files, relpath):
289
 
                raise views.FileOutsideView(filename, view_files)
 
329
                raise errors.FileOutsideView(filename, view_files)
290
330
            new_list.append(relpath)
291
331
        return new_list
292
332
 
294
334
    def open_downlevel(path=None):
295
335
        """Open an unsupported working tree.
296
336
 
297
 
        Only intended for advanced situations like upgrading part of a controldir.
 
337
        Only intended for advanced situations like upgrading part of a bzrdir.
298
338
        """
299
339
        return WorkingTree.open(path, _unsupported=True)
300
340
 
 
341
    @staticmethod
 
342
    def find_trees(location):
 
343
        def list_current(transport):
 
344
            return [d for d in transport.list_dir('') if d != '.bzr']
 
345
        def evaluate(bzrdir):
 
346
            try:
 
347
                tree = bzrdir.open_workingtree()
 
348
            except errors.NoWorkingTree:
 
349
                return True, None
 
350
            else:
 
351
                return True, tree
 
352
        t = transport.get_transport(location)
 
353
        iterator = controldir.ControlDir.find_bzrdirs(t, evaluate=evaluate,
 
354
                                              list_current=list_current)
 
355
        return [tr for tr in iterator if tr is not None]
 
356
 
301
357
    def __repr__(self):
302
358
        return "<%s of %s>" % (self.__class__.__name__,
303
359
                               getattr(self, 'basedir', None))
319
375
            # in the future this should return the tree for
320
376
            # 'empty:' - the implicit root empty tree.
321
377
            return self.branch.repository.revision_tree(
322
 
                _mod_revision.NULL_REVISION)
 
378
                       _mod_revision.NULL_REVISION)
323
379
        try:
324
380
            return self.revision_tree(revision_id)
325
381
        except errors.NoSuchRevision:
326
382
            pass
327
383
        # No cached copy available, retrieve from the repository.
328
 
        # FIXME? RBC 20060403 should we cache the tree locally
 
384
        # FIXME? RBC 20060403 should we cache the inventory locally
329
385
        # at this point ?
330
386
        try:
331
387
            return self.branch.repository.revision_tree(revision_id)
337
393
                raise
338
394
            # the basis tree is a ghost so return an empty tree.
339
395
            return self.branch.repository.revision_tree(
340
 
                _mod_revision.NULL_REVISION)
 
396
                       _mod_revision.NULL_REVISION)
 
397
 
 
398
    def _cleanup(self):
 
399
        self._flush_ignore_list_cache()
341
400
 
342
401
    def relpath(self, path):
343
402
        """Return the local path portion from a given path.
350
409
    def has_filename(self, filename):
351
410
        return osutils.lexists(self.abspath(filename))
352
411
 
353
 
    def get_file(self, path, filtered=True):
354
 
        return self.get_file_with_stat(path, filtered=filtered)[0]
 
412
    def get_file(self, file_id, path=None, filtered=True):
 
413
        return self.get_file_with_stat(file_id, path, filtered=filtered)[0]
355
414
 
356
 
    def get_file_with_stat(self, path, filtered=True,
 
415
    def get_file_with_stat(self, file_id, path=None, filtered=True,
357
416
                           _fstat=osutils.fstat):
358
417
        """See Tree.get_file_with_stat."""
359
 
        abspath = self.abspath(path)
360
 
        try:
361
 
            file_obj = open(abspath, 'rb')
362
 
        except EnvironmentError as e:
363
 
            if e.errno == errno.ENOENT:
364
 
                raise errors.NoSuchFile(path)
365
 
            raise
 
418
        if path is None:
 
419
            path = self.id2path(file_id)
 
420
        file_obj = self.get_file_byname(path, filtered=False)
366
421
        stat_value = _fstat(file_obj.fileno())
367
422
        if filtered and self.supports_content_filtering():
368
423
            filters = self._content_filter_stack(path)
369
 
            if filters:
370
 
                file_obj, size = _mod_filters.filtered_input_file(
371
 
                    file_obj, filters)
372
 
                stat_value = _mod_filters.FilteredStat(
373
 
                    stat_value, st_size=size)
 
424
            file_obj = _mod_filters.filtered_input_file(file_obj, filters)
374
425
        return (file_obj, stat_value)
375
426
 
376
 
    def get_file_text(self, path, filtered=True):
377
 
        with self.get_file(path, filtered=filtered) as my_file:
 
427
    def get_file_text(self, file_id, path=None, filtered=True):
 
428
        my_file = self.get_file(file_id, path=path, filtered=filtered)
 
429
        try:
378
430
            return my_file.read()
379
 
 
380
 
    def get_file_lines(self, path, filtered=True):
 
431
        finally:
 
432
            my_file.close()
 
433
 
 
434
    def get_file_byname(self, filename, filtered=True):
 
435
        path = self.abspath(filename)
 
436
        f = file(path, 'rb')
 
437
        if filtered and self.supports_content_filtering():
 
438
            filters = self._content_filter_stack(filename)
 
439
            return _mod_filters.filtered_input_file(f, filters)
 
440
        else:
 
441
            return f
 
442
 
 
443
    def get_file_lines(self, file_id, path=None, filtered=True):
381
444
        """See Tree.get_file_lines()"""
382
 
        with self.get_file(path, filtered=filtered) as file:
 
445
        file = self.get_file(file_id, path, filtered=filtered)
 
446
        try:
383
447
            return file.readlines()
 
448
        finally:
 
449
            file.close()
384
450
 
385
451
    def get_parent_ids(self):
386
452
        """See Tree.get_parent_ids.
399
465
            pass
400
466
        else:
401
467
            for l in osutils.split_lines(merges_bytes):
402
 
                revision_id = l.rstrip(b'\n')
 
468
                revision_id = l.rstrip('\n')
403
469
                parents.append(revision_id)
404
470
        return parents
405
471
 
 
472
    def get_root_id(self):
 
473
        """Return the id of this trees root"""
 
474
        raise NotImplementedError(self.get_root_id)
 
475
 
 
476
    @needs_read_lock
406
477
    def clone(self, to_controldir, revision_id=None):
407
478
        """Duplicate this working tree into to_bzr, including all state.
408
479
 
416
487
            revision, and difference between the source trees last revision
417
488
            and this one merged in.
418
489
        """
419
 
        with self.lock_read():
420
 
            # assumes the target bzr dir format is compatible.
421
 
            result = to_controldir.create_workingtree()
422
 
            self.copy_content_into(result, revision_id)
423
 
            return result
 
490
        # assumes the target bzr dir format is compatible.
 
491
        result = to_controldir.create_workingtree()
 
492
        self.copy_content_into(result, revision_id)
 
493
        return result
424
494
 
 
495
    @needs_read_lock
425
496
    def copy_content_into(self, tree, revision_id=None):
426
497
        """Copy the current content and user files of this tree into tree."""
427
 
        with self.lock_read():
428
 
            tree.set_root_id(self.path2id(''))
429
 
            if revision_id is None:
430
 
                merge.transform_tree(tree, self)
 
498
        tree.set_root_id(self.get_root_id())
 
499
        if revision_id is None:
 
500
            merge.transform_tree(tree, self)
 
501
        else:
 
502
            # TODO now merge from tree.last_revision to revision (to preserve
 
503
            # user local changes)
 
504
            try:
 
505
                other_tree = self.revision_tree(revision_id)
 
506
            except errors.NoSuchRevision:
 
507
                other_tree = self.branch.repository.revision_tree(revision_id)
 
508
 
 
509
            merge.transform_tree(tree, other_tree)
 
510
            if revision_id == _mod_revision.NULL_REVISION:
 
511
                new_parents = []
431
512
            else:
432
 
                # TODO now merge from tree.last_revision to revision (to
433
 
                # preserve user local changes)
434
 
                try:
435
 
                    other_tree = self.revision_tree(revision_id)
436
 
                except errors.NoSuchRevision:
437
 
                    other_tree = self.branch.repository.revision_tree(
438
 
                        revision_id)
439
 
 
440
 
                merge.transform_tree(tree, other_tree)
441
 
                if revision_id == _mod_revision.NULL_REVISION:
442
 
                    new_parents = []
443
 
                else:
444
 
                    new_parents = [revision_id]
445
 
                tree.set_parent_ids(new_parents)
446
 
 
447
 
    def get_file_size(self, path):
 
513
                new_parents = [revision_id]
 
514
            tree.set_parent_ids(new_parents)
 
515
 
 
516
    def id2abspath(self, file_id):
 
517
        return self.abspath(self.id2path(file_id))
 
518
 
 
519
    def get_file_size(self, file_id):
448
520
        """See Tree.get_file_size"""
449
521
        # XXX: this returns the on-disk size; it should probably return the
450
522
        # canonical size
451
523
        try:
452
 
            return os.path.getsize(self.abspath(path))
 
524
            return os.path.getsize(self.id2abspath(file_id))
453
525
        except OSError as e:
454
526
            if e.errno != errno.ENOENT:
455
527
                raise
456
528
            else:
457
529
                return None
458
530
 
 
531
    @needs_tree_write_lock
459
532
    def _gather_kinds(self, files, kinds):
460
533
        """See MutableTree._gather_kinds."""
461
 
        with self.lock_tree_write():
462
 
            for pos, f in enumerate(files):
463
 
                if kinds[pos] is None:
464
 
                    fullpath = osutils.normpath(self.abspath(f))
465
 
                    try:
466
 
                        kinds[pos] = osutils.file_kind(fullpath)
467
 
                    except OSError as e:
468
 
                        if e.errno == errno.ENOENT:
469
 
                            raise errors.NoSuchFile(fullpath)
 
534
        for pos, f in enumerate(files):
 
535
            if kinds[pos] is None:
 
536
                fullpath = osutils.normpath(self.abspath(f))
 
537
                try:
 
538
                    kinds[pos] = osutils.file_kind(fullpath)
 
539
                except OSError as e:
 
540
                    if e.errno == errno.ENOENT:
 
541
                        raise errors.NoSuchFile(fullpath)
470
542
 
 
543
    @needs_write_lock
471
544
    def add_parent_tree_id(self, revision_id, allow_leftmost_as_ghost=False):
472
545
        """Add revision_id as a parent.
473
546
 
479
552
            added, or the allow_leftmost_as_ghost parameter is set True.
480
553
        :param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
481
554
        """
482
 
        with self.lock_write():
483
 
            parents = self.get_parent_ids() + [revision_id]
484
 
            self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1
485
 
                                or allow_leftmost_as_ghost)
 
555
        parents = self.get_parent_ids() + [revision_id]
 
556
        self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1
 
557
            or allow_leftmost_as_ghost)
486
558
 
 
559
    @needs_tree_write_lock
487
560
    def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
488
561
        """Add revision_id, tree tuple as a parent.
489
562
 
497
570
            If the revision_id is a ghost, pass None for the tree.
498
571
        :param allow_leftmost_as_ghost: Allow the first parent to be a ghost.
499
572
        """
500
 
        with self.lock_tree_write():
501
 
            parent_ids = self.get_parent_ids() + [parent_tuple[0]]
502
 
            if len(parent_ids) > 1:
503
 
                # the leftmost may have already been a ghost, preserve that if it
504
 
                # was.
505
 
                allow_leftmost_as_ghost = True
506
 
            self.set_parent_ids(parent_ids,
507
 
                                allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
573
        parent_ids = self.get_parent_ids() + [parent_tuple[0]]
 
574
        if len(parent_ids) > 1:
 
575
            # the leftmost may have already been a ghost, preserve that if it
 
576
            # was.
 
577
            allow_leftmost_as_ghost = True
 
578
        self.set_parent_ids(parent_ids,
 
579
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
508
580
 
 
581
    @needs_tree_write_lock
509
582
    def add_pending_merge(self, *revision_ids):
510
 
        with self.lock_tree_write():
511
 
            # TODO: Perhaps should check at this point that the
512
 
            # history of the revision is actually present?
513
 
            parents = self.get_parent_ids()
514
 
            updated = False
515
 
            for rev_id in revision_ids:
516
 
                if rev_id in parents:
517
 
                    continue
518
 
                parents.append(rev_id)
519
 
                updated = True
520
 
            if updated:
521
 
                self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
 
583
        # TODO: Perhaps should check at this point that the
 
584
        # history of the revision is actually present?
 
585
        parents = self.get_parent_ids()
 
586
        updated = False
 
587
        for rev_id in revision_ids:
 
588
            if rev_id in parents:
 
589
                continue
 
590
            parents.append(rev_id)
 
591
            updated = True
 
592
        if updated:
 
593
            self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
522
594
 
523
595
    def path_content_summary(self, path, _lstat=os.lstat,
524
 
                             _mapper=osutils.file_kind_from_stat_mode):
 
596
        _mapper=osutils.file_kind_from_stat_mode):
525
597
        """See Tree.path_content_summary."""
526
598
        abspath = self.abspath(path)
527
599
        try:
563
635
        if len(revision_ids) > 0:
564
636
            leftmost_id = revision_ids[0]
565
637
            if (not allow_leftmost_as_ghost and not
566
 
                    self.branch.repository.has_revision(leftmost_id)):
 
638
                self.branch.repository.has_revision(leftmost_id)):
567
639
                raise errors.GhostRevisionUnusableHere(leftmost_id)
568
640
 
569
641
    def _set_merges_from_parent_ids(self, parent_ids):
570
642
        merges = parent_ids[1:]
571
 
        self._transport.put_bytes('pending-merges', b'\n'.join(merges),
572
 
                                  mode=self.controldir._get_file_mode())
 
643
        self._transport.put_bytes('pending-merges', '\n'.join(merges),
 
644
            mode=self.bzrdir._get_file_mode())
573
645
 
574
646
    def _filter_parent_ids_by_ancestry(self, revision_ids):
575
647
        """Check that all merged revisions are proper 'heads'.
587
659
                new_revision_ids.append(revision_id)
588
660
        if new_revision_ids != revision_ids:
589
661
            mutter('requested to set revision_ids = %s,'
590
 
                   ' but filtered to %s', revision_ids, new_revision_ids)
 
662
                         ' but filtered to %s', revision_ids, new_revision_ids)
591
663
        return new_revision_ids
592
664
 
 
665
    @needs_tree_write_lock
593
666
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
594
667
        """Set the parent ids to revision_ids.
595
668
 
602
675
        :param revision_ids: The revision_ids to set as the parent ids of this
603
676
            working tree. Any of these may be ghosts.
604
677
        """
605
 
        with self.lock_tree_write():
606
 
            self._check_parents_for_ghosts(revision_ids,
607
 
                                           allow_leftmost_as_ghost=allow_leftmost_as_ghost)
608
 
            for revision_id in revision_ids:
609
 
                _mod_revision.check_not_reserved_id(revision_id)
610
 
 
611
 
            revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
612
 
 
613
 
            if len(revision_ids) > 0:
614
 
                self.set_last_revision(revision_ids[0])
615
 
            else:
616
 
                self.set_last_revision(_mod_revision.NULL_REVISION)
617
 
 
618
 
            self._set_merges_from_parent_ids(revision_ids)
619
 
 
 
678
        self._check_parents_for_ghosts(revision_ids,
 
679
            allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
680
        for revision_id in revision_ids:
 
681
            _mod_revision.check_not_reserved_id(revision_id)
 
682
 
 
683
        revision_ids = self._filter_parent_ids_by_ancestry(revision_ids)
 
684
 
 
685
        if len(revision_ids) > 0:
 
686
            self.set_last_revision(revision_ids[0])
 
687
        else:
 
688
            self.set_last_revision(_mod_revision.NULL_REVISION)
 
689
 
 
690
        self._set_merges_from_parent_ids(revision_ids)
 
691
 
 
692
    @needs_tree_write_lock
620
693
    def set_pending_merges(self, rev_list):
621
 
        with self.lock_tree_write():
622
 
            parents = self.get_parent_ids()
623
 
            leftmost = parents[:1]
624
 
            new_parents = leftmost + rev_list
625
 
            self.set_parent_ids(new_parents)
 
694
        parents = self.get_parent_ids()
 
695
        leftmost = parents[:1]
 
696
        new_parents = leftmost + rev_list
 
697
        self.set_parent_ids(new_parents)
626
698
 
 
699
    @needs_tree_write_lock
627
700
    def set_merge_modified(self, modified_hashes):
628
701
        """Set the merge modified hashes."""
629
702
        raise NotImplementedError(self.set_merge_modified)
638
711
        """
639
712
        return None
640
713
 
 
714
    @needs_write_lock # because merge pulls data into the branch.
641
715
    def merge_from_branch(self, branch, to_revision=None, from_revision=None,
642
716
                          merge_type=None, force=False):
643
717
        """Merge from a branch into this working tree.
649
723
            branch.last_revision().
650
724
        """
651
725
        from .merge import Merger, Merge3Merger
652
 
        with self.lock_write():
653
 
            merger = Merger(self.branch, this_tree=self)
654
 
            # check that there are no local alterations
655
 
            if not force and self.has_changes():
656
 
                raise errors.UncommittedChanges(self)
657
 
            if to_revision is None:
658
 
                to_revision = _mod_revision.ensure_null(branch.last_revision())
659
 
            merger.other_rev_id = to_revision
660
 
            if _mod_revision.is_null(merger.other_rev_id):
661
 
                raise errors.NoCommits(branch)
662
 
            self.branch.fetch(branch, stop_revision=merger.other_rev_id)
663
 
            merger.other_basis = merger.other_rev_id
664
 
            merger.other_tree = self.branch.repository.revision_tree(
665
 
                merger.other_rev_id)
666
 
            merger.other_branch = branch
667
 
            if from_revision is None:
668
 
                merger.find_base()
669
 
            else:
670
 
                merger.set_base_revision(from_revision, branch)
671
 
            if merger.base_rev_id == merger.other_rev_id:
672
 
                raise errors.PointlessMerge
673
 
            merger.backup_files = False
674
 
            if merge_type is None:
675
 
                merger.merge_type = Merge3Merger
676
 
            else:
677
 
                merger.merge_type = merge_type
678
 
            merger.set_interesting_files(None)
679
 
            merger.show_base = False
680
 
            merger.reprocess = False
681
 
            conflicts = merger.do_merge()
682
 
            merger.set_pending()
683
 
            return conflicts
 
726
        merger = Merger(self.branch, this_tree=self)
 
727
        # check that there are no local alterations
 
728
        if not force and self.has_changes():
 
729
            raise errors.UncommittedChanges(self)
 
730
        if to_revision is None:
 
731
            to_revision = _mod_revision.ensure_null(branch.last_revision())
 
732
        merger.other_rev_id = to_revision
 
733
        if _mod_revision.is_null(merger.other_rev_id):
 
734
            raise errors.NoCommits(branch)
 
735
        self.branch.fetch(branch, last_revision=merger.other_rev_id)
 
736
        merger.other_basis = merger.other_rev_id
 
737
        merger.other_tree = self.branch.repository.revision_tree(
 
738
            merger.other_rev_id)
 
739
        merger.other_branch = branch
 
740
        if from_revision is None:
 
741
            merger.find_base()
 
742
        else:
 
743
            merger.set_base_revision(from_revision, branch)
 
744
        if merger.base_rev_id == merger.other_rev_id:
 
745
            raise errors.PointlessMerge
 
746
        merger.backup_files = False
 
747
        if merge_type is None:
 
748
            merger.merge_type = Merge3Merger
 
749
        else:
 
750
            merger.merge_type = merge_type
 
751
        merger.set_interesting_files(None)
 
752
        merger.show_base = False
 
753
        merger.reprocess = False
 
754
        conflicts = merger.do_merge()
 
755
        merger.set_pending()
 
756
        return conflicts
684
757
 
685
758
    def merge_modified(self):
686
759
        """Return a dictionary of files modified by a merge.
690
763
        because of a merge.
691
764
 
692
765
        This returns a map of file_id->sha1, containing only files which are
693
 
        still in the working tree and have that text hash.
 
766
        still in the working inventory and have that text hash.
694
767
        """
695
768
        raise NotImplementedError(self.merge_modified)
696
769
 
 
770
    @needs_write_lock
697
771
    def mkdir(self, path, file_id=None):
698
772
        """See MutableTree.mkdir()."""
699
773
        if file_id is None:
700
 
            if self.supports_setting_file_ids():
701
 
                file_id = generate_ids.gen_file_id(os.path.basename(path))
 
774
            file_id = generate_ids.gen_file_id(os.path.basename(path))
 
775
        os.mkdir(self.abspath(path))
 
776
        self.add(path, file_id, 'directory')
 
777
        return file_id
 
778
 
 
779
    def get_symlink_target(self, file_id, path=None):
 
780
        if path is not None:
 
781
            abspath = self.abspath(path)
702
782
        else:
703
 
            if not self.supports_setting_file_ids():
704
 
                raise SettingFileIdUnsupported()
705
 
        with self.lock_write():
706
 
            os.mkdir(self.abspath(path))
707
 
            self.add(path, file_id, 'directory')
708
 
            return file_id
709
 
 
710
 
    def get_symlink_target(self, path):
711
 
        abspath = self.abspath(path)
 
783
            abspath = self.id2abspath(file_id)
712
784
        target = osutils.readlink(abspath)
713
785
        return target
714
786
 
715
787
    def subsume(self, other_tree):
716
788
        raise NotImplementedError(self.subsume)
717
789
 
718
 
    def _directory_is_tree_reference(self, relpath):
719
 
        raise NotImplementedError(self._directory_is_tree_reference)
720
 
 
721
 
    def extract(self, path, format=None):
 
790
    def _setup_directory_is_tree_reference(self):
 
791
        if self._branch.repository._format.supports_tree_reference:
 
792
            self._directory_is_tree_reference = \
 
793
                self._directory_may_be_tree_reference
 
794
        else:
 
795
            self._directory_is_tree_reference = \
 
796
                self._directory_is_never_tree_reference
 
797
 
 
798
    def _directory_is_never_tree_reference(self, relpath):
 
799
        return False
 
800
 
 
801
    def _directory_may_be_tree_reference(self, relpath):
 
802
        # as a special case, if a directory contains control files then
 
803
        # it's a tree reference, except that the root of the tree is not
 
804
        return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
 
805
        # TODO: We could ask all the control formats whether they
 
806
        # recognize this directory, but at the moment there's no cheap api
 
807
        # to do that.  Since we probably can only nest bzr checkouts and
 
808
        # they always use this name it's ok for now.  -- mbp 20060306
 
809
        #
 
810
        # FIXME: There is an unhandled case here of a subdirectory
 
811
        # containing .bzr but not a branch; that will probably blow up
 
812
        # when you try to commit it.  It might happen if there is a
 
813
        # checkout in a subdirectory.  This can be avoided by not adding
 
814
        # it.  mbp 20070306
 
815
 
 
816
    def extract(self, file_id, format=None):
722
817
        """Extract a subtree from this tree.
723
818
 
724
819
        A new branch will be created, relative to the path for this tree.
729
824
        """Write the in memory meta data to disk."""
730
825
        raise NotImplementedError(self.flush)
731
826
 
732
 
    def kind(self, relpath):
 
827
    def _kind(self, relpath):
733
828
        return osutils.file_kind(self.abspath(relpath))
734
829
 
735
 
    def list_files(self, include_root=False, from_dir=None, recursive=True,
736
 
                   recurse_nested=False):
 
830
    def list_files(self, include_root=False, from_dir=None, recursive=True):
737
831
        """List all files as (path, class, kind, id, entry).
738
832
 
739
833
        Lists, but does not descend into unversioned directories.
782
876
        """
783
877
        raise NotImplementedError(self.move)
784
878
 
785
 
    def copy_one(self, from_rel, to_rel):
786
 
        """Copy a file in the tree to a new location.
787
 
 
788
 
        This default implementation just copies the file, then
789
 
        adds the target.
790
 
 
791
 
        :param from_rel: From location (relative to tree root)
792
 
        :param to_rel: Target location (relative to tree root)
 
879
    @needs_tree_write_lock
 
880
    def rename_one(self, from_rel, to_rel, after=False):
 
881
        """Rename one file.
 
882
 
 
883
        This can change the directory or the filename or both.
 
884
 
 
885
        rename_one has several 'modes' to work. First, it can rename a physical
 
886
        file and change the file_id. That is the normal mode. Second, it can
 
887
        only change the file_id without touching any physical file.
 
888
 
 
889
        rename_one uses the second mode if 'after == True' and 'to_rel' is
 
890
        either not versioned or newly added, and present in the working tree.
 
891
 
 
892
        rename_one uses the second mode if 'after == False' and 'from_rel' is
 
893
        versioned but no longer in the working tree, and 'to_rel' is not
 
894
        versioned but present in the working tree.
 
895
 
 
896
        rename_one uses the first mode if 'after == False' and 'from_rel' is
 
897
        versioned and present in the working tree, and 'to_rel' is not
 
898
        versioned and not present in the working tree.
 
899
 
 
900
        Everything else results in an error.
793
901
        """
794
 
        shutil.copyfile(self.abspath(from_rel), self.abspath(to_rel))
795
 
        self.add(to_rel)
 
902
        raise NotImplementedError(self.rename_one)
796
903
 
 
904
    @needs_read_lock
797
905
    def unknowns(self):
798
906
        """Return all unknown files.
799
907
 
800
908
        These are files in the working directory that are not versioned or
801
909
        control files or ignored.
802
910
        """
803
 
        with self.lock_read():
804
 
            # force the extras method to be fully executed before returning, to
805
 
            # prevent race conditions with the lock
806
 
            return iter(
807
 
                [subp for subp in self.extras() if not self.is_ignored(subp)])
808
 
 
809
 
    def unversion(self, paths):
810
 
        """Remove the path in pahs from the current versioned set.
811
 
 
812
 
        When a path is unversioned, all of its children are automatically
 
911
        # force the extras method to be fully executed before returning, to
 
912
        # prevent race conditions with the lock
 
913
        return iter(
 
914
            [subp for subp in self.extras() if not self.is_ignored(subp)])
 
915
 
 
916
    def unversion(self, file_ids):
 
917
        """Remove the file ids in file_ids from the current versioned set.
 
918
 
 
919
        When a file_id is unversioned, all of its children are automatically
813
920
        unversioned.
814
921
 
815
 
        :param paths: The paths to stop versioning.
816
 
        :raises NoSuchFile: if any path is not currently versioned.
 
922
        :param file_ids: The file ids to stop versioning.
 
923
        :raises: NoSuchId if any fileid is not currently versioned.
817
924
        """
818
925
        raise NotImplementedError(self.unversion)
819
926
 
 
927
    @needs_write_lock
820
928
    def pull(self, source, overwrite=False, stop_revision=None,
821
929
             change_reporter=None, possible_transports=None, local=False,
822
 
             show_base=False, tag_selector=None):
823
 
        with self.lock_write(), source.lock_read():
 
930
             show_base=False):
 
931
        source.lock_read()
 
932
        try:
824
933
            old_revision_info = self.branch.last_revision_info()
825
934
            basis_tree = self.basis_tree()
826
935
            count = self.branch.pull(source, overwrite, stop_revision,
827
936
                                     possible_transports=possible_transports,
828
 
                                     local=local, tag_selector=tag_selector)
 
937
                                     local=local)
829
938
            new_revision_info = self.branch.last_revision_info()
830
939
            if new_revision_info != old_revision_info:
831
940
                repository = self.branch.repository
834
943
                    if parent_ids:
835
944
                        basis_id = parent_ids[0]
836
945
                        basis_tree = repository.revision_tree(basis_id)
837
 
                with basis_tree.lock_read():
 
946
                basis_tree.lock_read()
 
947
                try:
838
948
                    new_basis_tree = self.branch.basis_tree()
839
949
                    merge.merge_inner(
840
 
                        self.branch,
841
 
                        new_basis_tree,
842
 
                        basis_tree,
843
 
                        this_tree=self,
844
 
                        change_reporter=change_reporter,
845
 
                        show_base=show_base)
846
 
                    basis_root_id = basis_tree.path2id('')
847
 
                    new_root_id = new_basis_tree.path2id('')
 
950
                                self.branch,
 
951
                                new_basis_tree,
 
952
                                basis_tree,
 
953
                                this_tree=self,
 
954
                                pb=None,
 
955
                                change_reporter=change_reporter,
 
956
                                show_base=show_base)
 
957
                    basis_root_id = basis_tree.get_root_id()
 
958
                    new_root_id = new_basis_tree.get_root_id()
848
959
                    if new_root_id is not None and basis_root_id != new_root_id:
849
960
                        self.set_root_id(new_root_id)
 
961
                finally:
 
962
                    basis_tree.unlock()
850
963
                # TODO - dedup parents list with things merged by pull ?
851
964
                # reuse the revisiontree we merged against to set the new
852
965
                # tree data.
862
975
                merges = self.get_parent_ids()[1:]
863
976
                parent_trees.extend([
864
977
                    (parent, repository.revision_tree(parent)) for
865
 
                    parent in merges])
 
978
                     parent in merges])
866
979
                self.set_parent_trees(parent_trees)
867
980
            return count
 
981
        finally:
 
982
            source.unlock()
868
983
 
869
 
    def put_file_bytes_non_atomic(self, path, bytes):
 
984
    @needs_write_lock
 
985
    def put_file_bytes_non_atomic(self, file_id, bytes):
870
986
        """See MutableTree.put_file_bytes_non_atomic."""
871
 
        with self.lock_write(), open(self.abspath(path), 'wb') as stream:
 
987
        stream = file(self.id2abspath(file_id), 'wb')
 
988
        try:
872
989
            stream.write(bytes)
 
990
        finally:
 
991
            stream.close()
873
992
 
874
993
    def extras(self):
875
994
        """Yield all unversioned files in this WorkingTree.
876
995
 
877
 
        If there are any unversioned directories and the file format
878
 
        supports versioning directories, then only the directory is returned,
879
 
        not all its children. But if there are unversioned files under a
880
 
        versioned subdirectory, they are returned.
 
996
        If there are any unversioned directories then only the directory is
 
997
        returned, not all its children.  But if there are unversioned files
 
998
        under a versioned subdirectory, they are returned.
881
999
 
882
1000
        Currently returned depth-first, sorted by name within directories.
883
1001
        This is the same order used by 'osutils.walkdirs'.
891
1009
            if pat is not None:
892
1010
                yield subp, pat
893
1011
 
 
1012
    def get_ignore_list(self):
 
1013
        """Return list of ignore patterns.
 
1014
 
 
1015
        Cached in the Tree object after the first call.
 
1016
        """
 
1017
        ignoreset = getattr(self, '_ignoreset', None)
 
1018
        if ignoreset is not None:
 
1019
            return ignoreset
 
1020
 
 
1021
        ignore_globs = set()
 
1022
        ignore_globs.update(ignores.get_runtime_ignores())
 
1023
        ignore_globs.update(ignores.get_user_ignores())
 
1024
        if self.has_filename(breezy.IGNORE_FILENAME):
 
1025
            f = self.get_file_byname(breezy.IGNORE_FILENAME)
 
1026
            try:
 
1027
                ignore_globs.update(ignores.parse_ignore_file(f))
 
1028
            finally:
 
1029
                f.close()
 
1030
        self._ignoreset = ignore_globs
 
1031
        return ignore_globs
 
1032
 
 
1033
    def _flush_ignore_list_cache(self):
 
1034
        """Resets the cached ignore list to force a cache rebuild."""
 
1035
        self._ignoreset = None
 
1036
        self._ignoreglobster = None
 
1037
 
894
1038
    def is_ignored(self, filename):
895
1039
        r"""Check whether the filename matches an ignore pattern.
896
 
        """
897
 
        raise NotImplementedError(self.is_ignored)
898
 
 
899
 
    def stored_kind(self, path):
 
1040
 
 
1041
        Patterns containing '/' or '\' need to match the whole path;
 
1042
        others match against only the last component.  Patterns starting
 
1043
        with '!' are ignore exceptions.  Exceptions take precedence
 
1044
        over regular patterns and cause the filename to not be ignored.
 
1045
 
 
1046
        If the file is ignored, returns the pattern which caused it to
 
1047
        be ignored, otherwise None.  So this can simply be used as a
 
1048
        boolean if desired."""
 
1049
        if getattr(self, '_ignoreglobster', None) is None:
 
1050
            self._ignoreglobster = globbing.ExceptionGlobster(self.get_ignore_list())
 
1051
        return self._ignoreglobster.match(filename)
 
1052
 
 
1053
    def kind(self, file_id):
 
1054
        return osutils.file_kind(self.id2abspath(file_id))
 
1055
 
 
1056
    def stored_kind(self, file_id):
900
1057
        """See Tree.stored_kind"""
901
1058
        raise NotImplementedError(self.stored_kind)
902
1059
 
920
1077
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
921
1078
        return kind, executable, stat_value
922
1079
 
 
1080
    def _file_size(self, entry, stat_value):
 
1081
        return stat_value.st_size
 
1082
 
923
1083
    def last_revision(self):
924
1084
        """Return the last revision of the branch for this tree.
925
1085
 
930
1090
        """
931
1091
        return self._last_revision()
932
1092
 
 
1093
    @needs_read_lock
933
1094
    def _last_revision(self):
934
1095
        """helper for get_parent_ids."""
935
 
        with self.lock_read():
936
 
            return _mod_revision.ensure_null(self.branch.last_revision())
 
1096
        return _mod_revision.ensure_null(self.branch.last_revision())
937
1097
 
938
1098
    def is_locked(self):
939
1099
        """Check if this tree is locked."""
986
1146
            self.branch._set_revision_history([new_revision])
987
1147
        return True
988
1148
 
 
1149
    @needs_tree_write_lock
989
1150
    def remove(self, files, verbose=False, to_file=None, keep_files=True,
990
 
               force=False):
 
1151
        force=False):
991
1152
        """Remove nominated files from the working tree metadata.
992
1153
 
993
1154
        :files: File paths relative to the basedir.
995
1156
        :force: Delete files and directories, even if they are changed and
996
1157
            even if the directories are not empty.
997
1158
        """
998
 
        raise NotImplementedError(self.remove)
999
 
 
 
1159
        if isinstance(files, basestring):
 
1160
            files = [files]
 
1161
 
 
1162
        inv_delta = []
 
1163
 
 
1164
        all_files = set() # specified and nested files 
 
1165
        unknown_nested_files=set()
 
1166
        if to_file is None:
 
1167
            to_file = sys.stdout
 
1168
 
 
1169
        files_to_backup = []
 
1170
 
 
1171
        def recurse_directory_to_add_files(directory):
 
1172
            # Recurse directory and add all files
 
1173
            # so we can check if they have changed.
 
1174
            for parent_info, file_infos in self.walkdirs(directory):
 
1175
                for relpath, basename, kind, lstat, fileid, kind in file_infos:
 
1176
                    # Is it versioned or ignored?
 
1177
                    if self.path2id(relpath):
 
1178
                        # Add nested content for deletion.
 
1179
                        all_files.add(relpath)
 
1180
                    else:
 
1181
                        # Files which are not versioned
 
1182
                        # should be treated as unknown.
 
1183
                        files_to_backup.append(relpath)
 
1184
 
 
1185
        for filename in files:
 
1186
            # Get file name into canonical form.
 
1187
            abspath = self.abspath(filename)
 
1188
            filename = self.relpath(abspath)
 
1189
            if len(filename) > 0:
 
1190
                all_files.add(filename)
 
1191
                recurse_directory_to_add_files(filename)
 
1192
 
 
1193
        files = list(all_files)
 
1194
 
 
1195
        if len(files) == 0:
 
1196
            return # nothing to do
 
1197
 
 
1198
        # Sort needed to first handle directory content before the directory
 
1199
        files.sort(reverse=True)
 
1200
 
 
1201
        # Bail out if we are going to delete files we shouldn't
 
1202
        if not keep_files and not force:
 
1203
            for (file_id, path, content_change, versioned, parent_id, name,
 
1204
                 kind, executable) in self.iter_changes(self.basis_tree(),
 
1205
                     include_unchanged=True, require_versioned=False,
 
1206
                     want_unversioned=True, specific_files=files):
 
1207
                if versioned[0] == False:
 
1208
                    # The record is unknown or newly added
 
1209
                    files_to_backup.append(path[1])
 
1210
                elif (content_change and (kind[1] is not None) and
 
1211
                        osutils.is_inside_any(files, path[1])):
 
1212
                    # Versioned and changed, but not deleted, and still
 
1213
                    # in one of the dirs to be deleted.
 
1214
                    files_to_backup.append(path[1])
 
1215
 
 
1216
        def backup(file_to_backup):
 
1217
            backup_name = self.bzrdir._available_backup_name(file_to_backup)
 
1218
            osutils.rename(abs_path, self.abspath(backup_name))
 
1219
            return "removed %s (but kept a copy: %s)" % (file_to_backup,
 
1220
                                                         backup_name)
 
1221
 
 
1222
        # Build inv_delta and delete files where applicable,
 
1223
        # do this before any modifications to meta data.
 
1224
        for f in files:
 
1225
            fid = self.path2id(f)
 
1226
            message = None
 
1227
            if not fid:
 
1228
                message = "%s is not versioned." % (f,)
 
1229
            else:
 
1230
                if verbose:
 
1231
                    # having removed it, it must be either ignored or unknown
 
1232
                    if self.is_ignored(f):
 
1233
                        new_status = 'I'
 
1234
                    else:
 
1235
                        new_status = '?'
 
1236
                    # XXX: Really should be a more abstract reporter interface
 
1237
                    kind_ch = osutils.kind_marker(self.kind(fid))
 
1238
                    to_file.write(new_status + '       ' + f + kind_ch + '\n')
 
1239
                # Unversion file
 
1240
                inv_delta.append((f, None, fid, None))
 
1241
                message = "removed %s" % (f,)
 
1242
 
 
1243
            if not keep_files:
 
1244
                abs_path = self.abspath(f)
 
1245
                if osutils.lexists(abs_path):
 
1246
                    if (osutils.isdir(abs_path) and
 
1247
                        len(os.listdir(abs_path)) > 0):
 
1248
                        if force:
 
1249
                            osutils.rmtree(abs_path)
 
1250
                            message = "deleted %s" % (f,)
 
1251
                        else:
 
1252
                            message = backup(f)
 
1253
                    else:
 
1254
                        if f in files_to_backup:
 
1255
                            message = backup(f)
 
1256
                        else:
 
1257
                            osutils.delete_any(abs_path)
 
1258
                            message = "deleted %s" % (f,)
 
1259
                elif message is not None:
 
1260
                    # Only care if we haven't done anything yet.
 
1261
                    message = "%s does not exist." % (f,)
 
1262
 
 
1263
            # Print only one message (if any) per file.
 
1264
            if message is not None:
 
1265
                note(message)
 
1266
        self.apply_inventory_delta(inv_delta)
 
1267
 
 
1268
    @needs_tree_write_lock
1000
1269
    def revert(self, filenames=None, old_tree=None, backups=True,
1001
1270
               pb=None, report_changes=False):
1002
1271
        from .conflicts import resolve
1003
 
        with contextlib.ExitStack() as exit_stack:
1004
 
            exit_stack.enter_context(self.lock_tree_write())
1005
 
            if old_tree is None:
1006
 
                basis_tree = self.basis_tree()
1007
 
                exit_stack.enter_context(basis_tree.lock_read())
1008
 
                old_tree = basis_tree
1009
 
            else:
1010
 
                basis_tree = None
 
1272
        if old_tree is None:
 
1273
            basis_tree = self.basis_tree()
 
1274
            basis_tree.lock_read()
 
1275
            old_tree = basis_tree
 
1276
        else:
 
1277
            basis_tree = None
 
1278
        try:
1011
1279
            conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1012
1280
                                         report_changes)
1013
1281
            if filenames is None and len(self.get_parent_ids()) > 1:
1016
1284
                if last_revision != _mod_revision.NULL_REVISION:
1017
1285
                    if basis_tree is None:
1018
1286
                        basis_tree = self.basis_tree()
1019
 
                        exit_stack.enter_context(basis_tree.lock_read())
 
1287
                        basis_tree.lock_read()
1020
1288
                    parent_trees.append((last_revision, basis_tree))
1021
1289
                self.set_parent_trees(parent_trees)
1022
1290
                resolve(self)
1023
1291
            else:
1024
1292
                resolve(self, filenames, ignore_misses=True, recursive=True)
1025
 
            return conflicts
 
1293
        finally:
 
1294
            if basis_tree is not None:
 
1295
                basis_tree.unlock()
 
1296
        return conflicts
1026
1297
 
 
1298
    @needs_write_lock
1027
1299
    def store_uncommitted(self):
1028
1300
        """Store uncommitted changes from the tree in the branch."""
1029
 
        raise NotImplementedError(self.store_uncommitted)
 
1301
        target_tree = self.basis_tree()
 
1302
        shelf_creator = shelf.ShelfCreator(self, target_tree)
 
1303
        try:
 
1304
            if not shelf_creator.shelve_all():
 
1305
                return
 
1306
            self.branch.store_uncommitted(shelf_creator)
 
1307
            shelf_creator.transform()
 
1308
        finally:
 
1309
            shelf_creator.finalize()
 
1310
        note('Uncommitted changes stored in branch "%s".', self.branch.nick)
1030
1311
 
 
1312
    @needs_write_lock
1031
1313
    def restore_uncommitted(self):
1032
1314
        """Restore uncommitted changes from the branch into the tree."""
1033
 
        raise NotImplementedError(self.restore_uncommitted)
 
1315
        unshelver = self.branch.get_unshelver(self)
 
1316
        if unshelver is None:
 
1317
            return
 
1318
        try:
 
1319
            merger = unshelver.make_merger()
 
1320
            merger.ignore_zero = True
 
1321
            merger.do_merge()
 
1322
            self.branch.store_uncommitted(None)
 
1323
        finally:
 
1324
            unshelver.finalize()
1034
1325
 
1035
1326
    def revision_tree(self, revision_id):
1036
1327
        """See Tree.revision_tree.
1037
1328
 
1038
 
        For trees that can be obtained from the working tree, this
1039
 
        will do so. For other trees, it will fall back to the repository.
 
1329
        WorkingTree can supply revision_trees for the basis revision only
 
1330
        because there is only one cached inventory in the bzr directory.
1040
1331
        """
1041
1332
        raise NotImplementedError(self.revision_tree)
1042
1333
 
 
1334
    @needs_tree_write_lock
1043
1335
    def set_root_id(self, file_id):
1044
1336
        """Set the root id for this tree."""
1045
 
        if not self.supports_setting_file_ids():
1046
 
            raise SettingFileIdUnsupported()
1047
 
        with self.lock_tree_write():
1048
 
            # for compatibility
1049
 
            if file_id is None:
1050
 
                raise ValueError(
1051
 
                    'WorkingTree.set_root_id with fileid=None')
1052
 
            self._set_root_id(file_id)
 
1337
        # for compatability
 
1338
        if file_id is None:
 
1339
            raise ValueError(
 
1340
                'WorkingTree.set_root_id with fileid=None')
 
1341
        file_id = osutils.safe_file_id(file_id)
 
1342
        self._set_root_id(file_id)
1053
1343
 
1054
1344
    def _set_root_id(self, file_id):
1055
1345
        """Set the root id for this tree, in a format specific manner.
1122
1412
        finally:
1123
1413
            self.unlock()
1124
1414
 
 
1415
    @needs_tree_write_lock
1125
1416
    def _update_tree(self, old_tip=None, change_reporter=None, revision=None,
1126
1417
                     show_base=False):
1127
1418
        """Update a tree to the master branch.
1139
1430
        # We MUST save it even if an error occurs, because otherwise the users
1140
1431
        # local work is unreferenced and will appear to have been lost.
1141
1432
        #
1142
 
        with self.lock_tree_write():
1143
 
            nb_conflicts = 0
 
1433
        nb_conflicts = 0
 
1434
        try:
 
1435
            last_rev = self.get_parent_ids()[0]
 
1436
        except IndexError:
 
1437
            last_rev = _mod_revision.NULL_REVISION
 
1438
        if revision is None:
 
1439
            revision = self.branch.last_revision()
 
1440
 
 
1441
        old_tip = old_tip or _mod_revision.NULL_REVISION
 
1442
 
 
1443
        if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
 
1444
            # the branch we are bound to was updated
 
1445
            # merge those changes in first
 
1446
            base_tree  = self.basis_tree()
 
1447
            other_tree = self.branch.repository.revision_tree(old_tip)
 
1448
            nb_conflicts = merge.merge_inner(self.branch, other_tree,
 
1449
                                             base_tree, this_tree=self,
 
1450
                                             change_reporter=change_reporter,
 
1451
                                             show_base=show_base)
 
1452
            if nb_conflicts:
 
1453
                self.add_parent_tree((old_tip, other_tree))
 
1454
                note(gettext('Rerun update after fixing the conflicts.'))
 
1455
                return nb_conflicts
 
1456
 
 
1457
        if last_rev != _mod_revision.ensure_null(revision):
 
1458
            # the working tree is up to date with the branch
 
1459
            # we can merge the specified revision from master
 
1460
            to_tree = self.branch.repository.revision_tree(revision)
 
1461
            to_root_id = to_tree.get_root_id()
 
1462
 
 
1463
            basis = self.basis_tree()
 
1464
            basis.lock_read()
1144
1465
            try:
1145
 
                last_rev = self.get_parent_ids()[0]
1146
 
            except IndexError:
1147
 
                last_rev = _mod_revision.NULL_REVISION
1148
 
            if revision is None:
1149
 
                revision = self.branch.last_revision()
1150
 
 
1151
 
            old_tip = old_tip or _mod_revision.NULL_REVISION
1152
 
 
1153
 
            if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1154
 
                # the branch we are bound to was updated
1155
 
                # merge those changes in first
1156
 
                base_tree = self.basis_tree()
1157
 
                other_tree = self.branch.repository.revision_tree(old_tip)
1158
 
                nb_conflicts = merge.merge_inner(self.branch, other_tree,
1159
 
                                                 base_tree, this_tree=self,
1160
 
                                                 change_reporter=change_reporter,
1161
 
                                                 show_base=show_base)
1162
 
                if nb_conflicts:
1163
 
                    self.add_parent_tree((old_tip, other_tree))
1164
 
                    note(gettext('Rerun update after fixing the conflicts.'))
1165
 
                    return nb_conflicts
1166
 
 
1167
 
            if last_rev != _mod_revision.ensure_null(revision):
1168
 
                # the working tree is up to date with the branch
1169
 
                # we can merge the specified revision from master
1170
 
                to_tree = self.branch.repository.revision_tree(revision)
1171
 
                to_root_id = to_tree.path2id('')
1172
 
 
1173
 
                basis = self.basis_tree()
1174
 
                with basis.lock_read():
1175
 
                    if (basis.path2id('') is None or basis.path2id('') != to_root_id):
1176
 
                        self.set_root_id(to_root_id)
1177
 
                        self.flush()
1178
 
 
1179
 
                # determine the branch point
1180
 
                graph = self.branch.repository.get_graph()
1181
 
                base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
1182
 
                                                    last_rev)
1183
 
                base_tree = self.branch.repository.revision_tree(base_rev_id)
1184
 
 
1185
 
                nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
1186
 
                                                 this_tree=self,
1187
 
                                                 change_reporter=change_reporter,
1188
 
                                                 show_base=show_base)
1189
 
                self.set_last_revision(revision)
1190
 
                # TODO - dedup parents list with things merged by pull ?
1191
 
                # reuse the tree we've updated to to set the basis:
1192
 
                parent_trees = [(revision, to_tree)]
1193
 
                merges = self.get_parent_ids()[1:]
1194
 
                # Ideally we ask the tree for the trees here, that way the working
1195
 
                # tree can decide whether to give us the entire tree or give us a
1196
 
                # lazy initialised tree. dirstate for instance will have the trees
1197
 
                # in ram already, whereas a last-revision + basis-inventory tree
1198
 
                # will not, but also does not need them when setting parents.
1199
 
                for parent in merges:
1200
 
                    parent_trees.append(
1201
 
                        (parent, self.branch.repository.revision_tree(parent)))
1202
 
                if not _mod_revision.is_null(old_tip):
1203
 
                    parent_trees.append(
1204
 
                        (old_tip, self.branch.repository.revision_tree(old_tip)))
1205
 
                self.set_parent_trees(parent_trees)
1206
 
                last_rev = parent_trees[0][0]
1207
 
            return nb_conflicts
 
1466
                if (basis.get_root_id() is None or basis.get_root_id() != to_root_id):
 
1467
                    self.set_root_id(to_root_id)
 
1468
                    self.flush()
 
1469
            finally:
 
1470
                basis.unlock()
 
1471
 
 
1472
            # determine the branch point
 
1473
            graph = self.branch.repository.get_graph()
 
1474
            base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
 
1475
                                                last_rev)
 
1476
            base_tree = self.branch.repository.revision_tree(base_rev_id)
 
1477
 
 
1478
            nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
 
1479
                                             this_tree=self,
 
1480
                                             change_reporter=change_reporter,
 
1481
                                             show_base=show_base)
 
1482
            self.set_last_revision(revision)
 
1483
            # TODO - dedup parents list with things merged by pull ?
 
1484
            # reuse the tree we've updated to to set the basis:
 
1485
            parent_trees = [(revision, to_tree)]
 
1486
            merges = self.get_parent_ids()[1:]
 
1487
            # Ideally we ask the tree for the trees here, that way the working
 
1488
            # tree can decide whether to give us the entire tree or give us a
 
1489
            # lazy initialised tree. dirstate for instance will have the trees
 
1490
            # in ram already, whereas a last-revision + basis-inventory tree
 
1491
            # will not, but also does not need them when setting parents.
 
1492
            for parent in merges:
 
1493
                parent_trees.append(
 
1494
                    (parent, self.branch.repository.revision_tree(parent)))
 
1495
            if not _mod_revision.is_null(old_tip):
 
1496
                parent_trees.append(
 
1497
                    (old_tip, self.branch.repository.revision_tree(old_tip)))
 
1498
            self.set_parent_trees(parent_trees)
 
1499
            last_rev = parent_trees[0][0]
 
1500
        return nb_conflicts
1208
1501
 
1209
1502
    def set_conflicts(self, arg):
1210
1503
        raise errors.UnsupportedOperation(self.set_conflicts, self)
1229
1522
        If the tree is not locked, it may cause an error to be raised,
1230
1523
        depending on the tree implementation.
1231
1524
        """
1232
 
        raise NotImplementedError(self.walkdirs)
1233
 
 
1234
 
    @deprecated_method(deprecated_in((3, 0, 1)))
 
1525
        disk_top = self.abspath(prefix)
 
1526
        if disk_top.endswith('/'):
 
1527
            disk_top = disk_top[:-1]
 
1528
        top_strip_len = len(disk_top) + 1
 
1529
        inventory_iterator = self._walkdirs(prefix)
 
1530
        disk_iterator = osutils.walkdirs(disk_top, prefix)
 
1531
        try:
 
1532
            current_disk = next(disk_iterator)
 
1533
            disk_finished = False
 
1534
        except OSError as e:
 
1535
            if not (e.errno == errno.ENOENT or
 
1536
                (sys.platform == 'win32' and e.errno == ERROR_PATH_NOT_FOUND)):
 
1537
                raise
 
1538
            current_disk = None
 
1539
            disk_finished = True
 
1540
        try:
 
1541
            current_inv = next(inventory_iterator)
 
1542
            inv_finished = False
 
1543
        except StopIteration:
 
1544
            current_inv = None
 
1545
            inv_finished = True
 
1546
        while not inv_finished or not disk_finished:
 
1547
            if current_disk:
 
1548
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
 
1549
                    cur_disk_dir_content) = current_disk
 
1550
            else:
 
1551
                ((cur_disk_dir_relpath, cur_disk_dir_path_from_top),
 
1552
                    cur_disk_dir_content) = ((None, None), None)
 
1553
            if not disk_finished:
 
1554
                # strip out .bzr dirs
 
1555
                if (cur_disk_dir_path_from_top[top_strip_len:] == '' and
 
1556
                    len(cur_disk_dir_content) > 0):
 
1557
                    # osutils.walkdirs can be made nicer -
 
1558
                    # yield the path-from-prefix rather than the pathjoined
 
1559
                    # value.
 
1560
                    bzrdir_loc = bisect_left(cur_disk_dir_content,
 
1561
                        ('.bzr', '.bzr'))
 
1562
                    if (bzrdir_loc < len(cur_disk_dir_content)
 
1563
                        and self.bzrdir.is_control_filename(
 
1564
                            cur_disk_dir_content[bzrdir_loc][0])):
 
1565
                        # we dont yield the contents of, or, .bzr itself.
 
1566
                        del cur_disk_dir_content[bzrdir_loc]
 
1567
            if inv_finished:
 
1568
                # everything is unknown
 
1569
                direction = 1
 
1570
            elif disk_finished:
 
1571
                # everything is missing
 
1572
                direction = -1
 
1573
            else:
 
1574
                direction = cmp(current_inv[0][0], cur_disk_dir_relpath)
 
1575
            if direction > 0:
 
1576
                # disk is before inventory - unknown
 
1577
                dirblock = [(relpath, basename, kind, stat, None, None) for
 
1578
                    relpath, basename, kind, stat, top_path in
 
1579
                    cur_disk_dir_content]
 
1580
                yield (cur_disk_dir_relpath, None), dirblock
 
1581
                try:
 
1582
                    current_disk = next(disk_iterator)
 
1583
                except StopIteration:
 
1584
                    disk_finished = True
 
1585
            elif direction < 0:
 
1586
                # inventory is before disk - missing.
 
1587
                dirblock = [(relpath, basename, 'unknown', None, fileid, kind)
 
1588
                    for relpath, basename, dkind, stat, fileid, kind in
 
1589
                    current_inv[1]]
 
1590
                yield (current_inv[0][0], current_inv[0][1]), dirblock
 
1591
                try:
 
1592
                    current_inv = next(inventory_iterator)
 
1593
                except StopIteration:
 
1594
                    inv_finished = True
 
1595
            else:
 
1596
                # versioned present directory
 
1597
                # merge the inventory and disk data together
 
1598
                dirblock = []
 
1599
                for relpath, subiterator in itertools.groupby(sorted(
 
1600
                    current_inv[1] + cur_disk_dir_content,
 
1601
                    key=operator.itemgetter(0)), operator.itemgetter(1)):
 
1602
                    path_elements = list(subiterator)
 
1603
                    if len(path_elements) == 2:
 
1604
                        inv_row, disk_row = path_elements
 
1605
                        # versioned, present file
 
1606
                        dirblock.append((inv_row[0],
 
1607
                            inv_row[1], disk_row[2],
 
1608
                            disk_row[3], inv_row[4],
 
1609
                            inv_row[5]))
 
1610
                    elif len(path_elements[0]) == 5:
 
1611
                        # unknown disk file
 
1612
                        dirblock.append((path_elements[0][0],
 
1613
                            path_elements[0][1], path_elements[0][2],
 
1614
                            path_elements[0][3], None, None))
 
1615
                    elif len(path_elements[0]) == 6:
 
1616
                        # versioned, absent file.
 
1617
                        dirblock.append((path_elements[0][0],
 
1618
                            path_elements[0][1], 'unknown', None,
 
1619
                            path_elements[0][4], path_elements[0][5]))
 
1620
                    else:
 
1621
                        raise NotImplementedError('unreachable code')
 
1622
                yield current_inv[0], dirblock
 
1623
                try:
 
1624
                    current_inv = next(inventory_iterator)
 
1625
                except StopIteration:
 
1626
                    inv_finished = True
 
1627
                try:
 
1628
                    current_disk = next(disk_iterator)
 
1629
                except StopIteration:
 
1630
                    disk_finished = True
 
1631
 
 
1632
    def _walkdirs(self, prefix=""):
 
1633
        """Walk the directories of this tree.
 
1634
 
 
1635
        :param prefix: is used as the directrory to start with.
 
1636
        :returns: a generator which yields items in the form::
 
1637
 
 
1638
            ((curren_directory_path, fileid),
 
1639
             [(file1_path, file1_name, file1_kind, None, file1_id,
 
1640
               file1_kind), ... ])
 
1641
        """
 
1642
        raise NotImplementedError(self._walkdirs)
 
1643
 
 
1644
    @needs_tree_write_lock
1235
1645
    def auto_resolve(self):
1236
1646
        """Automatically resolve text conflicts according to contents.
1237
1647
 
1242
1652
 
1243
1653
        :return: a tuple of ConflictLists: (un_resolved, resolved).
1244
1654
        """
1245
 
        with self.lock_tree_write():
1246
 
            un_resolved = _mod_conflicts.ConflictList()
1247
 
            resolved = _mod_conflicts.ConflictList()
1248
 
            for conflict in self.conflicts():
1249
 
                try:
1250
 
                    conflict.action_auto(self)
1251
 
                except NotImplementedError:
1252
 
                    un_resolved.append(conflict)
 
1655
        un_resolved = _mod_conflicts.ConflictList()
 
1656
        resolved = _mod_conflicts.ConflictList()
 
1657
        conflict_re = re.compile('^(<{7}|={7}|>{7})')
 
1658
        for conflict in self.conflicts():
 
1659
            if (conflict.typestring != 'text conflict' or
 
1660
                self.kind(conflict.file_id) != 'file'):
 
1661
                un_resolved.append(conflict)
 
1662
                continue
 
1663
            my_file = open(self.id2abspath(conflict.file_id), 'rb')
 
1664
            try:
 
1665
                for line in my_file:
 
1666
                    if conflict_re.search(line):
 
1667
                        un_resolved.append(conflict)
 
1668
                        break
1253
1669
                else:
1254
 
                    conflict.cleanup(self)
1255
1670
                    resolved.append(conflict)
1256
 
            self.set_conflicts(un_resolved)
1257
 
            return un_resolved, resolved
 
1671
            finally:
 
1672
                my_file.close()
 
1673
        resolved.remove_files(self)
 
1674
        self.set_conflicts(un_resolved)
 
1675
        return un_resolved, resolved
1258
1676
 
1259
1677
    def _validate(self):
1260
1678
        """Validate internal structures.
1283
1701
        """See Tree._get_rules_searcher."""
1284
1702
        if self._rules_searcher is None:
1285
1703
            self._rules_searcher = super(WorkingTree,
1286
 
                                         self)._get_rules_searcher(default_searcher)
 
1704
                self)._get_rules_searcher(default_searcher)
1287
1705
        return self._rules_searcher
1288
1706
 
1289
1707
    def get_shelf_manager(self):
1290
1708
        """Return the ShelfManager for this WorkingTree."""
1291
 
        raise NotImplementedError(self.get_shelf_manager)
1292
 
 
1293
 
    def get_canonical_paths(self, paths):
1294
 
        """Like get_canonical_path() but works on multiple items.
1295
 
 
1296
 
        :param paths: A sequence of paths relative to the root of the tree.
1297
 
        :return: A list of paths, with each item the corresponding input path
1298
 
            adjusted to account for existing elements that match case
1299
 
            insensitively.
1300
 
        """
1301
 
        with self.lock_read():
1302
 
            for path in paths:
1303
 
                yield path
1304
 
 
1305
 
    def get_canonical_path(self, path):
1306
 
        """Returns the first item in the tree that matches a path.
1307
 
 
1308
 
        This is meant to allow case-insensitive path lookups on e.g.
1309
 
        FAT filesystems.
1310
 
 
1311
 
        If a path matches exactly, it is returned. If no path matches exactly
1312
 
        but more than one path matches according to the underlying file system,
1313
 
        it is implementation defined which is returned.
1314
 
 
1315
 
        If no path matches according to the file system, the input path is
1316
 
        returned, but with as many path entries that do exist changed to their
1317
 
        canonical form.
1318
 
 
1319
 
        If you need to resolve many names from the same tree, you should
1320
 
        use get_canonical_paths() to avoid O(N) behaviour.
1321
 
 
1322
 
        :param path: A paths relative to the root of the tree.
1323
 
        :return: The input path adjusted to account for existing elements
1324
 
        that match case insensitively.
1325
 
        """
1326
 
        with self.lock_read():
1327
 
            return next(self.get_canonical_paths([path]))
1328
 
 
1329
 
    def reference_parent(self, path, branch=None, possible_transports=None):
1330
 
        raise errors.UnsupportedOperation(self.reference_parent, self)
1331
 
 
1332
 
    def get_reference_info(self, path, branch=None):
1333
 
        raise errors.UnsupportedOperation(self.get_reference_info, self)
1334
 
 
1335
 
    def set_reference_info(self, tree_path, branch_location):
1336
 
        raise errors.UnsupportedOperation(self.set_reference_info, self)
1337
 
 
1338
 
 
1339
 
class WorkingTreeFormatRegistry(ControlComponentFormatRegistry):
 
1709
        from .shelf import ShelfManager
 
1710
        return ShelfManager(self, self._transport)
 
1711
 
 
1712
 
 
1713
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
1340
1714
    """Registry for working tree formats."""
1341
1715
 
1342
1716
    def __init__(self, other_registry=None):
1347
1721
    def get_default(self):
1348
1722
        """Return the current default format."""
1349
1723
        if (self._default_format_key is not None and
1350
 
                self._default_format is None):
 
1724
            self._default_format is None):
1351
1725
            self._default_format = self.get(self._default_format_key)
1352
1726
        return self._default_format
1353
1727
 
1365
1739
format_registry = WorkingTreeFormatRegistry()
1366
1740
 
1367
1741
 
1368
 
class WorkingTreeFormat(ControlComponentFormat):
 
1742
class WorkingTreeFormat(controldir.ControlComponentFormat):
1369
1743
    """An encapsulation of the initialization and open routines for a format.
1370
1744
 
1371
1745
    Formats provide three things:
1396
1770
 
1397
1771
    supports_versioned_directories = None
1398
1772
 
1399
 
    supports_merge_modified = True
1400
 
    """If this format supports storing merge modified hashes."""
1401
 
 
1402
 
    supports_setting_file_ids = True
1403
 
    """If this format allows setting the file id."""
1404
 
 
1405
 
    supports_store_uncommitted = True
1406
 
    """If this format supports shelve-like functionality."""
1407
 
 
1408
 
    supports_leftmost_parent_id_as_ghost = True
1409
 
 
1410
 
    supports_righthand_parent_id_as_ghost = True
1411
 
 
1412
 
    ignore_filename = None
1413
 
    """Name of file with ignore patterns, if any. """
1414
 
 
1415
1773
    def initialize(self, controldir, revision_id=None, from_branch=None,
1416
1774
                   accelerator_tree=None, hardlink=False):
1417
1775
        """Initialize a new working tree in controldir.
1462
1820
        This is to support testing of working tree formats that can not exist
1463
1821
        in the same control directory as a branch.
1464
1822
        """
1465
 
        return self._matchingcontroldir
1466
 
 
1467
 
 
1468
 
format_registry.register_lazy(b"Bazaar Working Tree Format 4 (bzr 0.15)\n",
1469
 
                              "breezy.bzr.workingtree_4", "WorkingTreeFormat4")
1470
 
format_registry.register_lazy(b"Bazaar Working Tree Format 5 (bzr 1.11)\n",
1471
 
                              "breezy.bzr.workingtree_4", "WorkingTreeFormat5")
1472
 
format_registry.register_lazy(b"Bazaar Working Tree Format 6 (bzr 1.14)\n",
1473
 
                              "breezy.bzr.workingtree_4", "WorkingTreeFormat6")
1474
 
format_registry.register_lazy(b"Bazaar-NG Working Tree format 3",
1475
 
                              "breezy.bzr.workingtree_3", "WorkingTreeFormat3")
1476
 
format_registry.set_default_key(b"Bazaar Working Tree Format 6 (bzr 1.14)\n")
 
1823
        return self._matchingbzrdir
 
1824
 
 
1825
 
 
1826
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
 
1827
    "breezy.bzr.workingtree_4", "WorkingTreeFormat4")
 
1828
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",
 
1829
    "breezy.bzr.workingtree_4", "WorkingTreeFormat5")
 
1830
format_registry.register_lazy("Bazaar Working Tree Format 6 (bzr 1.14)\n",
 
1831
    "breezy.bzr.workingtree_4", "WorkingTreeFormat6")
 
1832
format_registry.register_lazy("Bazaar-NG Working Tree format 3",
 
1833
    "breezy.bzr.workingtree_3", "WorkingTreeFormat3")
 
1834
format_registry.set_default_key("Bazaar Working Tree Format 6 (bzr 1.14)\n")