~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Patch Queue Manager
  • Date: 2012-03-28 16:13:49 UTC
  • mfrom: (6499.2.3 948339-config-caching)
  • Revision ID: pqm@pqm.ubuntu.com-20120328161349-2gsc0g11fcu43hlc
(vila) Properly share mutable config sections and save the branch config
 only during the final unlock (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2012 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
from __future__ import absolute_import
 
18
 
 
19
import bzrlib.bzrdir
17
20
 
18
21
from cStringIO import StringIO
19
 
import sys
20
22
 
21
23
from bzrlib.lazy_import import lazy_import
22
24
lazy_import(globals(), """
23
 
from itertools import chain
 
25
import itertools
24
26
from bzrlib import (
25
 
        bzrdir,
26
 
        cache_utf8,
27
 
        cleanup,
28
 
        config as _mod_config,
29
 
        debug,
30
 
        errors,
31
 
        fetch,
32
 
        graph as _mod_graph,
33
 
        lockdir,
34
 
        lockable_files,
35
 
        remote,
36
 
        repository,
37
 
        revision as _mod_revision,
38
 
        rio,
39
 
        transport,
40
 
        ui,
41
 
        urlutils,
42
 
        )
43
 
from bzrlib.config import BranchConfig, TransportConfig
44
 
from bzrlib.tag import (
45
 
    BasicTags,
46
 
    DisabledTags,
 
27
    bzrdir,
 
28
    controldir,
 
29
    cache_utf8,
 
30
    cleanup,
 
31
    config as _mod_config,
 
32
    debug,
 
33
    errors,
 
34
    fetch,
 
35
    graph as _mod_graph,
 
36
    lockdir,
 
37
    lockable_files,
 
38
    remote,
 
39
    repository,
 
40
    revision as _mod_revision,
 
41
    rio,
 
42
    tag as _mod_tag,
 
43
    transport,
 
44
    ui,
 
45
    urlutils,
 
46
    vf_search,
47
47
    )
 
48
from bzrlib.i18n import gettext, ngettext
48
49
""")
49
50
 
 
51
# Explicitly import bzrlib.bzrdir so that the BzrProber
 
52
# is guaranteed to be registered.
 
53
import bzrlib.bzrdir
 
54
 
50
55
from bzrlib import (
 
56
    bzrdir,
51
57
    controldir,
52
58
    )
53
59
from bzrlib.decorators import (
66
72
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
67
73
 
68
74
 
69
 
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
70
 
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
71
 
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
72
 
 
73
 
 
74
75
class Branch(controldir.ControlComponent):
75
76
    """Branch holding a history of revisions.
76
77
 
93
94
    def user_transport(self):
94
95
        return self.bzrdir.user_transport
95
96
 
96
 
    def __init__(self, *ignored, **ignored_too):
 
97
    def __init__(self, possible_transports=None):
97
98
        self.tags = self._format.make_tags(self)
98
99
        self._revision_history_cache = None
99
100
        self._revision_id_to_revno_cache = None
103
104
        self._last_revision_info_cache = None
104
105
        self._master_branch_cache = None
105
106
        self._merge_sorted_revisions_cache = None
106
 
        self._open_hook()
 
107
        self._open_hook(possible_transports)
107
108
        hooks = Branch.hooks['open']
108
109
        for hook in hooks:
109
110
            hook(self)
110
111
 
111
 
    def _open_hook(self):
 
112
    def _open_hook(self, possible_transports):
112
113
        """Called by init to allow simpler extension of the base class."""
113
114
 
114
 
    def _activate_fallback_location(self, url):
 
115
    def _activate_fallback_location(self, url, possible_transports):
115
116
        """Activate the branch/repository from url as a fallback repository."""
116
117
        for existing_fallback_repo in self.repository._fallback_repositories:
117
118
            if existing_fallback_repo.user_url == url:
118
119
                # This fallback is already configured.  This probably only
119
 
                # happens because BzrDir.sprout is a horrible mess.  To avoid
 
120
                # happens because ControlDir.sprout is a horrible mess.  To avoid
120
121
                # confusing _unstack we don't add this a second time.
121
122
                mutter('duplicate activation of fallback %r on %r', url, self)
122
123
                return
123
 
        repo = self._get_fallback_repository(url)
 
124
        repo = self._get_fallback_repository(url, possible_transports)
124
125
        if repo.has_same_location(self.repository):
125
126
            raise errors.UnstackableLocationError(self.user_url, url)
126
127
        self.repository.add_fallback_repository(repo)
180
181
        For instance, if the branch is at URL/.bzr/branch,
181
182
        Branch.open(URL) -> a Branch instance.
182
183
        """
183
 
        control = bzrdir.BzrDir.open(base, _unsupported,
184
 
                                     possible_transports=possible_transports)
185
 
        return control.open_branch(unsupported=_unsupported)
 
184
        control = controldir.ControlDir.open(base,
 
185
            possible_transports=possible_transports, _unsupported=_unsupported)
 
186
        return control.open_branch(unsupported=_unsupported,
 
187
            possible_transports=possible_transports)
186
188
 
187
189
    @staticmethod
188
 
    def open_from_transport(transport, name=None, _unsupported=False):
 
190
    def open_from_transport(transport, name=None, _unsupported=False,
 
191
            possible_transports=None):
189
192
        """Open the branch rooted at transport"""
190
 
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
191
 
        return control.open_branch(name=name, unsupported=_unsupported)
 
193
        control = controldir.ControlDir.open_from_transport(transport, _unsupported)
 
194
        return control.open_branch(name=name, unsupported=_unsupported,
 
195
            possible_transports=possible_transports)
192
196
 
193
197
    @staticmethod
194
198
    def open_containing(url, possible_transports=None):
202
206
        format, UnknownFormatError or UnsupportedFormatError are raised.
203
207
        If there is one, it is returned, along with the unused portion of url.
204
208
        """
205
 
        control, relpath = bzrdir.BzrDir.open_containing(url,
 
209
        control, relpath = controldir.ControlDir.open_containing(url,
206
210
                                                         possible_transports)
207
 
        return control.open_branch(), relpath
 
211
        branch = control.open_branch(possible_transports=possible_transports)
 
212
        return (branch, relpath)
208
213
 
209
214
    def _push_should_merge_tags(self):
210
215
        """Should _basic_push merge this branch's tags into the target?
222
227
 
223
228
        :return: A bzrlib.config.BranchConfig.
224
229
        """
225
 
        return BranchConfig(self)
 
230
        return _mod_config.BranchConfig(self)
 
231
 
 
232
    def get_config_stack(self):
 
233
        """Get a bzrlib.config.BranchStack for this Branch.
 
234
 
 
235
        This can then be used to get and set configuration options for the
 
236
        branch.
 
237
 
 
238
        :return: A bzrlib.config.BranchStack.
 
239
        """
 
240
        return _mod_config.BranchStack(self)
226
241
 
227
242
    def _get_config(self):
228
243
        """Get the concrete config for just the config in this branch.
236
251
        """
237
252
        raise NotImplementedError(self._get_config)
238
253
 
239
 
    def _get_fallback_repository(self, url):
 
254
    def _get_fallback_repository(self, url, possible_transports):
240
255
        """Get the repository we fallback to at url."""
241
256
        url = urlutils.join(self.base, url)
242
 
        a_branch = Branch.open(url,
243
 
            possible_transports=[self.bzrdir.root_transport])
 
257
        a_branch = Branch.open(url, possible_transports=possible_transports)
244
258
        return a_branch.repository
245
259
 
246
260
    @needs_read_lock
520
534
                    # The decision to include the start or not
521
535
                    # depends on the stop_rule if a stop is provided
522
536
                    # so pop this node back into the iterator
523
 
                    rev_iter = chain(iter([node]), rev_iter)
 
537
                    rev_iter = itertools.chain(iter([node]), rev_iter)
524
538
                    break
525
539
        if stop_revision_id is None:
526
540
            # Yield everything
651
665
        """
652
666
        raise errors.UpgradeRequired(self.user_url)
653
667
 
 
668
    def get_append_revisions_only(self):
 
669
        """Whether it is only possible to append revisions to the history.
 
670
        """
 
671
        if not self._format.supports_set_append_revisions_only():
 
672
            return False
 
673
        return self.get_config_stack().get('append_revisions_only')
 
674
 
654
675
    def set_append_revisions_only(self, enabled):
655
676
        if not self._format.supports_set_append_revisions_only():
656
677
            raise errors.UpgradeRequired(self.user_url)
657
 
        if enabled:
658
 
            value = 'True'
659
 
        else:
660
 
            value = 'False'
661
 
        self.get_config().set_user_option('append_revisions_only', value,
662
 
            warn_masked=True)
 
678
        self.get_config_stack().set('append_revisions_only', enabled)
663
679
 
664
680
    def set_reference_info(self, file_id, tree_path, branch_location):
665
681
        """Set the branch location to use for a tree reference."""
694
710
        """
695
711
        raise errors.UpgradeRequired(self.user_url)
696
712
 
697
 
    def get_commit_builder(self, parents, config=None, timestamp=None,
 
713
    def get_commit_builder(self, parents, config_stack=None, timestamp=None,
698
714
                           timezone=None, committer=None, revprops=None,
699
715
                           revision_id=None, lossy=False):
700
716
        """Obtain a CommitBuilder for this branch.
710
726
            represented, when pushing to a foreign VCS 
711
727
        """
712
728
 
713
 
        if config is None:
714
 
            config = self.get_config()
 
729
        if config_stack is None:
 
730
            config_stack = self.get_config_stack()
715
731
 
716
 
        return self.repository.get_commit_builder(self, parents, config,
 
732
        return self.repository.get_commit_builder(self, parents, config_stack,
717
733
            timestamp, timezone, committer, revprops, revision_id,
718
734
            lossy)
719
735
 
724
740
        """
725
741
        return None
726
742
 
 
743
    @deprecated_method(deprecated_in((2, 5, 0)))
727
744
    def get_revision_delta(self, revno):
728
745
        """Return the delta for one revision.
729
746
 
730
747
        The delta is relative to its mainline predecessor, or the
731
748
        empty tree for revision 1.
732
749
        """
733
 
        rh = self.revision_history()
734
 
        if not (1 <= revno <= len(rh)):
 
750
        try:
 
751
            revid = self.get_rev_id(revno)
 
752
        except errors.NoSuchRevision:
735
753
            raise errors.InvalidRevisionNumber(revno)
736
 
        return self.repository.get_revision_delta(rh[revno-1])
 
754
        return self.repository.get_revision_delta(revid)
737
755
 
738
756
    def get_stacked_on_url(self):
739
757
        """Get the URL this branch is stacked against.
748
766
        """Print `file` to stdout."""
749
767
        raise NotImplementedError(self.print_file)
750
768
 
751
 
    @deprecated_method(deprecated_in((2, 4, 0)))
752
 
    def set_revision_history(self, rev_history):
753
 
        """See Branch.set_revision_history."""
754
 
        self._set_revision_history(rev_history)
755
 
 
756
 
    @needs_write_lock
757
 
    def _set_revision_history(self, rev_history):
758
 
        if len(rev_history) == 0:
759
 
            revid = _mod_revision.NULL_REVISION
760
 
        else:
761
 
            revid = rev_history[-1]
762
 
        if rev_history != self._lefthand_history(revid):
763
 
            raise errors.NotLefthandHistory(rev_history)
764
 
        self.set_last_revision_info(len(rev_history), revid)
765
 
        self._cache_revision_history(rev_history)
766
 
        for hook in Branch.hooks['set_rh']:
767
 
            hook(self, rev_history)
768
 
 
769
769
    @needs_write_lock
770
770
    def set_last_revision_info(self, revno, revision_id):
771
771
        """Set the last revision of this branch.
785
785
                                  other_branch=None):
786
786
        """See Branch.generate_revision_history"""
787
787
        graph = self.repository.get_graph()
 
788
        (last_revno, last_revid) = self.last_revision_info()
788
789
        known_revision_ids = [
789
 
            self.last_revision_info(),
 
790
            (last_revid, last_revno),
790
791
            (_mod_revision.NULL_REVISION, 0),
791
792
            ]
792
793
        if last_rev is not None:
837
838
                return
838
839
            self._unstack()
839
840
        else:
840
 
            self._activate_fallback_location(url)
 
841
            self._activate_fallback_location(url,
 
842
                possible_transports=[self.bzrdir.root_transport])
841
843
        # write this out after the repository is stacked to avoid setting a
842
844
        # stacked config that doesn't work.
843
845
        self._set_config_location('stacked_on_location', url)
849
851
        """
850
852
        pb = ui.ui_factory.nested_progress_bar()
851
853
        try:
852
 
            pb.update("Unstacking")
 
854
            pb.update(gettext("Unstacking"))
853
855
            # The basic approach here is to fetch the tip of the branch,
854
856
            # including all available ghosts, from the existing stacked
855
857
            # repository into a new repository object without the fallbacks. 
869
871
            # stream from one of them to the other.  This does mean doing
870
872
            # separate SSH connection setup, but unstacking is not a
871
873
            # common operation so it's tolerable.
872
 
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
 
874
            new_bzrdir = controldir.ControlDir.open(
 
875
                self.bzrdir.root_transport.base)
873
876
            new_repository = new_bzrdir.find_repository()
874
877
            if new_repository._fallback_repositories:
875
878
                raise AssertionError("didn't expect %r to have "
918
921
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
919
922
                except errors.TagsNotSupported:
920
923
                    tags_to_fetch = set()
921
 
                fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
 
924
                fetch_spec = vf_search.NotInOtherForRevs(self.repository,
922
925
                    old_repository, required_ids=[self.last_revision()],
923
926
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
924
927
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
965
968
        This means the next call to revision_history will need to call
966
969
        _gen_revision_history.
967
970
 
968
 
        This API is semi-public; it only for use by subclasses, all other code
969
 
        should consider it to be private.
 
971
        This API is semi-public; it is only for use by subclasses, all other
 
972
        code should consider it to be private.
970
973
        """
971
974
        self._revision_history_cache = None
972
975
        self._revision_id_to_revno_cache = None
992
995
        """
993
996
        raise NotImplementedError(self._gen_revision_history)
994
997
 
995
 
    @needs_read_lock
996
 
    def revision_history(self):
997
 
        """Return sequence of revision ids on this branch.
998
 
 
999
 
        This method will cache the revision history for as long as it is safe to
1000
 
        do so.
1001
 
        """
 
998
    def _revision_history(self):
1002
999
        if 'evil' in debug.debug_flags:
1003
1000
            mutter_callsite(3, "revision_history scales with history.")
1004
1001
        if self._revision_history_cache is not None:
1074
1071
        """Given a revision id, return its revno"""
1075
1072
        if _mod_revision.is_null(revision_id):
1076
1073
            return 0
1077
 
        history = self.revision_history()
 
1074
        history = self._revision_history()
1078
1075
        try:
1079
1076
            return history.index(revision_id) + 1
1080
1077
        except ValueError:
1145
1142
    def _set_config_location(self, name, url, config=None,
1146
1143
                             make_relative=False):
1147
1144
        if config is None:
1148
 
            config = self.get_config()
 
1145
            config = self.get_config_stack()
1149
1146
        if url is None:
1150
1147
            url = ''
1151
1148
        elif make_relative:
1152
1149
            url = urlutils.relative_url(self.base, url)
1153
 
        config.set_user_option(name, url, warn_masked=True)
 
1150
        config.set(name, url)
1154
1151
 
1155
1152
    def _get_config_location(self, name, config=None):
1156
1153
        if config is None:
1157
 
            config = self.get_config()
1158
 
        location = config.get_user_option(name)
 
1154
            config = self.get_config_stack()
 
1155
        location = config.get(name)
1159
1156
        if location == '':
1160
1157
            location = None
1161
1158
        return location
1162
1159
 
1163
1160
    def get_child_submit_format(self):
1164
1161
        """Return the preferred format of submissions to this branch."""
1165
 
        return self.get_config().get_user_option("child_submit_format")
 
1162
        return self.get_config_stack().get('child_submit_format')
1166
1163
 
1167
1164
    def get_submit_branch(self):
1168
1165
        """Return the submit location of the branch.
1171
1168
        pattern is that the user can override it by specifying a
1172
1169
        location.
1173
1170
        """
1174
 
        return self.get_config().get_user_option('submit_branch')
 
1171
        return self.get_config_stack().get('submit_branch')
1175
1172
 
1176
1173
    def set_submit_branch(self, location):
1177
1174
        """Return the submit location of the branch.
1180
1177
        pattern is that the user can override it by specifying a
1181
1178
        location.
1182
1179
        """
1183
 
        self.get_config().set_user_option('submit_branch', location,
1184
 
            warn_masked=True)
 
1180
        self.get_config_stack().set('submit_branch', location)
1185
1181
 
1186
1182
    def get_public_branch(self):
1187
1183
        """Return the public location of the branch.
1200
1196
        self._set_config_location('public_branch', location)
1201
1197
 
1202
1198
    def get_push_location(self):
1203
 
        """Return the None or the location to push this branch to."""
1204
 
        push_loc = self.get_config().get_user_option('push_location')
1205
 
        return push_loc
 
1199
        """Return None or the location to push this branch to."""
 
1200
        return self.get_config_stack().get('push_location')
1206
1201
 
1207
1202
    def set_push_location(self, location):
1208
1203
        """Set a new push location for this branch."""
1293
1288
            if repository_policy is not None:
1294
1289
                repository_policy.configure_branch(result)
1295
1290
            self.copy_content_into(result, revision_id=revision_id)
1296
 
            master_branch = self.get_master_branch()
1297
 
            if master_branch is None:
 
1291
            master_url = self.get_bound_location()
 
1292
            if master_url is None:
1298
1293
                result.set_parent(self.bzrdir.root_transport.base)
1299
1294
            else:
1300
 
                result.set_parent(master_branch.bzrdir.root_transport.base)
 
1295
                result.set_parent(master_url)
1301
1296
        finally:
1302
1297
            result.unlock()
1303
1298
        return result
1377
1372
        # TODO: We should probably also check that self.revision_history
1378
1373
        # matches the repository for older branch formats.
1379
1374
        # If looking for the code that cross-checks repository parents against
1380
 
        # the iter_reverse_revision_history output, that is now a repository
 
1375
        # the Graph.iter_lefthand_ancestry output, that is now a repository
1381
1376
        # specific check.
1382
1377
        return result
1383
1378
 
1384
 
    def _get_checkout_format(self):
 
1379
    def _get_checkout_format(self, lightweight=False):
1385
1380
        """Return the most suitable metadir for a checkout of this branch.
1386
1381
        Weaves are used if this branch's repository uses weaves.
1387
1382
        """
1433
1428
        """
1434
1429
        t = transport.get_transport(to_location)
1435
1430
        t.ensure_base()
 
1431
        format = self._get_checkout_format(lightweight=lightweight)
 
1432
        try:
 
1433
            checkout = format.initialize_on_transport(t)
 
1434
        except errors.AlreadyControlDirError:
 
1435
            # It's fine if the control directory already exists,
 
1436
            # as long as there is no existing branch and working tree.
 
1437
            checkout = controldir.ControlDir.open_from_transport(t)
 
1438
            try:
 
1439
                checkout.open_branch()
 
1440
            except errors.NotBranchError:
 
1441
                pass
 
1442
            else:
 
1443
                raise errors.AlreadyControlDirError(t.base)
 
1444
            if checkout.control_transport.base == self.bzrdir.control_transport.base:
 
1445
                # When checking out to the same control directory,
 
1446
                # always create a lightweight checkout
 
1447
                lightweight = True
 
1448
 
1436
1449
        if lightweight:
1437
 
            format = self._get_checkout_format()
1438
 
            checkout = format.initialize_on_transport(t)
1439
 
            from_branch = BranchReferenceFormat().initialize(checkout, 
1440
 
                target_branch=self)
 
1450
            from_branch = checkout.set_branch_reference(target_branch=self)
1441
1451
        else:
1442
 
            format = self._get_checkout_format()
1443
 
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1444
 
                to_location, force_new_tree=False, format=format)
1445
 
            checkout = checkout_branch.bzrdir
 
1452
            policy = checkout.determine_repository_policy()
 
1453
            repo = policy.acquire_repository()[0]
 
1454
            checkout_branch = checkout.create_branch()
1446
1455
            checkout_branch.bind(self)
1447
1456
            # pull up to the specified revision_id to set the initial
1448
1457
            # branch tip correctly, and seed it with history.
1449
1458
            checkout_branch.pull(self, stop_revision=revision_id)
1450
 
            from_branch=None
 
1459
            from_branch = None
1451
1460
        tree = checkout.create_workingtree(revision_id,
1452
1461
                                           from_branch=from_branch,
1453
1462
                                           accelerator_tree=accelerator_tree,
1542
1551
            heads that must be fetched if present, but no error is necessary if
1543
1552
            they are not present.
1544
1553
        """
1545
 
        # For bzr native formats must_fetch is just the tip, and if_present_fetch
1546
 
        # are the tags.
 
1554
        # For bzr native formats must_fetch is just the tip, and
 
1555
        # if_present_fetch are the tags.
1547
1556
        must_fetch = set([self.last_revision()])
1548
 
        try:
1549
 
            if_present_fetch = set(self.tags.get_reverse_tag_dict())
1550
 
        except errors.TagsNotSupported:
1551
 
            if_present_fetch = set()
 
1557
        if_present_fetch = set()
 
1558
        if self.get_config_stack().get('branch.fetch_tags'):
 
1559
            try:
 
1560
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
 
1561
            except errors.TagsNotSupported:
 
1562
                pass
1552
1563
        must_fetch.discard(_mod_revision.NULL_REVISION)
1553
1564
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
1554
1565
        return must_fetch, if_present_fetch
1559
1570
 
1560
1571
    Formats provide three things:
1561
1572
     * An initialization routine,
1562
 
     * a format string,
 
1573
     * a format description
1563
1574
     * an open routine.
1564
1575
 
1565
1576
    Formats are placed in an dict by their format string for reference
1572
1583
    object will be created every time regardless.
1573
1584
    """
1574
1585
 
1575
 
    can_set_append_revisions_only = True
1576
 
 
1577
1586
    def __eq__(self, other):
1578
1587
        return self.__class__ is other.__class__
1579
1588
 
1580
1589
    def __ne__(self, other):
1581
1590
        return not (self == other)
1582
1591
 
1583
 
    @classmethod
1584
 
    def find_format(klass, a_bzrdir, name=None):
1585
 
        """Return the format for the branch object in a_bzrdir."""
1586
 
        try:
1587
 
            transport = a_bzrdir.get_branch_transport(None, name=name)
1588
 
            format_string = transport.get_bytes("format")
1589
 
            return format_registry.get(format_string)
1590
 
        except errors.NoSuchFile:
1591
 
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1592
 
        except KeyError:
1593
 
            raise errors.UnknownFormatError(format=format_string, kind='branch')
1594
 
 
1595
 
    @classmethod
1596
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1597
 
    def get_default_format(klass):
1598
 
        """Return the current default format."""
1599
 
        return format_registry.get_default()
1600
 
 
1601
 
    @classmethod
1602
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1603
 
    def get_formats(klass):
1604
 
        """Get all the known formats.
1605
 
 
1606
 
        Warning: This triggers a load of all lazy registered formats: do not
1607
 
        use except when that is desireed.
1608
 
        """
1609
 
        return format_registry._get_all()
1610
 
 
1611
 
    def get_reference(self, a_bzrdir, name=None):
1612
 
        """Get the target reference of the branch in a_bzrdir.
 
1592
    def get_reference(self, controldir, name=None):
 
1593
        """Get the target reference of the branch in controldir.
1613
1594
 
1614
1595
        format probing must have been completed before calling
1615
1596
        this method - it is assumed that the format of the branch
1616
 
        in a_bzrdir is correct.
 
1597
        in controldir is correct.
1617
1598
 
1618
 
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1599
        :param controldir: The controldir to get the branch data from.
1619
1600
        :param name: Name of the colocated branch to fetch
1620
1601
        :return: None if the branch is not a reference branch.
1621
1602
        """
1622
1603
        return None
1623
1604
 
1624
1605
    @classmethod
1625
 
    def set_reference(self, a_bzrdir, name, to_branch):
1626
 
        """Set the target reference of the branch in a_bzrdir.
 
1606
    def set_reference(self, controldir, name, to_branch):
 
1607
        """Set the target reference of the branch in controldir.
1627
1608
 
1628
1609
        format probing must have been completed before calling
1629
1610
        this method - it is assumed that the format of the branch
1630
 
        in a_bzrdir is correct.
 
1611
        in controldir is correct.
1631
1612
 
1632
 
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1613
        :param controldir: The controldir to set the branch reference for.
1633
1614
        :param name: Name of colocated branch to set, None for default
1634
1615
        :param to_branch: branch that the checkout is to reference
1635
1616
        """
1636
1617
        raise NotImplementedError(self.set_reference)
1637
1618
 
1638
 
    def get_format_string(self):
1639
 
        """Return the ASCII format string that identifies this format."""
1640
 
        raise NotImplementedError(self.get_format_string)
1641
 
 
1642
1619
    def get_format_description(self):
1643
1620
        """Return the short format description for this format."""
1644
1621
        raise NotImplementedError(self.get_format_description)
1645
1622
 
1646
 
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
 
1623
    def _run_post_branch_init_hooks(self, controldir, name, branch):
1647
1624
        hooks = Branch.hooks['post_branch_init']
1648
1625
        if not hooks:
1649
1626
            return
1650
 
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
 
1627
        params = BranchInitHookParams(self, controldir, name, branch)
1651
1628
        for hook in hooks:
1652
1629
            hook(params)
1653
1630
 
1654
 
    def initialize(self, a_bzrdir, name=None, repository=None):
1655
 
        """Create a branch of this format in a_bzrdir.
1656
 
        
 
1631
    def initialize(self, controldir, name=None, repository=None,
 
1632
                   append_revisions_only=None):
 
1633
        """Create a branch of this format in controldir.
 
1634
 
1657
1635
        :param name: Name of the colocated branch to create.
1658
1636
        """
1659
1637
        raise NotImplementedError(self.initialize)
1679
1657
        Note that it is normal for branch to be a RemoteBranch when using tags
1680
1658
        on a RemoteBranch.
1681
1659
        """
1682
 
        return DisabledTags(branch)
 
1660
        return _mod_tag.DisabledTags(branch)
1683
1661
 
1684
1662
    def network_name(self):
1685
1663
        """A simple byte string uniquely identifying this format for RPC calls.
1691
1669
        """
1692
1670
        raise NotImplementedError(self.network_name)
1693
1671
 
1694
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
1695
 
            found_repository=None):
1696
 
        """Return the branch object for a_bzrdir
 
1672
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
 
1673
            found_repository=None, possible_transports=None):
 
1674
        """Return the branch object for controldir.
1697
1675
 
1698
 
        :param a_bzrdir: A BzrDir that contains a branch.
 
1676
        :param controldir: A ControlDir that contains a branch.
1699
1677
        :param name: Name of colocated branch to open
1700
1678
        :param _found: a private parameter, do not use it. It is used to
1701
1679
            indicate if format probing has already be done.
1704
1682
        """
1705
1683
        raise NotImplementedError(self.open)
1706
1684
 
1707
 
    @classmethod
1708
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1709
 
    def register_format(klass, format):
1710
 
        """Register a metadir format.
1711
 
 
1712
 
        See MetaDirBranchFormatFactory for the ability to register a format
1713
 
        without loading the code the format needs until it is actually used.
1714
 
        """
1715
 
        format_registry.register(format)
1716
 
 
1717
 
    @classmethod
1718
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1719
 
    def set_default_format(klass, format):
1720
 
        format_registry.set_default(format)
1721
 
 
1722
1685
    def supports_set_append_revisions_only(self):
1723
1686
        """True if this format supports set_append_revisions_only."""
1724
1687
        return False
1743
1706
        """True if this format supports tags stored in the branch"""
1744
1707
        return False  # by default
1745
1708
 
 
1709
    def tags_are_versioned(self):
 
1710
        """Whether the tag container for this branch versions tags."""
 
1711
        return False
 
1712
 
 
1713
    def supports_tags_referencing_ghosts(self):
 
1714
        """True if tags can reference ghost revisions."""
 
1715
        return True
 
1716
 
1746
1717
 
1747
1718
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1748
1719
    """A factory for a BranchFormat object, permitting simple lazy registration.
1762
1733
        """
1763
1734
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
1764
1735
        self._format_string = format_string
1765
 
        
 
1736
 
1766
1737
    def get_format_string(self):
1767
1738
        """See BranchFormat.get_format_string."""
1768
1739
        return self._format_string
1775
1746
class BranchHooks(Hooks):
1776
1747
    """A dictionary mapping hook name to a list of callables for branch hooks.
1777
1748
 
1778
 
    e.g. ['set_rh'] Is the list of items to be called when the
1779
 
    set_revision_history function is invoked.
 
1749
    e.g. ['post_push'] Is the list of items to be called when the
 
1750
    push function is invoked.
1780
1751
    """
1781
1752
 
1782
1753
    def __init__(self):
1786
1757
        notified.
1787
1758
        """
1788
1759
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1789
 
        self.add_hook('set_rh',
1790
 
            "Invoked whenever the revision history has been set via "
1791
 
            "set_revision_history. The api signature is (branch, "
1792
 
            "revision_history), and the branch will be write-locked. "
1793
 
            "The set_rh hook can be expensive for bzr to trigger, a better "
1794
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
1795
1760
        self.add_hook('open',
1796
1761
            "Called with the Branch object that has been opened after a "
1797
1762
            "branch is opened.", (1, 8))
1913
1878
    There are 4 fields that hooks may wish to access:
1914
1879
 
1915
1880
    :ivar format: the branch format
1916
 
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
 
1881
    :ivar bzrdir: the ControlDir where the branch will be/has been initialized
1917
1882
    :ivar name: name of colocated branch, if any (or None)
1918
1883
    :ivar branch: the branch created
1919
1884
 
1922
1887
    branch, which refer to the original branch.
1923
1888
    """
1924
1889
 
1925
 
    def __init__(self, format, a_bzrdir, name, branch):
 
1890
    def __init__(self, format, controldir, name, branch):
1926
1891
        """Create a group of BranchInitHook parameters.
1927
1892
 
1928
1893
        :param format: the branch format
1929
 
        :param a_bzrdir: the BzrDir where the branch will be/has been
 
1894
        :param controldir: the ControlDir where the branch will be/has been
1930
1895
            initialized
1931
1896
        :param name: name of colocated branch, if any (or None)
1932
1897
        :param branch: the branch created
1936
1901
        in branch, which refer to the original branch.
1937
1902
        """
1938
1903
        self.format = format
1939
 
        self.bzrdir = a_bzrdir
 
1904
        self.bzrdir = controldir
1940
1905
        self.name = name
1941
1906
        self.branch = branch
1942
1907
 
1952
1917
 
1953
1918
    There are 4 fields that hooks may wish to access:
1954
1919
 
1955
 
    :ivar control_dir: BzrDir of the checkout to change
 
1920
    :ivar control_dir: ControlDir of the checkout to change
1956
1921
    :ivar to_branch: branch that the checkout is to reference
1957
1922
    :ivar force: skip the check for local commits in a heavy checkout
1958
1923
    :ivar revision_id: revision ID to switch to (or None)
1961
1926
    def __init__(self, control_dir, to_branch, force, revision_id):
1962
1927
        """Create a group of SwitchHook parameters.
1963
1928
 
1964
 
        :param control_dir: BzrDir of the checkout to change
 
1929
        :param control_dir: ControlDir of the checkout to change
1965
1930
        :param to_branch: branch that the checkout is to reference
1966
1931
        :param force: skip the check for local commits in a heavy checkout
1967
1932
        :param revision_id: revision ID to switch to (or None)
1980
1945
            self.revision_id)
1981
1946
 
1982
1947
 
1983
 
class BranchFormatMetadir(BranchFormat):
1984
 
    """Common logic for meta-dir based branch formats."""
 
1948
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
 
1949
    """Base class for branch formats that live in meta directories.
 
1950
    """
 
1951
 
 
1952
    def __init__(self):
 
1953
        BranchFormat.__init__(self)
 
1954
        bzrdir.BzrFormat.__init__(self)
 
1955
 
 
1956
    @classmethod
 
1957
    def find_format(klass, controldir, name=None):
 
1958
        """Return the format for the branch object in controldir."""
 
1959
        try:
 
1960
            transport = controldir.get_branch_transport(None, name=name)
 
1961
        except errors.NoSuchFile:
 
1962
            raise errors.NotBranchError(path=name, bzrdir=controldir)
 
1963
        try:
 
1964
            format_string = transport.get_bytes("format")
 
1965
        except errors.NoSuchFile:
 
1966
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
 
1967
        return klass._find_format(format_registry, 'branch', format_string)
1985
1968
 
1986
1969
    def _branch_class(self):
1987
1970
        """What class to instantiate on open calls."""
1988
1971
        raise NotImplementedError(self._branch_class)
1989
1972
 
 
1973
    def _get_initial_config(self, append_revisions_only=None):
 
1974
        if append_revisions_only:
 
1975
            return "append_revisions_only = True\n"
 
1976
        else:
 
1977
            # Avoid writing anything if append_revisions_only is disabled,
 
1978
            # as that is the default.
 
1979
            return ""
 
1980
 
1990
1981
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1991
1982
                           repository=None):
1992
 
        """Initialize a branch in a bzrdir, with specified files
 
1983
        """Initialize a branch in a control dir, with specified files
1993
1984
 
1994
1985
        :param a_bzrdir: The bzrdir to initialize the branch in
1995
1986
        :param utf8_files: The files to create as a list of
1997
1988
        :param name: Name of colocated branch to create, if any
1998
1989
        :return: a branch in this format
1999
1990
        """
 
1991
        if name is None:
 
1992
            name = a_bzrdir._get_selected_branch()
2000
1993
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
2001
1994
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2002
1995
        control_files = lockable_files.LockableFiles(branch_transport,
2004
1997
        control_files.create_lock()
2005
1998
        control_files.lock_write()
2006
1999
        try:
2007
 
            utf8_files += [('format', self.get_format_string())]
 
2000
            utf8_files += [('format', self.as_string())]
2008
2001
            for (filename, content) in utf8_files:
2009
2002
                branch_transport.put_bytes(
2010
2003
                    filename, content,
2016
2009
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2017
2010
        return branch
2018
2011
 
2019
 
    def network_name(self):
2020
 
        """A simple byte string uniquely identifying this format for RPC calls.
2021
 
 
2022
 
        Metadir branch formats use their format string.
2023
 
        """
2024
 
        return self.get_format_string()
2025
 
 
2026
2012
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2027
 
            found_repository=None):
 
2013
            found_repository=None, possible_transports=None):
2028
2014
        """See BranchFormat.open()."""
 
2015
        if name is None:
 
2016
            name = a_bzrdir._get_selected_branch()
2029
2017
        if not _found:
2030
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2018
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2031
2019
            if format.__class__ != self.__class__:
2032
2020
                raise AssertionError("wrong format %r found for %r" %
2033
2021
                    (format, self))
2042
2030
                              name=name,
2043
2031
                              a_bzrdir=a_bzrdir,
2044
2032
                              _repository=found_repository,
2045
 
                              ignore_fallbacks=ignore_fallbacks)
 
2033
                              ignore_fallbacks=ignore_fallbacks,
 
2034
                              possible_transports=possible_transports)
2046
2035
        except errors.NoSuchFile:
2047
2036
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2048
2037
 
2049
 
    def __init__(self):
2050
 
        super(BranchFormatMetadir, self).__init__()
2051
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2052
 
        self._matchingbzrdir.set_branch_format(self)
 
2038
    @property
 
2039
    def _matchingbzrdir(self):
 
2040
        ret = bzrdir.BzrDirMetaFormat1()
 
2041
        ret.set_branch_format(self)
 
2042
        return ret
2053
2043
 
2054
2044
    def supports_tags(self):
2055
2045
        return True
2057
2047
    def supports_leaving_lock(self):
2058
2048
        return True
2059
2049
 
 
2050
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
2051
            basedir=None):
 
2052
        BranchFormat.check_support_status(self,
 
2053
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
 
2054
            basedir=basedir)
 
2055
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
 
2056
            recommend_upgrade=recommend_upgrade, basedir=basedir)
 
2057
 
2060
2058
 
2061
2059
class BzrBranchFormat5(BranchFormatMetadir):
2062
2060
    """Bzr branch format 5.
2074
2072
    def _branch_class(self):
2075
2073
        return BzrBranch5
2076
2074
 
2077
 
    def get_format_string(self):
 
2075
    @classmethod
 
2076
    def get_format_string(cls):
2078
2077
        """See BranchFormat.get_format_string()."""
2079
2078
        return "Bazaar-NG branch format 5\n"
2080
2079
 
2082
2081
        """See BranchFormat.get_format_description()."""
2083
2082
        return "Branch format 5"
2084
2083
 
2085
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2084
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2085
                   append_revisions_only=None):
2086
2086
        """Create a branch of this format in a_bzrdir."""
 
2087
        if append_revisions_only:
 
2088
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2087
2089
        utf8_files = [('revision-history', ''),
2088
2090
                      ('branch-name', ''),
2089
2091
                      ]
2107
2109
    def _branch_class(self):
2108
2110
        return BzrBranch6
2109
2111
 
2110
 
    def get_format_string(self):
 
2112
    @classmethod
 
2113
    def get_format_string(cls):
2111
2114
        """See BranchFormat.get_format_string()."""
2112
2115
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
2113
2116
 
2115
2118
        """See BranchFormat.get_format_description()."""
2116
2119
        return "Branch format 6"
2117
2120
 
2118
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2121
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2122
                   append_revisions_only=None):
2119
2123
        """Create a branch of this format in a_bzrdir."""
2120
2124
        utf8_files = [('last-revision', '0 null:\n'),
2121
 
                      ('branch.conf', ''),
 
2125
                      ('branch.conf',
 
2126
                          self._get_initial_config(append_revisions_only)),
2122
2127
                      ('tags', ''),
2123
2128
                      ]
2124
2129
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2125
2130
 
2126
2131
    def make_tags(self, branch):
2127
2132
        """See bzrlib.branch.BranchFormat.make_tags()."""
2128
 
        return BasicTags(branch)
 
2133
        return _mod_tag.BasicTags(branch)
2129
2134
 
2130
2135
    def supports_set_append_revisions_only(self):
2131
2136
        return True
2137
2142
    def _branch_class(self):
2138
2143
        return BzrBranch8
2139
2144
 
2140
 
    def get_format_string(self):
 
2145
    @classmethod
 
2146
    def get_format_string(cls):
2141
2147
        """See BranchFormat.get_format_string()."""
2142
2148
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2143
2149
 
2145
2151
        """See BranchFormat.get_format_description()."""
2146
2152
        return "Branch format 8"
2147
2153
 
2148
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2154
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2155
                   append_revisions_only=None):
2149
2156
        """Create a branch of this format in a_bzrdir."""
2150
2157
        utf8_files = [('last-revision', '0 null:\n'),
2151
 
                      ('branch.conf', ''),
 
2158
                      ('branch.conf',
 
2159
                          self._get_initial_config(append_revisions_only)),
2152
2160
                      ('tags', ''),
2153
2161
                      ('references', '')
2154
2162
                      ]
2156
2164
 
2157
2165
    def make_tags(self, branch):
2158
2166
        """See bzrlib.branch.BranchFormat.make_tags()."""
2159
 
        return BasicTags(branch)
 
2167
        return _mod_tag.BasicTags(branch)
2160
2168
 
2161
2169
    def supports_set_append_revisions_only(self):
2162
2170
        return True
2176
2184
    This format was introduced in bzr 1.6.
2177
2185
    """
2178
2186
 
2179
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2187
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2188
                   append_revisions_only=None):
2180
2189
        """Create a branch of this format in a_bzrdir."""
2181
2190
        utf8_files = [('last-revision', '0 null:\n'),
2182
 
                      ('branch.conf', ''),
 
2191
                      ('branch.conf',
 
2192
                          self._get_initial_config(append_revisions_only)),
2183
2193
                      ('tags', ''),
2184
2194
                      ]
2185
2195
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2187
2197
    def _branch_class(self):
2188
2198
        return BzrBranch7
2189
2199
 
2190
 
    def get_format_string(self):
 
2200
    @classmethod
 
2201
    def get_format_string(cls):
2191
2202
        """See BranchFormat.get_format_string()."""
2192
2203
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2193
2204
 
2203
2214
 
2204
2215
    def make_tags(self, branch):
2205
2216
        """See bzrlib.branch.BranchFormat.make_tags()."""
2206
 
        return BasicTags(branch)
 
2217
        return _mod_tag.BasicTags(branch)
2207
2218
 
2208
2219
    supports_reference_locations = False
2209
2220
 
2210
2221
 
2211
 
class BranchReferenceFormat(BranchFormat):
 
2222
class BranchReferenceFormat(BranchFormatMetadir):
2212
2223
    """Bzr branch reference format.
2213
2224
 
2214
2225
    Branch references are used in implementing checkouts, they
2219
2230
     - a format string
2220
2231
    """
2221
2232
 
2222
 
    def get_format_string(self):
 
2233
    @classmethod
 
2234
    def get_format_string(cls):
2223
2235
        """See BranchFormat.get_format_string()."""
2224
2236
        return "Bazaar-NG Branch Reference Format 1\n"
2225
2237
 
2238
2250
        location = transport.put_bytes('location', to_branch.base)
2239
2251
 
2240
2252
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2241
 
            repository=None):
 
2253
            repository=None, append_revisions_only=None):
2242
2254
        """Create a branch of this format in a_bzrdir."""
2243
2255
        if target_branch is None:
2244
2256
            # this format does not implement branch itself, thus the implicit
2245
2257
            # creation contract must see it as uninitializable
2246
2258
            raise errors.UninitializableFormat(self)
2247
2259
        mutter('creating branch reference in %s', a_bzrdir.user_url)
 
2260
        if a_bzrdir._format.fixed_components:
 
2261
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
 
2262
        if name is None:
 
2263
            name = a_bzrdir._get_selected_branch()
2248
2264
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2249
2265
        branch_transport.put_bytes('location',
2250
 
            target_branch.bzrdir.user_url)
2251
 
        branch_transport.put_bytes('format', self.get_format_string())
2252
 
        branch = self.open(
2253
 
            a_bzrdir, name, _found=True,
 
2266
            target_branch.user_url)
 
2267
        branch_transport.put_bytes('format', self.as_string())
 
2268
        branch = self.open(a_bzrdir, name, _found=True,
2254
2269
            possible_transports=[target_branch.bzrdir.root_transport])
2255
2270
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2256
2271
        return branch
2257
2272
 
2258
 
    def __init__(self):
2259
 
        super(BranchReferenceFormat, self).__init__()
2260
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2261
 
        self._matchingbzrdir.set_branch_format(self)
2262
 
 
2263
2273
    def _make_reference_clone_function(format, a_branch):
2264
2274
        """Create a clone() routine for a branch dynamically."""
2265
2275
        def clone(to_bzrdir, revision_id=None,
2287
2297
            a_bzrdir.
2288
2298
        :param possible_transports: An optional reusable transports list.
2289
2299
        """
 
2300
        if name is None:
 
2301
            name = a_bzrdir._get_selected_branch()
2290
2302
        if not _found:
2291
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2303
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2292
2304
            if format.__class__ != self.__class__:
2293
2305
                raise AssertionError("wrong format %r found for %r" %
2294
2306
                    (format, self))
2295
2307
        if location is None:
2296
2308
            location = self.get_reference(a_bzrdir, name)
2297
 
        real_bzrdir = bzrdir.BzrDir.open(
 
2309
        real_bzrdir = controldir.ControlDir.open(
2298
2310
            location, possible_transports=possible_transports)
2299
 
        result = real_bzrdir.open_branch(name=name, 
2300
 
            ignore_fallbacks=ignore_fallbacks)
 
2311
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
 
2312
            possible_transports=possible_transports)
2301
2313
        # this changes the behaviour of result.clone to create a new reference
2302
2314
        # rather than a copy of the content of the branch.
2303
2315
        # I did not use a proxy object because that needs much more extensive
2384
2396
 
2385
2397
    def __init__(self, _format=None,
2386
2398
                 _control_files=None, a_bzrdir=None, name=None,
2387
 
                 _repository=None, ignore_fallbacks=False):
 
2399
                 _repository=None, ignore_fallbacks=False,
 
2400
                 possible_transports=None):
2388
2401
        """Create new branch object at a particular location."""
2389
2402
        if a_bzrdir is None:
2390
2403
            raise ValueError('a_bzrdir must be supplied')
2391
 
        else:
2392
 
            self.bzrdir = a_bzrdir
2393
 
        self._base = self.bzrdir.transport.clone('..').base
 
2404
        if name is None:
 
2405
            raise ValueError('name must be supplied')
 
2406
        self.bzrdir = a_bzrdir
 
2407
        self._user_transport = self.bzrdir.transport.clone('..')
 
2408
        if name != "":
 
2409
            self._user_transport.set_segment_parameter(
 
2410
                "branch", urlutils.escape(name))
 
2411
        self._base = self._user_transport.base
2394
2412
        self.name = name
2395
 
        # XXX: We should be able to just do
2396
 
        #   self.base = self.bzrdir.root_transport.base
2397
 
        # but this does not quite work yet -- mbp 20080522
2398
2413
        self._format = _format
2399
2414
        if _control_files is None:
2400
2415
            raise ValueError('BzrBranch _control_files is None')
2401
2416
        self.control_files = _control_files
2402
2417
        self._transport = _control_files._transport
2403
2418
        self.repository = _repository
2404
 
        Branch.__init__(self)
 
2419
        self.conf_store = None
 
2420
        Branch.__init__(self, possible_transports)
2405
2421
 
2406
2422
    def __str__(self):
2407
 
        if self.name is None:
2408
 
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
2409
 
        else:
2410
 
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2411
 
                self.name)
 
2423
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
2412
2424
 
2413
2425
    __repr__ = __str__
2414
2426
 
2418
2430
 
2419
2431
    base = property(_get_base, doc="The URL for the root of this branch.")
2420
2432
 
 
2433
    @property
 
2434
    def user_transport(self):
 
2435
        return self._user_transport
 
2436
 
2421
2437
    def _get_config(self):
2422
 
        return TransportConfig(self._transport, 'branch.conf')
 
2438
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
 
2439
 
 
2440
    def _get_config_store(self):
 
2441
        if self.conf_store is None:
 
2442
            self.conf_store =  _mod_config.BranchStore(self)
 
2443
        return self.conf_store
2423
2444
 
2424
2445
    def is_locked(self):
2425
2446
        return self.control_files.is_locked()
2474
2495
 
2475
2496
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2476
2497
    def unlock(self):
 
2498
        if self.control_files._lock_count == 1 and self.conf_store is not None:
 
2499
            self.conf_store.save_changes()
2477
2500
        try:
2478
2501
            self.control_files.unlock()
2479
2502
        finally:
2506
2529
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2507
2530
        revision_id = _mod_revision.ensure_null(revision_id)
2508
2531
        old_revno, old_revid = self.last_revision_info()
2509
 
        if self._get_append_revisions_only():
 
2532
        if self.get_append_revisions_only():
2510
2533
            self._check_history_violation(revision_id)
2511
2534
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2512
2535
        self._write_last_revision_info(revno, revision_id)
2655
2678
        self._transport.put_bytes('last-revision', out_string,
2656
2679
            mode=self.bzrdir._get_file_mode())
2657
2680
 
 
2681
    @needs_write_lock
 
2682
    def update_feature_flags(self, updated_flags):
 
2683
        """Update the feature flags for this branch.
 
2684
 
 
2685
        :param updated_flags: Dictionary mapping feature names to necessities
 
2686
            A necessity can be None to indicate the feature should be removed
 
2687
        """
 
2688
        self._format._update_feature_flags(updated_flags)
 
2689
        self.control_transport.put_bytes('format', self._format.as_string())
 
2690
 
2658
2691
 
2659
2692
class FullHistoryBzrBranch(BzrBranch):
2660
2693
    """Bzr branch which contains the full revision history."""
2673
2706
        self._set_revision_history(history)
2674
2707
 
2675
2708
    def _read_last_revision_info(self):
2676
 
        rh = self.revision_history()
 
2709
        rh = self._revision_history()
2677
2710
        revno = len(rh)
2678
2711
        if revno:
2679
2712
            return (revno, rh[-1])
2680
2713
        else:
2681
2714
            return (0, _mod_revision.NULL_REVISION)
2682
2715
 
2683
 
    @deprecated_method(deprecated_in((2, 4, 0)))
2684
 
    @needs_write_lock
2685
 
    def set_revision_history(self, rev_history):
2686
 
        """See Branch.set_revision_history."""
2687
 
        self._set_revision_history(rev_history)
2688
 
 
2689
2716
    def _set_revision_history(self, rev_history):
2690
2717
        if 'evil' in debug.debug_flags:
2691
2718
            mutter_callsite(3, "set_revision_history scales with history.")
2704
2731
        self._write_revision_history(rev_history)
2705
2732
        self._clear_cached_state()
2706
2733
        self._cache_revision_history(rev_history)
2707
 
        for hook in Branch.hooks['set_rh']:
2708
 
            hook(self, rev_history)
2709
2734
        if Branch.hooks['post_change_branch_tip']:
2710
2735
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2711
2736
 
2733
2758
        if revision_id == _mod_revision.NULL_REVISION:
2734
2759
            new_history = []
2735
2760
        else:
2736
 
            new_history = self.revision_history()
 
2761
            new_history = self._revision_history()
2737
2762
        if revision_id is not None and new_history != []:
2738
2763
            try:
2739
2764
                new_history = new_history[:new_history.index(revision_id) + 1]
2767
2792
class BzrBranch8(BzrBranch):
2768
2793
    """A branch that stores tree-reference locations."""
2769
2794
 
2770
 
    def _open_hook(self):
 
2795
    def _open_hook(self, possible_transports=None):
2771
2796
        if self._ignore_fallbacks:
2772
2797
            return
 
2798
        if possible_transports is None:
 
2799
            possible_transports = [self.bzrdir.root_transport]
2773
2800
        try:
2774
2801
            url = self.get_stacked_on_url()
2775
2802
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2783
2810
                    raise AssertionError(
2784
2811
                        "'transform_fallback_location' hook %s returned "
2785
2812
                        "None, not a URL." % hook_name)
2786
 
            self._activate_fallback_location(url)
 
2813
            self._activate_fallback_location(url,
 
2814
                possible_transports=possible_transports)
2787
2815
 
2788
2816
    def __init__(self, *args, **kwargs):
2789
2817
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2907
2935
        """See Branch.set_push_location."""
2908
2936
        self._master_branch_cache = None
2909
2937
        result = None
2910
 
        config = self.get_config()
 
2938
        conf = self.get_config_stack()
2911
2939
        if location is None:
2912
 
            if config.get_user_option('bound') != 'True':
 
2940
            if not conf.get('bound'):
2913
2941
                return False
2914
2942
            else:
2915
 
                config.set_user_option('bound', 'False', warn_masked=True)
 
2943
                conf.set('bound', 'False')
2916
2944
                return True
2917
2945
        else:
2918
2946
            self._set_config_location('bound_location', location,
2919
 
                                      config=config)
2920
 
            config.set_user_option('bound', 'True', warn_masked=True)
 
2947
                                      config=conf)
 
2948
            conf.set('bound', 'True')
2921
2949
        return True
2922
2950
 
2923
2951
    def _get_bound_location(self, bound):
2924
2952
        """Return the bound location in the config file.
2925
2953
 
2926
2954
        Return None if the bound parameter does not match"""
2927
 
        config = self.get_config()
2928
 
        config_bound = (config.get_user_option('bound') == 'True')
2929
 
        if config_bound != bound:
 
2955
        conf = self.get_config_stack()
 
2956
        if conf.get('bound') != bound:
2930
2957
            return None
2931
 
        return self._get_config_location('bound_location', config=config)
 
2958
        return self._get_config_location('bound_location', config=conf)
2932
2959
 
2933
2960
    def get_bound_location(self):
2934
 
        """See Branch.set_push_location."""
 
2961
        """See Branch.get_bound_location."""
2935
2962
        return self._get_bound_location(True)
2936
2963
 
2937
2964
    def get_old_bound_location(self):
2944
2971
        ## self._check_stackable_repo()
2945
2972
        # stacked_on_location is only ever defined in branch.conf, so don't
2946
2973
        # waste effort reading the whole stack of config files.
2947
 
        config = self.get_config()._get_branch_data_config()
 
2974
        conf = _mod_config.BranchOnlyStack(self)
2948
2975
        stacked_url = self._get_config_location('stacked_on_location',
2949
 
            config=config)
 
2976
                                                config=conf)
2950
2977
        if stacked_url is None:
2951
2978
            raise errors.NotStacked(self)
2952
 
        return stacked_url
2953
 
 
2954
 
    def _get_append_revisions_only(self):
2955
 
        return self.get_config(
2956
 
            ).get_user_option_as_bool('append_revisions_only')
 
2979
        return stacked_url.encode('utf-8')
2957
2980
 
2958
2981
    @needs_read_lock
2959
2982
    def get_rev_id(self, revno, history=None):
2989
3012
            except errors.RevisionNotPresent, e:
2990
3013
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
2991
3014
            index = len(self._partial_revision_history_cache) - 1
 
3015
            if index < 0:
 
3016
                raise errors.NoSuchRevision(self, revision_id)
2992
3017
            if self._partial_revision_history_cache[index] != revision_id:
2993
3018
                raise errors.NoSuchRevision(self, revision_id)
2994
3019
        return self.revno() - index
3046
3071
    :ivar local_branch: target branch if there is a Master, else None
3047
3072
    :ivar target_branch: Target/destination branch object. (write locked)
3048
3073
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
 
3074
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3049
3075
    """
3050
3076
 
3051
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3052
 
    def __int__(self):
3053
 
        """Return the relative change in revno.
3054
 
 
3055
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3056
 
        """
3057
 
        return self.new_revno - self.old_revno
3058
 
 
3059
3077
    def report(self, to_file):
 
3078
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
3079
        tag_updates = getattr(self, "tag_updates", None)
3060
3080
        if not is_quiet():
3061
 
            if self.old_revid == self.new_revid:
3062
 
                to_file.write('No revisions to pull.\n')
3063
 
            else:
 
3081
            if self.old_revid != self.new_revid:
3064
3082
                to_file.write('Now on revision %d.\n' % self.new_revno)
 
3083
            if tag_updates:
 
3084
                to_file.write('%d tag(s) updated.\n' % len(tag_updates))
 
3085
            if self.old_revid == self.new_revid and not tag_updates:
 
3086
                if not tag_conflicts:
 
3087
                    to_file.write('No revisions or tags to pull.\n')
 
3088
                else:
 
3089
                    to_file.write('No revisions to pull.\n')
3065
3090
        self._show_tag_conficts(to_file)
3066
3091
 
3067
3092
 
3084
3109
        target, otherwise it will be None.
3085
3110
    """
3086
3111
 
3087
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3088
 
    def __int__(self):
3089
 
        """Return the relative change in revno.
3090
 
 
3091
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3092
 
        """
3093
 
        return self.new_revno - self.old_revno
3094
 
 
3095
3112
    def report(self, to_file):
3096
 
        """Write a human-readable description of the result."""
3097
 
        if self.old_revid == self.new_revid:
3098
 
            note('No new revisions to push.')
3099
 
        else:
3100
 
            note('Pushed up to revision %d.' % self.new_revno)
 
3113
        # TODO: This function gets passed a to_file, but then
 
3114
        # ignores it and calls note() instead. This is also
 
3115
        # inconsistent with PullResult(), which writes to stdout.
 
3116
        # -- JRV20110901, bug #838853
 
3117
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
3118
        tag_updates = getattr(self, "tag_updates", None)
 
3119
        if not is_quiet():
 
3120
            if self.old_revid != self.new_revid:
 
3121
                note(gettext('Pushed up to revision %d.') % self.new_revno)
 
3122
            if tag_updates:
 
3123
                note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
 
3124
            if self.old_revid == self.new_revid and not tag_updates:
 
3125
                if not tag_conflicts:
 
3126
                    note(gettext('No new revisions or tags to push.'))
 
3127
                else:
 
3128
                    note(gettext('No new revisions to push.'))
3101
3129
        self._show_tag_conficts(to_file)
3102
3130
 
3103
3131
 
3117
3145
        :param verbose: Requests more detailed display of what was checked,
3118
3146
            if any.
3119
3147
        """
3120
 
        note('checked branch %s format %s', self.branch.user_url,
3121
 
            self.branch._format)
 
3148
        note(gettext('checked branch {0} format {1}').format(
 
3149
                                self.branch.user_url, self.branch._format))
3122
3150
        for error in self.errors:
3123
 
            note('found error:%s', error)
 
3151
            note(gettext('found error:%s'), error)
3124
3152
 
3125
3153
 
3126
3154
class Converter5to6(object):
3133
3161
 
3134
3162
        # Copy source data into target
3135
3163
        new_branch._write_last_revision_info(*branch.last_revision_info())
3136
 
        new_branch.set_parent(branch.get_parent())
3137
 
        new_branch.set_bound_location(branch.get_bound_location())
3138
 
        new_branch.set_push_location(branch.get_push_location())
 
3164
        new_branch.lock_write()
 
3165
        try:
 
3166
            new_branch.set_parent(branch.get_parent())
 
3167
            new_branch.set_bound_location(branch.get_bound_location())
 
3168
            new_branch.set_push_location(branch.get_push_location())
 
3169
        finally:
 
3170
            new_branch.unlock()
3139
3171
 
3140
3172
        # New branch has no tags by default
3141
3173
        new_branch.tags._set_tag_dict({})
3142
3174
 
3143
3175
        # Copying done; now update target format
3144
3176
        new_branch._transport.put_bytes('format',
3145
 
            format.get_format_string(),
 
3177
            format.as_string(),
3146
3178
            mode=new_branch.bzrdir._get_file_mode())
3147
3179
 
3148
3180
        # Clean up old files
3149
3181
        new_branch._transport.delete('revision-history')
 
3182
        branch.lock_write()
3150
3183
        try:
3151
 
            branch.set_parent(None)
3152
 
        except errors.NoSuchFile:
3153
 
            pass
3154
 
        branch.set_bound_location(None)
 
3184
            try:
 
3185
                branch.set_parent(None)
 
3186
            except errors.NoSuchFile:
 
3187
                pass
 
3188
            branch.set_bound_location(None)
 
3189
        finally:
 
3190
            branch.unlock()
3155
3191
 
3156
3192
 
3157
3193
class Converter6to7(object):
3161
3197
        format = BzrBranchFormat7()
3162
3198
        branch._set_config_location('stacked_on_location', '')
3163
3199
        # update target format
3164
 
        branch._transport.put_bytes('format', format.get_format_string())
 
3200
        branch._transport.put_bytes('format', format.as_string())
3165
3201
 
3166
3202
 
3167
3203
class Converter7to8(object):
3168
 
    """Perform an in-place upgrade of format 6 to format 7"""
 
3204
    """Perform an in-place upgrade of format 7 to format 8"""
3169
3205
 
3170
3206
    def convert(self, branch):
3171
3207
        format = BzrBranchFormat8()
3172
3208
        branch._transport.put_bytes('references', '')
3173
3209
        # update target format
3174
 
        branch._transport.put_bytes('format', format.get_format_string())
 
3210
        branch._transport.put_bytes('format', format.as_string())
3175
3211
 
3176
3212
 
3177
3213
class InterBranch(InterObject):
3344
3380
        if local and not bound_location:
3345
3381
            raise errors.LocalRequiresBoundBranch()
3346
3382
        master_branch = None
3347
 
        source_is_master = (self.source.user_url == bound_location)
 
3383
        source_is_master = False
 
3384
        if bound_location:
 
3385
            # bound_location comes from a config file, some care has to be
 
3386
            # taken to relate it to source.user_url
 
3387
            normalized = urlutils.normalize_url(bound_location)
 
3388
            try:
 
3389
                relpath = self.source.user_transport.relpath(normalized)
 
3390
                source_is_master = (relpath == '')
 
3391
            except (errors.PathNotChild, errors.InvalidURL):
 
3392
                source_is_master = False
3348
3393
        if not local and bound_location and not source_is_master:
3349
3394
            # not pulling from master, so we need to update master.
3350
3395
            master_branch = self.target.get_master_branch(possible_transports)
3402
3447
            self._update_revisions(stop_revision, overwrite=overwrite,
3403
3448
                    graph=graph)
3404
3449
        if self.source._push_should_merge_tags():
3405
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3406
 
                overwrite)
 
3450
            result.tag_updates, result.tag_conflicts = (
 
3451
                self.source.tags.merge_to(self.target.tags, overwrite))
3407
3452
        result.new_revno, result.new_revid = self.target.last_revision_info()
3408
3453
        return result
3409
3454
 
3492
3537
            # TODO: The old revid should be specified when merging tags, 
3493
3538
            # so a tags implementation that versions tags can only 
3494
3539
            # pull in the most recent changes. -- JRV20090506
3495
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3496
 
                overwrite, ignore_master=not merge_tags_to_master)
 
3540
            result.tag_updates, result.tag_conflicts = (
 
3541
                self.source.tags.merge_to(self.target.tags, overwrite,
 
3542
                    ignore_master=not merge_tags_to_master))
3497
3543
            result.new_revno, result.new_revid = self.target.last_revision_info()
3498
3544
            if _hook_master:
3499
3545
                result.master_branch = _hook_master