~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Tarmac
  • Author(s): Vincent Ladeuil
  • Date: 2017-01-30 14:42:05 UTC
  • mfrom: (6620.1.1 trunk)
  • Revision ID: tarmac-20170130144205-r8fh2xpmiuxyozpv
Merge  2.7 into trunk including fix for bug #1657238 [r=vila]

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):
 
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
        :raises: ChangesAlreadyStored if the branch already has changes.
 
261
        """
 
262
        raise NotImplementedError(self.store_uncommitted)
 
263
 
 
264
    def get_unshelver(self, tree):
 
265
        """Return a shelf.Unshelver for this branch and tree.
 
266
 
 
267
        :param tree: The tree to use to construct the Unshelver.
 
268
        :return: an Unshelver or None if no changes are stored.
 
269
        """
 
270
        raise NotImplementedError(self.get_unshelver)
 
271
 
 
272
    def _get_fallback_repository(self, url, possible_transports):
241
273
        """Get the repository we fallback to at url."""
242
274
        url = urlutils.join(self.base, url)
243
 
        a_branch = Branch.open(url,
244
 
            possible_transports=[self.bzrdir.root_transport])
 
275
        a_branch = Branch.open(url, possible_transports=possible_transports)
245
276
        return a_branch.repository
246
277
 
247
278
    @needs_read_lock
657
688
        """
658
689
        if not self._format.supports_set_append_revisions_only():
659
690
            return False
660
 
        return self.get_config(
661
 
            ).get_user_option_as_bool('append_revisions_only')
 
691
        return self.get_config_stack().get('append_revisions_only')
662
692
 
663
693
    def set_append_revisions_only(self, enabled):
664
694
        if not self._format.supports_set_append_revisions_only():
665
695
            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)
 
696
        self.get_config_stack().set('append_revisions_only', enabled)
672
697
 
673
698
    def set_reference_info(self, file_id, tree_path, branch_location):
674
699
        """Set the branch location to use for a tree reference."""
703
728
        """
704
729
        raise errors.UpgradeRequired(self.user_url)
705
730
 
706
 
    def get_commit_builder(self, parents, config=None, timestamp=None,
 
731
    def get_commit_builder(self, parents, config_stack=None, timestamp=None,
707
732
                           timezone=None, committer=None, revprops=None,
708
733
                           revision_id=None, lossy=False):
709
734
        """Obtain a CommitBuilder for this branch.
719
744
            represented, when pushing to a foreign VCS 
720
745
        """
721
746
 
722
 
        if config is None:
723
 
            config = self.get_config()
 
747
        if config_stack is None:
 
748
            config_stack = self.get_config_stack()
724
749
 
725
 
        return self.repository.get_commit_builder(self, parents, config,
 
750
        return self.repository.get_commit_builder(self, parents, config_stack,
726
751
            timestamp, timezone, committer, revprops, revision_id,
727
752
            lossy)
728
753
 
733
758
        """
734
759
        return None
735
760
 
 
761
    @deprecated_method(deprecated_in((2, 5, 0)))
736
762
    def get_revision_delta(self, revno):
737
763
        """Return the delta for one revision.
738
764
 
739
765
        The delta is relative to its mainline predecessor, or the
740
766
        empty tree for revision 1.
741
767
        """
742
 
        rh = self.revision_history()
743
 
        if not (1 <= revno <= len(rh)):
 
768
        try:
 
769
            revid = self.get_rev_id(revno)
 
770
        except errors.NoSuchRevision:
744
771
            raise errors.InvalidRevisionNumber(revno)
745
 
        return self.repository.get_revision_delta(rh[revno-1])
 
772
        return self.repository.get_revision_delta(revid)
746
773
 
747
774
    def get_stacked_on_url(self):
748
775
        """Get the URL this branch is stacked against.
757
784
        """Print `file` to stdout."""
758
785
        raise NotImplementedError(self.print_file)
759
786
 
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
787
    @needs_write_lock
779
788
    def set_last_revision_info(self, revno, revision_id):
780
789
        """Set the last revision of this branch.
847
856
                return
848
857
            self._unstack()
849
858
        else:
850
 
            self._activate_fallback_location(url)
 
859
            self._activate_fallback_location(url,
 
860
                possible_transports=[self.bzrdir.root_transport])
851
861
        # write this out after the repository is stacked to avoid setting a
852
862
        # stacked config that doesn't work.
853
863
        self._set_config_location('stacked_on_location', url)
879
889
            # stream from one of them to the other.  This does mean doing
880
890
            # separate SSH connection setup, but unstacking is not a
881
891
            # common operation so it's tolerable.
882
 
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
 
892
            new_bzrdir = controldir.ControlDir.open(
 
893
                self.bzrdir.root_transport.base)
883
894
            new_repository = new_bzrdir.find_repository()
884
895
            if new_repository._fallback_repositories:
885
896
                raise AssertionError("didn't expect %r to have "
928
939
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
929
940
                except errors.TagsNotSupported:
930
941
                    tags_to_fetch = set()
931
 
                fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
 
942
                fetch_spec = vf_search.NotInOtherForRevs(self.repository,
932
943
                    old_repository, required_ids=[self.last_revision()],
933
944
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
934
945
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
975
986
        This means the next call to revision_history will need to call
976
987
        _gen_revision_history.
977
988
 
978
 
        This API is semi-public; it only for use by subclasses, all other code
979
 
        should consider it to be private.
 
989
        This API is semi-public; it is only for use by subclasses, all other
 
990
        code should consider it to be private.
980
991
        """
981
992
        self._revision_history_cache = None
982
993
        self._revision_id_to_revno_cache = None
1002
1013
        """
1003
1014
        raise NotImplementedError(self._gen_revision_history)
1004
1015
 
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
 
        """
 
1016
    def _revision_history(self):
1012
1017
        if 'evil' in debug.debug_flags:
1013
1018
            mutter_callsite(3, "revision_history scales with history.")
1014
1019
        if self._revision_history_cache is not None:
1047
1052
    def _read_last_revision_info(self):
1048
1053
        raise NotImplementedError(self._read_last_revision_info)
1049
1054
 
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
1055
    def import_last_revision_info_and_tags(self, source, revno, revid,
1063
1056
                                           lossy=False):
1064
1057
        """Set the last revision info, importing from another repo if necessary.
1084
1077
        """Given a revision id, return its revno"""
1085
1078
        if _mod_revision.is_null(revision_id):
1086
1079
            return 0
1087
 
        history = self.revision_history()
 
1080
        history = self._revision_history()
1088
1081
        try:
1089
1082
            return history.index(revision_id) + 1
1090
1083
        except ValueError:
1155
1148
    def _set_config_location(self, name, url, config=None,
1156
1149
                             make_relative=False):
1157
1150
        if config is None:
1158
 
            config = self.get_config()
 
1151
            config = self.get_config_stack()
1159
1152
        if url is None:
1160
1153
            url = ''
1161
1154
        elif make_relative:
1162
1155
            url = urlutils.relative_url(self.base, url)
1163
 
        config.set_user_option(name, url, warn_masked=True)
 
1156
        config.set(name, url)
1164
1157
 
1165
1158
    def _get_config_location(self, name, config=None):
1166
1159
        if config is None:
1167
 
            config = self.get_config()
1168
 
        location = config.get_user_option(name)
 
1160
            config = self.get_config_stack()
 
1161
        location = config.get(name)
1169
1162
        if location == '':
1170
1163
            location = None
1171
1164
        return location
1172
1165
 
1173
1166
    def get_child_submit_format(self):
1174
1167
        """Return the preferred format of submissions to this branch."""
1175
 
        return self.get_config().get_user_option("child_submit_format")
 
1168
        return self.get_config_stack().get('child_submit_format')
1176
1169
 
1177
1170
    def get_submit_branch(self):
1178
1171
        """Return the submit location of the branch.
1181
1174
        pattern is that the user can override it by specifying a
1182
1175
        location.
1183
1176
        """
1184
 
        return self.get_config().get_user_option('submit_branch')
 
1177
        return self.get_config_stack().get('submit_branch')
1185
1178
 
1186
1179
    def set_submit_branch(self, location):
1187
1180
        """Return the submit location of the branch.
1190
1183
        pattern is that the user can override it by specifying a
1191
1184
        location.
1192
1185
        """
1193
 
        self.get_config().set_user_option('submit_branch', location,
1194
 
            warn_masked=True)
 
1186
        self.get_config_stack().set('submit_branch', location)
1195
1187
 
1196
1188
    def get_public_branch(self):
1197
1189
        """Return the public location of the branch.
1210
1202
        self._set_config_location('public_branch', location)
1211
1203
 
1212
1204
    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
 
1205
        """Return None or the location to push this branch to."""
 
1206
        return self.get_config_stack().get('push_location')
1216
1207
 
1217
1208
    def set_push_location(self, location):
1218
1209
        """Set a new push location for this branch."""
1387
1378
        # TODO: We should probably also check that self.revision_history
1388
1379
        # matches the repository for older branch formats.
1389
1380
        # If looking for the code that cross-checks repository parents against
1390
 
        # the iter_reverse_revision_history output, that is now a repository
 
1381
        # the Graph.iter_lefthand_ancestry output, that is now a repository
1391
1382
        # specific check.
1392
1383
        return result
1393
1384
 
1444
1435
        t = transport.get_transport(to_location)
1445
1436
        t.ensure_base()
1446
1437
        format = self._get_checkout_format(lightweight=lightweight)
 
1438
        try:
 
1439
            checkout = format.initialize_on_transport(t)
 
1440
        except errors.AlreadyControlDirError:
 
1441
            # It's fine if the control directory already exists,
 
1442
            # as long as there is no existing branch and working tree.
 
1443
            checkout = controldir.ControlDir.open_from_transport(t)
 
1444
            try:
 
1445
                checkout.open_branch()
 
1446
            except errors.NotBranchError:
 
1447
                pass
 
1448
            else:
 
1449
                raise errors.AlreadyControlDirError(t.base)
 
1450
            if checkout.control_transport.base == self.bzrdir.control_transport.base:
 
1451
                # When checking out to the same control directory,
 
1452
                # always create a lightweight checkout
 
1453
                lightweight = True
 
1454
 
1447
1455
        if lightweight:
1448
 
            checkout = format.initialize_on_transport(t)
1449
 
            from_branch = BranchReferenceFormat().initialize(checkout, 
1450
 
                target_branch=self)
 
1456
            from_branch = checkout.set_branch_reference(target_branch=self)
1451
1457
        else:
1452
 
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1453
 
                to_location, force_new_tree=False, format=format)
1454
 
            checkout = checkout_branch.bzrdir
 
1458
            policy = checkout.determine_repository_policy()
 
1459
            repo = policy.acquire_repository()[0]
 
1460
            checkout_branch = checkout.create_branch()
1455
1461
            checkout_branch.bind(self)
1456
1462
            # pull up to the specified revision_id to set the initial
1457
1463
            # branch tip correctly, and seed it with history.
1458
1464
            checkout_branch.pull(self, stop_revision=revision_id)
1459
 
            from_branch=None
 
1465
            from_branch = None
1460
1466
        tree = checkout.create_workingtree(revision_id,
1461
1467
                                           from_branch=from_branch,
1462
1468
                                           accelerator_tree=accelerator_tree,
1551
1557
            heads that must be fetched if present, but no error is necessary if
1552
1558
            they are not present.
1553
1559
        """
1554
 
        # For bzr native formats must_fetch is just the tip, and if_present_fetch
1555
 
        # are the tags.
 
1560
        # For bzr native formats must_fetch is just the tip, and
 
1561
        # if_present_fetch are the tags.
1556
1562
        must_fetch = set([self.last_revision()])
1557
1563
        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:
 
1564
        if self.get_config_stack().get('branch.fetch_tags'):
1562
1565
            try:
1563
1566
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
1564
1567
            except errors.TagsNotSupported:
1573
1576
 
1574
1577
    Formats provide three things:
1575
1578
     * An initialization routine,
1576
 
     * a format string,
 
1579
     * a format description
1577
1580
     * an open routine.
1578
1581
 
1579
1582
    Formats are placed in an dict by their format string for reference
1592
1595
    def __ne__(self, other):
1593
1596
        return not (self == other)
1594
1597
 
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.
 
1598
    def get_reference(self, controldir, name=None):
 
1599
        """Get the target reference of the branch in controldir.
1625
1600
 
1626
1601
        format probing must have been completed before calling
1627
1602
        this method - it is assumed that the format of the branch
1628
 
        in a_bzrdir is correct.
 
1603
        in controldir is correct.
1629
1604
 
1630
 
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1605
        :param controldir: The controldir to get the branch data from.
1631
1606
        :param name: Name of the colocated branch to fetch
1632
1607
        :return: None if the branch is not a reference branch.
1633
1608
        """
1634
1609
        return None
1635
1610
 
1636
1611
    @classmethod
1637
 
    def set_reference(self, a_bzrdir, name, to_branch):
1638
 
        """Set the target reference of the branch in a_bzrdir.
 
1612
    def set_reference(self, controldir, name, to_branch):
 
1613
        """Set the target reference of the branch in controldir.
1639
1614
 
1640
1615
        format probing must have been completed before calling
1641
1616
        this method - it is assumed that the format of the branch
1642
 
        in a_bzrdir is correct.
 
1617
        in controldir is correct.
1643
1618
 
1644
 
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1619
        :param controldir: The controldir to set the branch reference for.
1645
1620
        :param name: Name of colocated branch to set, None for default
1646
1621
        :param to_branch: branch that the checkout is to reference
1647
1622
        """
1648
1623
        raise NotImplementedError(self.set_reference)
1649
1624
 
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
1625
    def get_format_description(self):
1655
1626
        """Return the short format description for this format."""
1656
1627
        raise NotImplementedError(self.get_format_description)
1657
1628
 
1658
 
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
 
1629
    def _run_post_branch_init_hooks(self, controldir, name, branch):
1659
1630
        hooks = Branch.hooks['post_branch_init']
1660
1631
        if not hooks:
1661
1632
            return
1662
 
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
 
1633
        params = BranchInitHookParams(self, controldir, name, branch)
1663
1634
        for hook in hooks:
1664
1635
            hook(params)
1665
1636
 
1666
 
    def initialize(self, a_bzrdir, name=None, repository=None,
 
1637
    def initialize(self, controldir, name=None, repository=None,
1667
1638
                   append_revisions_only=None):
1668
 
        """Create a branch of this format in a_bzrdir.
1669
 
        
 
1639
        """Create a branch of this format in controldir.
 
1640
 
1670
1641
        :param name: Name of the colocated branch to create.
1671
1642
        """
1672
1643
        raise NotImplementedError(self.initialize)
1704
1675
        """
1705
1676
        raise NotImplementedError(self.network_name)
1706
1677
 
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
 
1678
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
 
1679
            found_repository=None, possible_transports=None):
 
1680
        """Return the branch object for controldir.
1710
1681
 
1711
 
        :param a_bzrdir: A BzrDir that contains a branch.
 
1682
        :param controldir: A ControlDir that contains a branch.
1712
1683
        :param name: Name of colocated branch to open
1713
1684
        :param _found: a private parameter, do not use it. It is used to
1714
1685
            indicate if format probing has already be done.
1717
1688
        """
1718
1689
        raise NotImplementedError(self.open)
1719
1690
 
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
1691
    def supports_set_append_revisions_only(self):
1736
1692
        """True if this format supports set_append_revisions_only."""
1737
1693
        return False
1744
1700
        """True if this format supports leaving locks in place."""
1745
1701
        return False # by default
1746
1702
 
1747
 
    @classmethod
1748
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1749
 
    def unregister_format(klass, format):
1750
 
        format_registry.remove(format)
1751
 
 
1752
1703
    def __str__(self):
1753
1704
        return self.get_format_description().rstrip()
1754
1705
 
1783
1734
        """
1784
1735
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
1785
1736
        self._format_string = format_string
1786
 
        
 
1737
 
1787
1738
    def get_format_string(self):
1788
1739
        """See BranchFormat.get_format_string."""
1789
1740
        return self._format_string
1796
1747
class BranchHooks(Hooks):
1797
1748
    """A dictionary mapping hook name to a list of callables for branch hooks.
1798
1749
 
1799
 
    e.g. ['set_rh'] Is the list of items to be called when the
1800
 
    set_revision_history function is invoked.
 
1750
    e.g. ['post_push'] Is the list of items to be called when the
 
1751
    push function is invoked.
1801
1752
    """
1802
1753
 
1803
1754
    def __init__(self):
1807
1758
        notified.
1808
1759
        """
1809
1760
        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
1761
        self.add_hook('open',
1817
1762
            "Called with the Branch object that has been opened after a "
1818
1763
            "branch is opened.", (1, 8))
1934
1879
    There are 4 fields that hooks may wish to access:
1935
1880
 
1936
1881
    :ivar format: the branch format
1937
 
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
 
1882
    :ivar bzrdir: the ControlDir where the branch will be/has been initialized
1938
1883
    :ivar name: name of colocated branch, if any (or None)
1939
1884
    :ivar branch: the branch created
1940
1885
 
1943
1888
    branch, which refer to the original branch.
1944
1889
    """
1945
1890
 
1946
 
    def __init__(self, format, a_bzrdir, name, branch):
 
1891
    def __init__(self, format, controldir, name, branch):
1947
1892
        """Create a group of BranchInitHook parameters.
1948
1893
 
1949
1894
        :param format: the branch format
1950
 
        :param a_bzrdir: the BzrDir where the branch will be/has been
 
1895
        :param controldir: the ControlDir where the branch will be/has been
1951
1896
            initialized
1952
1897
        :param name: name of colocated branch, if any (or None)
1953
1898
        :param branch: the branch created
1957
1902
        in branch, which refer to the original branch.
1958
1903
        """
1959
1904
        self.format = format
1960
 
        self.bzrdir = a_bzrdir
 
1905
        self.bzrdir = controldir
1961
1906
        self.name = name
1962
1907
        self.branch = branch
1963
1908
 
1973
1918
 
1974
1919
    There are 4 fields that hooks may wish to access:
1975
1920
 
1976
 
    :ivar control_dir: BzrDir of the checkout to change
 
1921
    :ivar control_dir: ControlDir of the checkout to change
1977
1922
    :ivar to_branch: branch that the checkout is to reference
1978
1923
    :ivar force: skip the check for local commits in a heavy checkout
1979
1924
    :ivar revision_id: revision ID to switch to (or None)
1982
1927
    def __init__(self, control_dir, to_branch, force, revision_id):
1983
1928
        """Create a group of SwitchHook parameters.
1984
1929
 
1985
 
        :param control_dir: BzrDir of the checkout to change
 
1930
        :param control_dir: ControlDir of the checkout to change
1986
1931
        :param to_branch: branch that the checkout is to reference
1987
1932
        :param force: skip the check for local commits in a heavy checkout
1988
1933
        :param revision_id: revision ID to switch to (or None)
2001
1946
            self.revision_id)
2002
1947
 
2003
1948
 
2004
 
class BranchFormatMetadir(BranchFormat):
2005
 
    """Common logic for meta-dir based branch formats."""
 
1949
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
 
1950
    """Base class for branch formats that live in meta directories.
 
1951
    """
 
1952
 
 
1953
    def __init__(self):
 
1954
        BranchFormat.__init__(self)
 
1955
        bzrdir.BzrFormat.__init__(self)
 
1956
 
 
1957
    @classmethod
 
1958
    def find_format(klass, controldir, name=None):
 
1959
        """Return the format for the branch object in controldir."""
 
1960
        try:
 
1961
            transport = controldir.get_branch_transport(None, name=name)
 
1962
        except errors.NoSuchFile:
 
1963
            raise errors.NotBranchError(path=name, bzrdir=controldir)
 
1964
        try:
 
1965
            format_string = transport.get_bytes("format")
 
1966
        except errors.NoSuchFile:
 
1967
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
 
1968
        return klass._find_format(format_registry, 'branch', format_string)
2006
1969
 
2007
1970
    def _branch_class(self):
2008
1971
        """What class to instantiate on open calls."""
2018
1981
 
2019
1982
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
2020
1983
                           repository=None):
2021
 
        """Initialize a branch in a bzrdir, with specified files
 
1984
        """Initialize a branch in a control dir, with specified files
2022
1985
 
2023
1986
        :param a_bzrdir: The bzrdir to initialize the branch in
2024
1987
        :param utf8_files: The files to create as a list of
2026
1989
        :param name: Name of colocated branch to create, if any
2027
1990
        :return: a branch in this format
2028
1991
        """
 
1992
        if name is None:
 
1993
            name = a_bzrdir._get_selected_branch()
2029
1994
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
2030
1995
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2031
1996
        control_files = lockable_files.LockableFiles(branch_transport,
2033
1998
        control_files.create_lock()
2034
1999
        control_files.lock_write()
2035
2000
        try:
2036
 
            utf8_files += [('format', self.get_format_string())]
 
2001
            utf8_files += [('format', self.as_string())]
2037
2002
            for (filename, content) in utf8_files:
2038
2003
                branch_transport.put_bytes(
2039
2004
                    filename, content,
2045
2010
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2046
2011
        return branch
2047
2012
 
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
2013
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2056
 
            found_repository=None):
 
2014
            found_repository=None, possible_transports=None):
2057
2015
        """See BranchFormat.open()."""
 
2016
        if name is None:
 
2017
            name = a_bzrdir._get_selected_branch()
2058
2018
        if not _found:
2059
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2019
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2060
2020
            if format.__class__ != self.__class__:
2061
2021
                raise AssertionError("wrong format %r found for %r" %
2062
2022
                    (format, self))
2071
2031
                              name=name,
2072
2032
                              a_bzrdir=a_bzrdir,
2073
2033
                              _repository=found_repository,
2074
 
                              ignore_fallbacks=ignore_fallbacks)
 
2034
                              ignore_fallbacks=ignore_fallbacks,
 
2035
                              possible_transports=possible_transports)
2075
2036
        except errors.NoSuchFile:
2076
2037
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2077
2038
 
2078
 
    def __init__(self):
2079
 
        super(BranchFormatMetadir, self).__init__()
2080
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2081
 
        self._matchingbzrdir.set_branch_format(self)
 
2039
    @property
 
2040
    def _matchingbzrdir(self):
 
2041
        ret = bzrdir.BzrDirMetaFormat1()
 
2042
        ret.set_branch_format(self)
 
2043
        return ret
2082
2044
 
2083
2045
    def supports_tags(self):
2084
2046
        return True
2086
2048
    def supports_leaving_lock(self):
2087
2049
        return True
2088
2050
 
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
 
2051
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
2052
            basedir=None):
 
2053
        BranchFormat.check_support_status(self,
 
2054
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
 
2055
            basedir=basedir)
 
2056
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
 
2057
            recommend_upgrade=recommend_upgrade, basedir=basedir)
2126
2058
 
2127
2059
 
2128
2060
class BzrBranchFormat6(BranchFormatMetadir):
2139
2071
    def _branch_class(self):
2140
2072
        return BzrBranch6
2141
2073
 
2142
 
    def get_format_string(self):
 
2074
    @classmethod
 
2075
    def get_format_string(cls):
2143
2076
        """See BranchFormat.get_format_string()."""
2144
2077
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
2145
2078
 
2171
2104
    def _branch_class(self):
2172
2105
        return BzrBranch8
2173
2106
 
2174
 
    def get_format_string(self):
 
2107
    @classmethod
 
2108
    def get_format_string(cls):
2175
2109
        """See BranchFormat.get_format_string()."""
2176
2110
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2177
2111
 
2225
2159
    def _branch_class(self):
2226
2160
        return BzrBranch7
2227
2161
 
2228
 
    def get_format_string(self):
 
2162
    @classmethod
 
2163
    def get_format_string(cls):
2229
2164
        """See BranchFormat.get_format_string()."""
2230
2165
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2231
2166
 
2246
2181
    supports_reference_locations = False
2247
2182
 
2248
2183
 
2249
 
class BranchReferenceFormat(BranchFormat):
 
2184
class BranchReferenceFormat(BranchFormatMetadir):
2250
2185
    """Bzr branch reference format.
2251
2186
 
2252
2187
    Branch references are used in implementing checkouts, they
2257
2192
     - a format string
2258
2193
    """
2259
2194
 
2260
 
    def get_format_string(self):
 
2195
    @classmethod
 
2196
    def get_format_string(cls):
2261
2197
        """See BranchFormat.get_format_string()."""
2262
2198
        return "Bazaar-NG Branch Reference Format 1\n"
2263
2199
 
2285
2221
        mutter('creating branch reference in %s', a_bzrdir.user_url)
2286
2222
        if a_bzrdir._format.fixed_components:
2287
2223
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
 
2224
        if name is None:
 
2225
            name = a_bzrdir._get_selected_branch()
2288
2226
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2289
2227
        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,
 
2228
            target_branch.user_url)
 
2229
        branch_transport.put_bytes('format', self.as_string())
 
2230
        branch = self.open(a_bzrdir, name, _found=True,
2294
2231
            possible_transports=[target_branch.bzrdir.root_transport])
2295
2232
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2296
2233
        return branch
2297
2234
 
2298
 
    def __init__(self):
2299
 
        super(BranchReferenceFormat, self).__init__()
2300
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2301
 
        self._matchingbzrdir.set_branch_format(self)
2302
 
 
2303
2235
    def _make_reference_clone_function(format, a_branch):
2304
2236
        """Create a clone() routine for a branch dynamically."""
2305
2237
        def clone(to_bzrdir, revision_id=None,
2327
2259
            a_bzrdir.
2328
2260
        :param possible_transports: An optional reusable transports list.
2329
2261
        """
 
2262
        if name is None:
 
2263
            name = a_bzrdir._get_selected_branch()
2330
2264
        if not _found:
2331
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2265
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2332
2266
            if format.__class__ != self.__class__:
2333
2267
                raise AssertionError("wrong format %r found for %r" %
2334
2268
                    (format, self))
2335
2269
        if location is None:
2336
2270
            location = self.get_reference(a_bzrdir, name)
2337
 
        real_bzrdir = bzrdir.BzrDir.open(
 
2271
        real_bzrdir = controldir.ControlDir.open(
2338
2272
            location, possible_transports=possible_transports)
2339
 
        result = real_bzrdir.open_branch(name=name, 
2340
 
            ignore_fallbacks=ignore_fallbacks)
 
2273
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
 
2274
            possible_transports=possible_transports)
2341
2275
        # this changes the behaviour of result.clone to create a new reference
2342
2276
        # rather than a copy of the content of the branch.
2343
2277
        # I did not use a proxy object because that needs much more extensive
2377
2311
 
2378
2312
# formats which have no format string are not discoverable
2379
2313
# and not independently creatable, so are not registered.
2380
 
__format5 = BzrBranchFormat5()
2381
2314
__format6 = BzrBranchFormat6()
2382
2315
__format7 = BzrBranchFormat7()
2383
2316
__format8 = BzrBranchFormat8()
2384
 
format_registry.register(__format5)
 
2317
format_registry.register_lazy(
 
2318
    "Bazaar-NG branch format 5\n", "bzrlib.branchfmt.fullhistory", "BzrBranchFormat5")
2385
2319
format_registry.register(BranchReferenceFormat())
2386
2320
format_registry.register(__format6)
2387
2321
format_registry.register(__format7)
2424
2358
 
2425
2359
    def __init__(self, _format=None,
2426
2360
                 _control_files=None, a_bzrdir=None, name=None,
2427
 
                 _repository=None, ignore_fallbacks=False):
 
2361
                 _repository=None, ignore_fallbacks=False,
 
2362
                 possible_transports=None):
2428
2363
        """Create new branch object at a particular location."""
2429
2364
        if a_bzrdir is None:
2430
2365
            raise ValueError('a_bzrdir must be supplied')
2431
 
        else:
2432
 
            self.bzrdir = a_bzrdir
2433
 
        self._base = self.bzrdir.transport.clone('..').base
 
2366
        if name is None:
 
2367
            raise ValueError('name must be supplied')
 
2368
        self.bzrdir = a_bzrdir
 
2369
        self._user_transport = self.bzrdir.transport.clone('..')
 
2370
        if name != "":
 
2371
            self._user_transport.set_segment_parameter(
 
2372
                "branch", urlutils.escape(name))
 
2373
        self._base = self._user_transport.base
2434
2374
        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
2375
        self._format = _format
2439
2376
        if _control_files is None:
2440
2377
            raise ValueError('BzrBranch _control_files is None')
2441
2378
        self.control_files = _control_files
2442
2379
        self._transport = _control_files._transport
2443
2380
        self.repository = _repository
2444
 
        Branch.__init__(self)
 
2381
        self.conf_store = None
 
2382
        Branch.__init__(self, possible_transports)
2445
2383
 
2446
2384
    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)
 
2385
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
2452
2386
 
2453
2387
    __repr__ = __str__
2454
2388
 
2458
2392
 
2459
2393
    base = property(_get_base, doc="The URL for the root of this branch.")
2460
2394
 
 
2395
    @property
 
2396
    def user_transport(self):
 
2397
        return self._user_transport
 
2398
 
2461
2399
    def _get_config(self):
2462
2400
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
2463
2401
 
 
2402
    def _get_config_store(self):
 
2403
        if self.conf_store is None:
 
2404
            self.conf_store =  _mod_config.BranchStore(self)
 
2405
        return self.conf_store
 
2406
 
 
2407
    def _uncommitted_branch(self):
 
2408
        """Return the branch that may contain uncommitted changes."""
 
2409
        master = self.get_master_branch()
 
2410
        if master is not None:
 
2411
            return master
 
2412
        else:
 
2413
            return self
 
2414
 
 
2415
    def store_uncommitted(self, creator):
 
2416
        """Store uncommitted changes from a ShelfCreator.
 
2417
 
 
2418
        :param creator: The ShelfCreator containing uncommitted changes, or
 
2419
            None to delete any stored changes.
 
2420
        :raises: ChangesAlreadyStored if the branch already has changes.
 
2421
        """
 
2422
        branch = self._uncommitted_branch()
 
2423
        if creator is None:
 
2424
            branch._transport.delete('stored-transform')
 
2425
            return
 
2426
        if branch._transport.has('stored-transform'):
 
2427
            raise errors.ChangesAlreadyStored
 
2428
        transform = StringIO()
 
2429
        creator.write_shelf(transform)
 
2430
        transform.seek(0)
 
2431
        branch._transport.put_file('stored-transform', transform)
 
2432
 
 
2433
    def get_unshelver(self, tree):
 
2434
        """Return a shelf.Unshelver for this branch and tree.
 
2435
 
 
2436
        :param tree: The tree to use to construct the Unshelver.
 
2437
        :return: an Unshelver or None if no changes are stored.
 
2438
        """
 
2439
        branch = self._uncommitted_branch()
 
2440
        try:
 
2441
            transform = branch._transport.get('stored-transform')
 
2442
        except errors.NoSuchFile:
 
2443
            return None
 
2444
        return shelf.Unshelver.from_tree_and_shelf(tree, transform)
 
2445
 
2464
2446
    def is_locked(self):
2465
2447
        return self.control_files.is_locked()
2466
2448
 
2473
2455
        """
2474
2456
        if not self.is_locked():
2475
2457
            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
2458
            self.repository._warn_if_deprecated(self)
2480
2459
            self.repository.lock_write()
2481
2460
            took_lock = True
2496
2475
        """
2497
2476
        if not self.is_locked():
2498
2477
            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
2478
            self.repository._warn_if_deprecated(self)
2503
2479
            self.repository.lock_read()
2504
2480
            took_lock = True
2514
2490
 
2515
2491
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2516
2492
    def unlock(self):
 
2493
        if self.control_files._lock_count == 1 and self.conf_store is not None:
 
2494
            self.conf_store.save_changes()
2517
2495
        try:
2518
2496
            self.control_files.unlock()
2519
2497
        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
2498
            if not self.control_files.is_locked():
 
2499
                self.repository.unlock()
2526
2500
                # we just released the lock
2527
2501
                self._clear_cached_state()
2528
2502
 
2695
2669
        self._transport.put_bytes('last-revision', out_string,
2696
2670
            mode=self.bzrdir._get_file_mode())
2697
2671
 
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.
 
2672
    @needs_write_lock
 
2673
    def update_feature_flags(self, updated_flags):
 
2674
        """Update the feature flags for this branch.
 
2675
 
 
2676
        :param updated_flags: Dictionary mapping feature names to necessities
 
2677
            A necessity can be None to indicate the feature should be removed
2795
2678
        """
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
 
    """
 
2679
        self._format._update_feature_flags(updated_flags)
 
2680
        self.control_transport.put_bytes('format', self._format.as_string())
2805
2681
 
2806
2682
 
2807
2683
class BzrBranch8(BzrBranch):
2808
2684
    """A branch that stores tree-reference locations."""
2809
2685
 
2810
 
    def _open_hook(self):
 
2686
    def _open_hook(self, possible_transports=None):
2811
2687
        if self._ignore_fallbacks:
2812
2688
            return
 
2689
        if possible_transports is None:
 
2690
            possible_transports = [self.bzrdir.root_transport]
2813
2691
        try:
2814
2692
            url = self.get_stacked_on_url()
2815
2693
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2823
2701
                    raise AssertionError(
2824
2702
                        "'transform_fallback_location' hook %s returned "
2825
2703
                        "None, not a URL." % hook_name)
2826
 
            self._activate_fallback_location(url)
 
2704
            self._activate_fallback_location(url,
 
2705
                possible_transports=possible_transports)
2827
2706
 
2828
2707
    def __init__(self, *args, **kwargs):
2829
2708
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2947
2826
        """See Branch.set_push_location."""
2948
2827
        self._master_branch_cache = None
2949
2828
        result = None
2950
 
        config = self.get_config()
 
2829
        conf = self.get_config_stack()
2951
2830
        if location is None:
2952
 
            if config.get_user_option('bound') != 'True':
 
2831
            if not conf.get('bound'):
2953
2832
                return False
2954
2833
            else:
2955
 
                config.set_user_option('bound', 'False', warn_masked=True)
 
2834
                conf.set('bound', 'False')
2956
2835
                return True
2957
2836
        else:
2958
2837
            self._set_config_location('bound_location', location,
2959
 
                                      config=config)
2960
 
            config.set_user_option('bound', 'True', warn_masked=True)
 
2838
                                      config=conf)
 
2839
            conf.set('bound', 'True')
2961
2840
        return True
2962
2841
 
2963
2842
    def _get_bound_location(self, bound):
2964
2843
        """Return the bound location in the config file.
2965
2844
 
2966
2845
        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:
 
2846
        conf = self.get_config_stack()
 
2847
        if conf.get('bound') != bound:
2970
2848
            return None
2971
 
        return self._get_config_location('bound_location', config=config)
 
2849
        return self._get_config_location('bound_location', config=conf)
2972
2850
 
2973
2851
    def get_bound_location(self):
2974
 
        """See Branch.set_push_location."""
 
2852
        """See Branch.get_bound_location."""
2975
2853
        return self._get_bound_location(True)
2976
2854
 
2977
2855
    def get_old_bound_location(self):
2984
2862
        ## self._check_stackable_repo()
2985
2863
        # stacked_on_location is only ever defined in branch.conf, so don't
2986
2864
        # waste effort reading the whole stack of config files.
2987
 
        config = self.get_config()._get_branch_data_config()
 
2865
        conf = _mod_config.BranchOnlyStack(self)
2988
2866
        stacked_url = self._get_config_location('stacked_on_location',
2989
 
            config=config)
 
2867
                                                config=conf)
2990
2868
        if stacked_url is None:
2991
2869
            raise errors.NotStacked(self)
2992
 
        return stacked_url
 
2870
        return stacked_url.encode('utf-8')
2993
2871
 
2994
2872
    @needs_read_lock
2995
2873
    def get_rev_id(self, revno, history=None):
3025
2903
            except errors.RevisionNotPresent, e:
3026
2904
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
3027
2905
            index = len(self._partial_revision_history_cache) - 1
 
2906
            if index < 0:
 
2907
                raise errors.NoSuchRevision(self, revision_id)
3028
2908
            if self._partial_revision_history_cache[index] != revision_id:
3029
2909
                raise errors.NoSuchRevision(self, revision_id)
3030
2910
        return self.revno() - index
3085
2965
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3086
2966
    """
3087
2967
 
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
2968
    def report(self, to_file):
3097
2969
        tag_conflicts = getattr(self, "tag_conflicts", None)
3098
2970
        tag_updates = getattr(self, "tag_updates", None)
3128
3000
        target, otherwise it will be None.
3129
3001
    """
3130
3002
 
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
3003
    def report(self, to_file):
3140
3004
        # TODO: This function gets passed a to_file, but then
3141
3005
        # ignores it and calls note() instead. This is also
3188
3052
 
3189
3053
        # Copy source data into target
3190
3054
        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())
 
3055
        new_branch.lock_write()
 
3056
        try:
 
3057
            new_branch.set_parent(branch.get_parent())
 
3058
            new_branch.set_bound_location(branch.get_bound_location())
 
3059
            new_branch.set_push_location(branch.get_push_location())
 
3060
        finally:
 
3061
            new_branch.unlock()
3194
3062
 
3195
3063
        # New branch has no tags by default
3196
3064
        new_branch.tags._set_tag_dict({})
3197
3065
 
3198
3066
        # Copying done; now update target format
3199
3067
        new_branch._transport.put_bytes('format',
3200
 
            format.get_format_string(),
 
3068
            format.as_string(),
3201
3069
            mode=new_branch.bzrdir._get_file_mode())
3202
3070
 
3203
3071
        # Clean up old files
3204
3072
        new_branch._transport.delete('revision-history')
 
3073
        branch.lock_write()
3205
3074
        try:
3206
 
            branch.set_parent(None)
3207
 
        except errors.NoSuchFile:
3208
 
            pass
3209
 
        branch.set_bound_location(None)
 
3075
            try:
 
3076
                branch.set_parent(None)
 
3077
            except errors.NoSuchFile:
 
3078
                pass
 
3079
            branch.set_bound_location(None)
 
3080
        finally:
 
3081
            branch.unlock()
3210
3082
 
3211
3083
 
3212
3084
class Converter6to7(object):
3216
3088
        format = BzrBranchFormat7()
3217
3089
        branch._set_config_location('stacked_on_location', '')
3218
3090
        # update target format
3219
 
        branch._transport.put_bytes('format', format.get_format_string())
 
3091
        branch._transport.put_bytes('format', format.as_string())
3220
3092
 
3221
3093
 
3222
3094
class Converter7to8(object):
3226
3098
        format = BzrBranchFormat8()
3227
3099
        branch._transport.put_bytes('references', '')
3228
3100
        # update target format
3229
 
        branch._transport.put_bytes('format', format.get_format_string())
 
3101
        branch._transport.put_bytes('format', format.as_string())
3230
3102
 
3231
3103
 
3232
3104
class InterBranch(InterObject):
3289
3161
        raise NotImplementedError(self.fetch)
3290
3162
 
3291
3163
 
 
3164
def _fix_overwrite_type(overwrite):
 
3165
    if isinstance(overwrite, bool):
 
3166
        if overwrite:
 
3167
            return ["history", "tags"]
 
3168
        else:
 
3169
            return []
 
3170
    return overwrite
 
3171
 
 
3172
 
3292
3173
class GenericInterBranch(InterBranch):
3293
3174
    """InterBranch implementation that uses public Branch functions."""
3294
3175
 
3459
3340
        result.target_branch = self.target
3460
3341
        result.old_revno, result.old_revid = self.target.last_revision_info()
3461
3342
        self.source.update_references(self.target)
 
3343
        overwrite = _fix_overwrite_type(overwrite)
3462
3344
        if result.old_revid != stop_revision:
3463
3345
            # We assume that during 'push' this repository is closer than
3464
3346
            # the target.
3465
3347
            graph = self.source.repository.get_graph(self.target.repository)
3466
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3467
 
                    graph=graph)
 
3348
            self._update_revisions(stop_revision,
 
3349
                overwrite=("history" in overwrite),
 
3350
                graph=graph)
3468
3351
        if self.source._push_should_merge_tags():
3469
3352
            result.tag_updates, result.tag_conflicts = (
3470
 
                self.source.tags.merge_to(self.target.tags, overwrite))
 
3353
                self.source.tags.merge_to(
 
3354
                self.target.tags, "tags" in overwrite))
3471
3355
        result.new_revno, result.new_revid = self.target.last_revision_info()
3472
3356
        return result
3473
3357
 
3551
3435
            # -- JRV20090506
3552
3436
            result.old_revno, result.old_revid = \
3553
3437
                self.target.last_revision_info()
3554
 
            self._update_revisions(stop_revision, overwrite=overwrite,
 
3438
            overwrite = _fix_overwrite_type(overwrite)
 
3439
            self._update_revisions(stop_revision,
 
3440
                overwrite=("history" in overwrite),
3555
3441
                graph=graph)
3556
3442
            # TODO: The old revid should be specified when merging tags, 
3557
3443
            # so a tags implementation that versions tags can only 
3558
3444
            # pull in the most recent changes. -- JRV20090506
3559
3445
            result.tag_updates, result.tag_conflicts = (
3560
 
                self.source.tags.merge_to(self.target.tags, overwrite,
 
3446
                self.source.tags.merge_to(self.target.tags,
 
3447
                    "tags" in overwrite,
3561
3448
                    ignore_master=not merge_tags_to_master))
3562
3449
            result.new_revno, result.new_revid = self.target.last_revision_info()
3563
3450
            if _hook_master: