1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from cStringIO import StringIO
20
from bzrlib.lazy_import import lazy_import
21
lazy_import(globals(), """
22
from copy import deepcopy
23
from unittest import TestSuite
24
from warnings import warn
30
config as _mod_config,
35
revision as _mod_revision,
41
from bzrlib.config import BranchConfig, TreeConfig
42
from bzrlib.lockable_files import LockableFiles, TransportLock
43
from bzrlib.tag import (
49
from bzrlib.decorators import needs_read_lock, needs_write_lock
50
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
51
HistoryMissing, InvalidRevisionId,
52
InvalidRevisionNumber, LockError, NoSuchFile,
53
NoSuchRevision, NoWorkingTree, NotVersionedError,
54
NotBranchError, UninitializableFormat,
55
UnlistableStore, UnlistableBranch,
57
from bzrlib.symbol_versioning import (deprecated_function,
61
zero_eight, zero_nine,
63
from bzrlib.trace import mutter, note
66
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
67
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
68
BZR_BRANCH_FORMAT_6 = "Bazaar-NG branch, format 6\n"
71
# TODO: Maybe include checks for common corruption of newlines, etc?
73
# TODO: Some operations like log might retrieve the same revisions
74
# repeatedly to calculate deltas. We could perhaps have a weakref
75
# cache in memory to make this faster. In general anything can be
76
# cached in memory between lock and unlock operations. .. nb thats
77
# what the transaction identity map provides
80
######################################################################
84
"""Branch holding a history of revisions.
87
Base directory/url of the branch.
89
hooks: An instance of BranchHooks.
91
# this is really an instance variable - FIXME move it there
95
# override this to set the strategy for storing tags
96
def _make_tag_store(self):
97
return DisabledTagStore(self)
99
def __init__(self, *ignored, **ignored_too):
100
self._tag_store = self._make_tag_store()
102
def break_lock(self):
103
"""Break a lock if one is present from another instance.
105
Uses the ui factory to ask for confirmation if the lock may be from
108
This will probe the repository for its lock as well.
110
self.control_files.break_lock()
111
self.repository.break_lock()
112
master = self.get_master_branch()
113
if master is not None:
117
@deprecated_method(zero_eight)
118
def open_downlevel(base):
119
"""Open a branch which may be of an old format."""
120
return Branch.open(base, _unsupported=True)
123
def open(base, _unsupported=False):
124
"""Open the branch rooted at base.
126
For instance, if the branch is at URL/.bzr/branch,
127
Branch.open(URL) -> a Branch instance.
129
control = bzrdir.BzrDir.open(base, _unsupported)
130
return control.open_branch(_unsupported)
133
def open_containing(url):
134
"""Open an existing branch which contains url.
136
This probes for a branch at url, and searches upwards from there.
138
Basically we keep looking up until we find the control directory or
139
run into the root. If there isn't one, raises NotBranchError.
140
If there is one and it is either an unrecognised format or an unsupported
141
format, UnknownFormatError or UnsupportedFormatError are raised.
142
If there is one, it is returned, along with the unused portion of url.
144
control, relpath = bzrdir.BzrDir.open_containing(url)
145
return control.open_branch(), relpath
148
@deprecated_function(zero_eight)
149
def initialize(base):
150
"""Create a new working tree and branch, rooted at 'base' (url)
152
NOTE: This will soon be deprecated in favour of creation
155
return bzrdir.BzrDir.create_standalone_workingtree(base).branch
157
@deprecated_function(zero_eight)
158
def setup_caching(self, cache_root):
159
"""Subclasses that care about caching should override this, and set
160
up cached stores located under cache_root.
162
NOTE: This is unused.
166
def get_config(self):
167
return BranchConfig(self)
170
return self.get_config().get_nickname()
172
def _set_nick(self, nick):
173
self.get_config().set_user_option('nickname', nick)
175
nick = property(_get_nick, _set_nick)
178
raise NotImplementedError(self.is_locked)
180
def lock_write(self):
181
raise NotImplementedError(self.lock_write)
184
raise NotImplementedError(self.lock_read)
187
raise NotImplementedError(self.unlock)
189
def peek_lock_mode(self):
190
"""Return lock mode for the Branch: 'r', 'w' or None"""
191
raise NotImplementedError(self.peek_lock_mode)
193
def get_physical_lock_status(self):
194
raise NotImplementedError(self.get_physical_lock_status)
196
def abspath(self, name):
197
"""Return absolute filename for something in the branch
199
XXX: Robert Collins 20051017 what is this used for? why is it a branch
200
method and not a tree method.
202
raise NotImplementedError(self.abspath)
204
def bind(self, other):
205
"""Bind the local branch the other branch.
207
:param other: The branch to bind to
210
raise errors.UpgradeRequired(self.base)
213
def fetch(self, from_branch, last_revision=None, pb=None):
214
"""Copy revisions from from_branch into this branch.
216
:param from_branch: Where to copy from.
217
:param last_revision: What revision to stop at (None for at the end
219
:param pb: An optional progress bar to use.
221
Returns the copied revision count and the failed revisions in a tuple:
224
if self.base == from_branch.base:
227
nested_pb = ui.ui_factory.nested_progress_bar()
232
from_branch.lock_read()
234
if last_revision is None:
235
pb.update('get source history')
236
last_revision = from_branch.last_revision_info()[1]
237
return self.repository.fetch(from_branch.repository,
238
revision_id=last_revision,
241
if nested_pb is not None:
245
def get_bound_location(self):
246
"""Return the URL of the branch we are bound to.
248
Older format branches cannot bind, please be sure to use a metadir
253
def get_commit_builder(self, parents, config=None, timestamp=None,
254
timezone=None, committer=None, revprops=None,
256
"""Obtain a CommitBuilder for this branch.
258
:param parents: Revision ids of the parents of the new revision.
259
:param config: Optional configuration to use.
260
:param timestamp: Optional timestamp recorded for commit.
261
:param timezone: Optional timezone for timestamp.
262
:param committer: Optional committer to set for commit.
263
:param revprops: Optional dictionary of revision properties.
264
:param revision_id: Optional revision id.
268
config = self.get_config()
270
return self.repository.get_commit_builder(self, parents, config,
271
timestamp, timezone, committer, revprops, revision_id)
273
def get_master_branch(self):
274
"""Return the branch we are bound to.
276
:return: Either a Branch, or None
280
def get_revision_delta(self, revno):
281
"""Return the delta for one revision.
283
The delta is relative to its mainline predecessor, or the
284
empty tree for revision 1.
286
assert isinstance(revno, int)
287
rh = self.revision_history()
288
if not (1 <= revno <= len(rh)):
289
raise InvalidRevisionNumber(revno)
290
return self.repository.get_revision_delta(rh[revno-1])
292
def get_root_id(self):
293
"""Return the id of this branches root"""
294
raise NotImplementedError(self.get_root_id)
296
def print_file(self, file, revision_id):
297
"""Print `file` to stdout."""
298
raise NotImplementedError(self.print_file)
300
def append_revision(self, *revision_ids):
301
raise NotImplementedError(self.append_revision)
303
def set_revision_history(self, rev_history):
304
raise NotImplementedError(self.set_revision_history)
306
def revision_history(self):
307
"""Return sequence of revision hashes on to this branch."""
308
raise NotImplementedError(self.revision_history)
311
"""Return current revision number for this branch.
313
That is equivalent to the number of revisions committed to
316
return len(self.revision_history())
319
"""Older format branches cannot bind or unbind."""
320
raise errors.UpgradeRequired(self.base)
322
def last_revision(self):
323
"""Return last revision id, or None"""
324
ph = self.revision_history()
330
def last_revision_info(self):
331
"""Return information about the last revision.
333
:return: A tuple (revno, last_revision_id).
335
rh = self.revision_history()
338
return (revno, rh[-1])
340
return (0, _mod_revision.NULL_REVISION)
342
def missing_revisions(self, other, stop_revision=None):
343
"""Return a list of new revisions that would perfectly fit.
345
If self and other have not diverged, return a list of the revisions
346
present in other, but missing from self.
348
self_history = self.revision_history()
349
self_len = len(self_history)
350
other_history = other.revision_history()
351
other_len = len(other_history)
352
common_index = min(self_len, other_len) -1
353
if common_index >= 0 and \
354
self_history[common_index] != other_history[common_index]:
355
raise DivergedBranches(self, other)
357
if stop_revision is None:
358
stop_revision = other_len
360
assert isinstance(stop_revision, int)
361
if stop_revision > other_len:
362
raise errors.NoSuchRevision(self, stop_revision)
363
return other_history[self_len:stop_revision]
365
def update_revisions(self, other, stop_revision=None):
366
"""Pull in new perfect-fit revisions.
368
:param other: Another Branch to pull from
369
:param stop_revision: Updated until the given revision
372
raise NotImplementedError(self.update_revisions)
374
def revision_id_to_revno(self, revision_id):
375
"""Given a revision id, return its revno"""
376
if revision_id is None:
378
history = self.revision_history()
380
return history.index(revision_id) + 1
382
raise bzrlib.errors.NoSuchRevision(self, revision_id)
384
def get_rev_id(self, revno, history=None):
385
"""Find the revision id of the specified revno."""
389
history = self.revision_history()
390
if revno <= 0 or revno > len(history):
391
raise bzrlib.errors.NoSuchRevision(self, revno)
392
return history[revno - 1]
394
def pull(self, source, overwrite=False, stop_revision=None):
395
"""Mirror source into this branch.
397
This branch is considered to be 'local', having low latency.
399
raise NotImplementedError(self.pull)
401
def push(self, target, overwrite=False, stop_revision=None):
402
"""Mirror this branch into target.
404
This branch is considered to be 'local', having low latency.
406
raise NotImplementedError(self.push)
408
def basis_tree(self):
409
"""Return `Tree` object for last revision."""
410
return self.repository.revision_tree(self.last_revision())
412
def rename_one(self, from_rel, to_rel):
415
This can change the directory or the filename or both.
417
raise NotImplementedError(self.rename_one)
419
def move(self, from_paths, to_name):
422
to_name must exist as a versioned directory.
424
If to_name exists and is a directory, the files are moved into
425
it, keeping their old names. If it is a directory,
427
Note that to_name is only the last component of the new name;
428
this doesn't change the directory.
430
This returns a list of (from_path, to_path) pairs for each
433
raise NotImplementedError(self.move)
435
def get_parent(self):
436
"""Return the parent location of the branch.
438
This is the default location for push/pull/missing. The usual
439
pattern is that the user can override it by specifying a
442
raise NotImplementedError(self.get_parent)
444
def get_submit_branch(self):
445
"""Return the submit location of the branch.
447
This is the default location for bundle. The usual
448
pattern is that the user can override it by specifying a
451
return self.get_config().get_user_option('submit_branch')
453
def set_submit_branch(self, location):
454
"""Return the submit location of the branch.
456
This is the default location for bundle. The usual
457
pattern is that the user can override it by specifying a
460
self.get_config().set_user_option('submit_branch', location)
462
def get_push_location(self):
463
"""Return the None or the location to push this branch to."""
464
raise NotImplementedError(self.get_push_location)
466
def set_push_location(self, location):
467
"""Set a new push location for this branch."""
468
raise NotImplementedError(self.set_push_location)
470
def set_parent(self, url):
471
raise NotImplementedError(self.set_parent)
475
"""Synchronise this branch with the master branch if any.
477
:return: None or the last_revision pivoted out during the update.
481
def check_revno(self, revno):
483
Check whether a revno corresponds to any revision.
484
Zero (the NULL revision) is considered valid.
487
self.check_real_revno(revno)
489
def check_real_revno(self, revno):
491
Check whether a revno corresponds to a real revision.
492
Zero (the NULL revision) is considered invalid
494
if revno < 1 or revno > self.revno():
495
raise InvalidRevisionNumber(revno)
498
def clone(self, *args, **kwargs):
499
"""Clone this branch into to_bzrdir preserving all semantic values.
501
revision_id: if not None, the revision history in the new branch will
502
be truncated to end with revision_id.
504
# for API compatibility, until 0.8 releases we provide the old api:
505
# def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
506
# after 0.8 releases, the *args and **kwargs should be changed:
507
# def clone(self, to_bzrdir, revision_id=None):
508
if (kwargs.get('to_location', None) or
509
kwargs.get('revision', None) or
510
kwargs.get('basis_branch', None) or
511
(len(args) and isinstance(args[0], basestring))):
512
# backwards compatibility api:
513
warn("Branch.clone() has been deprecated for BzrDir.clone() from"
514
" bzrlib 0.8.", DeprecationWarning, stacklevel=3)
517
basis_branch = args[2]
519
basis_branch = kwargs.get('basis_branch', None)
521
basis = basis_branch.bzrdir
526
revision_id = args[1]
528
revision_id = kwargs.get('revision', None)
533
# no default to raise if not provided.
534
url = kwargs.get('to_location')
535
return self.bzrdir.clone(url,
536
revision_id=revision_id,
537
basis=basis).open_branch()
539
# generate args by hand
541
revision_id = args[1]
543
revision_id = kwargs.get('revision_id', None)
547
# no default to raise if not provided.
548
to_bzrdir = kwargs.get('to_bzrdir')
549
result = self._format.initialize(to_bzrdir)
550
self.copy_content_into(result, revision_id=revision_id)
554
def sprout(self, to_bzrdir, revision_id=None):
555
"""Create a new line of development from the branch, into to_bzrdir.
557
revision_id: if not None, the revision history in the new branch will
558
be truncated to end with revision_id.
560
result = self._format.initialize(to_bzrdir)
561
self.copy_content_into(result, revision_id=revision_id)
562
result.set_parent(self.bzrdir.root_transport.base)
566
def copy_content_into(self, destination, revision_id=None):
567
"""Copy the content of self into destination.
569
revision_id: if not None, the revision history in the new branch will
570
be truncated to end with revision_id.
572
new_history = self.revision_history()
573
if revision_id is not None:
575
new_history = new_history[:new_history.index(revision_id) + 1]
577
rev = self.repository.get_revision(revision_id)
578
new_history = rev.get_history(self.repository)[1:]
579
destination.set_revision_history(new_history)
581
parent = self.get_parent()
582
except errors.InaccessibleParent, e:
583
mutter('parent was not accessible to copy: %s', e)
586
destination.set_parent(parent)
590
"""Check consistency of the branch.
592
In particular this checks that revisions given in the revision-history
593
do actually match up in the revision graph, and that they're all
594
present in the repository.
596
Callers will typically also want to check the repository.
598
:return: A BranchCheckResult.
600
mainline_parent_id = None
601
for revision_id in self.revision_history():
603
revision = self.repository.get_revision(revision_id)
604
except errors.NoSuchRevision, e:
605
raise errors.BzrCheckError("mainline revision {%s} not in repository"
607
# In general the first entry on the revision history has no parents.
608
# But it's not illegal for it to have parents listed; this can happen
609
# in imports from Arch when the parents weren't reachable.
610
if mainline_parent_id is not None:
611
if mainline_parent_id not in revision.parent_ids:
612
raise errors.BzrCheckError("previous revision {%s} not listed among "
614
% (mainline_parent_id, revision_id))
615
mainline_parent_id = revision_id
616
return BranchCheckResult(self)
618
def _get_checkout_format(self):
619
"""Return the most suitable metadir for a checkout of this branch.
620
Weaves are used if this branch's repostory uses weaves.
622
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
623
from bzrlib.repofmt import weaverepo
624
format = bzrdir.BzrDirMetaFormat1()
625
format.repository_format = weaverepo.RepositoryFormat7()
627
format = self.repository.bzrdir.cloning_metadir()
630
def create_checkout(self, to_location, revision_id=None,
632
"""Create a checkout of a branch.
634
:param to_location: The url to produce the checkout at
635
:param revision_id: The revision to check out
636
:param lightweight: If True, produce a lightweight checkout, otherwise,
637
produce a bound branch (heavyweight checkout)
638
:return: The tree of the created checkout
640
t = transport.get_transport(to_location)
643
except errors.FileExists:
646
checkout = bzrdir.BzrDirMetaFormat1().initialize_on_transport(t)
647
BranchReferenceFormat().initialize(checkout, self)
649
format = self._get_checkout_format()
650
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
651
to_location, force_new_tree=False, format=format)
652
checkout = checkout_branch.bzrdir
653
checkout_branch.bind(self)
654
# pull up to the specified revision_id to set the initial
655
# branch tip correctly, and seed it with history.
656
checkout_branch.pull(self, stop_revision=revision_id)
657
return checkout.create_workingtree(revision_id)
659
def set_tag(self, tag_name, tag_target):
660
self._tag_store.set_tag(tag_name, tag_target)
662
def lookup_tag(self, tag_name):
663
return self._tag_store.lookup_tag(tag_name)
665
def get_tag_dict(self):
666
return self._tag_store.get_tag_dict()
668
def _set_tag_dict(self, new_dict):
669
return self._tag_store._set_tag_dict(new_dict)
671
def supports_tags(self):
672
return self._tag_store.supports_tags()
674
def copy_tags_to(self, to_branch):
675
"""Copy tags to another branch.
677
# TODO: Allow for doing a smarter merge, etc
678
if self == to_branch:
680
to_branch.lock_write()
682
to_branch._set_tag_dict(self.get_tag_dict())
686
class BranchFormat(object):
687
"""An encapsulation of the initialization and open routines for a format.
689
Formats provide three things:
690
* An initialization routine,
694
Formats are placed in an dict by their format string for reference
695
during branch opening. Its not required that these be instances, they
696
can be classes themselves with class methods - it simply depends on
697
whether state is needed for a given format or not.
699
Once a format is deprecated, just deprecate the initialize and open
700
methods on the format class. Do not deprecate the object, as the
701
object will be created every time regardless.
704
_default_format = None
705
"""The default format used for new branches."""
708
"""The known formats."""
711
def find_format(klass, a_bzrdir):
712
"""Return the format for the branch object in a_bzrdir."""
714
transport = a_bzrdir.get_branch_transport(None)
715
format_string = transport.get("format").read()
716
return klass._formats[format_string]
718
raise NotBranchError(path=transport.base)
720
raise errors.UnknownFormatError(format=format_string)
723
def get_default_format(klass):
724
"""Return the current default format."""
725
return klass._default_format
727
def get_format_string(self):
728
"""Return the ASCII format string that identifies this format."""
729
raise NotImplementedError(self.get_format_string)
731
def get_format_description(self):
732
"""Return the short format description for this format."""
733
raise NotImplementedError(self.get_format_description)
735
def initialize(self, a_bzrdir):
736
"""Create a branch of this format in a_bzrdir."""
737
raise NotImplementedError(self.initialize)
739
def is_supported(self):
740
"""Is this format supported?
742
Supported formats can be initialized and opened.
743
Unsupported formats may not support initialization or committing or
744
some other features depending on the reason for not being supported.
748
def open(self, a_bzrdir, _found=False):
749
"""Return the branch object for a_bzrdir
751
_found is a private parameter, do not use it. It is used to indicate
752
if format probing has already be done.
754
raise NotImplementedError(self.open)
757
def register_format(klass, format):
758
klass._formats[format.get_format_string()] = format
761
def set_default_format(klass, format):
762
klass._default_format = format
765
def unregister_format(klass, format):
766
assert klass._formats[format.get_format_string()] is format
767
del klass._formats[format.get_format_string()]
770
return self.get_format_string().rstrip()
772
def supports_tags(self):
773
"""True if this format supports tags stored in the branch"""
774
return False # by default
776
# XXX: Probably doesn't really belong here -- mbp 20070212
777
def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
779
branch_transport = a_bzrdir.get_branch_transport(self)
780
control_files = lockable_files.LockableFiles(branch_transport,
781
lock_filename, lock_class)
782
control_files.create_lock()
783
control_files.lock_write()
785
for filename, content in utf8_files:
786
control_files.put_utf8(filename, content)
788
control_files.unlock()
791
class BranchHooks(dict):
792
"""A dictionary mapping hook name to a list of callables for branch hooks.
794
e.g. ['set_rh'] Is the list of items to be called when the
795
set_revision_history function is invoked.
799
"""Create the default hooks.
801
These are all empty initially, because by default nothing should get
805
# Introduced in 0.15:
806
# invoked whenever the revision history has been set
807
# with set_revision_history. The api signature is
808
# (branch, revision_history), and the branch will
811
# invoked after a push operation completes.
812
# the api signature is
813
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
814
# where local is the local branch or None, master is the target
815
# master branch, and the rest should be self explanatory. The source
816
# is read locked and the target branches write locked. Source will
817
# be the local low-latency branch.
818
self['post_push'] = []
819
# invoked after a pull operation completes.
820
# the api signature is
821
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
822
# where local is the local branch or None, master is the target
823
# master branch, and the rest should be self explanatory. The source
824
# is read locked and the target branches write locked. The local
825
# branch is the low-latency branch.
826
self['post_pull'] = []
827
# invoked after a commit operation completes.
828
# the api signature is
829
# (local, master, old_revno, old_revid, new_revno, new_revid)
830
# old_revid is NULL_REVISION for the first commit to a branch.
831
self['post_commit'] = []
832
# invoked after a uncommit operation completes.
833
# the api signature is
834
# (local, master, old_revno, old_revid, new_revno, new_revid) where
835
# local is the local branch or None, master is the target branch,
836
# and an empty branch recieves new_revno of 0, new_revid of None.
837
self['post_uncommit'] = []
839
def install_hook(self, hook_name, a_callable):
840
"""Install a_callable in to the hook hook_name.
842
:param hook_name: A hook name. See the __init__ method of BranchHooks
843
for the complete list of hooks.
844
:param a_callable: The callable to be invoked when the hook triggers.
845
The exact signature will depend on the hook - see the __init__
846
method of BranchHooks for details on each hook.
849
self[hook_name].append(a_callable)
851
raise errors.UnknownHook('branch', hook_name)
854
# install the default hooks into the Branch class.
855
Branch.hooks = BranchHooks()
858
class BzrBranchFormat4(BranchFormat):
859
"""Bzr branch format 4.
862
- a revision-history file.
863
- a branch-lock lock file [ to be shared with the bzrdir ]
866
def get_format_description(self):
867
"""See BranchFormat.get_format_description()."""
868
return "Branch format 4"
870
def initialize(self, a_bzrdir):
871
"""Create a branch of this format in a_bzrdir."""
872
utf8_files = [('revision-history', ''),
875
self._initialize_control_files(a_bzrdir, utf8_files,
876
'branch-lock', lockable_files.TransportLock)
877
return self.open(a_bzrdir, _found=True)
880
super(BzrBranchFormat4, self).__init__()
881
self._matchingbzrdir = bzrdir.BzrDirFormat6()
883
def open(self, a_bzrdir, _found=False):
884
"""Return the branch object for a_bzrdir
886
_found is a private parameter, do not use it. It is used to indicate
887
if format probing has already be done.
890
# we are being called directly and must probe.
891
raise NotImplementedError
892
return BzrBranch(_format=self,
893
_control_files=a_bzrdir._control_files,
895
_repository=a_bzrdir.open_repository())
898
return "Bazaar-NG branch format 4"
901
class BzrBranchFormat5(BranchFormat):
902
"""Bzr branch format 5.
905
- a revision-history file.
907
- a lock dir guarding the branch itself
908
- all of this stored in a branch/ subdirectory
909
- works with shared repositories.
911
This format is new in bzr 0.8.
914
def get_format_string(self):
915
"""See BranchFormat.get_format_string()."""
916
return "Bazaar-NG branch format 5\n"
918
def get_format_description(self):
919
"""See BranchFormat.get_format_description()."""
920
return "Branch format 5"
922
def initialize(self, a_bzrdir):
923
"""Create a branch of this format in a_bzrdir."""
924
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
925
branch_transport = a_bzrdir.get_branch_transport(self)
926
utf8_files = [('revision-history', ''),
929
control_files = lockable_files.LockableFiles(branch_transport, 'lock',
931
control_files.create_lock()
932
control_files.lock_write()
933
control_files.put_utf8('format', self.get_format_string())
935
for file, content in utf8_files:
936
control_files.put_utf8(file, content)
938
control_files.unlock()
939
return self.open(a_bzrdir, _found=True, )
942
super(BzrBranchFormat5, self).__init__()
943
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
945
def open(self, a_bzrdir, _found=False):
946
"""Return the branch object for a_bzrdir
948
_found is a private parameter, do not use it. It is used to indicate
949
if format probing has already be done.
952
format = BranchFormat.find_format(a_bzrdir)
953
assert format.__class__ == self.__class__
954
transport = a_bzrdir.get_branch_transport(None)
955
control_files = lockable_files.LockableFiles(transport, 'lock',
957
return BzrBranch5(_format=self,
958
_control_files=control_files,
960
_repository=a_bzrdir.find_repository())
963
class BranchReferenceFormat(BranchFormat):
964
"""Bzr branch reference format.
966
Branch references are used in implementing checkouts, they
967
act as an alias to the real branch which is at some other url.
974
def get_format_string(self):
975
"""See BranchFormat.get_format_string()."""
976
return "Bazaar-NG Branch Reference Format 1\n"
978
def get_format_description(self):
979
"""See BranchFormat.get_format_description()."""
980
return "Checkout reference format 1"
982
def initialize(self, a_bzrdir, target_branch=None):
983
"""Create a branch of this format in a_bzrdir."""
984
if target_branch is None:
985
# this format does not implement branch itself, thus the implicit
986
# creation contract must see it as uninitializable
987
raise errors.UninitializableFormat(self)
988
mutter('creating branch reference in %s', a_bzrdir.transport.base)
989
branch_transport = a_bzrdir.get_branch_transport(self)
990
branch_transport.put_bytes('location',
991
target_branch.bzrdir.root_transport.base)
992
branch_transport.put_bytes('format', self.get_format_string())
993
return self.open(a_bzrdir, _found=True)
996
super(BranchReferenceFormat, self).__init__()
997
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
999
def _make_reference_clone_function(format, a_branch):
1000
"""Create a clone() routine for a branch dynamically."""
1001
def clone(to_bzrdir, revision_id=None):
1002
"""See Branch.clone()."""
1003
return format.initialize(to_bzrdir, a_branch)
1004
# cannot obey revision_id limits when cloning a reference ...
1005
# FIXME RBC 20060210 either nuke revision_id for clone, or
1006
# emit some sort of warning/error to the caller ?!
1009
def open(self, a_bzrdir, _found=False):
1010
"""Return the branch that the branch reference in a_bzrdir points at.
1012
_found is a private parameter, do not use it. It is used to indicate
1013
if format probing has already be done.
1016
format = BranchFormat.find_format(a_bzrdir)
1017
assert format.__class__ == self.__class__
1018
transport = a_bzrdir.get_branch_transport(None)
1019
real_bzrdir = bzrdir.BzrDir.open(transport.get('location').read())
1020
result = real_bzrdir.open_branch()
1021
# this changes the behaviour of result.clone to create a new reference
1022
# rather than a copy of the content of the branch.
1023
# I did not use a proxy object because that needs much more extensive
1024
# testing, and we are only changing one behaviour at the moment.
1025
# If we decide to alter more behaviours - i.e. the implicit nickname
1026
# then this should be refactored to introduce a tested proxy branch
1027
# and a subclass of that for use in overriding clone() and ....
1029
result.clone = self._make_reference_clone_function(result)
1033
# formats which have no format string are not discoverable
1034
# and not independently creatable, so are not registered.
1035
__default_format = BzrBranchFormat5()
1036
BranchFormat.register_format(__default_format)
1037
BranchFormat.register_format(BranchReferenceFormat())
1038
BranchFormat.set_default_format(__default_format)
1039
_legacy_formats = [BzrBranchFormat4(),
1042
class BzrBranch(Branch):
1043
"""A branch stored in the actual filesystem.
1045
Note that it's "local" in the context of the filesystem; it doesn't
1046
really matter if it's on an nfs/smb/afs/coda/... share, as long as
1047
it's writable, and can be accessed via the normal filesystem API.
1050
def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
1051
relax_version_check=DEPRECATED_PARAMETER, _format=None,
1052
_control_files=None, a_bzrdir=None, _repository=None):
1053
"""Create new branch object at a particular location.
1055
transport -- A Transport object, defining how to access files.
1057
init -- If True, create new control files in a previously
1058
unversioned directory. If False, the branch must already
1061
relax_version_check -- If true, the usual check for the branch
1062
version is not applied. This is intended only for
1063
upgrade/recovery type use; it's not guaranteed that
1064
all operations will work on old format branches.
1066
Branch.__init__(self)
1067
if a_bzrdir is None:
1068
self.bzrdir = bzrdir.BzrDir.open(transport.base)
1070
self.bzrdir = a_bzrdir
1071
# self._transport used to point to the directory containing the
1072
# control directory, but was not used - now it's just the transport
1073
# for the branch control files. mbp 20070212
1074
self._base = self.bzrdir.transport.clone('..').base
1075
self._format = _format
1076
if _control_files is None:
1077
raise ValueError('BzrBranch _control_files is None')
1078
self.control_files = _control_files
1079
self._transport = _control_files._transport
1080
if deprecated_passed(init):
1081
warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
1082
"deprecated as of bzr 0.8. Please use Branch.create().",
1086
# this is slower than before deprecation, oh well never mind.
1087
# -> its deprecated.
1088
self._initialize(transport.base)
1089
self._check_format(_format)
1090
if deprecated_passed(relax_version_check):
1091
warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
1092
"relax_version_check parameter is deprecated as of bzr 0.8. "
1093
"Please use BzrDir.open_downlevel, or a BzrBranchFormat's "
1097
if (not relax_version_check
1098
and not self._format.is_supported()):
1099
raise errors.UnsupportedFormatError(format=fmt)
1100
if deprecated_passed(transport):
1101
warn("BzrBranch.__init__(transport=XXX...): The transport "
1102
"parameter is deprecated as of bzr 0.8. "
1103
"Please use Branch.open, or bzrdir.open_branch().",
1106
self.repository = _repository
1109
return '%s(%r)' % (self.__class__.__name__, self.base)
1113
def _get_base(self):
1114
"""Returns the directory containing the control directory."""
1117
base = property(_get_base, doc="The URL for the root of this branch.")
1119
def _finish_transaction(self):
1120
"""Exit the current transaction."""
1121
return self.control_files._finish_transaction()
1123
def get_transaction(self):
1124
"""Return the current active transaction.
1126
If no transaction is active, this returns a passthrough object
1127
for which all data is immediately flushed and no caching happens.
1129
# this is an explicit function so that we can do tricky stuff
1130
# when the storage in rev_storage is elsewhere.
1131
# we probably need to hook the two 'lock a location' and
1132
# 'have a transaction' together more delicately, so that
1133
# we can have two locks (branch and storage) and one transaction
1134
# ... and finishing the transaction unlocks both, but unlocking
1135
# does not. - RBC 20051121
1136
return self.control_files.get_transaction()
1138
def _set_transaction(self, transaction):
1139
"""Set a new active transaction."""
1140
return self.control_files._set_transaction(transaction)
1142
def abspath(self, name):
1143
"""See Branch.abspath."""
1144
return self.control_files._transport.abspath(name)
1146
def _check_format(self, format):
1147
"""Identify the branch format if needed.
1149
The format is stored as a reference to the format object in
1150
self._format for code that needs to check it later.
1152
The format parameter is either None or the branch format class
1153
used to open this branch.
1155
FIXME: DELETE THIS METHOD when pre 0.8 support is removed.
1158
format = BranchFormat.find_format(self.bzrdir)
1159
self._format = format
1160
mutter("got branch format %s", self._format)
1163
def get_root_id(self):
1164
"""See Branch.get_root_id."""
1165
tree = self.repository.revision_tree(self.last_revision())
1166
return tree.inventory.root.file_id
1168
def is_locked(self):
1169
return self.control_files.is_locked()
1171
def lock_write(self):
1172
self.repository.lock_write()
1174
self.control_files.lock_write()
1176
self.repository.unlock()
1179
def lock_read(self):
1180
self.repository.lock_read()
1182
self.control_files.lock_read()
1184
self.repository.unlock()
1188
# TODO: test for failed two phase locks. This is known broken.
1190
self.control_files.unlock()
1192
self.repository.unlock()
1194
def peek_lock_mode(self):
1195
if self.control_files._lock_count == 0:
1198
return self.control_files._lock_mode
1200
def get_physical_lock_status(self):
1201
return self.control_files.get_physical_lock_status()
1204
def print_file(self, file, revision_id):
1205
"""See Branch.print_file."""
1206
return self.repository.print_file(file, revision_id)
1209
def append_revision(self, *revision_ids):
1210
"""See Branch.append_revision."""
1211
for revision_id in revision_ids:
1212
_mod_revision.check_not_reserved_id(revision_id)
1213
mutter("add {%s} to revision-history" % revision_id)
1214
rev_history = self.revision_history()
1215
rev_history.extend(revision_ids)
1216
self.set_revision_history(rev_history)
1219
def set_revision_history(self, rev_history):
1220
"""See Branch.set_revision_history."""
1221
self.control_files.put_utf8(
1222
'revision-history', '\n'.join(rev_history))
1223
transaction = self.get_transaction()
1224
history = transaction.map.find_revision_history()
1225
if history is not None:
1226
# update the revision history in the identity map.
1227
history[:] = list(rev_history)
1228
# this call is disabled because revision_history is
1229
# not really an object yet, and the transaction is for objects.
1230
# transaction.register_dirty(history)
1232
transaction.map.add_revision_history(rev_history)
1233
# this call is disabled because revision_history is
1234
# not really an object yet, and the transaction is for objects.
1235
# transaction.register_clean(history)
1236
for hook in Branch.hooks['set_rh']:
1237
hook(self, rev_history)
1240
def revision_history(self):
1241
"""See Branch.revision_history."""
1242
transaction = self.get_transaction()
1243
history = transaction.map.find_revision_history()
1244
if history is not None:
1245
# mutter("cache hit for revision-history in %s", self)
1246
return list(history)
1247
decode_utf8 = cache_utf8.decode
1248
history = [decode_utf8(l.rstrip('\r\n')) for l in
1249
self.control_files.get('revision-history').readlines()]
1250
transaction.map.add_revision_history(history)
1251
# this call is disabled because revision_history is
1252
# not really an object yet, and the transaction is for objects.
1253
# transaction.register_clean(history, precious=True)
1254
return list(history)
1257
def generate_revision_history(self, revision_id, last_rev=None,
1259
"""Create a new revision history that will finish with revision_id.
1261
:param revision_id: the new tip to use.
1262
:param last_rev: The previous last_revision. If not None, then this
1263
must be a ancestory of revision_id, or DivergedBranches is raised.
1264
:param other_branch: The other branch that DivergedBranches should
1265
raise with respect to.
1267
# stop_revision must be a descendant of last_revision
1268
stop_graph = self.repository.get_revision_graph(revision_id)
1269
if last_rev is not None and last_rev not in stop_graph:
1270
# our previous tip is not merged into stop_revision
1271
raise errors.DivergedBranches(self, other_branch)
1272
# make a new revision history from the graph
1273
current_rev_id = revision_id
1275
while current_rev_id not in (None, _mod_revision.NULL_REVISION):
1276
new_history.append(current_rev_id)
1277
current_rev_id_parents = stop_graph[current_rev_id]
1279
current_rev_id = current_rev_id_parents[0]
1281
current_rev_id = None
1282
new_history.reverse()
1283
self.set_revision_history(new_history)
1286
def update_revisions(self, other, stop_revision=None):
1287
"""See Branch.update_revisions."""
1290
if stop_revision is None:
1291
stop_revision = other.last_revision()
1292
if stop_revision is None:
1293
# if there are no commits, we're done.
1295
# whats the current last revision, before we fetch [and change it
1297
last_rev = self.last_revision()
1298
# we fetch here regardless of whether we need to so that we pickup
1300
self.fetch(other, stop_revision)
1301
my_ancestry = self.repository.get_ancestry(last_rev)
1302
if stop_revision in my_ancestry:
1303
# last_revision is a descendant of stop_revision
1305
self.generate_revision_history(stop_revision, last_rev=last_rev,
1310
def basis_tree(self):
1311
"""See Branch.basis_tree."""
1312
return self.repository.revision_tree(self.last_revision())
1314
@deprecated_method(zero_eight)
1315
def working_tree(self):
1316
"""Create a Working tree object for this branch."""
1318
from bzrlib.transport.local import LocalTransport
1319
if (self.base.find('://') != -1 or
1320
not isinstance(self._transport, LocalTransport)):
1321
raise NoWorkingTree(self.base)
1322
return self.bzrdir.open_workingtree()
1325
def pull(self, source, overwrite=False, stop_revision=None,
1326
_hook_master=None, _run_hooks=True):
1329
:param _hook_master: Private parameter - set the branch to
1330
be supplied as the master to push hooks.
1331
:param _run_hooks: Private parameter - allow disabling of
1332
hooks, used when pushing to a master branch.
1336
old_count, old_tip = self.last_revision_info()
1338
self.update_revisions(source, stop_revision)
1339
except DivergedBranches:
1343
self.set_revision_history(source.revision_history())
1344
new_count, new_tip = self.last_revision_info()
1351
for hook in Branch.hooks['post_pull']:
1352
hook(source, _hook_local, _hook_master, old_count, old_tip,
1354
return new_count - old_count
1359
def push(self, target, overwrite=False, stop_revision=None,
1360
_hook_master=None, _run_hooks=True):
1363
:param _hook_master: Private parameter - set the branch to
1364
be supplied as the master to push hooks.
1365
:param _run_hooks: Private parameter - allow disabling of
1366
hooks, used when pushing to a master branch.
1370
old_count, old_tip = target.last_revision_info()
1372
target.update_revisions(self, stop_revision)
1373
except DivergedBranches:
1377
target.set_revision_history(self.revision_history())
1378
new_count, new_tip = target.last_revision_info()
1381
_hook_local = target
1383
_hook_master = target
1385
for hook in Branch.hooks['post_push']:
1386
hook(self, _hook_local, _hook_master, old_count, old_tip,
1388
return new_count - old_count
1392
def get_parent(self):
1393
"""See Branch.get_parent."""
1395
_locs = ['parent', 'pull', 'x-pull']
1396
assert self.base[-1] == '/'
1399
parent = self.control_files.get(l).read().strip('\n')
1402
# This is an old-format absolute path to a local branch
1403
# turn it into a url
1404
if parent.startswith('/'):
1405
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1407
return urlutils.join(self.base[:-1], parent)
1408
except errors.InvalidURLJoin, e:
1409
raise errors.InaccessibleParent(parent, self.base)
1412
def get_push_location(self):
1413
"""See Branch.get_push_location."""
1414
push_loc = self.get_config().get_user_option('push_location')
1417
def set_push_location(self, location):
1418
"""See Branch.set_push_location."""
1419
self.get_config().set_user_option(
1420
'push_location', location,
1421
store=_mod_config.STORE_LOCATION_NORECURSE)
1424
def set_parent(self, url):
1425
"""See Branch.set_parent."""
1426
# TODO: Maybe delete old location files?
1427
# URLs should never be unicode, even on the local fs,
1428
# FIXUP this and get_parent in a future branch format bump:
1429
# read and rewrite the file, and have the new format code read
1430
# using .get not .get_utf8. RBC 20060125
1432
self.control_files._transport.delete('parent')
1434
if isinstance(url, unicode):
1436
url = url.encode('ascii')
1437
except UnicodeEncodeError:
1438
raise bzrlib.errors.InvalidURL(url,
1439
"Urls must be 7-bit ascii, "
1440
"use bzrlib.urlutils.escape")
1442
url = urlutils.relative_url(self.base, url)
1443
self.control_files.put('parent', StringIO(url + '\n'))
1445
@deprecated_function(zero_nine)
1446
def tree_config(self):
1447
"""DEPRECATED; call get_config instead.
1448
TreeConfig has become part of BranchConfig."""
1449
return TreeConfig(self)
1452
class BzrBranch5(BzrBranch):
1453
"""A format 5 branch. This supports new features over plan branches.
1455
It has support for a master_branch which is the data for bound branches.
1463
super(BzrBranch5, self).__init__(_format=_format,
1464
_control_files=_control_files,
1466
_repository=_repository)
1469
def pull(self, source, overwrite=False, stop_revision=None,
1471
"""Extends branch.pull to be bound branch aware.
1473
:param _run_hooks: Private parameter used to force hook running
1474
off during bound branch double-pushing.
1476
bound_location = self.get_bound_location()
1477
master_branch = None
1478
if bound_location and source.base != bound_location:
1479
# not pulling from master, so we need to update master.
1480
master_branch = self.get_master_branch()
1481
master_branch.lock_write()
1484
# pull from source into master.
1485
master_branch.pull(source, overwrite, stop_revision,
1487
return super(BzrBranch5, self).pull(source, overwrite,
1488
stop_revision, _hook_master=master_branch,
1489
_run_hooks=_run_hooks)
1492
master_branch.unlock()
1495
def push(self, target, overwrite=False, stop_revision=None):
1496
"""Updates branch.push to be bound branch aware."""
1497
bound_location = target.get_bound_location()
1498
master_branch = None
1499
if bound_location and target.base != bound_location:
1500
# not pushing to master, so we need to update master.
1501
master_branch = target.get_master_branch()
1502
master_branch.lock_write()
1505
# push into the master from this branch.
1506
super(BzrBranch5, self).push(master_branch, overwrite,
1507
stop_revision, _run_hooks=False)
1508
# and push into the target branch from this. Note that we push from
1509
# this branch again, because its considered the highest bandwidth
1511
return super(BzrBranch5, self).push(target, overwrite,
1512
stop_revision, _hook_master=master_branch)
1515
master_branch.unlock()
1517
def get_bound_location(self):
1519
return self.control_files.get_utf8('bound').read()[:-1]
1520
except errors.NoSuchFile:
1524
def get_master_branch(self):
1525
"""Return the branch we are bound to.
1527
:return: Either a Branch, or None
1529
This could memoise the branch, but if thats done
1530
it must be revalidated on each new lock.
1531
So for now we just don't memoise it.
1532
# RBC 20060304 review this decision.
1534
bound_loc = self.get_bound_location()
1538
return Branch.open(bound_loc)
1539
except (errors.NotBranchError, errors.ConnectionError), e:
1540
raise errors.BoundBranchConnectionFailure(
1544
def set_bound_location(self, location):
1545
"""Set the target where this branch is bound to.
1547
:param location: URL to the target branch
1550
self.control_files.put_utf8('bound', location+'\n')
1553
self.control_files._transport.delete('bound')
1559
def bind(self, other):
1560
"""Bind this branch to the branch other.
1562
This does not push or pull data between the branches, though it does
1563
check for divergence to raise an error when the branches are not
1564
either the same, or one a prefix of the other. That behaviour may not
1565
be useful, so that check may be removed in future.
1567
:param other: The branch to bind to
1570
# TODO: jam 20051230 Consider checking if the target is bound
1571
# It is debatable whether you should be able to bind to
1572
# a branch which is itself bound.
1573
# Committing is obviously forbidden,
1574
# but binding itself may not be.
1575
# Since we *have* to check at commit time, we don't
1576
# *need* to check here
1578
# we want to raise diverged if:
1579
# last_rev is not in the other_last_rev history, AND
1580
# other_last_rev is not in our history, and do it without pulling
1582
last_rev = self.last_revision()
1583
if last_rev is not None:
1586
other_last_rev = other.last_revision()
1587
if other_last_rev is not None:
1588
# neither branch is new, we have to do some work to
1589
# ascertain diversion.
1590
remote_graph = other.repository.get_revision_graph(
1592
local_graph = self.repository.get_revision_graph(last_rev)
1593
if (last_rev not in remote_graph and
1594
other_last_rev not in local_graph):
1595
raise errors.DivergedBranches(self, other)
1598
self.set_bound_location(other.base)
1602
"""If bound, unbind"""
1603
return self.set_bound_location(None)
1607
"""Synchronise this branch with the master branch if any.
1609
:return: None or the last_revision that was pivoted out during the
1612
master = self.get_master_branch()
1613
if master is not None:
1614
old_tip = self.last_revision()
1615
self.pull(master, overwrite=True)
1616
if old_tip in self.repository.get_ancestry(self.last_revision()):
1622
class BzrBranchExperimental(BzrBranch5):
1623
"""Bzr experimental branch format
1626
- a revision-history file.
1628
- a lock dir guarding the branch itself
1629
- all of this stored in a branch/ subdirectory
1630
- works with shared repositories.
1631
- a tag dictionary in the branch
1633
This format is new in bzr 0.15, but shouldn't be used for real data,
1636
This class acts as it's own BranchFormat.
1639
_matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1642
def get_format_string(cls):
1643
"""See BranchFormat.get_format_string()."""
1644
return "Bazaar-NG branch format experimental\n"
1647
def get_format_description(cls):
1648
"""See BranchFormat.get_format_description()."""
1649
return "Experimental branch format"
1652
def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1654
branch_transport = a_bzrdir.get_branch_transport(cls)
1655
control_files = lockable_files.LockableFiles(branch_transport,
1656
lock_filename, lock_class)
1657
control_files.create_lock()
1658
control_files.lock_write()
1660
for filename, content in utf8_files:
1661
control_files.put_utf8(filename, content)
1663
control_files.unlock()
1666
def initialize(cls, a_bzrdir):
1667
"""Create a branch of this format in a_bzrdir."""
1668
utf8_files = [('format', cls.get_format_string()),
1669
('revision-history', ''),
1670
('branch-name', ''),
1673
cls._initialize_control_files(a_bzrdir, utf8_files,
1674
'lock', lockdir.LockDir)
1675
return cls.open(a_bzrdir, _found=True)
1678
def open(cls, a_bzrdir, _found=False):
1679
"""Return the branch object for a_bzrdir
1681
_found is a private parameter, do not use it. It is used to indicate
1682
if format probing has already be done.
1685
format = BranchFormat.find_format(a_bzrdir)
1686
assert format.__class__ == cls
1687
transport = a_bzrdir.get_branch_transport(None)
1688
control_files = lockable_files.LockableFiles(transport, 'lock',
1690
return cls(_format=cls,
1691
_control_files=control_files,
1693
_repository=a_bzrdir.find_repository())
1696
def is_supported(cls):
1699
def _make_tag_store(self):
1700
return BasicTagStore(self)
1703
def supports_tags(cls):
1707
BranchFormat.register_format(BzrBranchExperimental)
1710
class BranchTestProviderAdapter(object):
1711
"""A tool to generate a suite testing multiple branch formats at once.
1713
This is done by copying the test once for each transport and injecting
1714
the transport_server, transport_readonly_server, and branch_format
1715
classes into each copy. Each copy is also given a new id() to make it
1719
def __init__(self, transport_server, transport_readonly_server, formats):
1720
self._transport_server = transport_server
1721
self._transport_readonly_server = transport_readonly_server
1722
self._formats = formats
1724
def adapt(self, test):
1725
result = TestSuite()
1726
for branch_format, bzrdir_format in self._formats:
1727
new_test = deepcopy(test)
1728
new_test.transport_server = self._transport_server
1729
new_test.transport_readonly_server = self._transport_readonly_server
1730
new_test.bzrdir_format = bzrdir_format
1731
new_test.branch_format = branch_format
1732
def make_new_test_id():
1733
# the format can be either a class or an instance
1734
name = getattr(branch_format, '__name__',
1735
branch_format.__class__.__name__)
1736
new_id = "%s(%s)" % (new_test.id(), name)
1737
return lambda: new_id
1738
new_test.id = make_new_test_id()
1739
result.addTest(new_test)
1743
class BranchCheckResult(object):
1744
"""Results of checking branch consistency.
1749
def __init__(self, branch):
1750
self.branch = branch
1752
def report_results(self, verbose):
1753
"""Report the check results via trace.note.
1755
:param verbose: Requests more detailed display of what was checked,
1758
note('checked branch %s format %s',
1760
self.branch._format)
1763
######################################################################
1767
@deprecated_function(zero_eight)
1768
def is_control_file(*args, **kwargs):
1769
"""See bzrlib.workingtree.is_control_file."""
1770
from bzrlib import workingtree
1771
return workingtree.is_control_file(*args, **kwargs)