/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: 2018-02-18 21:42:57 UTC
  • mto: This revision was merged to the branch mainline in revision 6859.
  • Revision ID: jelmer@jelmer.uk-20180218214257-jpevutp1wa30tz3v
Update TODO to reference Breezy, not Bazaar.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
35
import shutil
33
36
import sys
34
37
 
35
38
import breezy
36
39
 
37
40
from .lazy_import import lazy_import
38
41
lazy_import(globals(), """
39
 
import shutil
40
42
import stat
41
43
 
42
44
from breezy import (
 
45
    branch,
43
46
    conflicts as _mod_conflicts,
 
47
    controldir,
 
48
    errors,
44
49
    filters as _mod_filters,
 
50
    generate_ids,
45
51
    merge,
46
52
    revision as _mod_revision,
47
53
    transform,
48
54
    transport,
 
55
    ui,
49
56
    views,
50
57
    )
51
 
from breezy.bzr import (
52
 
    generate_ids,
53
 
    )
54
58
""")
55
59
 
56
60
from . import (
57
 
    errors,
58
 
    )
59
 
from .controldir import (
60
 
    ControlComponent,
61
 
    ControlComponentFormatRegistry,
62
 
    ControlComponentFormat,
63
 
    ControlDir,
64
 
    ControlDirFormat,
65
 
    )
66
 
from . import (
67
61
    osutils,
68
62
    )
69
63
from .i18n import gettext
70
64
from . import mutabletree
71
 
from .symbol_versioning import deprecated_method, deprecated_in
 
65
from .sixish import (
 
66
    text_type,
 
67
    )
72
68
from .trace import mutter, note
73
69
 
74
70
 
 
71
ERROR_PATH_NOT_FOUND = 3    # WindowsError errno code, equivalent to ENOENT
 
72
 
 
73
 
75
74
class SettingFileIdUnsupported(errors.BzrError):
76
75
 
77
76
    _fmt = "This format does not support setting file ids."
82
81
    _fmt = "This format does not support shelving changes."
83
82
 
84
83
 
85
 
class WorkingTree(mutabletree.MutableTree, ControlComponent):
 
84
class WorkingTree(mutabletree.MutableTree,
 
85
    controldir.ControlComponent):
86
86
    """Working copy tree.
87
87
 
88
88
    :ivar basedir: The root of the tree on disk. This is a unicode path object
107
107
        self.controldir = _controldir
108
108
        if not _internal:
109
109
            raise errors.BzrError("Please use controldir.open_workingtree or "
110
 
                                  "WorkingTree.open() to obtain a WorkingTree.")
 
110
                "WorkingTree.open() to obtain a WorkingTree.")
111
111
        basedir = osutils.safe_unicode(basedir)
112
112
        mutter("opening working tree %r", basedir)
113
113
        if branch is not None:
127
127
    def control_transport(self):
128
128
        return self._transport
129
129
 
130
 
    def supports_symlinks(self):
131
 
        return osutils.supports_symlinks(self.basedir)
132
 
 
133
130
    def is_control_filename(self, filename):
134
131
        """True if filename is the name of a control file in this tree.
135
132
 
154
151
        """See `Tree.has_versioned_directories`."""
155
152
        return self._format.supports_versioned_directories
156
153
 
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
154
    def _supports_executable(self):
163
 
        return osutils.supports_executable(self.basedir)
 
155
        if sys.platform == 'win32':
 
156
            return False
 
157
        # FIXME: Ideally this should check the file system
 
158
        return True
164
159
 
165
160
    def break_lock(self):
166
161
        """Break a lock if one is present from another instance.
202
197
        """
203
198
        if path is None:
204
199
            path = osutils.getcwd()
205
 
        control = ControlDir.open(path, _unsupported=_unsupported)
 
200
        control = controldir.ControlDir.open(path, _unsupported=_unsupported)
206
201
        return control.open_workingtree(unsupported=_unsupported)
207
202
 
208
203
    @staticmethod
220
215
        """
221
216
        if path is None:
222
217
            path = osutils.getcwd()
223
 
        control, relpath = ControlDir.open_containing(path)
 
218
        control, relpath = controldir.ControlDir.open_containing(path)
224
219
        return control.open_workingtree(), relpath
225
220
 
226
221
    @staticmethod
279
274
        # self.relpath exists as a "thunk" to osutils, but canonical_relpath
280
275
        # doesn't - fix that up here before we enter the loop.
281
276
        if canonicalize:
282
 
            def fixer(p):
283
 
                return osutils.canonical_relpath(self.basedir, p)
 
277
            fixer = lambda p: osutils.canonical_relpath(self.basedir, p)
284
278
        else:
285
279
            fixer = self.relpath
286
280
        for filename in file_list:
298
292
        """
299
293
        return WorkingTree.open(path, _unsupported=True)
300
294
 
 
295
    @staticmethod
 
296
    def find_trees(location):
 
297
        def list_current(transport):
 
298
            return [d for d in transport.list_dir('')
 
299
                    if not controldir.is_control_filename(d)]
 
300
        def evaluate(controldir):
 
301
            try:
 
302
                tree = controldir.open_workingtree()
 
303
            except errors.NoWorkingTree:
 
304
                return True, None
 
305
            else:
 
306
                return True, tree
 
307
        t = transport.get_transport(location)
 
308
        iterator = controldir.ControlDir.find_controldirs(t, evaluate=evaluate,
 
309
                                              list_current=list_current)
 
310
        return [tr for tr in iterator if tr is not None]
 
311
 
301
312
    def __repr__(self):
302
313
        return "<%s of %s>" % (self.__class__.__name__,
303
314
                               getattr(self, 'basedir', None))
319
330
            # in the future this should return the tree for
320
331
            # 'empty:' - the implicit root empty tree.
321
332
            return self.branch.repository.revision_tree(
322
 
                _mod_revision.NULL_REVISION)
 
333
                       _mod_revision.NULL_REVISION)
323
334
        try:
324
335
            return self.revision_tree(revision_id)
325
336
        except errors.NoSuchRevision:
337
348
                raise
338
349
            # the basis tree is a ghost so return an empty tree.
339
350
            return self.branch.repository.revision_tree(
340
 
                _mod_revision.NULL_REVISION)
 
351
                       _mod_revision.NULL_REVISION)
341
352
 
342
353
    def relpath(self, path):
343
354
        """Return the local path portion from a given path.
350
361
    def has_filename(self, filename):
351
362
        return osutils.lexists(self.abspath(filename))
352
363
 
353
 
    def get_file(self, path, filtered=True):
354
 
        return self.get_file_with_stat(path, filtered=filtered)[0]
 
364
    def get_file(self, path, file_id=None, filtered=True):
 
365
        return self.get_file_with_stat(path, file_id, filtered=filtered)[0]
355
366
 
356
 
    def get_file_with_stat(self, path, filtered=True,
 
367
    def get_file_with_stat(self, path, file_id=None, filtered=True,
357
368
                           _fstat=osutils.fstat):
358
369
        """See Tree.get_file_with_stat."""
359
370
        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
 
371
        file_obj = file(abspath, 'rb')
366
372
        stat_value = _fstat(file_obj.fileno())
367
373
        if filtered and self.supports_content_filtering():
368
374
            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)
 
375
            file_obj = _mod_filters.filtered_input_file(file_obj, filters)
374
376
        return (file_obj, stat_value)
375
377
 
376
 
    def get_file_text(self, path, filtered=True):
377
 
        with self.get_file(path, filtered=filtered) as my_file:
 
378
    def get_file_text(self, path, file_id=None, filtered=True):
 
379
        my_file = self.get_file(path, file_id, filtered=filtered)
 
380
        try:
378
381
            return my_file.read()
 
382
        finally:
 
383
            my_file.close()
379
384
 
380
 
    def get_file_lines(self, path, filtered=True):
 
385
    def get_file_lines(self, path, file_id=None, filtered=True):
381
386
        """See Tree.get_file_lines()"""
382
 
        with self.get_file(path, filtered=filtered) as file:
 
387
        file = self.get_file(path, file_id, filtered=filtered)
 
388
        try:
383
389
            return file.readlines()
 
390
        finally:
 
391
            file.close()
384
392
 
385
393
    def get_parent_ids(self):
386
394
        """See Tree.get_parent_ids.
399
407
            pass
400
408
        else:
401
409
            for l in osutils.split_lines(merges_bytes):
402
 
                revision_id = l.rstrip(b'\n')
 
410
                revision_id = l.rstrip('\n')
403
411
                parents.append(revision_id)
404
412
        return parents
405
413
 
 
414
    def get_root_id(self):
 
415
        """Return the id of this trees root"""
 
416
        raise NotImplementedError(self.get_root_id)
 
417
 
406
418
    def clone(self, to_controldir, revision_id=None):
407
419
        """Duplicate this working tree into to_bzr, including all state.
408
420
 
425
437
    def copy_content_into(self, tree, revision_id=None):
426
438
        """Copy the current content and user files of this tree into tree."""
427
439
        with self.lock_read():
428
 
            tree.set_root_id(self.path2id(''))
 
440
            tree.set_root_id(self.get_root_id())
429
441
            if revision_id is None:
430
442
                merge.transform_tree(tree, self)
431
443
            else:
435
447
                    other_tree = self.revision_tree(revision_id)
436
448
                except errors.NoSuchRevision:
437
449
                    other_tree = self.branch.repository.revision_tree(
438
 
                        revision_id)
 
450
                            revision_id)
439
451
 
440
452
                merge.transform_tree(tree, other_tree)
441
453
                if revision_id == _mod_revision.NULL_REVISION:
444
456
                    new_parents = [revision_id]
445
457
                tree.set_parent_ids(new_parents)
446
458
 
447
 
    def get_file_size(self, path):
 
459
    def id2abspath(self, file_id):
 
460
        return self.abspath(self.id2path(file_id))
 
461
 
 
462
    def get_file_size(self, path, file_id=None):
448
463
        """See Tree.get_file_size"""
449
464
        # XXX: this returns the on-disk size; it should probably return the
450
465
        # canonical size
482
497
        with self.lock_write():
483
498
            parents = self.get_parent_ids() + [revision_id]
484
499
            self.set_parent_ids(parents, allow_leftmost_as_ghost=len(parents) > 1
485
 
                                or allow_leftmost_as_ghost)
 
500
                or allow_leftmost_as_ghost)
486
501
 
487
502
    def add_parent_tree(self, parent_tuple, allow_leftmost_as_ghost=False):
488
503
        """Add revision_id, tree tuple as a parent.
504
519
                # was.
505
520
                allow_leftmost_as_ghost = True
506
521
            self.set_parent_ids(parent_ids,
507
 
                                allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
522
                allow_leftmost_as_ghost=allow_leftmost_as_ghost)
508
523
 
509
524
    def add_pending_merge(self, *revision_ids):
510
525
        with self.lock_tree_write():
521
536
                self.set_parent_ids(parents, allow_leftmost_as_ghost=True)
522
537
 
523
538
    def path_content_summary(self, path, _lstat=os.lstat,
524
 
                             _mapper=osutils.file_kind_from_stat_mode):
 
539
        _mapper=osutils.file_kind_from_stat_mode):
525
540
        """See Tree.path_content_summary."""
526
541
        abspath = self.abspath(path)
527
542
        try:
563
578
        if len(revision_ids) > 0:
564
579
            leftmost_id = revision_ids[0]
565
580
            if (not allow_leftmost_as_ghost and not
566
 
                    self.branch.repository.has_revision(leftmost_id)):
 
581
                self.branch.repository.has_revision(leftmost_id)):
567
582
                raise errors.GhostRevisionUnusableHere(leftmost_id)
568
583
 
569
584
    def _set_merges_from_parent_ids(self, parent_ids):
570
585
        merges = parent_ids[1:]
571
 
        self._transport.put_bytes('pending-merges', b'\n'.join(merges),
572
 
                                  mode=self.controldir._get_file_mode())
 
586
        self._transport.put_bytes('pending-merges', '\n'.join(merges),
 
587
            mode=self.controldir._get_file_mode())
573
588
 
574
589
    def _filter_parent_ids_by_ancestry(self, revision_ids):
575
590
        """Check that all merged revisions are proper 'heads'.
587
602
                new_revision_ids.append(revision_id)
588
603
        if new_revision_ids != revision_ids:
589
604
            mutter('requested to set revision_ids = %s,'
590
 
                   ' but filtered to %s', revision_ids, new_revision_ids)
 
605
                         ' but filtered to %s', revision_ids, new_revision_ids)
591
606
        return new_revision_ids
592
607
 
593
608
    def set_parent_ids(self, revision_ids, allow_leftmost_as_ghost=False):
604
619
        """
605
620
        with self.lock_tree_write():
606
621
            self._check_parents_for_ghosts(revision_ids,
607
 
                                           allow_leftmost_as_ghost=allow_leftmost_as_ghost)
 
622
                allow_leftmost_as_ghost=allow_leftmost_as_ghost)
608
623
            for revision_id in revision_ids:
609
624
                _mod_revision.check_not_reserved_id(revision_id)
610
625
 
659
674
            merger.other_rev_id = to_revision
660
675
            if _mod_revision.is_null(merger.other_rev_id):
661
676
                raise errors.NoCommits(branch)
662
 
            self.branch.fetch(branch, stop_revision=merger.other_rev_id)
 
677
            self.branch.fetch(branch, last_revision=merger.other_rev_id)
663
678
            merger.other_basis = merger.other_rev_id
664
679
            merger.other_tree = self.branch.repository.revision_tree(
665
680
                merger.other_rev_id)
707
722
            self.add(path, file_id, 'directory')
708
723
            return file_id
709
724
 
710
 
    def get_symlink_target(self, path):
711
 
        abspath = self.abspath(path)
 
725
    def get_symlink_target(self, path, file_id=None):
 
726
        if path is not None:
 
727
            abspath = self.abspath(path)
 
728
        else:
 
729
            abspath = self.id2abspath(file_id)
712
730
        target = osutils.readlink(abspath)
713
731
        return target
714
732
 
715
733
    def subsume(self, other_tree):
716
734
        raise NotImplementedError(self.subsume)
717
735
 
718
 
    def _directory_is_tree_reference(self, relpath):
719
 
        raise NotImplementedError(self._directory_is_tree_reference)
720
 
 
721
 
    def extract(self, path, format=None):
 
736
    def _setup_directory_is_tree_reference(self):
 
737
        if self._branch.repository._format.supports_tree_reference:
 
738
            self._directory_is_tree_reference = \
 
739
                self._directory_may_be_tree_reference
 
740
        else:
 
741
            self._directory_is_tree_reference = \
 
742
                self._directory_is_never_tree_reference
 
743
 
 
744
    def _directory_is_never_tree_reference(self, relpath):
 
745
        return False
 
746
 
 
747
    def _directory_may_be_tree_reference(self, relpath):
 
748
        # as a special case, if a directory contains control files then
 
749
        # it's a tree reference, except that the root of the tree is not
 
750
        return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
 
751
        # TODO: We could ask all the control formats whether they
 
752
        # recognize this directory, but at the moment there's no cheap api
 
753
        # to do that.  Since we probably can only nest bzr checkouts and
 
754
        # they always use this name it's ok for now.  -- mbp 20060306
 
755
        #
 
756
        # FIXME: There is an unhandled case here of a subdirectory
 
757
        # containing .bzr but not a branch; that will probably blow up
 
758
        # when you try to commit it.  It might happen if there is a
 
759
        # checkout in a subdirectory.  This can be avoided by not adding
 
760
        # it.  mbp 20070306
 
761
 
 
762
    def extract(self, path, file_id=None, format=None):
722
763
        """Extract a subtree from this tree.
723
764
 
724
765
        A new branch will be created, relative to the path for this tree.
729
770
        """Write the in memory meta data to disk."""
730
771
        raise NotImplementedError(self.flush)
731
772
 
732
 
    def kind(self, relpath):
 
773
    def kind(self, relpath, file_id=None):
 
774
        if file_id is not None:
 
775
            return osutils.file_kind(self.id2abspath(file_id))
733
776
        return osutils.file_kind(self.abspath(relpath))
734
777
 
735
 
    def list_files(self, include_root=False, from_dir=None, recursive=True,
736
 
                   recurse_nested=False):
 
778
    def list_files(self, include_root=False, from_dir=None, recursive=True):
737
779
        """List all files as (path, class, kind, id, entry).
738
780
 
739
781
        Lists, but does not descend into unversioned directories.
819
861
 
820
862
    def pull(self, source, overwrite=False, stop_revision=None,
821
863
             change_reporter=None, possible_transports=None, local=False,
822
 
             show_base=False, tag_selector=None):
 
864
             show_base=False):
823
865
        with self.lock_write(), source.lock_read():
824
866
            old_revision_info = self.branch.last_revision_info()
825
867
            basis_tree = self.basis_tree()
826
868
            count = self.branch.pull(source, overwrite, stop_revision,
827
869
                                     possible_transports=possible_transports,
828
 
                                     local=local, tag_selector=tag_selector)
 
870
                                     local=local)
829
871
            new_revision_info = self.branch.last_revision_info()
830
872
            if new_revision_info != old_revision_info:
831
873
                repository = self.branch.repository
837
879
                with basis_tree.lock_read():
838
880
                    new_basis_tree = self.branch.basis_tree()
839
881
                    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('')
 
882
                                self.branch,
 
883
                                new_basis_tree,
 
884
                                basis_tree,
 
885
                                this_tree=self,
 
886
                                change_reporter=change_reporter,
 
887
                                show_base=show_base)
 
888
                    basis_root_id = basis_tree.get_root_id()
 
889
                    new_root_id = new_basis_tree.get_root_id()
848
890
                    if new_root_id is not None and basis_root_id != new_root_id:
849
891
                        self.set_root_id(new_root_id)
850
892
                # TODO - dedup parents list with things merged by pull ?
862
904
                merges = self.get_parent_ids()[1:]
863
905
                parent_trees.extend([
864
906
                    (parent, repository.revision_tree(parent)) for
865
 
                    parent in merges])
 
907
                     parent in merges])
866
908
                self.set_parent_trees(parent_trees)
867
909
            return count
868
910
 
869
 
    def put_file_bytes_non_atomic(self, path, bytes):
 
911
    def put_file_bytes_non_atomic(self, path, bytes, file_id=None):
870
912
        """See MutableTree.put_file_bytes_non_atomic."""
871
 
        with self.lock_write(), open(self.abspath(path), 'wb') as stream:
872
 
            stream.write(bytes)
 
913
        with self.lock_write():
 
914
            stream = file(self.abspath(path), 'wb')
 
915
            try:
 
916
                stream.write(bytes)
 
917
            finally:
 
918
                stream.close()
873
919
 
874
920
    def extras(self):
875
921
        """Yield all unversioned files in this WorkingTree.
876
922
 
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.
 
923
        If there are any unversioned directories then only the directory is
 
924
        returned, not all its children.  But if there are unversioned files
 
925
        under a versioned subdirectory, they are returned.
881
926
 
882
927
        Currently returned depth-first, sorted by name within directories.
883
928
        This is the same order used by 'osutils.walkdirs'.
896
941
        """
897
942
        raise NotImplementedError(self.is_ignored)
898
943
 
899
 
    def stored_kind(self, path):
 
944
    def stored_kind(self, path, file_id=None):
900
945
        """See Tree.stored_kind"""
901
946
        raise NotImplementedError(self.stored_kind)
902
947
 
920
965
                executable = bool(stat.S_ISREG(mode) and stat.S_IEXEC & mode)
921
966
        return kind, executable, stat_value
922
967
 
 
968
    def _file_size(self, entry, stat_value):
 
969
        return stat_value.st_size
 
970
 
923
971
    def last_revision(self):
924
972
        """Return the last revision of the branch for this tree.
925
973
 
1000
1048
    def revert(self, filenames=None, old_tree=None, backups=True,
1001
1049
               pb=None, report_changes=False):
1002
1050
        from .conflicts import resolve
1003
 
        with contextlib.ExitStack() as exit_stack:
1004
 
            exit_stack.enter_context(self.lock_tree_write())
 
1051
        with self.lock_tree_write():
1005
1052
            if old_tree is None:
1006
1053
                basis_tree = self.basis_tree()
1007
 
                exit_stack.enter_context(basis_tree.lock_read())
 
1054
                basis_tree.lock_read()
1008
1055
                old_tree = basis_tree
1009
1056
            else:
1010
1057
                basis_tree = None
1011
 
            conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1012
 
                                         report_changes)
1013
 
            if filenames is None and len(self.get_parent_ids()) > 1:
1014
 
                parent_trees = []
1015
 
                last_revision = self.last_revision()
1016
 
                if last_revision != _mod_revision.NULL_REVISION:
1017
 
                    if basis_tree is None:
1018
 
                        basis_tree = self.basis_tree()
1019
 
                        exit_stack.enter_context(basis_tree.lock_read())
1020
 
                    parent_trees.append((last_revision, basis_tree))
1021
 
                self.set_parent_trees(parent_trees)
1022
 
                resolve(self)
1023
 
            else:
1024
 
                resolve(self, filenames, ignore_misses=True, recursive=True)
 
1058
            try:
 
1059
                conflicts = transform.revert(self, old_tree, filenames, backups, pb,
 
1060
                                             report_changes)
 
1061
                if filenames is None and len(self.get_parent_ids()) > 1:
 
1062
                    parent_trees = []
 
1063
                    last_revision = self.last_revision()
 
1064
                    if last_revision != _mod_revision.NULL_REVISION:
 
1065
                        if basis_tree is None:
 
1066
                            basis_tree = self.basis_tree()
 
1067
                            basis_tree.lock_read()
 
1068
                        parent_trees.append((last_revision, basis_tree))
 
1069
                    self.set_parent_trees(parent_trees)
 
1070
                    resolve(self)
 
1071
                else:
 
1072
                    resolve(self, filenames, ignore_misses=True, recursive=True)
 
1073
            finally:
 
1074
                if basis_tree is not None:
 
1075
                    basis_tree.unlock()
1025
1076
            return conflicts
1026
1077
 
1027
1078
    def store_uncommitted(self):
1045
1096
        if not self.supports_setting_file_ids():
1046
1097
            raise SettingFileIdUnsupported()
1047
1098
        with self.lock_tree_write():
1048
 
            # for compatibility
 
1099
            # for compatability
1049
1100
            if file_id is None:
1050
1101
                raise ValueError(
1051
1102
                    'WorkingTree.set_root_id with fileid=None')
 
1103
            file_id = osutils.safe_file_id(file_id)
1052
1104
            self._set_root_id(file_id)
1053
1105
 
1054
1106
    def _set_root_id(self, file_id):
1153
1205
            if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1154
1206
                # the branch we are bound to was updated
1155
1207
                # merge those changes in first
1156
 
                base_tree = self.basis_tree()
 
1208
                base_tree  = self.basis_tree()
1157
1209
                other_tree = self.branch.repository.revision_tree(old_tip)
1158
1210
                nb_conflicts = merge.merge_inner(self.branch, other_tree,
1159
1211
                                                 base_tree, this_tree=self,
1168
1220
                # the working tree is up to date with the branch
1169
1221
                # we can merge the specified revision from master
1170
1222
                to_tree = self.branch.repository.revision_tree(revision)
1171
 
                to_root_id = to_tree.path2id('')
 
1223
                to_root_id = to_tree.get_root_id()
1172
1224
 
1173
1225
                basis = self.basis_tree()
1174
1226
                with basis.lock_read():
1175
 
                    if (basis.path2id('') is None or basis.path2id('') != to_root_id):
 
1227
                    if (basis.get_root_id() is None or basis.get_root_id() != to_root_id):
1176
1228
                        self.set_root_id(to_root_id)
1177
1229
                        self.flush()
1178
1230
 
1231
1283
        """
1232
1284
        raise NotImplementedError(self.walkdirs)
1233
1285
 
1234
 
    @deprecated_method(deprecated_in((3, 0, 1)))
1235
1286
    def auto_resolve(self):
1236
1287
        """Automatically resolve text conflicts according to contents.
1237
1288
 
1245
1296
        with self.lock_tree_write():
1246
1297
            un_resolved = _mod_conflicts.ConflictList()
1247
1298
            resolved = _mod_conflicts.ConflictList()
 
1299
            conflict_re = re.compile('^(<{7}|={7}|>{7})')
1248
1300
            for conflict in self.conflicts():
1249
 
                try:
1250
 
                    conflict.action_auto(self)
1251
 
                except NotImplementedError:
 
1301
                if (conflict.typestring != 'text conflict' or
 
1302
                    self.kind(self.id2path(conflict.file_id), conflict.file_id) != 'file'):
1252
1303
                    un_resolved.append(conflict)
1253
 
                else:
1254
 
                    conflict.cleanup(self)
1255
 
                    resolved.append(conflict)
 
1304
                    continue
 
1305
                my_file = open(self.id2abspath(conflict.file_id), 'rb')
 
1306
                try:
 
1307
                    for line in my_file:
 
1308
                        if conflict_re.search(line):
 
1309
                            un_resolved.append(conflict)
 
1310
                            break
 
1311
                    else:
 
1312
                        resolved.append(conflict)
 
1313
                finally:
 
1314
                    my_file.close()
 
1315
            resolved.remove_files(self)
1256
1316
            self.set_conflicts(un_resolved)
1257
1317
            return un_resolved, resolved
1258
1318
 
1283
1343
        """See Tree._get_rules_searcher."""
1284
1344
        if self._rules_searcher is None:
1285
1345
            self._rules_searcher = super(WorkingTree,
1286
 
                                         self)._get_rules_searcher(default_searcher)
 
1346
                self)._get_rules_searcher(default_searcher)
1287
1347
        return self._rules_searcher
1288
1348
 
1289
1349
    def get_shelf_manager(self):
1290
1350
        """Return the ShelfManager for this WorkingTree."""
1291
1351
        raise NotImplementedError(self.get_shelf_manager)
1292
1352
 
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):
 
1353
 
 
1354
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
1340
1355
    """Registry for working tree formats."""
1341
1356
 
1342
1357
    def __init__(self, other_registry=None):
1347
1362
    def get_default(self):
1348
1363
        """Return the current default format."""
1349
1364
        if (self._default_format_key is not None and
1350
 
                self._default_format is None):
 
1365
            self._default_format is None):
1351
1366
            self._default_format = self.get(self._default_format_key)
1352
1367
        return self._default_format
1353
1368
 
1365
1380
format_registry = WorkingTreeFormatRegistry()
1366
1381
 
1367
1382
 
1368
 
class WorkingTreeFormat(ControlComponentFormat):
 
1383
class WorkingTreeFormat(controldir.ControlComponentFormat):
1369
1384
    """An encapsulation of the initialization and open routines for a format.
1370
1385
 
1371
1386
    Formats provide three things:
1396
1411
 
1397
1412
    supports_versioned_directories = None
1398
1413
 
1399
 
    supports_merge_modified = True
1400
 
    """If this format supports storing merge modified hashes."""
1401
 
 
1402
1414
    supports_setting_file_ids = True
1403
1415
    """If this format allows setting the file id."""
1404
1416
 
1407
1419
 
1408
1420
    supports_leftmost_parent_id_as_ghost = True
1409
1421
 
1410
 
    supports_righthand_parent_id_as_ghost = True
1411
 
 
1412
 
    ignore_filename = None
1413
 
    """Name of file with ignore patterns, if any. """
1414
 
 
1415
1422
    def initialize(self, controldir, revision_id=None, from_branch=None,
1416
1423
                   accelerator_tree=None, hardlink=False):
1417
1424
        """Initialize a new working tree in controldir.
1465
1472
        return self._matchingcontroldir
1466
1473
 
1467
1474
 
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")
 
1475
format_registry.register_lazy("Bazaar Working Tree Format 4 (bzr 0.15)\n",
 
1476
    "breezy.bzr.workingtree_4", "WorkingTreeFormat4")
 
1477
format_registry.register_lazy("Bazaar Working Tree Format 5 (bzr 1.11)\n",
 
1478
    "breezy.bzr.workingtree_4", "WorkingTreeFormat5")
 
1479
format_registry.register_lazy("Bazaar Working Tree Format 6 (bzr 1.14)\n",
 
1480
    "breezy.bzr.workingtree_4", "WorkingTreeFormat6")
 
1481
format_registry.register_lazy("Bazaar-NG Working Tree format 3",
 
1482
    "breezy.bzr.workingtree_3", "WorkingTreeFormat3")
 
1483
format_registry.set_default_key("Bazaar Working Tree Format 6 (bzr 1.14)\n")