~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Aaron Bentley
  • Date: 2012-07-19 16:57:16 UTC
  • mto: This revision was merged to the branch mainline in revision 6540.
  • Revision ID: aaron@aaronbentley.com-20120719165716-b4iupzkb17b9l9wx
Avoid branch write lock to preserve VFS call count.

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
22
 
21
24
lazy_import(globals(), """
22
25
import itertools
23
26
from bzrlib import (
24
 
        bzrdir,
25
 
        cache_utf8,
26
 
        cleanup,
27
 
        config as _mod_config,
28
 
        debug,
29
 
        errors,
30
 
        fetch,
31
 
        graph as _mod_graph,
32
 
        lockdir,
33
 
        lockable_files,
34
 
        remote,
35
 
        repository,
36
 
        revision as _mod_revision,
37
 
        rio,
38
 
        tag as _mod_tag,
39
 
        transport,
40
 
        ui,
41
 
        urlutils,
42
 
        )
 
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
    shelf,
 
43
    tag as _mod_tag,
 
44
    transport,
 
45
    ui,
 
46
    urlutils,
 
47
    vf_search,
 
48
    )
43
49
from bzrlib.i18n import gettext, ngettext
44
50
""")
45
51
 
 
52
# Explicitly import bzrlib.bzrdir so that the BzrProber
 
53
# is guaranteed to be registered.
 
54
import bzrlib.bzrdir
 
55
 
46
56
from bzrlib import (
 
57
    bzrdir,
47
58
    controldir,
48
59
    )
49
60
from bzrlib.decorators import (
84
95
    def user_transport(self):
85
96
        return self.bzrdir.user_transport
86
97
 
87
 
    def __init__(self, *ignored, **ignored_too):
 
98
    def __init__(self, possible_transports=None):
88
99
        self.tags = self._format.make_tags(self)
89
100
        self._revision_history_cache = None
90
101
        self._revision_id_to_revno_cache = None
94
105
        self._last_revision_info_cache = None
95
106
        self._master_branch_cache = None
96
107
        self._merge_sorted_revisions_cache = None
97
 
        self._open_hook()
 
108
        self._open_hook(possible_transports)
98
109
        hooks = Branch.hooks['open']
99
110
        for hook in hooks:
100
111
            hook(self)
101
112
 
102
 
    def _open_hook(self):
 
113
    def _open_hook(self, possible_transports):
103
114
        """Called by init to allow simpler extension of the base class."""
104
115
 
105
 
    def _activate_fallback_location(self, url):
 
116
    def _activate_fallback_location(self, url, possible_transports):
106
117
        """Activate the branch/repository from url as a fallback repository."""
107
118
        for existing_fallback_repo in self.repository._fallback_repositories:
108
119
            if existing_fallback_repo.user_url == url:
109
120
                # This fallback is already configured.  This probably only
110
 
                # happens because BzrDir.sprout is a horrible mess.  To avoid
 
121
                # happens because ControlDir.sprout is a horrible mess.  To avoid
111
122
                # confusing _unstack we don't add this a second time.
112
123
                mutter('duplicate activation of fallback %r on %r', url, self)
113
124
                return
114
 
        repo = self._get_fallback_repository(url)
 
125
        repo = self._get_fallback_repository(url, possible_transports)
115
126
        if repo.has_same_location(self.repository):
116
127
            raise errors.UnstackableLocationError(self.user_url, url)
117
128
        self.repository.add_fallback_repository(repo)
171
182
        For instance, if the branch is at URL/.bzr/branch,
172
183
        Branch.open(URL) -> a Branch instance.
173
184
        """
174
 
        control = bzrdir.BzrDir.open(base, _unsupported,
175
 
                                     possible_transports=possible_transports)
176
 
        return control.open_branch(unsupported=_unsupported)
 
185
        control = controldir.ControlDir.open(base,
 
186
            possible_transports=possible_transports, _unsupported=_unsupported)
 
187
        return control.open_branch(unsupported=_unsupported,
 
188
            possible_transports=possible_transports)
177
189
 
178
190
    @staticmethod
179
 
    def open_from_transport(transport, name=None, _unsupported=False):
 
191
    def open_from_transport(transport, name=None, _unsupported=False,
 
192
            possible_transports=None):
180
193
        """Open the branch rooted at transport"""
181
 
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
182
 
        return control.open_branch(name=name, unsupported=_unsupported)
 
194
        control = controldir.ControlDir.open_from_transport(transport, _unsupported)
 
195
        return control.open_branch(name=name, unsupported=_unsupported,
 
196
            possible_transports=possible_transports)
183
197
 
184
198
    @staticmethod
185
199
    def open_containing(url, possible_transports=None):
193
207
        format, UnknownFormatError or UnsupportedFormatError are raised.
194
208
        If there is one, it is returned, along with the unused portion of url.
195
209
        """
196
 
        control, relpath = bzrdir.BzrDir.open_containing(url,
 
210
        control, relpath = controldir.ControlDir.open_containing(url,
197
211
                                                         possible_transports)
198
 
        return control.open_branch(), relpath
 
212
        branch = control.open_branch(possible_transports=possible_transports)
 
213
        return (branch, relpath)
199
214
 
200
215
    def _push_should_merge_tags(self):
201
216
        """Should _basic_push merge this branch's tags into the target?
237
252
        """
238
253
        raise NotImplementedError(self._get_config)
239
254
 
240
 
    def _get_fallback_repository(self, url):
 
255
    def store_uncommitted(self, creator, message=None):
 
256
        """Store uncommitted changes from a ShelfCreator.
 
257
 
 
258
        :param creator: The ShelfCreator containing uncommitted changes, or
 
259
            None to delete any stored changes.
 
260
        :param message: The message to associate with the changes.
 
261
        :raises: ChangesAlreadyStored if the branch already has changes.
 
262
        """
 
263
        raise NotImplementedError(self.store_uncommitted)
 
264
 
 
265
    def get_unshelver(self, tree):
 
266
        """Return a shelf.Unshelver for this branch and tree.
 
267
 
 
268
        :param tree: The tree to use to construct the Unshelver.
 
269
        :return: an Unshelver or None if no changes are stored.
 
270
        """
 
271
        raise NotImplementedError(self.get_unshelver)
 
272
 
 
273
    def _get_fallback_repository(self, url, possible_transports):
241
274
        """Get the repository we fallback to at url."""
242
275
        url = urlutils.join(self.base, url)
243
 
        a_branch = Branch.open(url,
244
 
            possible_transports=[self.bzrdir.root_transport])
 
276
        a_branch = Branch.open(url, possible_transports=possible_transports)
245
277
        return a_branch.repository
246
278
 
247
279
    @needs_read_lock
657
689
        """
658
690
        if not self._format.supports_set_append_revisions_only():
659
691
            return False
660
 
        return self.get_config(
661
 
            ).get_user_option_as_bool('append_revisions_only')
 
692
        return self.get_config_stack().get('append_revisions_only')
662
693
 
663
694
    def set_append_revisions_only(self, enabled):
664
695
        if not self._format.supports_set_append_revisions_only():
665
696
            raise errors.UpgradeRequired(self.user_url)
666
 
        if enabled:
667
 
            value = 'True'
668
 
        else:
669
 
            value = 'False'
670
 
        self.get_config().set_user_option('append_revisions_only', value,
671
 
            warn_masked=True)
 
697
        self.get_config_stack().set('append_revisions_only', enabled)
672
698
 
673
699
    def set_reference_info(self, file_id, tree_path, branch_location):
674
700
        """Set the branch location to use for a tree reference."""
703
729
        """
704
730
        raise errors.UpgradeRequired(self.user_url)
705
731
 
706
 
    def get_commit_builder(self, parents, config=None, timestamp=None,
 
732
    def get_commit_builder(self, parents, config_stack=None, timestamp=None,
707
733
                           timezone=None, committer=None, revprops=None,
708
734
                           revision_id=None, lossy=False):
709
735
        """Obtain a CommitBuilder for this branch.
719
745
            represented, when pushing to a foreign VCS 
720
746
        """
721
747
 
722
 
        if config is None:
723
 
            config = self.get_config()
 
748
        if config_stack is None:
 
749
            config_stack = self.get_config_stack()
724
750
 
725
 
        return self.repository.get_commit_builder(self, parents, config,
 
751
        return self.repository.get_commit_builder(self, parents, config_stack,
726
752
            timestamp, timezone, committer, revprops, revision_id,
727
753
            lossy)
728
754
 
733
759
        """
734
760
        return None
735
761
 
 
762
    @deprecated_method(deprecated_in((2, 5, 0)))
736
763
    def get_revision_delta(self, revno):
737
764
        """Return the delta for one revision.
738
765
 
739
766
        The delta is relative to its mainline predecessor, or the
740
767
        empty tree for revision 1.
741
768
        """
742
 
        rh = self.revision_history()
743
 
        if not (1 <= revno <= len(rh)):
 
769
        try:
 
770
            revid = self.get_rev_id(revno)
 
771
        except errors.NoSuchRevision:
744
772
            raise errors.InvalidRevisionNumber(revno)
745
 
        return self.repository.get_revision_delta(rh[revno-1])
 
773
        return self.repository.get_revision_delta(revid)
746
774
 
747
775
    def get_stacked_on_url(self):
748
776
        """Get the URL this branch is stacked against.
757
785
        """Print `file` to stdout."""
758
786
        raise NotImplementedError(self.print_file)
759
787
 
760
 
    @deprecated_method(deprecated_in((2, 4, 0)))
761
 
    def set_revision_history(self, rev_history):
762
 
        """See Branch.set_revision_history."""
763
 
        self._set_revision_history(rev_history)
764
 
 
765
 
    @needs_write_lock
766
 
    def _set_revision_history(self, rev_history):
767
 
        if len(rev_history) == 0:
768
 
            revid = _mod_revision.NULL_REVISION
769
 
        else:
770
 
            revid = rev_history[-1]
771
 
        if rev_history != self._lefthand_history(revid):
772
 
            raise errors.NotLefthandHistory(rev_history)
773
 
        self.set_last_revision_info(len(rev_history), revid)
774
 
        self._cache_revision_history(rev_history)
775
 
        for hook in Branch.hooks['set_rh']:
776
 
            hook(self, rev_history)
777
 
 
778
788
    @needs_write_lock
779
789
    def set_last_revision_info(self, revno, revision_id):
780
790
        """Set the last revision of this branch.
847
857
                return
848
858
            self._unstack()
849
859
        else:
850
 
            self._activate_fallback_location(url)
 
860
            self._activate_fallback_location(url,
 
861
                possible_transports=[self.bzrdir.root_transport])
851
862
        # write this out after the repository is stacked to avoid setting a
852
863
        # stacked config that doesn't work.
853
864
        self._set_config_location('stacked_on_location', url)
879
890
            # stream from one of them to the other.  This does mean doing
880
891
            # separate SSH connection setup, but unstacking is not a
881
892
            # common operation so it's tolerable.
882
 
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
 
893
            new_bzrdir = controldir.ControlDir.open(
 
894
                self.bzrdir.root_transport.base)
883
895
            new_repository = new_bzrdir.find_repository()
884
896
            if new_repository._fallback_repositories:
885
897
                raise AssertionError("didn't expect %r to have "
928
940
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
929
941
                except errors.TagsNotSupported:
930
942
                    tags_to_fetch = set()
931
 
                fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
 
943
                fetch_spec = vf_search.NotInOtherForRevs(self.repository,
932
944
                    old_repository, required_ids=[self.last_revision()],
933
945
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
934
946
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
975
987
        This means the next call to revision_history will need to call
976
988
        _gen_revision_history.
977
989
 
978
 
        This API is semi-public; it only for use by subclasses, all other code
979
 
        should consider it to be private.
 
990
        This API is semi-public; it is only for use by subclasses, all other
 
991
        code should consider it to be private.
980
992
        """
981
993
        self._revision_history_cache = None
982
994
        self._revision_id_to_revno_cache = None
1002
1014
        """
1003
1015
        raise NotImplementedError(self._gen_revision_history)
1004
1016
 
1005
 
    @needs_read_lock
1006
 
    def revision_history(self):
1007
 
        """Return sequence of revision ids on this branch.
1008
 
 
1009
 
        This method will cache the revision history for as long as it is safe to
1010
 
        do so.
1011
 
        """
 
1017
    def _revision_history(self):
1012
1018
        if 'evil' in debug.debug_flags:
1013
1019
            mutter_callsite(3, "revision_history scales with history.")
1014
1020
        if self._revision_history_cache is not None:
1047
1053
    def _read_last_revision_info(self):
1048
1054
        raise NotImplementedError(self._read_last_revision_info)
1049
1055
 
1050
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1051
 
    def import_last_revision_info(self, source_repo, revno, revid):
1052
 
        """Set the last revision info, importing from another repo if necessary.
1053
 
 
1054
 
        :param source_repo: Source repository to optionally fetch from
1055
 
        :param revno: Revision number of the new tip
1056
 
        :param revid: Revision id of the new tip
1057
 
        """
1058
 
        if not self.repository.has_same_location(source_repo):
1059
 
            self.repository.fetch(source_repo, revision_id=revid)
1060
 
        self.set_last_revision_info(revno, revid)
1061
 
 
1062
1056
    def import_last_revision_info_and_tags(self, source, revno, revid,
1063
1057
                                           lossy=False):
1064
1058
        """Set the last revision info, importing from another repo if necessary.
1084
1078
        """Given a revision id, return its revno"""
1085
1079
        if _mod_revision.is_null(revision_id):
1086
1080
            return 0
1087
 
        history = self.revision_history()
 
1081
        history = self._revision_history()
1088
1082
        try:
1089
1083
            return history.index(revision_id) + 1
1090
1084
        except ValueError:
1155
1149
    def _set_config_location(self, name, url, config=None,
1156
1150
                             make_relative=False):
1157
1151
        if config is None:
1158
 
            config = self.get_config()
 
1152
            config = self.get_config_stack()
1159
1153
        if url is None:
1160
1154
            url = ''
1161
1155
        elif make_relative:
1162
1156
            url = urlutils.relative_url(self.base, url)
1163
 
        config.set_user_option(name, url, warn_masked=True)
 
1157
        config.set(name, url)
1164
1158
 
1165
1159
    def _get_config_location(self, name, config=None):
1166
1160
        if config is None:
1167
 
            config = self.get_config()
1168
 
        location = config.get_user_option(name)
 
1161
            config = self.get_config_stack()
 
1162
        location = config.get(name)
1169
1163
        if location == '':
1170
1164
            location = None
1171
1165
        return location
1172
1166
 
1173
1167
    def get_child_submit_format(self):
1174
1168
        """Return the preferred format of submissions to this branch."""
1175
 
        return self.get_config().get_user_option("child_submit_format")
 
1169
        return self.get_config_stack().get('child_submit_format')
1176
1170
 
1177
1171
    def get_submit_branch(self):
1178
1172
        """Return the submit location of the branch.
1181
1175
        pattern is that the user can override it by specifying a
1182
1176
        location.
1183
1177
        """
1184
 
        return self.get_config().get_user_option('submit_branch')
 
1178
        return self.get_config_stack().get('submit_branch')
1185
1179
 
1186
1180
    def set_submit_branch(self, location):
1187
1181
        """Return the submit location of the branch.
1190
1184
        pattern is that the user can override it by specifying a
1191
1185
        location.
1192
1186
        """
1193
 
        self.get_config().set_user_option('submit_branch', location,
1194
 
            warn_masked=True)
 
1187
        self.get_config_stack().set('submit_branch', location)
1195
1188
 
1196
1189
    def get_public_branch(self):
1197
1190
        """Return the public location of the branch.
1210
1203
        self._set_config_location('public_branch', location)
1211
1204
 
1212
1205
    def get_push_location(self):
1213
 
        """Return the None or the location to push this branch to."""
1214
 
        push_loc = self.get_config().get_user_option('push_location')
1215
 
        return push_loc
 
1206
        """Return None or the location to push this branch to."""
 
1207
        return self.get_config_stack().get('push_location')
1216
1208
 
1217
1209
    def set_push_location(self, location):
1218
1210
        """Set a new push location for this branch."""
1387
1379
        # TODO: We should probably also check that self.revision_history
1388
1380
        # matches the repository for older branch formats.
1389
1381
        # If looking for the code that cross-checks repository parents against
1390
 
        # the iter_reverse_revision_history output, that is now a repository
 
1382
        # the Graph.iter_lefthand_ancestry output, that is now a repository
1391
1383
        # specific check.
1392
1384
        return result
1393
1385
 
1444
1436
        t = transport.get_transport(to_location)
1445
1437
        t.ensure_base()
1446
1438
        format = self._get_checkout_format(lightweight=lightweight)
 
1439
        try:
 
1440
            checkout = format.initialize_on_transport(t)
 
1441
        except errors.AlreadyControlDirError:
 
1442
            # It's fine if the control directory already exists,
 
1443
            # as long as there is no existing branch and working tree.
 
1444
            checkout = controldir.ControlDir.open_from_transport(t)
 
1445
            try:
 
1446
                checkout.open_branch()
 
1447
            except errors.NotBranchError:
 
1448
                pass
 
1449
            else:
 
1450
                raise errors.AlreadyControlDirError(t.base)
 
1451
            if checkout.control_transport.base == self.bzrdir.control_transport.base:
 
1452
                # When checking out to the same control directory,
 
1453
                # always create a lightweight checkout
 
1454
                lightweight = True
 
1455
 
1447
1456
        if lightweight:
1448
 
            checkout = format.initialize_on_transport(t)
1449
 
            from_branch = BranchReferenceFormat().initialize(checkout, 
1450
 
                target_branch=self)
 
1457
            from_branch = checkout.set_branch_reference(target_branch=self)
1451
1458
        else:
1452
 
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1453
 
                to_location, force_new_tree=False, format=format)
1454
 
            checkout = checkout_branch.bzrdir
 
1459
            policy = checkout.determine_repository_policy()
 
1460
            repo = policy.acquire_repository()[0]
 
1461
            checkout_branch = checkout.create_branch()
1455
1462
            checkout_branch.bind(self)
1456
1463
            # pull up to the specified revision_id to set the initial
1457
1464
            # branch tip correctly, and seed it with history.
1458
1465
            checkout_branch.pull(self, stop_revision=revision_id)
1459
 
            from_branch=None
 
1466
            from_branch = None
1460
1467
        tree = checkout.create_workingtree(revision_id,
1461
1468
                                           from_branch=from_branch,
1462
1469
                                           accelerator_tree=accelerator_tree,
1551
1558
            heads that must be fetched if present, but no error is necessary if
1552
1559
            they are not present.
1553
1560
        """
1554
 
        # For bzr native formats must_fetch is just the tip, and if_present_fetch
1555
 
        # are the tags.
 
1561
        # For bzr native formats must_fetch is just the tip, and
 
1562
        # if_present_fetch are the tags.
1556
1563
        must_fetch = set([self.last_revision()])
1557
1564
        if_present_fetch = set()
1558
 
        c = self.get_config()
1559
 
        include_tags = c.get_user_option_as_bool('branch.fetch_tags',
1560
 
                                                 default=False)
1561
 
        if include_tags:
 
1565
        if self.get_config_stack().get('branch.fetch_tags'):
1562
1566
            try:
1563
1567
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
1564
1568
            except errors.TagsNotSupported:
1573
1577
 
1574
1578
    Formats provide three things:
1575
1579
     * An initialization routine,
1576
 
     * a format string,
 
1580
     * a format description
1577
1581
     * an open routine.
1578
1582
 
1579
1583
    Formats are placed in an dict by their format string for reference
1592
1596
    def __ne__(self, other):
1593
1597
        return not (self == other)
1594
1598
 
1595
 
    @classmethod
1596
 
    def find_format(klass, a_bzrdir, name=None):
1597
 
        """Return the format for the branch object in a_bzrdir."""
1598
 
        try:
1599
 
            transport = a_bzrdir.get_branch_transport(None, name=name)
1600
 
            format_string = transport.get_bytes("format")
1601
 
            return format_registry.get(format_string)
1602
 
        except errors.NoSuchFile:
1603
 
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1604
 
        except KeyError:
1605
 
            raise errors.UnknownFormatError(format=format_string, kind='branch')
1606
 
 
1607
 
    @classmethod
1608
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1609
 
    def get_default_format(klass):
1610
 
        """Return the current default format."""
1611
 
        return format_registry.get_default()
1612
 
 
1613
 
    @classmethod
1614
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1615
 
    def get_formats(klass):
1616
 
        """Get all the known formats.
1617
 
 
1618
 
        Warning: This triggers a load of all lazy registered formats: do not
1619
 
        use except when that is desireed.
1620
 
        """
1621
 
        return format_registry._get_all()
1622
 
 
1623
 
    def get_reference(self, a_bzrdir, name=None):
1624
 
        """Get the target reference of the branch in a_bzrdir.
 
1599
    def get_reference(self, controldir, name=None):
 
1600
        """Get the target reference of the branch in controldir.
1625
1601
 
1626
1602
        format probing must have been completed before calling
1627
1603
        this method - it is assumed that the format of the branch
1628
 
        in a_bzrdir is correct.
 
1604
        in controldir is correct.
1629
1605
 
1630
 
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1606
        :param controldir: The controldir to get the branch data from.
1631
1607
        :param name: Name of the colocated branch to fetch
1632
1608
        :return: None if the branch is not a reference branch.
1633
1609
        """
1634
1610
        return None
1635
1611
 
1636
1612
    @classmethod
1637
 
    def set_reference(self, a_bzrdir, name, to_branch):
1638
 
        """Set the target reference of the branch in a_bzrdir.
 
1613
    def set_reference(self, controldir, name, to_branch):
 
1614
        """Set the target reference of the branch in controldir.
1639
1615
 
1640
1616
        format probing must have been completed before calling
1641
1617
        this method - it is assumed that the format of the branch
1642
 
        in a_bzrdir is correct.
 
1618
        in controldir is correct.
1643
1619
 
1644
 
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1620
        :param controldir: The controldir to set the branch reference for.
1645
1621
        :param name: Name of colocated branch to set, None for default
1646
1622
        :param to_branch: branch that the checkout is to reference
1647
1623
        """
1648
1624
        raise NotImplementedError(self.set_reference)
1649
1625
 
1650
 
    def get_format_string(self):
1651
 
        """Return the ASCII format string that identifies this format."""
1652
 
        raise NotImplementedError(self.get_format_string)
1653
 
 
1654
1626
    def get_format_description(self):
1655
1627
        """Return the short format description for this format."""
1656
1628
        raise NotImplementedError(self.get_format_description)
1657
1629
 
1658
 
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
 
1630
    def _run_post_branch_init_hooks(self, controldir, name, branch):
1659
1631
        hooks = Branch.hooks['post_branch_init']
1660
1632
        if not hooks:
1661
1633
            return
1662
 
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
 
1634
        params = BranchInitHookParams(self, controldir, name, branch)
1663
1635
        for hook in hooks:
1664
1636
            hook(params)
1665
1637
 
1666
 
    def initialize(self, a_bzrdir, name=None, repository=None,
 
1638
    def initialize(self, controldir, name=None, repository=None,
1667
1639
                   append_revisions_only=None):
1668
 
        """Create a branch of this format in a_bzrdir.
1669
 
        
 
1640
        """Create a branch of this format in controldir.
 
1641
 
1670
1642
        :param name: Name of the colocated branch to create.
1671
1643
        """
1672
1644
        raise NotImplementedError(self.initialize)
1704
1676
        """
1705
1677
        raise NotImplementedError(self.network_name)
1706
1678
 
1707
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
1708
 
            found_repository=None):
1709
 
        """Return the branch object for a_bzrdir
 
1679
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
 
1680
            found_repository=None, possible_transports=None):
 
1681
        """Return the branch object for controldir.
1710
1682
 
1711
 
        :param a_bzrdir: A BzrDir that contains a branch.
 
1683
        :param controldir: A ControlDir that contains a branch.
1712
1684
        :param name: Name of colocated branch to open
1713
1685
        :param _found: a private parameter, do not use it. It is used to
1714
1686
            indicate if format probing has already be done.
1717
1689
        """
1718
1690
        raise NotImplementedError(self.open)
1719
1691
 
1720
 
    @classmethod
1721
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1722
 
    def register_format(klass, format):
1723
 
        """Register a metadir format.
1724
 
 
1725
 
        See MetaDirBranchFormatFactory for the ability to register a format
1726
 
        without loading the code the format needs until it is actually used.
1727
 
        """
1728
 
        format_registry.register(format)
1729
 
 
1730
 
    @classmethod
1731
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1732
 
    def set_default_format(klass, format):
1733
 
        format_registry.set_default(format)
1734
 
 
1735
1692
    def supports_set_append_revisions_only(self):
1736
1693
        """True if this format supports set_append_revisions_only."""
1737
1694
        return False
1744
1701
        """True if this format supports leaving locks in place."""
1745
1702
        return False # by default
1746
1703
 
1747
 
    @classmethod
1748
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1749
 
    def unregister_format(klass, format):
1750
 
        format_registry.remove(format)
1751
 
 
1752
1704
    def __str__(self):
1753
1705
        return self.get_format_description().rstrip()
1754
1706
 
1783
1735
        """
1784
1736
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
1785
1737
        self._format_string = format_string
1786
 
        
 
1738
 
1787
1739
    def get_format_string(self):
1788
1740
        """See BranchFormat.get_format_string."""
1789
1741
        return self._format_string
1796
1748
class BranchHooks(Hooks):
1797
1749
    """A dictionary mapping hook name to a list of callables for branch hooks.
1798
1750
 
1799
 
    e.g. ['set_rh'] Is the list of items to be called when the
1800
 
    set_revision_history function is invoked.
 
1751
    e.g. ['post_push'] Is the list of items to be called when the
 
1752
    push function is invoked.
1801
1753
    """
1802
1754
 
1803
1755
    def __init__(self):
1807
1759
        notified.
1808
1760
        """
1809
1761
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1810
 
        self.add_hook('set_rh',
1811
 
            "Invoked whenever the revision history has been set via "
1812
 
            "set_revision_history. The api signature is (branch, "
1813
 
            "revision_history), and the branch will be write-locked. "
1814
 
            "The set_rh hook can be expensive for bzr to trigger, a better "
1815
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
1816
1762
        self.add_hook('open',
1817
1763
            "Called with the Branch object that has been opened after a "
1818
1764
            "branch is opened.", (1, 8))
1934
1880
    There are 4 fields that hooks may wish to access:
1935
1881
 
1936
1882
    :ivar format: the branch format
1937
 
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
 
1883
    :ivar bzrdir: the ControlDir where the branch will be/has been initialized
1938
1884
    :ivar name: name of colocated branch, if any (or None)
1939
1885
    :ivar branch: the branch created
1940
1886
 
1943
1889
    branch, which refer to the original branch.
1944
1890
    """
1945
1891
 
1946
 
    def __init__(self, format, a_bzrdir, name, branch):
 
1892
    def __init__(self, format, controldir, name, branch):
1947
1893
        """Create a group of BranchInitHook parameters.
1948
1894
 
1949
1895
        :param format: the branch format
1950
 
        :param a_bzrdir: the BzrDir where the branch will be/has been
 
1896
        :param controldir: the ControlDir where the branch will be/has been
1951
1897
            initialized
1952
1898
        :param name: name of colocated branch, if any (or None)
1953
1899
        :param branch: the branch created
1957
1903
        in branch, which refer to the original branch.
1958
1904
        """
1959
1905
        self.format = format
1960
 
        self.bzrdir = a_bzrdir
 
1906
        self.bzrdir = controldir
1961
1907
        self.name = name
1962
1908
        self.branch = branch
1963
1909
 
1973
1919
 
1974
1920
    There are 4 fields that hooks may wish to access:
1975
1921
 
1976
 
    :ivar control_dir: BzrDir of the checkout to change
 
1922
    :ivar control_dir: ControlDir of the checkout to change
1977
1923
    :ivar to_branch: branch that the checkout is to reference
1978
1924
    :ivar force: skip the check for local commits in a heavy checkout
1979
1925
    :ivar revision_id: revision ID to switch to (or None)
1982
1928
    def __init__(self, control_dir, to_branch, force, revision_id):
1983
1929
        """Create a group of SwitchHook parameters.
1984
1930
 
1985
 
        :param control_dir: BzrDir of the checkout to change
 
1931
        :param control_dir: ControlDir of the checkout to change
1986
1932
        :param to_branch: branch that the checkout is to reference
1987
1933
        :param force: skip the check for local commits in a heavy checkout
1988
1934
        :param revision_id: revision ID to switch to (or None)
2001
1947
            self.revision_id)
2002
1948
 
2003
1949
 
2004
 
class BranchFormatMetadir(BranchFormat):
2005
 
    """Common logic for meta-dir based branch formats."""
 
1950
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
 
1951
    """Base class for branch formats that live in meta directories.
 
1952
    """
 
1953
 
 
1954
    def __init__(self):
 
1955
        BranchFormat.__init__(self)
 
1956
        bzrdir.BzrFormat.__init__(self)
 
1957
 
 
1958
    @classmethod
 
1959
    def find_format(klass, controldir, name=None):
 
1960
        """Return the format for the branch object in controldir."""
 
1961
        try:
 
1962
            transport = controldir.get_branch_transport(None, name=name)
 
1963
        except errors.NoSuchFile:
 
1964
            raise errors.NotBranchError(path=name, bzrdir=controldir)
 
1965
        try:
 
1966
            format_string = transport.get_bytes("format")
 
1967
        except errors.NoSuchFile:
 
1968
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
 
1969
        return klass._find_format(format_registry, 'branch', format_string)
2006
1970
 
2007
1971
    def _branch_class(self):
2008
1972
        """What class to instantiate on open calls."""
2018
1982
 
2019
1983
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
2020
1984
                           repository=None):
2021
 
        """Initialize a branch in a bzrdir, with specified files
 
1985
        """Initialize a branch in a control dir, with specified files
2022
1986
 
2023
1987
        :param a_bzrdir: The bzrdir to initialize the branch in
2024
1988
        :param utf8_files: The files to create as a list of
2026
1990
        :param name: Name of colocated branch to create, if any
2027
1991
        :return: a branch in this format
2028
1992
        """
 
1993
        if name is None:
 
1994
            name = a_bzrdir._get_selected_branch()
2029
1995
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
2030
1996
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2031
1997
        control_files = lockable_files.LockableFiles(branch_transport,
2033
1999
        control_files.create_lock()
2034
2000
        control_files.lock_write()
2035
2001
        try:
2036
 
            utf8_files += [('format', self.get_format_string())]
 
2002
            utf8_files += [('format', self.as_string())]
2037
2003
            for (filename, content) in utf8_files:
2038
2004
                branch_transport.put_bytes(
2039
2005
                    filename, content,
2045
2011
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2046
2012
        return branch
2047
2013
 
2048
 
    def network_name(self):
2049
 
        """A simple byte string uniquely identifying this format for RPC calls.
2050
 
 
2051
 
        Metadir branch formats use their format string.
2052
 
        """
2053
 
        return self.get_format_string()
2054
 
 
2055
2014
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2056
 
            found_repository=None):
 
2015
            found_repository=None, possible_transports=None):
2057
2016
        """See BranchFormat.open()."""
 
2017
        if name is None:
 
2018
            name = a_bzrdir._get_selected_branch()
2058
2019
        if not _found:
2059
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2020
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2060
2021
            if format.__class__ != self.__class__:
2061
2022
                raise AssertionError("wrong format %r found for %r" %
2062
2023
                    (format, self))
2071
2032
                              name=name,
2072
2033
                              a_bzrdir=a_bzrdir,
2073
2034
                              _repository=found_repository,
2074
 
                              ignore_fallbacks=ignore_fallbacks)
 
2035
                              ignore_fallbacks=ignore_fallbacks,
 
2036
                              possible_transports=possible_transports)
2075
2037
        except errors.NoSuchFile:
2076
2038
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2077
2039
 
2078
 
    def __init__(self):
2079
 
        super(BranchFormatMetadir, self).__init__()
2080
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2081
 
        self._matchingbzrdir.set_branch_format(self)
 
2040
    @property
 
2041
    def _matchingbzrdir(self):
 
2042
        ret = bzrdir.BzrDirMetaFormat1()
 
2043
        ret.set_branch_format(self)
 
2044
        return ret
2082
2045
 
2083
2046
    def supports_tags(self):
2084
2047
        return True
2086
2049
    def supports_leaving_lock(self):
2087
2050
        return True
2088
2051
 
2089
 
 
2090
 
class BzrBranchFormat5(BranchFormatMetadir):
2091
 
    """Bzr branch format 5.
2092
 
 
2093
 
    This format has:
2094
 
     - a revision-history file.
2095
 
     - a format string
2096
 
     - a lock dir guarding the branch itself
2097
 
     - all of this stored in a branch/ subdirectory
2098
 
     - works with shared repositories.
2099
 
 
2100
 
    This format is new in bzr 0.8.
2101
 
    """
2102
 
 
2103
 
    def _branch_class(self):
2104
 
        return BzrBranch5
2105
 
 
2106
 
    def get_format_string(self):
2107
 
        """See BranchFormat.get_format_string()."""
2108
 
        return "Bazaar-NG branch format 5\n"
2109
 
 
2110
 
    def get_format_description(self):
2111
 
        """See BranchFormat.get_format_description()."""
2112
 
        return "Branch format 5"
2113
 
 
2114
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2115
 
                   append_revisions_only=None):
2116
 
        """Create a branch of this format in a_bzrdir."""
2117
 
        if append_revisions_only:
2118
 
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2119
 
        utf8_files = [('revision-history', ''),
2120
 
                      ('branch-name', ''),
2121
 
                      ]
2122
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2123
 
 
2124
 
    def supports_tags(self):
2125
 
        return False
 
2052
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
2053
            basedir=None):
 
2054
        BranchFormat.check_support_status(self,
 
2055
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
 
2056
            basedir=basedir)
 
2057
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
 
2058
            recommend_upgrade=recommend_upgrade, basedir=basedir)
2126
2059
 
2127
2060
 
2128
2061
class BzrBranchFormat6(BranchFormatMetadir):
2139
2072
    def _branch_class(self):
2140
2073
        return BzrBranch6
2141
2074
 
2142
 
    def get_format_string(self):
 
2075
    @classmethod
 
2076
    def get_format_string(cls):
2143
2077
        """See BranchFormat.get_format_string()."""
2144
2078
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
2145
2079
 
2171
2105
    def _branch_class(self):
2172
2106
        return BzrBranch8
2173
2107
 
2174
 
    def get_format_string(self):
 
2108
    @classmethod
 
2109
    def get_format_string(cls):
2175
2110
        """See BranchFormat.get_format_string()."""
2176
2111
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2177
2112
 
2225
2160
    def _branch_class(self):
2226
2161
        return BzrBranch7
2227
2162
 
2228
 
    def get_format_string(self):
 
2163
    @classmethod
 
2164
    def get_format_string(cls):
2229
2165
        """See BranchFormat.get_format_string()."""
2230
2166
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2231
2167
 
2246
2182
    supports_reference_locations = False
2247
2183
 
2248
2184
 
2249
 
class BranchReferenceFormat(BranchFormat):
 
2185
class BranchReferenceFormat(BranchFormatMetadir):
2250
2186
    """Bzr branch reference format.
2251
2187
 
2252
2188
    Branch references are used in implementing checkouts, they
2257
2193
     - a format string
2258
2194
    """
2259
2195
 
2260
 
    def get_format_string(self):
 
2196
    @classmethod
 
2197
    def get_format_string(cls):
2261
2198
        """See BranchFormat.get_format_string()."""
2262
2199
        return "Bazaar-NG Branch Reference Format 1\n"
2263
2200
 
2285
2222
        mutter('creating branch reference in %s', a_bzrdir.user_url)
2286
2223
        if a_bzrdir._format.fixed_components:
2287
2224
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
 
2225
        if name is None:
 
2226
            name = a_bzrdir._get_selected_branch()
2288
2227
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2289
2228
        branch_transport.put_bytes('location',
2290
 
            target_branch.bzrdir.user_url)
2291
 
        branch_transport.put_bytes('format', self.get_format_string())
2292
 
        branch = self.open(
2293
 
            a_bzrdir, name, _found=True,
 
2229
            target_branch.user_url)
 
2230
        branch_transport.put_bytes('format', self.as_string())
 
2231
        branch = self.open(a_bzrdir, name, _found=True,
2294
2232
            possible_transports=[target_branch.bzrdir.root_transport])
2295
2233
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2296
2234
        return branch
2297
2235
 
2298
 
    def __init__(self):
2299
 
        super(BranchReferenceFormat, self).__init__()
2300
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2301
 
        self._matchingbzrdir.set_branch_format(self)
2302
 
 
2303
2236
    def _make_reference_clone_function(format, a_branch):
2304
2237
        """Create a clone() routine for a branch dynamically."""
2305
2238
        def clone(to_bzrdir, revision_id=None,
2327
2260
            a_bzrdir.
2328
2261
        :param possible_transports: An optional reusable transports list.
2329
2262
        """
 
2263
        if name is None:
 
2264
            name = a_bzrdir._get_selected_branch()
2330
2265
        if not _found:
2331
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2266
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2332
2267
            if format.__class__ != self.__class__:
2333
2268
                raise AssertionError("wrong format %r found for %r" %
2334
2269
                    (format, self))
2335
2270
        if location is None:
2336
2271
            location = self.get_reference(a_bzrdir, name)
2337
 
        real_bzrdir = bzrdir.BzrDir.open(
 
2272
        real_bzrdir = controldir.ControlDir.open(
2338
2273
            location, possible_transports=possible_transports)
2339
 
        result = real_bzrdir.open_branch(name=name, 
2340
 
            ignore_fallbacks=ignore_fallbacks)
 
2274
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
 
2275
            possible_transports=possible_transports)
2341
2276
        # this changes the behaviour of result.clone to create a new reference
2342
2277
        # rather than a copy of the content of the branch.
2343
2278
        # I did not use a proxy object because that needs much more extensive
2377
2312
 
2378
2313
# formats which have no format string are not discoverable
2379
2314
# and not independently creatable, so are not registered.
2380
 
__format5 = BzrBranchFormat5()
2381
2315
__format6 = BzrBranchFormat6()
2382
2316
__format7 = BzrBranchFormat7()
2383
2317
__format8 = BzrBranchFormat8()
2384
 
format_registry.register(__format5)
 
2318
format_registry.register_lazy(
 
2319
    "Bazaar-NG branch format 5\n", "bzrlib.branchfmt.fullhistory", "BzrBranchFormat5")
2385
2320
format_registry.register(BranchReferenceFormat())
2386
2321
format_registry.register(__format6)
2387
2322
format_registry.register(__format7)
2424
2359
 
2425
2360
    def __init__(self, _format=None,
2426
2361
                 _control_files=None, a_bzrdir=None, name=None,
2427
 
                 _repository=None, ignore_fallbacks=False):
 
2362
                 _repository=None, ignore_fallbacks=False,
 
2363
                 possible_transports=None):
2428
2364
        """Create new branch object at a particular location."""
2429
2365
        if a_bzrdir is None:
2430
2366
            raise ValueError('a_bzrdir must be supplied')
2431
 
        else:
2432
 
            self.bzrdir = a_bzrdir
2433
 
        self._base = self.bzrdir.transport.clone('..').base
 
2367
        if name is None:
 
2368
            raise ValueError('name must be supplied')
 
2369
        self.bzrdir = a_bzrdir
 
2370
        self._user_transport = self.bzrdir.transport.clone('..')
 
2371
        if name != "":
 
2372
            self._user_transport.set_segment_parameter(
 
2373
                "branch", urlutils.escape(name))
 
2374
        self._base = self._user_transport.base
2434
2375
        self.name = name
2435
 
        # XXX: We should be able to just do
2436
 
        #   self.base = self.bzrdir.root_transport.base
2437
 
        # but this does not quite work yet -- mbp 20080522
2438
2376
        self._format = _format
2439
2377
        if _control_files is None:
2440
2378
            raise ValueError('BzrBranch _control_files is None')
2441
2379
        self.control_files = _control_files
2442
2380
        self._transport = _control_files._transport
2443
2381
        self.repository = _repository
2444
 
        Branch.__init__(self)
 
2382
        self.conf_store = None
 
2383
        Branch.__init__(self, possible_transports)
2445
2384
 
2446
2385
    def __str__(self):
2447
 
        if self.name is None:
2448
 
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
2449
 
        else:
2450
 
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2451
 
                self.name)
 
2386
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
2452
2387
 
2453
2388
    __repr__ = __str__
2454
2389
 
2458
2393
 
2459
2394
    base = property(_get_base, doc="The URL for the root of this branch.")
2460
2395
 
 
2396
    @property
 
2397
    def user_transport(self):
 
2398
        return self._user_transport
 
2399
 
2461
2400
    def _get_config(self):
2462
2401
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
2463
2402
 
 
2403
    def _get_config_store(self):
 
2404
        if self.conf_store is None:
 
2405
            self.conf_store =  _mod_config.BranchStore(self)
 
2406
        return self.conf_store
 
2407
 
 
2408
    def _uncommitted_branch(self):
 
2409
        """Return the branch that may contain uncommitted changes."""
 
2410
        master = self.get_master_branch()
 
2411
        if master is not None:
 
2412
            return master
 
2413
        else:
 
2414
            return self
 
2415
 
 
2416
    def store_uncommitted(self, creator, message=None):
 
2417
        """Store uncommitted changes from a ShelfCreator.
 
2418
 
 
2419
        :param creator: The ShelfCreator containing uncommitted changes, or
 
2420
            None to delete any stored changes.
 
2421
        :param message: The message to associate with the changes.
 
2422
        :raises: ChangesAlreadyStored if the branch already has changes.
 
2423
        """
 
2424
        branch = self._uncommitted_branch()
 
2425
        if creator is None:
 
2426
            branch._transport.delete('stored-transform')
 
2427
            return
 
2428
        if branch._transport.has('stored-transform'):
 
2429
            raise errors.ChangesAlreadyStored
 
2430
        transform = StringIO()
 
2431
        creator.write_shelf(transform, message)
 
2432
        transform.seek(0)
 
2433
        branch._transport.put_file('stored-transform', transform)
 
2434
 
 
2435
    def get_unshelver(self, tree):
 
2436
        """Return a shelf.Unshelver for this branch and tree.
 
2437
 
 
2438
        :param tree: The tree to use to construct the Unshelver.
 
2439
        :return: an Unshelver or None if no changes are stored.
 
2440
        """
 
2441
        branch = self._uncommitted_branch()
 
2442
        try:
 
2443
            transform = branch._transport.get('stored-transform')
 
2444
        except errors.NoSuchFile:
 
2445
            return None
 
2446
        return shelf.Unshelver.from_tree_and_shelf(tree, transform)
 
2447
 
2464
2448
    def is_locked(self):
2465
2449
        return self.control_files.is_locked()
2466
2450
 
2473
2457
        """
2474
2458
        if not self.is_locked():
2475
2459
            self._note_lock('w')
2476
 
        # All-in-one needs to always unlock/lock.
2477
 
        repo_control = getattr(self.repository, 'control_files', None)
2478
 
        if self.control_files == repo_control or not self.is_locked():
2479
2460
            self.repository._warn_if_deprecated(self)
2480
2461
            self.repository.lock_write()
2481
2462
            took_lock = True
2496
2477
        """
2497
2478
        if not self.is_locked():
2498
2479
            self._note_lock('r')
2499
 
        # All-in-one needs to always unlock/lock.
2500
 
        repo_control = getattr(self.repository, 'control_files', None)
2501
 
        if self.control_files == repo_control or not self.is_locked():
2502
2480
            self.repository._warn_if_deprecated(self)
2503
2481
            self.repository.lock_read()
2504
2482
            took_lock = True
2514
2492
 
2515
2493
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2516
2494
    def unlock(self):
 
2495
        if self.control_files._lock_count == 1 and self.conf_store is not None:
 
2496
            self.conf_store.save_changes()
2517
2497
        try:
2518
2498
            self.control_files.unlock()
2519
2499
        finally:
2520
 
            # All-in-one needs to always unlock/lock.
2521
 
            repo_control = getattr(self.repository, 'control_files', None)
2522
 
            if (self.control_files == repo_control or
2523
 
                not self.control_files.is_locked()):
2524
 
                self.repository.unlock()
2525
2500
            if not self.control_files.is_locked():
 
2501
                self.repository.unlock()
2526
2502
                # we just released the lock
2527
2503
                self._clear_cached_state()
2528
2504
 
2695
2671
        self._transport.put_bytes('last-revision', out_string,
2696
2672
            mode=self.bzrdir._get_file_mode())
2697
2673
 
2698
 
 
2699
 
class FullHistoryBzrBranch(BzrBranch):
2700
 
    """Bzr branch which contains the full revision history."""
2701
 
 
2702
 
    @needs_write_lock
2703
 
    def set_last_revision_info(self, revno, revision_id):
2704
 
        if not revision_id or not isinstance(revision_id, basestring):
2705
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2706
 
        revision_id = _mod_revision.ensure_null(revision_id)
2707
 
        # this old format stores the full history, but this api doesn't
2708
 
        # provide it, so we must generate, and might as well check it's
2709
 
        # correct
2710
 
        history = self._lefthand_history(revision_id)
2711
 
        if len(history) != revno:
2712
 
            raise AssertionError('%d != %d' % (len(history), revno))
2713
 
        self._set_revision_history(history)
2714
 
 
2715
 
    def _read_last_revision_info(self):
2716
 
        rh = self.revision_history()
2717
 
        revno = len(rh)
2718
 
        if revno:
2719
 
            return (revno, rh[-1])
2720
 
        else:
2721
 
            return (0, _mod_revision.NULL_REVISION)
2722
 
 
2723
 
    @deprecated_method(deprecated_in((2, 4, 0)))
2724
 
    @needs_write_lock
2725
 
    def set_revision_history(self, rev_history):
2726
 
        """See Branch.set_revision_history."""
2727
 
        self._set_revision_history(rev_history)
2728
 
 
2729
 
    def _set_revision_history(self, rev_history):
2730
 
        if 'evil' in debug.debug_flags:
2731
 
            mutter_callsite(3, "set_revision_history scales with history.")
2732
 
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2733
 
        for rev_id in rev_history:
2734
 
            check_not_reserved_id(rev_id)
2735
 
        if Branch.hooks['post_change_branch_tip']:
2736
 
            # Don't calculate the last_revision_info() if there are no hooks
2737
 
            # that will use it.
2738
 
            old_revno, old_revid = self.last_revision_info()
2739
 
        if len(rev_history) == 0:
2740
 
            revid = _mod_revision.NULL_REVISION
2741
 
        else:
2742
 
            revid = rev_history[-1]
2743
 
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2744
 
        self._write_revision_history(rev_history)
2745
 
        self._clear_cached_state()
2746
 
        self._cache_revision_history(rev_history)
2747
 
        for hook in Branch.hooks['set_rh']:
2748
 
            hook(self, rev_history)
2749
 
        if Branch.hooks['post_change_branch_tip']:
2750
 
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2751
 
 
2752
 
    def _write_revision_history(self, history):
2753
 
        """Factored out of set_revision_history.
2754
 
 
2755
 
        This performs the actual writing to disk.
2756
 
        It is intended to be called by set_revision_history."""
2757
 
        self._transport.put_bytes(
2758
 
            'revision-history', '\n'.join(history),
2759
 
            mode=self.bzrdir._get_file_mode())
2760
 
 
2761
 
    def _gen_revision_history(self):
2762
 
        history = self._transport.get_bytes('revision-history').split('\n')
2763
 
        if history[-1:] == ['']:
2764
 
            # There shouldn't be a trailing newline, but just in case.
2765
 
            history.pop()
2766
 
        return history
2767
 
 
2768
 
    def _synchronize_history(self, destination, revision_id):
2769
 
        if not isinstance(destination, FullHistoryBzrBranch):
2770
 
            super(BzrBranch, self)._synchronize_history(
2771
 
                destination, revision_id)
2772
 
            return
2773
 
        if revision_id == _mod_revision.NULL_REVISION:
2774
 
            new_history = []
2775
 
        else:
2776
 
            new_history = self.revision_history()
2777
 
        if revision_id is not None and new_history != []:
2778
 
            try:
2779
 
                new_history = new_history[:new_history.index(revision_id) + 1]
2780
 
            except ValueError:
2781
 
                rev = self.repository.get_revision(revision_id)
2782
 
                new_history = rev.get_history(self.repository)[1:]
2783
 
        destination._set_revision_history(new_history)
2784
 
 
2785
 
    @needs_write_lock
2786
 
    def generate_revision_history(self, revision_id, last_rev=None,
2787
 
        other_branch=None):
2788
 
        """Create a new revision history that will finish with revision_id.
2789
 
 
2790
 
        :param revision_id: the new tip to use.
2791
 
        :param last_rev: The previous last_revision. If not None, then this
2792
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
2793
 
        :param other_branch: The other branch that DivergedBranches should
2794
 
            raise with respect to.
 
2674
    @needs_write_lock
 
2675
    def update_feature_flags(self, updated_flags):
 
2676
        """Update the feature flags for this branch.
 
2677
 
 
2678
        :param updated_flags: Dictionary mapping feature names to necessities
 
2679
            A necessity can be None to indicate the feature should be removed
2795
2680
        """
2796
 
        self._set_revision_history(self._lefthand_history(revision_id,
2797
 
            last_rev, other_branch))
2798
 
 
2799
 
 
2800
 
class BzrBranch5(FullHistoryBzrBranch):
2801
 
    """A format 5 branch. This supports new features over plain branches.
2802
 
 
2803
 
    It has support for a master_branch which is the data for bound branches.
2804
 
    """
 
2681
        self._format._update_feature_flags(updated_flags)
 
2682
        self.control_transport.put_bytes('format', self._format.as_string())
2805
2683
 
2806
2684
 
2807
2685
class BzrBranch8(BzrBranch):
2808
2686
    """A branch that stores tree-reference locations."""
2809
2687
 
2810
 
    def _open_hook(self):
 
2688
    def _open_hook(self, possible_transports=None):
2811
2689
        if self._ignore_fallbacks:
2812
2690
            return
 
2691
        if possible_transports is None:
 
2692
            possible_transports = [self.bzrdir.root_transport]
2813
2693
        try:
2814
2694
            url = self.get_stacked_on_url()
2815
2695
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2823
2703
                    raise AssertionError(
2824
2704
                        "'transform_fallback_location' hook %s returned "
2825
2705
                        "None, not a URL." % hook_name)
2826
 
            self._activate_fallback_location(url)
 
2706
            self._activate_fallback_location(url,
 
2707
                possible_transports=possible_transports)
2827
2708
 
2828
2709
    def __init__(self, *args, **kwargs):
2829
2710
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2947
2828
        """See Branch.set_push_location."""
2948
2829
        self._master_branch_cache = None
2949
2830
        result = None
2950
 
        config = self.get_config()
 
2831
        conf = self.get_config_stack()
2951
2832
        if location is None:
2952
 
            if config.get_user_option('bound') != 'True':
 
2833
            if not conf.get('bound'):
2953
2834
                return False
2954
2835
            else:
2955
 
                config.set_user_option('bound', 'False', warn_masked=True)
 
2836
                conf.set('bound', 'False')
2956
2837
                return True
2957
2838
        else:
2958
2839
            self._set_config_location('bound_location', location,
2959
 
                                      config=config)
2960
 
            config.set_user_option('bound', 'True', warn_masked=True)
 
2840
                                      config=conf)
 
2841
            conf.set('bound', 'True')
2961
2842
        return True
2962
2843
 
2963
2844
    def _get_bound_location(self, bound):
2964
2845
        """Return the bound location in the config file.
2965
2846
 
2966
2847
        Return None if the bound parameter does not match"""
2967
 
        config = self.get_config()
2968
 
        config_bound = (config.get_user_option('bound') == 'True')
2969
 
        if config_bound != bound:
 
2848
        conf = self.get_config_stack()
 
2849
        if conf.get('bound') != bound:
2970
2850
            return None
2971
 
        return self._get_config_location('bound_location', config=config)
 
2851
        return self._get_config_location('bound_location', config=conf)
2972
2852
 
2973
2853
    def get_bound_location(self):
2974
 
        """See Branch.set_push_location."""
 
2854
        """See Branch.get_bound_location."""
2975
2855
        return self._get_bound_location(True)
2976
2856
 
2977
2857
    def get_old_bound_location(self):
2984
2864
        ## self._check_stackable_repo()
2985
2865
        # stacked_on_location is only ever defined in branch.conf, so don't
2986
2866
        # waste effort reading the whole stack of config files.
2987
 
        config = self.get_config()._get_branch_data_config()
 
2867
        conf = _mod_config.BranchOnlyStack(self)
2988
2868
        stacked_url = self._get_config_location('stacked_on_location',
2989
 
            config=config)
 
2869
                                                config=conf)
2990
2870
        if stacked_url is None:
2991
2871
            raise errors.NotStacked(self)
2992
 
        return stacked_url
 
2872
        return stacked_url.encode('utf-8')
2993
2873
 
2994
2874
    @needs_read_lock
2995
2875
    def get_rev_id(self, revno, history=None):
3025
2905
            except errors.RevisionNotPresent, e:
3026
2906
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
3027
2907
            index = len(self._partial_revision_history_cache) - 1
 
2908
            if index < 0:
 
2909
                raise errors.NoSuchRevision(self, revision_id)
3028
2910
            if self._partial_revision_history_cache[index] != revision_id:
3029
2911
                raise errors.NoSuchRevision(self, revision_id)
3030
2912
        return self.revno() - index
3085
2967
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3086
2968
    """
3087
2969
 
3088
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3089
 
    def __int__(self):
3090
 
        """Return the relative change in revno.
3091
 
 
3092
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3093
 
        """
3094
 
        return self.new_revno - self.old_revno
3095
 
 
3096
2970
    def report(self, to_file):
3097
2971
        tag_conflicts = getattr(self, "tag_conflicts", None)
3098
2972
        tag_updates = getattr(self, "tag_updates", None)
3128
3002
        target, otherwise it will be None.
3129
3003
    """
3130
3004
 
3131
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3132
 
    def __int__(self):
3133
 
        """Return the relative change in revno.
3134
 
 
3135
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3136
 
        """
3137
 
        return self.new_revno - self.old_revno
3138
 
 
3139
3005
    def report(self, to_file):
3140
3006
        # TODO: This function gets passed a to_file, but then
3141
3007
        # ignores it and calls note() instead. This is also
3188
3054
 
3189
3055
        # Copy source data into target
3190
3056
        new_branch._write_last_revision_info(*branch.last_revision_info())
3191
 
        new_branch.set_parent(branch.get_parent())
3192
 
        new_branch.set_bound_location(branch.get_bound_location())
3193
 
        new_branch.set_push_location(branch.get_push_location())
 
3057
        new_branch.lock_write()
 
3058
        try:
 
3059
            new_branch.set_parent(branch.get_parent())
 
3060
            new_branch.set_bound_location(branch.get_bound_location())
 
3061
            new_branch.set_push_location(branch.get_push_location())
 
3062
        finally:
 
3063
            new_branch.unlock()
3194
3064
 
3195
3065
        # New branch has no tags by default
3196
3066
        new_branch.tags._set_tag_dict({})
3197
3067
 
3198
3068
        # Copying done; now update target format
3199
3069
        new_branch._transport.put_bytes('format',
3200
 
            format.get_format_string(),
 
3070
            format.as_string(),
3201
3071
            mode=new_branch.bzrdir._get_file_mode())
3202
3072
 
3203
3073
        # Clean up old files
3204
3074
        new_branch._transport.delete('revision-history')
 
3075
        branch.lock_write()
3205
3076
        try:
3206
 
            branch.set_parent(None)
3207
 
        except errors.NoSuchFile:
3208
 
            pass
3209
 
        branch.set_bound_location(None)
 
3077
            try:
 
3078
                branch.set_parent(None)
 
3079
            except errors.NoSuchFile:
 
3080
                pass
 
3081
            branch.set_bound_location(None)
 
3082
        finally:
 
3083
            branch.unlock()
3210
3084
 
3211
3085
 
3212
3086
class Converter6to7(object):
3216
3090
        format = BzrBranchFormat7()
3217
3091
        branch._set_config_location('stacked_on_location', '')
3218
3092
        # update target format
3219
 
        branch._transport.put_bytes('format', format.get_format_string())
 
3093
        branch._transport.put_bytes('format', format.as_string())
3220
3094
 
3221
3095
 
3222
3096
class Converter7to8(object):
3226
3100
        format = BzrBranchFormat8()
3227
3101
        branch._transport.put_bytes('references', '')
3228
3102
        # update target format
3229
 
        branch._transport.put_bytes('format', format.get_format_string())
 
3103
        branch._transport.put_bytes('format', format.as_string())
3230
3104
 
3231
3105
 
3232
3106
class InterBranch(InterObject):
3289
3163
        raise NotImplementedError(self.fetch)
3290
3164
 
3291
3165
 
 
3166
def _fix_overwrite_type(overwrite):
 
3167
    if isinstance(overwrite, bool):
 
3168
        if overwrite:
 
3169
            return ["history", "tags"]
 
3170
        else:
 
3171
            return []
 
3172
    return overwrite
 
3173
 
 
3174
 
3292
3175
class GenericInterBranch(InterBranch):
3293
3176
    """InterBranch implementation that uses public Branch functions."""
3294
3177
 
3459
3342
        result.target_branch = self.target
3460
3343
        result.old_revno, result.old_revid = self.target.last_revision_info()
3461
3344
        self.source.update_references(self.target)
 
3345
        overwrite = _fix_overwrite_type(overwrite)
3462
3346
        if result.old_revid != stop_revision:
3463
3347
            # We assume that during 'push' this repository is closer than
3464
3348
            # the target.
3465
3349
            graph = self.source.repository.get_graph(self.target.repository)
3466
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3467
 
                    graph=graph)
 
3350
            self._update_revisions(stop_revision,
 
3351
                overwrite=("history" in overwrite),
 
3352
                graph=graph)
3468
3353
        if self.source._push_should_merge_tags():
3469
3354
            result.tag_updates, result.tag_conflicts = (
3470
 
                self.source.tags.merge_to(self.target.tags, overwrite))
 
3355
                self.source.tags.merge_to(
 
3356
                self.target.tags, "tags" in overwrite))
3471
3357
        result.new_revno, result.new_revid = self.target.last_revision_info()
3472
3358
        return result
3473
3359
 
3551
3437
            # -- JRV20090506
3552
3438
            result.old_revno, result.old_revid = \
3553
3439
                self.target.last_revision_info()
3554
 
            self._update_revisions(stop_revision, overwrite=overwrite,
 
3440
            overwrite = _fix_overwrite_type(overwrite)
 
3441
            self._update_revisions(stop_revision,
 
3442
                overwrite=("history" in overwrite),
3555
3443
                graph=graph)
3556
3444
            # TODO: The old revid should be specified when merging tags, 
3557
3445
            # so a tags implementation that versions tags can only 
3558
3446
            # pull in the most recent changes. -- JRV20090506
3559
3447
            result.tag_updates, result.tag_conflicts = (
3560
 
                self.source.tags.merge_to(self.target.tags, overwrite,
 
3448
                self.source.tags.merge_to(self.target.tags,
 
3449
                    "tags" in overwrite,
3561
3450
                    ignore_master=not merge_tags_to_master))
3562
3451
            result.new_revno, result.new_revid = self.target.last_revision_info()
3563
3452
            if _hook_master: