/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: Gustav Hartvigsson
  • Date: 2021-01-09 21:36:27 UTC
  • Revision ID: gustav.hartvigsson@gmail.com-20210109213627-h1xwcutzy9m7a99b
Added 'Case Preserving Working Tree Use Cases' from Canonical Wiki

* Addod a page from the Canonical Bazaar wiki
  with information on the scmeatics of case
  perserving filesystems an a case insensitive
  filesystem works.
  
  * Needs re-work, but this will do as it is the
    same inforamoton as what was on the linked
    page in the currint documentation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
WorkingTree.open(dir).
28
28
"""
29
29
 
30
 
from __future__ import absolute_import
31
 
 
 
30
import contextlib
32
31
import errno
33
32
import os
34
 
import re
35
33
import sys
36
34
 
37
35
import breezy
43
41
 
44
42
from breezy import (
45
43
    conflicts as _mod_conflicts,
46
 
    controldir,
47
 
    errors,
48
44
    filters as _mod_filters,
49
 
    generate_ids,
50
45
    merge,
51
46
    revision as _mod_revision,
52
47
    transform,
53
48
    transport,
54
49
    views,
55
50
    )
 
51
from breezy.bzr import (
 
52
    generate_ids,
 
53
    )
56
54
""")
57
55
 
58
56
from . import (
 
57
    errors,
 
58
    )
 
59
from .controldir import (
 
60
    ControlComponent,
 
61
    ControlComponentFormatRegistry,
 
62
    ControlComponentFormat,
 
63
    ControlDir,
 
64
    ControlDirFormat,
 
65
    )
 
66
from . import (
59
67
    osutils,
60
68
    )
61
69
from .i18n import gettext
62
70
from . import mutabletree
 
71
from .symbol_versioning import deprecated_method, deprecated_in
63
72
from .trace import mutter, note
64
73
 
65
74
 
66
 
ERROR_PATH_NOT_FOUND = 3    # WindowsError errno code, equivalent to ENOENT
67
 
 
68
 
 
69
75
class SettingFileIdUnsupported(errors.BzrError):
70
76
 
71
77
    _fmt = "This format does not support setting file ids."
76
82
    _fmt = "This format does not support shelving changes."
77
83
 
78
84
 
79
 
class WorkingTree(mutabletree.MutableTree, controldir.ControlComponent):
 
85
class WorkingTree(mutabletree.MutableTree, ControlComponent):
80
86
    """Working copy tree.
81
87
 
82
88
    :ivar basedir: The root of the tree on disk. This is a unicode path object
121
127
    def control_transport(self):
122
128
        return self._transport
123
129
 
 
130
    def supports_symlinks(self):
 
131
        return osutils.supports_symlinks(self.basedir)
 
132
 
124
133
    def is_control_filename(self, filename):
125
134
        """True if filename is the name of a control file in this tree.
126
135
 
151
160
        return self._format.supports_merge_modified
152
161
 
153
162
    def _supports_executable(self):
154
 
        if sys.platform == 'win32':
155
 
            return False
156
 
        # FIXME: Ideally this should check the file system
157
 
        return True
 
163
        return osutils.supports_executable(self.basedir)
158
164
 
159
165
    def break_lock(self):
160
166
        """Break a lock if one is present from another instance.
196
202
        """
197
203
        if path is None:
198
204
            path = osutils.getcwd()
199
 
        control = controldir.ControlDir.open(path, _unsupported=_unsupported)
 
205
        control = ControlDir.open(path, _unsupported=_unsupported)
200
206
        return control.open_workingtree(unsupported=_unsupported)
201
207
 
202
208
    @staticmethod
214
220
        """
215
221
        if path is None:
216
222
            path = osutils.getcwd()
217
 
        control, relpath = controldir.ControlDir.open_containing(path)
 
223
        control, relpath = ControlDir.open_containing(path)
218
224
        return control.open_workingtree(), relpath
219
225
 
220
226
    @staticmethod
292
298
        """
293
299
        return WorkingTree.open(path, _unsupported=True)
294
300
 
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
 
 
301
 
        def evaluate(controldir):
302
 
            try:
303
 
                tree = controldir.open_workingtree()
304
 
            except errors.NoWorkingTree:
305
 
                return True, None
306
 
            else:
307
 
                return True, tree
308
 
        t = transport.get_transport(location)
309
 
        iterator = controldir.ControlDir.find_controldirs(t, evaluate=evaluate,
310
 
                                                          list_current=list_current)
311
 
        return [tr for tr in iterator if tr is not None]
312
 
 
313
301
    def __repr__(self):
314
302
        return "<%s of %s>" % (self.__class__.__name__,
315
303
                               getattr(self, 'basedir', None))
362
350
    def has_filename(self, filename):
363
351
        return osutils.lexists(self.abspath(filename))
364
352
 
365
 
    def get_file(self, path, file_id=None, filtered=True):
366
 
        return self.get_file_with_stat(path, file_id, filtered=filtered)[0]
 
353
    def get_file(self, path, filtered=True):
 
354
        return self.get_file_with_stat(path, filtered=filtered)[0]
367
355
 
368
 
    def get_file_with_stat(self, path, file_id=None, filtered=True,
 
356
    def get_file_with_stat(self, path, filtered=True,
369
357
                           _fstat=osutils.fstat):
370
358
        """See Tree.get_file_with_stat."""
371
359
        abspath = self.abspath(path)
378
366
        stat_value = _fstat(file_obj.fileno())
379
367
        if filtered and self.supports_content_filtering():
380
368
            filters = self._content_filter_stack(path)
381
 
            file_obj = _mod_filters.filtered_input_file(file_obj, filters)
 
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)
382
374
        return (file_obj, stat_value)
383
375
 
384
 
    def get_file_text(self, path, file_id=None, filtered=True):
385
 
        with self.get_file(path, file_id, filtered=filtered) as my_file:
 
376
    def get_file_text(self, path, filtered=True):
 
377
        with self.get_file(path, filtered=filtered) as my_file:
386
378
            return my_file.read()
387
379
 
388
 
    def get_file_lines(self, path, file_id=None, filtered=True):
 
380
    def get_file_lines(self, path, filtered=True):
389
381
        """See Tree.get_file_lines()"""
390
 
        with self.get_file(path, file_id, filtered=filtered) as file:
 
382
        with self.get_file(path, filtered=filtered) as file:
391
383
            return file.readlines()
392
384
 
393
385
    def get_parent_ids(self):
411
403
                parents.append(revision_id)
412
404
        return parents
413
405
 
414
 
    def get_root_id(self):
415
 
        """Return the id of this trees root"""
416
 
        raise NotImplementedError(self.get_root_id)
417
 
 
418
406
    def clone(self, to_controldir, revision_id=None):
419
407
        """Duplicate this working tree into to_bzr, including all state.
420
408
 
437
425
    def copy_content_into(self, tree, revision_id=None):
438
426
        """Copy the current content and user files of this tree into tree."""
439
427
        with self.lock_read():
440
 
            tree.set_root_id(self.get_root_id())
 
428
            tree.set_root_id(self.path2id(''))
441
429
            if revision_id is None:
442
430
                merge.transform_tree(tree, self)
443
431
            else:
456
444
                    new_parents = [revision_id]
457
445
                tree.set_parent_ids(new_parents)
458
446
 
459
 
    def get_file_size(self, path, file_id=None):
 
447
    def get_file_size(self, path):
460
448
        """See Tree.get_file_size"""
461
449
        # XXX: this returns the on-disk size; it should probably return the
462
450
        # canonical size
671
659
            merger.other_rev_id = to_revision
672
660
            if _mod_revision.is_null(merger.other_rev_id):
673
661
                raise errors.NoCommits(branch)
674
 
            self.branch.fetch(branch, last_revision=merger.other_rev_id)
 
662
            self.branch.fetch(branch, stop_revision=merger.other_rev_id)
675
663
            merger.other_basis = merger.other_rev_id
676
664
            merger.other_tree = self.branch.repository.revision_tree(
677
665
                merger.other_rev_id)
719
707
            self.add(path, file_id, 'directory')
720
708
            return file_id
721
709
 
722
 
    def get_symlink_target(self, path, file_id=None):
 
710
    def get_symlink_target(self, path):
723
711
        abspath = self.abspath(path)
724
 
        target = osutils.readlink(abspath)
725
 
        return target
 
712
        try:
 
713
            return osutils.readlink(abspath)
 
714
        except OSError as e:
 
715
            if getattr(e, 'errno', None) == errno.ENOENT:
 
716
                raise errors.NoSuchFile(path)
 
717
            raise
726
718
 
727
719
    def subsume(self, other_tree):
728
720
        raise NotImplementedError(self.subsume)
729
721
 
730
 
    def _setup_directory_is_tree_reference(self):
731
 
        if self._branch.repository._format.supports_tree_reference:
732
 
            self._directory_is_tree_reference = \
733
 
                self._directory_may_be_tree_reference
734
 
        else:
735
 
            self._directory_is_tree_reference = \
736
 
                self._directory_is_never_tree_reference
737
 
 
738
 
    def _directory_is_never_tree_reference(self, relpath):
739
 
        return False
740
 
 
741
 
    def _directory_may_be_tree_reference(self, relpath):
742
 
        # as a special case, if a directory contains control files then
743
 
        # it's a tree reference, except that the root of the tree is not
744
 
        return relpath and osutils.isdir(self.abspath(relpath) + u"/.bzr")
745
 
        # TODO: We could ask all the control formats whether they
746
 
        # recognize this directory, but at the moment there's no cheap api
747
 
        # to do that.  Since we probably can only nest bzr checkouts and
748
 
        # they always use this name it's ok for now.  -- mbp 20060306
749
 
        #
750
 
        # FIXME: There is an unhandled case here of a subdirectory
751
 
        # containing .bzr but not a branch; that will probably blow up
752
 
        # when you try to commit it.  It might happen if there is a
753
 
        # checkout in a subdirectory.  This can be avoided by not adding
754
 
        # it.  mbp 20070306
755
 
 
756
 
    def extract(self, path, file_id=None, format=None):
 
722
    def _directory_is_tree_reference(self, relpath):
 
723
        raise NotImplementedError(self._directory_is_tree_reference)
 
724
 
 
725
    def extract(self, path, format=None):
757
726
        """Extract a subtree from this tree.
758
727
 
759
728
        A new branch will be created, relative to the path for this tree.
764
733
        """Write the in memory meta data to disk."""
765
734
        raise NotImplementedError(self.flush)
766
735
 
767
 
    def kind(self, relpath, file_id=None):
 
736
    def kind(self, relpath):
768
737
        return osutils.file_kind(self.abspath(relpath))
769
738
 
770
 
    def list_files(self, include_root=False, from_dir=None, recursive=True):
 
739
    def list_files(self, include_root=False, from_dir=None, recursive=True,
 
740
                   recurse_nested=False):
771
741
        """List all files as (path, class, kind, id, entry).
772
742
 
773
743
        Lists, but does not descend into unversioned directories.
853
823
 
854
824
    def pull(self, source, overwrite=False, stop_revision=None,
855
825
             change_reporter=None, possible_transports=None, local=False,
856
 
             show_base=False):
 
826
             show_base=False, tag_selector=None):
857
827
        with self.lock_write(), source.lock_read():
858
828
            old_revision_info = self.branch.last_revision_info()
859
829
            basis_tree = self.basis_tree()
860
830
            count = self.branch.pull(source, overwrite, stop_revision,
861
831
                                     possible_transports=possible_transports,
862
 
                                     local=local)
 
832
                                     local=local, tag_selector=tag_selector)
863
833
            new_revision_info = self.branch.last_revision_info()
864
834
            if new_revision_info != old_revision_info:
865
835
                repository = self.branch.repository
877
847
                        this_tree=self,
878
848
                        change_reporter=change_reporter,
879
849
                        show_base=show_base)
880
 
                    basis_root_id = basis_tree.get_root_id()
881
 
                    new_root_id = new_basis_tree.get_root_id()
 
850
                    basis_root_id = basis_tree.path2id('')
 
851
                    new_root_id = new_basis_tree.path2id('')
882
852
                    if new_root_id is not None and basis_root_id != new_root_id:
883
853
                        self.set_root_id(new_root_id)
884
854
                # TODO - dedup parents list with things merged by pull ?
900
870
                self.set_parent_trees(parent_trees)
901
871
            return count
902
872
 
903
 
    def put_file_bytes_non_atomic(self, path, bytes, file_id=None):
 
873
    def put_file_bytes_non_atomic(self, path, bytes):
904
874
        """See MutableTree.put_file_bytes_non_atomic."""
905
875
        with self.lock_write(), open(self.abspath(path), 'wb') as stream:
906
876
            stream.write(bytes)
930
900
        """
931
901
        raise NotImplementedError(self.is_ignored)
932
902
 
933
 
    def stored_kind(self, path, file_id=None):
 
903
    def stored_kind(self, path):
934
904
        """See Tree.stored_kind"""
935
905
        raise NotImplementedError(self.stored_kind)
936
906
 
1034
1004
    def revert(self, filenames=None, old_tree=None, backups=True,
1035
1005
               pb=None, report_changes=False):
1036
1006
        from .conflicts import resolve
1037
 
        with self.lock_tree_write():
 
1007
        with contextlib.ExitStack() as exit_stack:
 
1008
            exit_stack.enter_context(self.lock_tree_write())
1038
1009
            if old_tree is None:
1039
1010
                basis_tree = self.basis_tree()
1040
 
                basis_tree.lock_read()
 
1011
                exit_stack.enter_context(basis_tree.lock_read())
1041
1012
                old_tree = basis_tree
1042
1013
            else:
1043
1014
                basis_tree = None
1044
 
            try:
1045
 
                conflicts = transform.revert(self, old_tree, filenames, backups, pb,
1046
 
                                             report_changes)
1047
 
                if filenames is None and len(self.get_parent_ids()) > 1:
1048
 
                    parent_trees = []
1049
 
                    last_revision = self.last_revision()
1050
 
                    if last_revision != _mod_revision.NULL_REVISION:
1051
 
                        if basis_tree is None:
1052
 
                            basis_tree = self.basis_tree()
1053
 
                            basis_tree.lock_read()
1054
 
                        parent_trees.append((last_revision, basis_tree))
1055
 
                    self.set_parent_trees(parent_trees)
1056
 
                    resolve(self)
1057
 
                else:
1058
 
                    resolve(self, filenames, ignore_misses=True, recursive=True)
1059
 
            finally:
1060
 
                if basis_tree is not None:
1061
 
                    basis_tree.unlock()
 
1015
            conflicts = transform.revert(self, old_tree, filenames, backups, pb,
 
1016
                                         report_changes)
 
1017
            if filenames is None and len(self.get_parent_ids()) > 1:
 
1018
                parent_trees = []
 
1019
                last_revision = self.last_revision()
 
1020
                if last_revision != _mod_revision.NULL_REVISION:
 
1021
                    if basis_tree is None:
 
1022
                        basis_tree = self.basis_tree()
 
1023
                        exit_stack.enter_context(basis_tree.lock_read())
 
1024
                    parent_trees.append((last_revision, basis_tree))
 
1025
                self.set_parent_trees(parent_trees)
 
1026
                resolve(self)
 
1027
            else:
 
1028
                resolve(self, filenames, ignore_misses=True, recursive=True)
1062
1029
            return conflicts
1063
1030
 
1064
1031
    def store_uncommitted(self):
1082
1049
        if not self.supports_setting_file_ids():
1083
1050
            raise SettingFileIdUnsupported()
1084
1051
        with self.lock_tree_write():
1085
 
            # for compatability
 
1052
            # for compatibility
1086
1053
            if file_id is None:
1087
1054
                raise ValueError(
1088
1055
                    'WorkingTree.set_root_id with fileid=None')
1089
 
            file_id = osutils.safe_file_id(file_id)
1090
1056
            self._set_root_id(file_id)
1091
1057
 
1092
1058
    def _set_root_id(self, file_id):
1109
1075
        """
1110
1076
        raise NotImplementedError(self.unlock)
1111
1077
 
1112
 
    _marker = object()
1113
 
 
1114
1078
    def update(self, change_reporter=None, possible_transports=None,
1115
 
               revision=None, old_tip=_marker, show_base=False):
 
1079
               revision=None, old_tip=None, show_base=False):
1116
1080
        """Update a working tree along its branch.
1117
1081
 
1118
1082
        This will update the branch if its bound too, which means we have
1144
1108
            returned (old tip of the branch or None). _marker is used
1145
1109
            otherwise.
1146
1110
        """
1147
 
        if self.branch.get_bound_location() is not None:
1148
 
            self.lock_write()
1149
 
            update_branch = (old_tip is self._marker)
1150
 
        else:
1151
 
            self.lock_tree_write()
1152
 
            update_branch = False
1153
 
        try:
1154
 
            if update_branch:
1155
 
                old_tip = self.branch.update(possible_transports)
1156
 
            else:
1157
 
                if old_tip is self._marker:
1158
 
                    old_tip = None
1159
 
            return self._update_tree(old_tip, change_reporter, revision, show_base)
1160
 
        finally:
1161
 
            self.unlock()
1162
 
 
1163
 
    def _update_tree(self, old_tip=None, change_reporter=None, revision=None,
1164
 
                     show_base=False):
1165
 
        """Update a tree to the master branch.
1166
 
 
1167
 
        :param old_tip: if supplied, the previous tip revision the branch,
1168
 
            before it was changed to the master branch's tip.
1169
 
        """
1170
 
        # here if old_tip is not None, it is the old tip of the branch before
1171
 
        # it was updated from the master branch. This should become a pending
1172
 
        # merge in the working tree to preserve the user existing work.  we
1173
 
        # cant set that until we update the working trees last revision to be
1174
 
        # one from the new branch, because it will just get absorbed by the
1175
 
        # parent de-duplication logic.
1176
 
        #
1177
 
        # We MUST save it even if an error occurs, because otherwise the users
1178
 
        # local work is unreferenced and will appear to have been lost.
1179
 
        #
1180
 
        with self.lock_tree_write():
1181
 
            nb_conflicts = 0
1182
 
            try:
1183
 
                last_rev = self.get_parent_ids()[0]
1184
 
            except IndexError:
1185
 
                last_rev = _mod_revision.NULL_REVISION
1186
 
            if revision is None:
1187
 
                revision = self.branch.last_revision()
1188
 
 
1189
 
            old_tip = old_tip or _mod_revision.NULL_REVISION
1190
 
 
1191
 
            if not _mod_revision.is_null(old_tip) and old_tip != last_rev:
1192
 
                # the branch we are bound to was updated
1193
 
                # merge those changes in first
1194
 
                base_tree = self.basis_tree()
1195
 
                other_tree = self.branch.repository.revision_tree(old_tip)
1196
 
                nb_conflicts = merge.merge_inner(self.branch, other_tree,
1197
 
                                                 base_tree, this_tree=self,
1198
 
                                                 change_reporter=change_reporter,
1199
 
                                                 show_base=show_base)
1200
 
                if nb_conflicts:
1201
 
                    self.add_parent_tree((old_tip, other_tree))
1202
 
                    note(gettext('Rerun update after fixing the conflicts.'))
1203
 
                    return nb_conflicts
1204
 
 
1205
 
            if last_rev != _mod_revision.ensure_null(revision):
1206
 
                # the working tree is up to date with the branch
1207
 
                # we can merge the specified revision from master
1208
 
                to_tree = self.branch.repository.revision_tree(revision)
1209
 
                to_root_id = to_tree.get_root_id()
1210
 
 
1211
 
                basis = self.basis_tree()
1212
 
                with basis.lock_read():
1213
 
                    if (basis.get_root_id() is None or basis.get_root_id() != to_root_id):
1214
 
                        self.set_root_id(to_root_id)
1215
 
                        self.flush()
1216
 
 
1217
 
                # determine the branch point
1218
 
                graph = self.branch.repository.get_graph()
1219
 
                base_rev_id = graph.find_unique_lca(self.branch.last_revision(),
1220
 
                                                    last_rev)
1221
 
                base_tree = self.branch.repository.revision_tree(base_rev_id)
1222
 
 
1223
 
                nb_conflicts = merge.merge_inner(self.branch, to_tree, base_tree,
1224
 
                                                 this_tree=self,
1225
 
                                                 change_reporter=change_reporter,
1226
 
                                                 show_base=show_base)
1227
 
                self.set_last_revision(revision)
1228
 
                # TODO - dedup parents list with things merged by pull ?
1229
 
                # reuse the tree we've updated to to set the basis:
1230
 
                parent_trees = [(revision, to_tree)]
1231
 
                merges = self.get_parent_ids()[1:]
1232
 
                # Ideally we ask the tree for the trees here, that way the working
1233
 
                # tree can decide whether to give us the entire tree or give us a
1234
 
                # lazy initialised tree. dirstate for instance will have the trees
1235
 
                # in ram already, whereas a last-revision + basis-inventory tree
1236
 
                # will not, but also does not need them when setting parents.
1237
 
                for parent in merges:
1238
 
                    parent_trees.append(
1239
 
                        (parent, self.branch.repository.revision_tree(parent)))
1240
 
                if not _mod_revision.is_null(old_tip):
1241
 
                    parent_trees.append(
1242
 
                        (old_tip, self.branch.repository.revision_tree(old_tip)))
1243
 
                self.set_parent_trees(parent_trees)
1244
 
                last_rev = parent_trees[0][0]
1245
 
            return nb_conflicts
 
1111
        raise NotImplementedError(self.update)
1246
1112
 
1247
1113
    def set_conflicts(self, arg):
1248
1114
        raise errors.UnsupportedOperation(self.set_conflicts, self)
1257
1123
        """Walk the directories of this tree.
1258
1124
 
1259
1125
        returns a generator which yields items in the form:
1260
 
                ((curren_directory_path, fileid),
1261
 
                 [(file1_path, file1_name, file1_kind, (lstat), file1_id,
 
1126
                (current_directory_path,
 
1127
                 [(file1_path, file1_name, file1_kind, (lstat),
1262
1128
                   file1_kind), ... ])
1263
1129
 
1264
1130
        This API returns a generator, which is only valid during the current
1269
1135
        """
1270
1136
        raise NotImplementedError(self.walkdirs)
1271
1137
 
 
1138
    @deprecated_method(deprecated_in((3, 0, 1)))
1272
1139
    def auto_resolve(self):
1273
1140
        """Automatically resolve text conflicts according to contents.
1274
1141
 
1277
1144
        into files that have text conflicts.  The corresponding .THIS .BASE and
1278
1145
        .OTHER files are deleted, as per 'resolve'.
1279
1146
 
1280
 
        :return: a tuple of ConflictLists: (un_resolved, resolved).
 
1147
        :return: a tuple of lists: (un_resolved, resolved).
1281
1148
        """
1282
1149
        with self.lock_tree_write():
1283
 
            un_resolved = _mod_conflicts.ConflictList()
1284
 
            resolved = _mod_conflicts.ConflictList()
1285
 
            conflict_re = re.compile(b'^(<{7}|={7}|>{7})')
 
1150
            un_resolved = []
 
1151
            resolved = []
1286
1152
            for conflict in self.conflicts():
1287
 
                path = self.id2path(conflict.file_id)
1288
 
                if (conflict.typestring != 'text conflict' or
1289
 
                        self.kind(path) != 'file'):
 
1153
                try:
 
1154
                    conflict.action_auto(self)
 
1155
                except NotImplementedError:
1290
1156
                    un_resolved.append(conflict)
1291
 
                    continue
1292
 
                with open(self.abspath(path), 'rb') as my_file:
1293
 
                    for line in my_file:
1294
 
                        if conflict_re.search(line):
1295
 
                            un_resolved.append(conflict)
1296
 
                            break
1297
 
                    else:
1298
 
                        resolved.append(conflict)
1299
 
            resolved.remove_files(self)
 
1157
                else:
 
1158
                    conflict.cleanup(self)
 
1159
                    resolved.append(conflict)
1300
1160
            self.set_conflicts(un_resolved)
1301
1161
            return un_resolved, resolved
1302
1162
 
1370
1230
        with self.lock_read():
1371
1231
            return next(self.get_canonical_paths([path]))
1372
1232
 
1373
 
 
1374
 
class WorkingTreeFormatRegistry(controldir.ControlComponentFormatRegistry):
 
1233
    def reference_parent(self, path, branch=None, possible_transports=None):
 
1234
        raise errors.UnsupportedOperation(self.reference_parent, self)
 
1235
 
 
1236
    def get_reference_info(self, path, branch=None):
 
1237
        raise errors.UnsupportedOperation(self.get_reference_info, self)
 
1238
 
 
1239
    def set_reference_info(self, tree_path, branch_location):
 
1240
        raise errors.UnsupportedOperation(self.set_reference_info, self)
 
1241
 
 
1242
 
 
1243
class WorkingTreeFormatRegistry(ControlComponentFormatRegistry):
1375
1244
    """Registry for working tree formats."""
1376
1245
 
1377
1246
    def __init__(self, other_registry=None):
1400
1269
format_registry = WorkingTreeFormatRegistry()
1401
1270
 
1402
1271
 
1403
 
class WorkingTreeFormat(controldir.ControlComponentFormat):
 
1272
class WorkingTreeFormat(ControlComponentFormat):
1404
1273
    """An encapsulation of the initialization and open routines for a format.
1405
1274
 
1406
1275
    Formats provide three things: