~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Matthieu Moy
  • Date: 2006-07-08 19:32:30 UTC
  • mfrom: (1845 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1857.
  • Revision ID: Matthieu.Moy@imag.fr-20060708193230-3eb72d871471bd5b
merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
from copy import deepcopy
19
19
from cStringIO import StringIO
20
 
import errno
21
 
import os
22
 
import shutil
23
 
import sys
24
20
from unittest import TestSuite
25
21
from warnings import warn
26
22
 
27
23
import bzrlib
28
 
import bzrlib.bzrdir as bzrdir
 
24
from bzrlib import bzrdir, errors, lockdir, osutils, revision, \
 
25
        tree, \
 
26
        ui, \
 
27
        urlutils
29
28
from bzrlib.config import TreeConfig
30
29
from bzrlib.decorators import needs_read_lock, needs_write_lock
31
 
from bzrlib.delta import compare_trees
32
30
import bzrlib.errors as errors
33
 
from bzrlib.errors import (BzrError, InvalidRevisionNumber, InvalidRevisionId,
34
 
                           NoSuchRevision, HistoryMissing, NotBranchError,
35
 
                           DivergedBranches, LockError,
36
 
                           UninitializableFormat,
37
 
                           UnlistableStore,
38
 
                           UnlistableBranch, NoSuchFile, NotVersionedError,
39
 
                           NoWorkingTree)
40
 
import bzrlib.inventory as inventory
41
 
from bzrlib.inventory import Inventory
 
31
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches, 
 
32
                           HistoryMissing, InvalidRevisionId, 
 
33
                           InvalidRevisionNumber, LockError, NoSuchFile, 
 
34
                           NoSuchRevision, NoWorkingTree, NotVersionedError,
 
35
                           NotBranchError, UninitializableFormat, 
 
36
                           UnlistableStore, UnlistableBranch, 
 
37
                           )
42
38
from bzrlib.lockable_files import LockableFiles, TransportLock
43
 
from bzrlib.lockdir import LockDir
44
 
from bzrlib.osutils import (isdir, quotefn,
45
 
                            rename, splitpath, sha_file,
46
 
                            file_kind, abspath, normpath, pathjoin,
47
 
                            safe_unicode,
48
 
                            rmtree,
49
 
                            )
50
 
from bzrlib.textui import show_status
 
39
from bzrlib.symbol_versioning import (deprecated_function,
 
40
                                      deprecated_method,
 
41
                                      DEPRECATED_PARAMETER,
 
42
                                      deprecated_passed,
 
43
                                      zero_eight, zero_nine,
 
44
                                      )
51
45
from bzrlib.trace import mutter, note
52
 
from bzrlib.tree import EmptyTree, RevisionTree
53
 
from bzrlib.repository import Repository
54
 
from bzrlib.revision import (
55
 
                             is_ancestor,
56
 
                             NULL_REVISION,
57
 
                             Revision,
58
 
                             )
59
 
from bzrlib.store import copy_all
60
 
from bzrlib.symbol_versioning import *
61
 
import bzrlib.transactions as transactions
62
 
from bzrlib.transport import Transport, get_transport
63
 
from bzrlib.tree import EmptyTree, RevisionTree
64
 
import bzrlib.ui
65
 
import bzrlib.xml5
66
46
 
67
47
 
68
48
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
117
97
        
118
98
    @staticmethod
119
99
    def open(base, _unsupported=False):
120
 
        """Open the repository rooted at base.
 
100
        """Open the branch rooted at base.
121
101
 
122
 
        For instance, if the repository is at URL/.bzr/repository,
123
 
        Repository.open(URL) -> a Repository instance.
 
102
        For instance, if the branch is at URL/.bzr/branch,
 
103
        Branch.open(URL) -> a Branch instance.
124
104
        """
125
105
        control = bzrdir.BzrDir.open(base, _unsupported)
126
106
        return control.open_branch(_unsupported)
158
138
        warn('%s is deprecated' % self.setup_caching)
159
139
        self.cache_root = cache_root
160
140
 
 
141
    def get_config(self):
 
142
        return bzrlib.config.BranchConfig(self)
 
143
 
161
144
    def _get_nick(self):
162
 
        cfg = self.tree_config()
163
 
        return cfg.get_option(u"nickname", default=self.base.split('/')[-2])
 
145
        return self.get_config().get_nickname()
164
146
 
165
147
    def _set_nick(self, nick):
166
 
        cfg = self.tree_config()
167
 
        cfg.set_option(nick, "nickname")
168
 
        assert cfg.get_option("nickname") == nick
 
148
        self.get_config().set_user_option('nickname', nick)
169
149
 
170
150
    nick = property(_get_nick, _set_nick)
171
151
 
219
199
        if self.base == from_branch.base:
220
200
            return (0, [])
221
201
        if pb is None:
222
 
            nested_pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
202
            nested_pb = ui.ui_factory.nested_progress_bar()
223
203
            pb = nested_pb
224
204
        else:
225
205
            nested_pb = None
233
213
                    last_revision = from_history[-1]
234
214
                else:
235
215
                    # no history in the source branch
236
 
                    last_revision = NULL_REVISION
 
216
                    last_revision = revision.NULL_REVISION
237
217
            return self.repository.fetch(from_branch.repository,
238
218
                                         revision_id=last_revision,
239
219
                                         pb=nested_pb)
249
229
        branch.
250
230
        """
251
231
        return None
 
232
    
 
233
    def get_commit_builder(self, parents, config=None, timestamp=None, 
 
234
                           timezone=None, committer=None, revprops=None, 
 
235
                           revision_id=None):
 
236
        """Obtain a CommitBuilder for this branch.
 
237
        
 
238
        :param parents: Revision ids of the parents of the new revision.
 
239
        :param config: Optional configuration to use.
 
240
        :param timestamp: Optional timestamp recorded for commit.
 
241
        :param timezone: Optional timezone for timestamp.
 
242
        :param committer: Optional committer to set for commit.
 
243
        :param revprops: Optional dictionary of revision properties.
 
244
        :param revision_id: Optional revision id.
 
245
        """
 
246
 
 
247
        if config is None:
 
248
            config = self.get_config()
 
249
        
 
250
        return self.repository.get_commit_builder(self, parents, config, 
 
251
            timestamp, timezone, committer, revprops, revision_id)
252
252
 
253
253
    def get_master_branch(self):
254
254
        """Return the branch we are bound to.
257
257
        """
258
258
        return None
259
259
 
 
260
    def get_revision_delta(self, revno):
 
261
        """Return the delta for one revision.
 
262
 
 
263
        The delta is relative to its mainline predecessor, or the
 
264
        empty tree for revision 1.
 
265
        """
 
266
        assert isinstance(revno, int)
 
267
        rh = self.revision_history()
 
268
        if not (1 <= revno <= len(rh)):
 
269
            raise InvalidRevisionNumber(revno)
 
270
        return self.repository.get_revision_delta(rh[revno-1])
 
271
 
260
272
    def get_root_id(self):
261
273
        """Return the id of this branches root"""
262
274
        raise NotImplementedError('get_root_id is abstract')
300
312
        
301
313
        If self and other have not diverged, return a list of the revisions
302
314
        present in other, but missing from self.
303
 
 
304
 
        >>> from bzrlib.workingtree import WorkingTree
305
 
        >>> bzrlib.trace.silent = True
306
 
        >>> d1 = bzrdir.ScratchDir()
307
 
        >>> br1 = d1.open_branch()
308
 
        >>> wt1 = d1.open_workingtree()
309
 
        >>> d2 = bzrdir.ScratchDir()
310
 
        >>> br2 = d2.open_branch()
311
 
        >>> wt2 = d2.open_workingtree()
312
 
        >>> br1.missing_revisions(br2)
313
 
        []
314
 
        >>> wt2.commit("lala!", rev_id="REVISION-ID-1")
315
 
        >>> br1.missing_revisions(br2)
316
 
        [u'REVISION-ID-1']
317
 
        >>> br2.missing_revisions(br1)
318
 
        []
319
 
        >>> wt1.commit("lala!", rev_id="REVISION-ID-1")
320
 
        >>> br1.missing_revisions(br2)
321
 
        []
322
 
        >>> wt2.commit("lala!", rev_id="REVISION-ID-2A")
323
 
        >>> br1.missing_revisions(br2)
324
 
        [u'REVISION-ID-2A']
325
 
        >>> wt1.commit("lala!", rev_id="REVISION-ID-2B")
326
 
        >>> br1.missing_revisions(br2)
327
 
        Traceback (most recent call last):
328
 
        DivergedBranches: These branches have diverged.  Try merge.
329
315
        """
330
316
        self_history = self.revision_history()
331
317
        self_len = len(self_history)
341
327
        else:
342
328
            assert isinstance(stop_revision, int)
343
329
            if stop_revision > other_len:
344
 
                raise bzrlib.errors.NoSuchRevision(self, stop_revision)
 
330
                raise errors.NoSuchRevision(self, stop_revision)
345
331
        return other_history[self_len:stop_revision]
346
332
 
347
333
    def update_revisions(self, other, stop_revision=None):
415
401
        """
416
402
        raise NotImplementedError('get_parent is abstract')
417
403
 
 
404
    def get_submit_branch(self):
 
405
        """Return the submit location of the branch.
 
406
 
 
407
        This is the default location for bundle.  The usual
 
408
        pattern is that the user can override it by specifying a
 
409
        location.
 
410
        """
 
411
        return self.get_config().get_user_option('submit_branch')
 
412
 
 
413
    def set_submit_branch(self, location):
 
414
        """Return the submit location of the branch.
 
415
 
 
416
        This is the default location for bundle.  The usual
 
417
        pattern is that the user can override it by specifying a
 
418
        location.
 
419
        """
 
420
        self.get_config().set_user_option('submit_branch', location)
 
421
 
418
422
    def get_push_location(self):
419
423
        """Return the None or the location to push this branch to."""
420
424
        raise NotImplementedError('get_push_location is abstract')
457
461
        revision_id: if not None, the revision history in the new branch will
458
462
                     be truncated to end with revision_id.
459
463
        """
460
 
        # for API compatability, until 0.8 releases we provide the old api:
 
464
        # for API compatibility, until 0.8 releases we provide the old api:
461
465
        # def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
462
466
        # after 0.8 releases, the *args and **kwargs should be changed:
463
467
        # def clone(self, to_bzrdir, revision_id=None):
465
469
            kwargs.get('revision', None) or
466
470
            kwargs.get('basis_branch', None) or
467
471
            (len(args) and isinstance(args[0], basestring))):
468
 
            # backwards compatability api:
 
472
            # backwards compatibility api:
469
473
            warn("Branch.clone() has been deprecated for BzrDir.clone() from"
470
474
                 " bzrlib 0.8.", DeprecationWarning, stacklevel=3)
471
475
            # get basis_branch
537
541
        if parent:
538
542
            destination.set_parent(parent)
539
543
 
 
544
    @needs_read_lock
 
545
    def check(self):
 
546
        """Check consistency of the branch.
 
547
 
 
548
        In particular this checks that revisions given in the revision-history
 
549
        do actually match up in the revision graph, and that they're all 
 
550
        present in the repository.
 
551
        
 
552
        Callers will typically also want to check the repository.
 
553
 
 
554
        :return: A BranchCheckResult.
 
555
        """
 
556
        mainline_parent_id = None
 
557
        for revision_id in self.revision_history():
 
558
            try:
 
559
                revision = self.repository.get_revision(revision_id)
 
560
            except errors.NoSuchRevision, e:
 
561
                raise errors.BzrCheckError("mainline revision {%s} not in repository"
 
562
                            % revision_id)
 
563
            # In general the first entry on the revision history has no parents.
 
564
            # But it's not illegal for it to have parents listed; this can happen
 
565
            # in imports from Arch when the parents weren't reachable.
 
566
            if mainline_parent_id is not None:
 
567
                if mainline_parent_id not in revision.parent_ids:
 
568
                    raise errors.BzrCheckError("previous revision {%s} not listed among "
 
569
                                        "parents of {%s}"
 
570
                                        % (mainline_parent_id, revision_id))
 
571
            mainline_parent_id = revision_id
 
572
        return BranchCheckResult(self)
 
573
 
540
574
 
541
575
class BranchFormat(object):
542
576
    """An encapsulation of the initialization and open routines for a format.
572
606
        except NoSuchFile:
573
607
            raise NotBranchError(path=transport.base)
574
608
        except KeyError:
575
 
            raise errors.UnknownFormatError(format_string)
 
609
            raise errors.UnknownFormatError(format=format_string)
576
610
 
577
611
    @classmethod
578
612
    def get_default_format(klass):
589
623
 
590
624
    def initialize(self, a_bzrdir):
591
625
        """Create a branch of this format in a_bzrdir."""
592
 
        raise NotImplementedError(self.initialized)
 
626
        raise NotImplementedError(self.initialize)
593
627
 
594
628
    def is_supported(self):
595
629
        """Is this format supported?
705
739
        utf8_files = [('revision-history', ''),
706
740
                      ('branch-name', ''),
707
741
                      ]
708
 
        control_files = LockableFiles(branch_transport, 'lock', LockDir)
 
742
        control_files = LockableFiles(branch_transport, 'lock', lockdir.LockDir)
709
743
        control_files.create_lock()
710
744
        control_files.lock_write()
711
745
        control_files.put_utf8('format', self.get_format_string())
730
764
            format = BranchFormat.find_format(a_bzrdir)
731
765
            assert format.__class__ == self.__class__
732
766
        transport = a_bzrdir.get_branch_transport(None)
733
 
        control_files = LockableFiles(transport, 'lock', LockDir)
 
767
        control_files = LockableFiles(transport, 'lock', lockdir.LockDir)
734
768
        return BzrBranch5(_format=self,
735
769
                          _control_files=control_files,
736
770
                          a_bzrdir=a_bzrdir,
851
885
        self._base = self._transport.base
852
886
        self._format = _format
853
887
        if _control_files is None:
854
 
            raise BzrBadParameterMissing('_control_files')
 
888
            raise ValueError('BzrBranch _control_files is None')
855
889
        self.control_files = _control_files
856
890
        if deprecated_passed(init):
857
891
            warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
872
906
                 stacklevel=2)
873
907
            if (not relax_version_check
874
908
                and not self._format.is_supported()):
875
 
                raise errors.UnsupportedFormatError(
876
 
                        'sorry, branch format %r not supported' % fmt,
877
 
                        ['use a different bzr version',
878
 
                         'or remove the .bzr directory'
879
 
                         ' and "bzr init" again'])
 
909
                raise errors.UnsupportedFormatError(format=fmt)
880
910
        if deprecated_passed(transport):
881
911
            warn("BzrBranch.__init__(transport=XXX...): The transport "
882
912
                 "parameter is deprecated as of bzr 0.8. "
900
930
        # XXX: cache_root seems to be unused, 2006-01-13 mbp
901
931
        if hasattr(self, 'cache_root') and self.cache_root is not None:
902
932
            try:
903
 
                rmtree(self.cache_root)
 
933
                osutils.rmtree(self.cache_root)
904
934
            except:
905
935
                pass
906
936
            self.cache_root = None
949
979
        FIXME: DELETE THIS METHOD when pre 0.8 support is removed.
950
980
        """
951
981
        if format is None:
952
 
            format = BzrBranchFormat.find_format(self.bzrdir)
 
982
            format = BranchFormat.find_format(self.bzrdir)
953
983
        self._format = format
954
984
        mutter("got branch format %s", self._format)
955
985
 
1021
1051
            # not really an object yet, and the transaction is for objects.
1022
1052
            # transaction.register_clean(history)
1023
1053
 
1024
 
    def get_revision_delta(self, revno):
1025
 
        """Return the delta for one revision.
1026
 
 
1027
 
        The delta is relative to its mainline predecessor, or the
1028
 
        empty tree for revision 1.
1029
 
        """
1030
 
        assert isinstance(revno, int)
1031
 
        rh = self.revision_history()
1032
 
        if not (1 <= revno <= len(rh)):
1033
 
            raise InvalidRevisionNumber(revno)
1034
 
 
1035
 
        # revno is 1-based; list is 0-based
1036
 
 
1037
 
        new_tree = self.repository.revision_tree(rh[revno-1])
1038
 
        if revno == 1:
1039
 
            old_tree = EmptyTree()
1040
 
        else:
1041
 
            old_tree = self.repository.revision_tree(rh[revno-2])
1042
 
        return compare_trees(old_tree, new_tree)
1043
 
 
1044
1054
    @needs_read_lock
1045
1055
    def revision_history(self):
1046
1056
        """See Branch.revision_history."""
1058
1068
        return list(history)
1059
1069
 
1060
1070
    @needs_write_lock
 
1071
    def generate_revision_history(self, revision_id, last_rev=None, 
 
1072
        other_branch=None):
 
1073
        """Create a new revision history that will finish with revision_id.
 
1074
        
 
1075
        :param revision_id: the new tip to use.
 
1076
        :param last_rev: The previous last_revision. If not None, then this
 
1077
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
1078
        :param other_branch: The other branch that DivergedBranches should
 
1079
            raise with respect to.
 
1080
        """
 
1081
        # stop_revision must be a descendant of last_revision
 
1082
        stop_graph = self.repository.get_revision_graph(revision_id)
 
1083
        if last_rev is not None and last_rev not in stop_graph:
 
1084
            # our previous tip is not merged into stop_revision
 
1085
            raise errors.DivergedBranches(self, other_branch)
 
1086
        # make a new revision history from the graph
 
1087
        current_rev_id = revision_id
 
1088
        new_history = []
 
1089
        while current_rev_id not in (None, revision.NULL_REVISION):
 
1090
            new_history.append(current_rev_id)
 
1091
            current_rev_id_parents = stop_graph[current_rev_id]
 
1092
            try:
 
1093
                current_rev_id = current_rev_id_parents[0]
 
1094
            except IndexError:
 
1095
                current_rev_id = None
 
1096
        new_history.reverse()
 
1097
        self.set_revision_history(new_history)
 
1098
 
 
1099
    @needs_write_lock
1061
1100
    def update_revisions(self, other, stop_revision=None):
1062
1101
        """See Branch.update_revisions."""
1063
1102
        other.lock_read()
1077
1116
            if stop_revision in my_ancestry:
1078
1117
                # last_revision is a descendant of stop_revision
1079
1118
                return
1080
 
            # stop_revision must be a descendant of last_revision
1081
 
            stop_graph = self.repository.get_revision_graph(stop_revision)
1082
 
            if last_rev is not None and last_rev not in stop_graph:
1083
 
                # our previous tip is not merged into stop_revision
1084
 
                raise errors.DivergedBranches(self, other)
1085
 
            # make a new revision history from the graph
1086
 
            current_rev_id = stop_revision
1087
 
            new_history = []
1088
 
            while current_rev_id not in (None, NULL_REVISION):
1089
 
                new_history.append(current_rev_id)
1090
 
                current_rev_id_parents = stop_graph[current_rev_id]
1091
 
                try:
1092
 
                    current_rev_id = current_rev_id_parents[0]
1093
 
                except IndexError:
1094
 
                    current_rev_id = None
1095
 
            new_history.reverse()
1096
 
            self.set_revision_history(new_history)
 
1119
            self.generate_revision_history(stop_revision, last_rev=last_rev,
 
1120
                other_branch=other)
1097
1121
        finally:
1098
1122
            other.unlock()
1099
1123
 
1104
1128
    @deprecated_method(zero_eight)
1105
1129
    def working_tree(self):
1106
1130
        """Create a Working tree object for this branch."""
1107
 
        from bzrlib.workingtree import WorkingTree
 
1131
 
1108
1132
        from bzrlib.transport.local import LocalTransport
1109
1133
        if (self.base.find('://') != -1 or 
1110
1134
            not isinstance(self._transport, LocalTransport)):
1131
1155
 
1132
1156
    def get_parent(self):
1133
1157
        """See Branch.get_parent."""
1134
 
        import errno
 
1158
 
1135
1159
        _locs = ['parent', 'pull', 'x-pull']
 
1160
        assert self.base[-1] == '/'
1136
1161
        for l in _locs:
1137
1162
            try:
1138
 
                return self.control_files.get_utf8(l).read().strip('\n')
 
1163
                parent = self.control_files.get(l).read().strip('\n')
1139
1164
            except NoSuchFile:
1140
 
                pass
 
1165
                continue
 
1166
            # This is an old-format absolute path to a local branch
 
1167
            # turn it into a url
 
1168
            if parent.startswith('/'):
 
1169
                parent = urlutils.local_path_to_url(parent.decode('utf8'))
 
1170
            return urlutils.join(self.base[:-1], parent)
1141
1171
        return None
1142
1172
 
1143
1173
    def get_push_location(self):
1144
1174
        """See Branch.get_push_location."""
1145
 
        config = bzrlib.config.BranchConfig(self)
1146
 
        push_loc = config.get_user_option('push_location')
 
1175
        push_loc = self.get_config().get_user_option('push_location')
1147
1176
        return push_loc
1148
1177
 
1149
1178
    def set_push_location(self, location):
1150
1179
        """See Branch.set_push_location."""
1151
 
        config = bzrlib.config.LocationConfig(self.base)
1152
 
        config.set_user_option('push_location', location)
 
1180
        self.get_config().set_user_option('push_location', location, 
 
1181
                                          local=True)
1153
1182
 
1154
1183
    @needs_write_lock
1155
1184
    def set_parent(self, url):
1162
1191
        if url is None:
1163
1192
            self.control_files._transport.delete('parent')
1164
1193
        else:
1165
 
            self.control_files.put_utf8('parent', url + '\n')
 
1194
            if isinstance(url, unicode):
 
1195
                try: 
 
1196
                    url = url.encode('ascii')
 
1197
                except UnicodeEncodeError:
 
1198
                    raise bzrlib.errors.InvalidURL(url,
 
1199
                        "Urls must be 7-bit ascii, "
 
1200
                        "use bzrlib.urlutils.escape")
 
1201
                    
 
1202
            url = urlutils.relative_url(self.base, url)
 
1203
            self.control_files.put('parent', url + '\n')
1166
1204
 
 
1205
    @deprecated_function(zero_nine)
1167
1206
    def tree_config(self):
 
1207
        """DEPRECATED; call get_config instead.  
 
1208
        TreeConfig has become part of BranchConfig."""
1168
1209
        return TreeConfig(self)
1169
1210
 
1170
1211
 
1210
1251
 
1211
1252
        This could memoise the branch, but if thats done
1212
1253
        it must be revalidated on each new lock.
1213
 
        So for now we just dont memoise it.
 
1254
        So for now we just don't memoise it.
1214
1255
        # RBC 20060304 review this decision.
1215
1256
        """
1216
1257
        bound_loc = self.get_bound_location()
1262
1303
        # There may be a different check you could do here
1263
1304
        # rather than actually trying to install revisions remotely.
1264
1305
        # TODO: capture an exception which indicates the remote branch
1265
 
        #       is not writeable. 
 
1306
        #       is not writable. 
1266
1307
        #       If it is up-to-date, this probably should not be a failure
1267
1308
        
1268
1309
        # lock other for write so the revision-history syncing cannot race
1334
1375
        return result
1335
1376
 
1336
1377
 
 
1378
class BranchCheckResult(object):
 
1379
    """Results of checking branch consistency.
 
1380
 
 
1381
    :see: Branch.check
 
1382
    """
 
1383
 
 
1384
    def __init__(self, branch):
 
1385
        self.branch = branch
 
1386
 
 
1387
    def report_results(self, verbose):
 
1388
        """Report the check results via trace.note.
 
1389
        
 
1390
        :param verbose: Requests more detailed display of what was checked,
 
1391
            if any.
 
1392
        """
 
1393
        note('checked branch %s format %s',
 
1394
             self.branch.base,
 
1395
             self.branch._format)
 
1396
 
 
1397
 
1337
1398
######################################################################
1338
1399
# predicates
1339
1400
 
1340
1401
 
1341
1402
@deprecated_function(zero_eight)
1342
 
def ScratchBranch(*args, **kwargs):
1343
 
    """See bzrlib.bzrdir.ScratchDir."""
1344
 
    d = ScratchDir(*args, **kwargs)
1345
 
    return d.open_branch()
1346
 
 
1347
 
 
1348
 
@deprecated_function(zero_eight)
1349
1403
def is_control_file(*args, **kwargs):
1350
1404
    """See bzrlib.workingtree.is_control_file."""
1351
1405
    return bzrlib.workingtree.is_control_file(*args, **kwargs)